Merge branch 'DC11'

This commit is contained in:
folkert van heusden 2024-05-05 00:56:52 +02:00
commit 9c3acb5f63
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
11 changed files with 441 additions and 40 deletions

View file

@ -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
View file

@ -0,0 +1 @@
../dc11.cpp

1
ESP32/dc11.h Symbolic link
View file

@ -0,0 +1 @@
../dc11.h

View file

@ -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
View file

@ -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
View file

@ -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
View 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
View 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()();
};

View file

@ -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
View file

@ -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);