Merge branch 'DC11'
This commit is contained in:
commit
9c3acb5f63
11 changed files with 441 additions and 40 deletions
|
@ -29,6 +29,7 @@ add_executable(
|
|||
console_ncurses.cpp
|
||||
console_posix.cpp
|
||||
cpu.cpp
|
||||
dc11.cpp
|
||||
debugger.cpp
|
||||
disk_backend.cpp
|
||||
disk_backend_file.cpp
|
||||
|
|
1
ESP32/dc11.cpp
Symbolic link
1
ESP32/dc11.cpp
Symbolic link
|
@ -0,0 +1 @@
|
|||
../dc11.cpp
|
1
ESP32/dc11.h
Symbolic link
1
ESP32/dc11.h
Symbolic link
|
@ -0,0 +1 @@
|
|||
../dc11.h
|
|
@ -193,6 +193,15 @@ void start_network(console *const c)
|
|||
|
||||
c->put_string_lf("");
|
||||
c->put_string_lf(format("Local IP address: %s", WiFi.localIP().toString().c_str()));
|
||||
|
||||
static bool dc11_loaded = false;
|
||||
if (!dc11_loaded) {
|
||||
dc11_loaded = true;
|
||||
|
||||
Serial.println(F("* Adding DC11"));
|
||||
dc11 *dc11_ = new dc11(1100, b);
|
||||
b->add_DC11(dc11_);
|
||||
}
|
||||
}
|
||||
|
||||
void recall_configuration(console *const cnsl)
|
||||
|
@ -245,7 +254,6 @@ void setup() {
|
|||
Serial.println(getCpuFrequencyMhz());
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#if defined(BUILD_FOR_RP2040)
|
||||
SPI.setRX(MISO);
|
||||
SPI.setTX(MOSI);
|
||||
|
@ -258,7 +266,6 @@ void setup() {
|
|||
Serial.println(F("Cannot initialize SD card"));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BUILD_FOR_RP2040)
|
||||
LittleFSConfig cfg;
|
||||
|
@ -270,37 +277,40 @@ void setup() {
|
|||
Serial.println(F("LittleFS.begin() failed"));
|
||||
#endif
|
||||
|
||||
Serial.println(F("* Init bus"));
|
||||
b = new bus();
|
||||
|
||||
Serial.println(F("* Allocate memory"));
|
||||
uint32_t n_pages = DEFAULT_N_PAGES;
|
||||
|
||||
#if !defined(BUILD_FOR_RP2040)
|
||||
Serial.print(F("Free RAM after init (decimal bytes): "));
|
||||
Serial.println(ESP.getFreeHeap());
|
||||
|
||||
if (psramInit()) {
|
||||
uint32_t free_psram = ESP.getFreePsram();
|
||||
Serial.printf("Free PSRAM: %d decimal bytes (or %d pages (see 'ramsize' in the debugger))", free_psram, free_psram / 8192l);
|
||||
n_pages = min(free_psram / 8192, uint32_t(128)); // start size is 1 MB max.
|
||||
Serial.printf("Free PSRAM: %d decimal bytes (or %d pages (see 'ramsize' in the debugger))", free_psram, n_pages);
|
||||
Serial.println(F(""));
|
||||
}
|
||||
#endif
|
||||
|
||||
Serial.println(F("Init bus"));
|
||||
b = new bus();
|
||||
Serial.printf("Allocating %d (decimal) pages", n_pages);
|
||||
b->set_memory_size(n_pages);
|
||||
Serial.println(F(""));
|
||||
|
||||
Serial.println(F("Allocate memory"));
|
||||
b->set_memory_size(DEFAULT_N_PAGES);
|
||||
|
||||
Serial.println(F("Init CPU"));
|
||||
Serial.println(F("* Init CPU"));
|
||||
c = new cpu(b, &stop_event);
|
||||
|
||||
Serial.println(F("Connect CPU to BUS"));
|
||||
b->add_cpu(c);
|
||||
|
||||
#if !defined(BUILD_FOR_RP2040) && defined(CONSOLE_SERIAL_RX)
|
||||
constexpr uint32_t hwSerialConfig = SERIAL_8N1;
|
||||
uint32_t bitrate = load_serial_speed_configuration();
|
||||
|
||||
Serial.print(F("Init console, baudrate: "));
|
||||
Serial.print(F("* Init console, baudrate: "));
|
||||
Serial.print(bitrate);
|
||||
Serial.println(F("bps"));
|
||||
|
||||
#if !defined(BUILD_FOR_RP2040) && defined(CONSOLE_SERIAL_RX)
|
||||
Serial_RS232.begin(bitrate, hwSerialConfig, CONSOLE_SERIAL_RX, CONSOLE_SERIAL_TX);
|
||||
Serial_RS232.setHwFlowCtrlMode(0);
|
||||
|
||||
|
@ -320,7 +330,7 @@ void setup() {
|
|||
|
||||
running = cnsl->get_running_flag();
|
||||
|
||||
Serial.println(F("Connect RK05 and RL02 devices to BUS"));
|
||||
Serial.println(F("* Connect RK05 and RL02 devices to BUS"));
|
||||
auto rk05_dev = new rk05(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
|
||||
rk05_dev->begin();
|
||||
b->add_rk05(rk05_dev);
|
||||
|
@ -329,12 +339,11 @@ void setup() {
|
|||
rl02_dev->begin();
|
||||
b->add_rl02(rl02_dev);
|
||||
|
||||
Serial.println(F("Init TTY"));
|
||||
Serial.println(F("* Adding TTY"));
|
||||
tty_ = new tty(cnsl, b);
|
||||
Serial.println(F("Connect TTY to bus"));
|
||||
b->add_tty(tty_);
|
||||
|
||||
#if !defined(BUILD_FOR_RP2040) // FIXME: led ring
|
||||
#if !defined(BUILD_FOR_RP2040) && defined(NEOPIXELS_PIN)
|
||||
Serial.println(F("Starting panel"));
|
||||
xTaskCreate(&console_thread_wrapper_panel, "panel", 3072, cnsl, 1, nullptr);
|
||||
#endif
|
||||
|
@ -351,7 +360,7 @@ void setup() {
|
|||
|
||||
Serial.flush();
|
||||
|
||||
Serial.println(F("Starting I/O"));
|
||||
Serial.println(F("* Starting console"));
|
||||
cnsl->start_thread();
|
||||
|
||||
cnsl->put_string_lf("PDP-11/70 emulator, (C) Folkert van Heusden");
|
||||
|
|
42
bus.cpp
42
bus.cpp
|
@ -8,6 +8,7 @@
|
|||
#include "bus.h"
|
||||
#include "gen.h"
|
||||
#include "cpu.h"
|
||||
#include "dc11.h"
|
||||
#include "kw11-l.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
|
@ -40,6 +41,7 @@ bus::~bus()
|
|||
delete tty_;
|
||||
delete mmu_;
|
||||
delete m;
|
||||
delete dc11_;
|
||||
}
|
||||
|
||||
#if IS_POSIX
|
||||
|
@ -68,7 +70,7 @@ json_t *bus::serialize() const
|
|||
if (rk05_)
|
||||
json_object_set(j_out, "rk05", rk05_->serialize());
|
||||
|
||||
// TODO: tm11
|
||||
// TODO: tm11, dc11
|
||||
|
||||
return j_out;
|
||||
}
|
||||
|
@ -122,7 +124,7 @@ bus *bus::deserialize(const json_t *const j, console *const cnsl, std::atomic_ui
|
|||
b->add_rk05(rk05_);
|
||||
}
|
||||
|
||||
// TODO: tm11
|
||||
// TODO: tm11, dc11
|
||||
|
||||
return b;
|
||||
}
|
||||
|
@ -158,6 +160,8 @@ void bus::reset()
|
|||
tty_->reset();
|
||||
if (kw11_l_)
|
||||
kw11_l_->reset();
|
||||
if (dc11_)
|
||||
dc11_->reset();
|
||||
}
|
||||
|
||||
void bus::add_KW11_L(kw11_l *const kw11_l_)
|
||||
|
@ -210,6 +214,12 @@ void bus::add_tty(tty *const tty_)
|
|||
this->tty_ = tty_;
|
||||
}
|
||||
|
||||
void bus::add_DC11(dc11 *const dc11_)
|
||||
{
|
||||
delete this->dc11_;
|
||||
this->dc11_ = dc11_;
|
||||
}
|
||||
|
||||
void bus::init()
|
||||
{
|
||||
mmu_->setMMR0(0);
|
||||
|
@ -449,29 +459,20 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm
|
|||
}
|
||||
}
|
||||
|
||||
if (tm11 && a >= TM_11_BASE && a < TM_11_END && !peek_only) {
|
||||
TRACE("READ-I/O TM11 register %d", (a - TM_11_BASE) / 2);
|
||||
|
||||
if (tm11 && a >= TM_11_BASE && a < TM_11_END && !peek_only)
|
||||
return word_mode == wm_byte ? tm11->read_byte(a) : tm11->read_word(a);
|
||||
}
|
||||
|
||||
if (rk05_ && a >= RK05_BASE && a < RK05_END && !peek_only) {
|
||||
TRACE("READ-I/O RK05 register %d", (a - RK05_BASE) / 2);
|
||||
|
||||
if (rk05_ && a >= RK05_BASE && a < RK05_END && !peek_only)
|
||||
return word_mode == wm_byte ? rk05_->read_byte(a) : rk05_->read_word(a);
|
||||
}
|
||||
|
||||
if (rl02_ && a >= RL02_BASE && a < RL02_END && !peek_only) {
|
||||
TRACE("READ-I/O RL02 register %d", (a - RL02_BASE) / 2);
|
||||
|
||||
if (rl02_ && a >= RL02_BASE && a < RL02_END && !peek_only)
|
||||
return word_mode == wm_byte ? rl02_->read_byte(a) : rl02_->read_word(a);
|
||||
}
|
||||
|
||||
if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END && !peek_only) {
|
||||
TRACE("READ-I/O TTY register %d", (a - PDP11TTY_BASE) / 2);
|
||||
|
||||
if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END && !peek_only)
|
||||
return word_mode == wm_byte ? tty_->read_byte(a) : tty_->read_word(a);
|
||||
}
|
||||
|
||||
if (dc11_ && a >= DC11_BASE && a < DC11_END && !peek_only)
|
||||
return word_mode == wm_byte ? dc11_->read_byte(a) : dc11_->read_word(a);
|
||||
|
||||
// LO size register field must be all 1s, so subtract 1
|
||||
uint32_t system_size = m->get_memory_size() / 64 - 1;
|
||||
|
@ -724,6 +725,11 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1
|
|||
return { false };
|
||||
}
|
||||
|
||||
if (dc11_ && a >= DC11_BASE && a < DC11_END) {
|
||||
word_mode == wm_byte ? dc11_->write_byte(a, value) : dc11_->write_word(a, value);
|
||||
return { false };
|
||||
}
|
||||
|
||||
if (a >= 0172100 && a <= 0172137) { // MM11-LP parity
|
||||
TRACE("WRITE-I/O MM11-LP parity (%06o): %o", a, value);
|
||||
return { false };
|
||||
|
|
4
bus.h
4
bus.h
|
@ -9,6 +9,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "gen.h"
|
||||
#include "dc11.h"
|
||||
#include "mmu.h"
|
||||
#include "rk05.h"
|
||||
#include "rl02.h"
|
||||
|
@ -67,6 +68,7 @@ private:
|
|||
kw11_l *kw11_l_ { nullptr };
|
||||
mmu *mmu_ { nullptr };
|
||||
memory *m { nullptr };
|
||||
dc11 *dc11_ { nullptr };
|
||||
|
||||
uint16_t microprogram_break_register { 0 };
|
||||
|
||||
|
@ -101,6 +103,7 @@ public:
|
|||
void add_rl02 (rl02 *const rl02_ );
|
||||
void add_tty (tty *const tty_ );
|
||||
void add_KW11_L(kw11_l *const kw11_l_);
|
||||
void add_DC11 (dc11 *const dc11_ );
|
||||
|
||||
memory *getRAM() { return m; }
|
||||
cpu *getCpu() { return c; }
|
||||
|
@ -109,6 +112,7 @@ public:
|
|||
mmu *getMMU() { return mmu_; }
|
||||
rk05 *getRK05() { return rk05_; }
|
||||
rl02 *getRL02() { return rl02_; }
|
||||
dc11 *getDC11() { return dc11_; }
|
||||
tm_11 *getTM11() { return tm11; }
|
||||
|
||||
uint16_t read(const uint16_t a, const word_mode_t word_mode, const rm_selection_t mode_selection, const bool peek_only=false, const d_i_space_t s = i_space);
|
||||
|
|
312
dc11.cpp
Normal file
312
dc11.cpp
Normal file
|
@ -0,0 +1,312 @@
|
|||
// (C) 2024 by Folkert van Heusden
|
||||
// Released under MIT license
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <lwip/sockets.h>
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "bus.h"
|
||||
#include "cpu.h"
|
||||
#include "dc11.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
const char *const dc11_register_names[] { "RCSR", "RBUF", "TSCR", "TBUF" };
|
||||
|
||||
dc11::dc11(const int base_port, bus *const b):
|
||||
base_port(base_port),
|
||||
b(b)
|
||||
{
|
||||
pfds = new pollfd[dc11_n_lines * 2]();
|
||||
|
||||
// TODO move to begin()
|
||||
th = new std::thread(std::ref(*this));
|
||||
}
|
||||
|
||||
dc11::~dc11()
|
||||
{
|
||||
stop_flag = true;
|
||||
|
||||
if (th) {
|
||||
th->join();
|
||||
delete th;
|
||||
}
|
||||
|
||||
delete [] pfds;
|
||||
}
|
||||
|
||||
void dc11::trigger_interrupt(const int line_nr, const bool is_tx)
|
||||
{
|
||||
b->getCpu()->queue_interrupt(5, 0300 + line_nr * 010 + 4 * is_tx);
|
||||
}
|
||||
|
||||
void dc11::operator()()
|
||||
{
|
||||
set_thread_name("kek:DC11");
|
||||
|
||||
DOLOG(info, true, "DC11 thread started");
|
||||
|
||||
for(int i=0; i<dc11_n_lines; i++) {
|
||||
// client session
|
||||
pfds[dc11_n_lines + i].fd = -1;
|
||||
pfds[dc11_n_lines + i].events = POLLIN;
|
||||
|
||||
// listen on port
|
||||
int port = base_port + i + 1;
|
||||
|
||||
pfds[i].fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
int reuse_addr = 1;
|
||||
if (setsockopt(pfds[i].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse_addr, sizeof(reuse_addr)) == -1) {
|
||||
close(pfds[i].fd);
|
||||
pfds[i].fd = -1;
|
||||
|
||||
DOLOG(warning, true, "Cannot set reuseaddress for port %d (DC11)", port);
|
||||
continue;
|
||||
}
|
||||
|
||||
set_nodelay(pfds[i].fd);
|
||||
|
||||
sockaddr_in listen_addr;
|
||||
memset(&listen_addr, 0, sizeof(listen_addr));
|
||||
listen_addr.sin_family = AF_INET;
|
||||
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
listen_addr.sin_port = htons(port);
|
||||
|
||||
if (bind(pfds[i].fd, reinterpret_cast<struct sockaddr *>(&listen_addr), sizeof(listen_addr)) == -1) {
|
||||
close(pfds[i].fd);
|
||||
pfds[i].fd = -1;
|
||||
|
||||
DOLOG(warning, true, "Cannot bind to port %d (DC11)", port);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listen(pfds[i].fd, SOMAXCONN) == -1) {
|
||||
close(pfds[i].fd);
|
||||
pfds[i].fd = -1;
|
||||
|
||||
DOLOG(warning, true, "Cannot listen on port %d (DC11)", port);
|
||||
continue;
|
||||
}
|
||||
|
||||
pfds[i].events = POLLIN;
|
||||
}
|
||||
|
||||
while(!stop_flag) {
|
||||
int rc = poll(pfds, dc11_n_lines * 2, 100);
|
||||
if (rc == 0)
|
||||
continue;
|
||||
|
||||
// accept any new session
|
||||
for(int i=0; i<dc11_n_lines; i++) {
|
||||
if (pfds[i].revents != POLLIN)
|
||||
continue;
|
||||
|
||||
int client_i = dc11_n_lines + i;
|
||||
|
||||
// disconnect any existing client session
|
||||
// yes, one can ddos with this
|
||||
if (pfds[client_i].fd != -1) {
|
||||
close(pfds[client_i].fd);
|
||||
DOLOG(info, false, "Restarting session for port %d", base_port + i + 1);
|
||||
}
|
||||
|
||||
pfds[client_i].fd = accept(pfds[i].fd, nullptr, nullptr);
|
||||
|
||||
if (pfds[client_i].fd != -1) {
|
||||
set_nodelay(pfds[client_i].fd);
|
||||
|
||||
std::unique_lock<std::mutex> lck(input_lock[i]);
|
||||
|
||||
registers[i * 4 + 0] |= 0160000; // "ERROR", RING INDICATOR, CARRIER TRANSITION
|
||||
if (is_rx_interrupt_enabled(i))
|
||||
trigger_interrupt(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
// receive data
|
||||
for(int i=dc11_n_lines; i<dc11_n_lines * 2; i++) {
|
||||
if (pfds[i].revents != POLLIN)
|
||||
continue;
|
||||
|
||||
char buffer[32] { };
|
||||
int rc = read(pfds[i].fd, buffer, sizeof buffer);
|
||||
|
||||
int line_nr = i - dc11_n_lines;
|
||||
|
||||
std::unique_lock<std::mutex> lck(input_lock[line_nr]);
|
||||
|
||||
if (rc <= 0) { // closed or error?
|
||||
DOLOG(info, false, "Failed reading from port %d", i - dc11_n_lines + 1);
|
||||
|
||||
registers[line_nr * 4 + 0] |= 0140000; // "ERROR", CARRIER TRANSITION
|
||||
|
||||
close(pfds[i].fd);
|
||||
pfds[i].fd = -1;
|
||||
}
|
||||
else {
|
||||
for(int k=0; k<rc; k++)
|
||||
recv_buffers[line_nr].push_back(buffer[k]);
|
||||
|
||||
registers[line_nr * 4 + 0] |= 128; // DONE: bit 7
|
||||
}
|
||||
|
||||
if (is_rx_interrupt_enabled(line_nr))
|
||||
trigger_interrupt(line_nr, false);
|
||||
}
|
||||
}
|
||||
|
||||
DOLOG(info, true, "DC11 thread terminating");
|
||||
|
||||
for(int i=0; i<dc11_n_lines * 2; i++) {
|
||||
if (pfds[i].fd != -1)
|
||||
close(pfds[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
void dc11::reset()
|
||||
{
|
||||
}
|
||||
|
||||
bool dc11::is_rx_interrupt_enabled(const int line_nr)
|
||||
{
|
||||
return !!(registers[line_nr * 4 + 0] & 64);
|
||||
}
|
||||
|
||||
bool dc11::is_tx_interrupt_enabled(const int line_nr)
|
||||
{
|
||||
return !!(registers[line_nr * 4 + 2] & 64);
|
||||
}
|
||||
|
||||
uint8_t dc11::read_byte(const uint16_t addr)
|
||||
{
|
||||
uint16_t v = read_word(addr & ~1);
|
||||
|
||||
if (addr & 1)
|
||||
return v >> 8;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
uint16_t dc11::read_word(const uint16_t addr)
|
||||
{
|
||||
int reg = (addr - DC11_BASE) / 2;
|
||||
int line_nr = reg / 4;
|
||||
int sub_reg = reg & 3;
|
||||
|
||||
std::unique_lock<std::mutex> lck(input_lock[line_nr]);
|
||||
|
||||
uint16_t vtemp = registers[reg];
|
||||
|
||||
if (sub_reg == 0) { // receive status
|
||||
// emulate DTR, CTS & READY
|
||||
registers[line_nr * 4 + 0] &= ~1; // DTR: bit 0 [RCSR]
|
||||
registers[line_nr * 4 + 0] &= ~4; // CD : bit 2
|
||||
|
||||
if (pfds[line_nr + dc11_n_lines].fd != -1) {
|
||||
registers[line_nr * 4 + 0] |= 1;
|
||||
registers[line_nr * 4 + 0] |= 4;
|
||||
}
|
||||
|
||||
vtemp = registers[line_nr * 4 + 0];
|
||||
|
||||
// clear error(s)
|
||||
registers[line_nr * 4 + 0] &= ~0160000;
|
||||
}
|
||||
else if (sub_reg == 1) { // read data register
|
||||
TRACE("DC11: %zu characters in buffer for line %d", recv_buffers[line_nr].size(), line_nr);
|
||||
|
||||
// get oldest byte in buffer
|
||||
if (recv_buffers[line_nr].empty() == false) {
|
||||
vtemp = *recv_buffers[line_nr].begin();
|
||||
|
||||
// parity check
|
||||
registers[line_nr * 4 + 0] &= ~(1 << 5);
|
||||
registers[line_nr * 4 + 0] |= parity(vtemp) << 5;
|
||||
|
||||
recv_buffers[line_nr].erase(recv_buffers[line_nr].begin());
|
||||
|
||||
// still data in buffer? generate interrupt
|
||||
if (recv_buffers[line_nr].empty() == false) {
|
||||
registers[line_nr * 4 + 0] |= 128; // DONE: bit 7
|
||||
|
||||
if (is_rx_interrupt_enabled(line_nr))
|
||||
trigger_interrupt(line_nr, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sub_reg == 2) { // transmit status
|
||||
registers[line_nr * 4 + 2] &= ~2; // CTS: bit 1 [TSCR]
|
||||
registers[line_nr * 4 + 2] &= ~128; // READY: bit 7
|
||||
|
||||
if (pfds[line_nr + dc11_n_lines].fd != -1) {
|
||||
registers[line_nr * 4 + 2] |= 2;
|
||||
registers[line_nr * 4 + 2] |= 128;
|
||||
}
|
||||
|
||||
vtemp = registers[line_nr * 4 + 2];
|
||||
}
|
||||
|
||||
TRACE("DC11: read register %06o (\"%s\", %d line %d): %06o", addr, dc11_register_names[sub_reg], sub_reg, line_nr, vtemp);
|
||||
|
||||
return vtemp;
|
||||
}
|
||||
|
||||
void dc11::write_byte(const uint16_t addr, const uint8_t v)
|
||||
{
|
||||
uint16_t vtemp = registers[(addr - DC11_BASE) / 2];
|
||||
|
||||
if (addr & 1) {
|
||||
vtemp &= ~0xff00;
|
||||
vtemp |= v << 8;
|
||||
}
|
||||
else {
|
||||
vtemp &= ~0x00ff;
|
||||
vtemp |= v;
|
||||
}
|
||||
|
||||
write_word(addr, vtemp);
|
||||
}
|
||||
|
||||
void dc11::write_word(const uint16_t addr, uint16_t v)
|
||||
{
|
||||
int reg = (addr - DC11_BASE) / 2;
|
||||
int line_nr = reg / 4;
|
||||
int sub_reg = reg & 3;
|
||||
|
||||
std::unique_lock<std::mutex> lck(input_lock[line_nr]);
|
||||
|
||||
TRACE("DC11: write register %06o (\"%s\", %d line_nr %d) to %06o", addr, dc11_register_names[sub_reg], sub_reg, line_nr, v);
|
||||
|
||||
if (sub_reg == 3) { // transmit buffer
|
||||
char c = v & 127; // strip parity
|
||||
|
||||
if (c <= 32 || c >= 127)
|
||||
TRACE("DC11: transmit [%d] on line %d", c, line_nr);
|
||||
else
|
||||
TRACE("DC11: transmit %c on line %d", c, line_nr);
|
||||
|
||||
int fd = pfds[dc11_n_lines + line_nr].fd;
|
||||
|
||||
if (fd != -1 && write(fd, &c, 1) != 1) {
|
||||
DOLOG(info, false, "DC11 line %d disconnected\n", line_nr + 1);
|
||||
|
||||
registers[line_nr * 4 + 0] |= 0140000; // "ERROR", CARRIER TRANSITION
|
||||
|
||||
close(fd);
|
||||
pfds[dc11_n_lines + line_nr].fd = -1;
|
||||
}
|
||||
|
||||
if (is_tx_interrupt_enabled(line_nr))
|
||||
trigger_interrupt(line_nr, true);
|
||||
}
|
||||
|
||||
registers[reg] = v;
|
||||
}
|
62
dc11.h
Normal file
62
dc11.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
// (C) 2024 by Folkert van Heusden
|
||||
// Released under MIT license
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "gen.h"
|
||||
#include "bus.h"
|
||||
#include "log.h"
|
||||
|
||||
#define DC11_RCSR 0174000 // receiver status register
|
||||
#define DC11_BASE DC11_RCSR
|
||||
#define DC11_END (DC11_BASE + (4 * 4 + 1) * 2) // 4 interfaces, + 2 to point after it
|
||||
|
||||
class bus;
|
||||
struct pollfd;
|
||||
|
||||
// 4 interfaces
|
||||
constexpr const int dc11_n_lines = 4;
|
||||
|
||||
class dc11
|
||||
{
|
||||
private:
|
||||
int base_port { 1100 };
|
||||
bus *const b { nullptr };
|
||||
uint16_t registers[4 * dc11_n_lines] { 0 };
|
||||
std::atomic_bool stop_flag { false };
|
||||
std::thread *th { nullptr };
|
||||
|
||||
// not statically allocated because of compiling problems on arduino
|
||||
pollfd *pfds { nullptr };
|
||||
std::vector<char> recv_buffers[dc11_n_lines];
|
||||
std::mutex input_lock[dc11_n_lines];
|
||||
|
||||
void trigger_interrupt(const int line_nr, const bool is_tx);
|
||||
bool is_rx_interrupt_enabled(const int line_nr);
|
||||
bool is_tx_interrupt_enabled(const int line_nr);
|
||||
|
||||
public:
|
||||
dc11(const int base_port, bus *const b);
|
||||
virtual ~dc11();
|
||||
|
||||
#if IS_POSIX
|
||||
// json_t *serialize();
|
||||
// static tty *deserialize(const json_t *const j, bus *const b, console *const cnsl);
|
||||
#endif
|
||||
|
||||
void reset();
|
||||
|
||||
uint8_t read_byte(const uint16_t addr);
|
||||
uint16_t read_word(const uint16_t addr);
|
||||
|
||||
void write_byte(const uint16_t addr, const uint8_t v);
|
||||
void write_word(const uint16_t addr, uint16_t v);
|
||||
|
||||
void operator()();
|
||||
};
|
5
main.cpp
5
main.cpp
|
@ -21,6 +21,7 @@
|
|||
#include "disk_backend.h"
|
||||
#include "disk_backend_file.h"
|
||||
#include "disk_backend_nbd.h"
|
||||
#include "dc11.h"
|
||||
#include "gen.h"
|
||||
#include "kw11-l.h"
|
||||
#include "loaders.h"
|
||||
|
@ -580,6 +581,10 @@ int main(int argc, char *argv[])
|
|||
cnsl->set_bus(b);
|
||||
cnsl->begin();
|
||||
|
||||
// TODO
|
||||
dc11 *dc11_ = new dc11(1100, b);
|
||||
b->add_DC11(dc11_);
|
||||
|
||||
tm_11 *tm_11_ = new tm_11(b);
|
||||
b->add_tm11(tm_11_);
|
||||
|
||||
|
|
2
mmu.h
2
mmu.h
|
@ -87,7 +87,7 @@ public:
|
|||
int get_pdr_direction (const int run_mode, const bool d, const int apf) { return pages[run_mode][d][apf].pdr & 8; }
|
||||
uint32_t get_physical_memory_offset(const int run_mode, const bool d, const int apf) const { return pages[run_mode][d][apf].par * 64; }
|
||||
bool get_use_data_space(const int run_mode) const;
|
||||
uint32_t get_io_base() const { return getMMR0() & 1 ? (getMMR3() & 16 ? 017760000 : 0760000) : 0160000; }
|
||||
uint32_t get_io_base() const { return is_enabled() ? (getMMR3() & 16 ? 017760000 : 0760000) : 0160000; }
|
||||
|
||||
memory_addresses_t calculate_physical_address(const int run_mode, const uint16_t a) const;
|
||||
std::pair<trap_action_t, int> get_trap_action(const int run_mode, const bool d, const int apf, const bool is_write);
|
||||
|
|
Loading…
Add table
Reference in a new issue