Merge branch 'master' into MMR1

This commit is contained in:
folkert van heusden 2023-03-22 13:16:54 +01:00
commit a00226e118
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
27 changed files with 897 additions and 271 deletions

View file

@ -14,6 +14,9 @@ add_executable(
console_posix.cpp
cpu.cpp
debugger.cpp
disk_backend.cpp
disk_backend_file.cpp
disk_backend_nbd.cpp
error.cpp
kw11-l.cpp
loaders.cpp

1
ESP32/disk_backend.cpp Symbolic link
View file

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

1
ESP32/disk_backend.h Symbolic link
View file

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

View file

@ -0,0 +1,93 @@
#include <fcntl.h>
#include <unistd.h>
#include "disk_backend_esp32.h"
#include "error.h"
#include "log.h"
disk_backend_esp32::disk_backend_esp32(const std::string & filename) :
filename(filename),
fh(new File32())
{
}
disk_backend_esp32::~disk_backend_esp32()
{
fh->close();
delete fh;
}
bool disk_backend_esp32::begin()
{
if (!fh->open(filename.c_str(), O_RDWR)) {
DOLOG(ll_error, true, "rk05: cannot open \"%s\"", filename.c_str());
return false;
}
return true;
}
bool disk_backend_esp32::read(const off_t offset, const size_t n, uint8_t *const target)
{
DOLOG(debug, false, "disk_backend_esp32::read: read %zu bytes from offset %zu", n, offset);
#if defined(ESP32)
digitalWrite(LED_BUILTIN, LOW);
#endif
if (!fh->seek(offset))
DOLOG(ll_error, true, "seek error %s", strerror(errno));
yield();
if (fh->read(target, n) != size_t(n)) {
DOLOG(debug, true, "fread error: %s", strerror(errno));
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
return false;
}
yield();
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
return true;
}
bool disk_backend_esp32::write(const off_t offset, const size_t n, const uint8_t *const from)
{
DOLOG(debug, false, "disk_backend_esp32::write: write %zu bytes to offset %zu", n, offset);
#if defined(ESP32)
digitalWrite(LED_BUILTIN, LOW);
#endif
if (!fh->seek(offset))
DOLOG(ll_error, true, "seek error %s", strerror(errno));
yield();
if (fh->write(from, n) != n) {
DOLOG(ll_error, true, "RK05 fwrite error %s", strerror(errno));
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
return false;
}
yield();
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
return true;
}

View file

@ -0,0 +1,22 @@
#include <string>
#include "disk_backend.h"
#include "esp32.h"
class disk_backend_esp32 : public disk_backend
{
private:
const std::string filename;
File32 *const fh { nullptr };
public:
disk_backend_esp32(const std::string & filename);
virtual ~disk_backend_esp32();
bool begin() override;
bool read(const off_t offset, const size_t n, uint8_t *const target) override;
bool write(const off_t offset, const size_t n, const uint8_t *const from) override;
};

1
ESP32/disk_backend_nbd.cpp Symbolic link
View file

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

1
ESP32/disk_backend_nbd.h Symbolic link
View file

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

View file

@ -1,7 +1,10 @@
#pragma once
#if defined(ESP32)
#include <Arduino.h>
#include <SPI.h>
#define USE_SDFAT
#define SD_FAT_TYPE 1
#include <SdFat.h>

View file

@ -1,4 +1,4 @@
// (C) 2018-2022 by Folkert van Heusden
// (C) 2018-2023 by Folkert van Heusden
// Released under Apache License v2.0
#include <atomic>
#include <stdio.h>
@ -12,6 +12,9 @@
#include "console_esp32.h"
#include "cpu.h"
#include "debugger.h"
#include "disk_backend.h"
#include "disk_backend_esp32.h"
#include "disk_backend_nbd.h"
#include "error.h"
#include "esp32.h"
#include "gen.h"
@ -53,58 +56,87 @@ void console_thread_wrapper_io(void *const c)
cnsl->operator()();
}
void setup_wifi_stations()
typedef enum { BE_NETWORK, BE_SD } disk_backend_t;
std::optional<disk_backend_t> select_disk_backend(console *const c)
{
#if 0
WiFi.mode(WIFI_STA);
c->put_string("1. network (NBD), 2. local SD card, 9. abort");
WiFi.softAP("PDP-11 KEK", nullptr, 5, 0, 4);
int ch = -1;
while(ch == -1 && ch != '1' && ch != '2' && ch != '9')
ch = c->wait_char(500);
#if 0
Serial.println(F("Scanning for WiFi access points..."));
c->put_string_lf(format("%c", ch));
int n = WiFi.scanNetworks();
if (ch == '9')
return { };
Serial.println(F("scan done"));
if (ch == '1')
return BE_NETWORK;
if (n == 0)
Serial.println(F("no networks found"));
else {
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(F(": "));
Serial.print(WiFi.SSID(i));
Serial.print(F(" ("));
Serial.print(WiFi.RSSI(i));
Serial.print(F(")"));
Serial.println(WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? " " : "*");
delay(10);
}
// if (ch == '2')
return BE_SD;
}
typedef enum { DT_RK05, DT_RL02 } disk_type_t;
std::optional<disk_type_t> select_disk_type(console *const c)
{
c->put_string("1. RK05, 2. RL02, 9. abort");
int ch = -1;
while(ch == -1 && ch != '1' && ch != '2' && ch != '9')
ch = c->wait_char(500);
c->put_string_lf(format("%c", ch));
if (ch == '9')
return { };
if (ch == '1')
return DT_RK05;
// if (ch == '2')
return DT_RL02;
}
std::optional<std::pair<std::vector<disk_backend *>, std::vector<disk_backend *> > > select_nbd_server(console *const c)
{
c->flush_input();
std::string hostname = c->read_line("Enter hostname (or empty to abort): ");
if (hostname.empty())
return { };
std::string port_str = c->read_line("Enter port number (or empty to abort): ");
if (port_str.empty())
return { };
auto disk_type = select_disk_type(c);
if (disk_type.has_value() == false)
return { };
disk_backend *d = new disk_backend_nbd(hostname, atoi(port_str.c_str()));
if (d->begin() == false) {
c->put_string_lf("Cannot initialize NBD client");
delete d;
return { };
}
std::string ssid = read_terminal_line("SSID: ");
std::string password = read_terminal_line("password: ");
WiFi.begin(ssid.c_str(), password.c_str());
#else
WiFi.begin("www.vanheusden.com", "Ditiseentest31415926");
//WiFi.begin("NURDspace-guest", "harkharkhark");
#endif
if (disk_type.value() == DT_RK05)
return { { { d }, { } } };
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
if (disk_type.value() == DT_RL02)
return { { { }, { d } } };
delay(250);
}
on_wifi = true;
Serial.println(WiFi.localIP());
#endif
return { };
}
// RK05, RL02 files
std::pair<std::vector<std::string>, std::vector<std::string> > select_disk_files(console *const c)
std::optional<std::pair<std::vector<disk_backend *>, std::vector<disk_backend *> > > select_disk_files(console *const c)
{
c->debug("MISO: %d", int(MISO));
c->debug("MOSI: %d", int(MOSI));
@ -126,16 +158,10 @@ std::pair<std::vector<std::string>, std::vector<std::string> > select_disk_files
if (selected_file.empty())
continue;
c->put_string("1. RK05, 2. RL02, 3. re-select file");
auto disk_type = select_disk_type(c);
int ch = -1;
while(ch == -1 && ch != '1' && ch != '2' && ch != '3')
ch = c->wait_char(500);
c->put_string_lf(format("%c", ch));
if (ch == '3')
continue;
if (disk_type.has_value() == false)
return { };
c->put_string("Opening file: ");
c->put_string_lf(selected_file.c_str());
@ -145,17 +171,126 @@ std::pair<std::vector<std::string>, std::vector<std::string> > select_disk_files
if (fh.open(selected_file.c_str(), O_RDWR)) {
fh.close();
if (ch == '1')
return { { selected_file }, { } };
disk_backend *temp = new disk_backend_esp32(selected_file);
if (ch == '2')
return { { }, { selected_file } };
if (!temp->begin()) {
c->put_string("Cannot use: ");
c->put_string_lf(selected_file.c_str());
delete temp;
continue;
}
if (disk_type.value() == DT_RK05)
return { { { temp }, { } } };
if (disk_type.value() == DT_RL02)
return { { { }, { temp } } };
}
c->put_string_lf("open failed");
}
}
void configure_disk(console *const c)
{
for(;;) {
Serial.println(F("Load disk"));
auto backend = select_disk_backend(cnsl);
if (backend.has_value() == false)
break;
std::optional<std::pair<std::vector<disk_backend *>, std::vector<disk_backend *> > > files;
if (backend == BE_NETWORK)
files = select_nbd_server(cnsl);
else // if (backend == BE_SD)
files = select_disk_files(cnsl);
if (files.has_value() == false)
break;
if (files.value().first.empty() == false)
b->add_rk05(new rk05(files.value().first, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
if (files.value().second.empty() == false)
b->add_rl02(new rl02(files.value().second, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
// TODO: allow bootloader to be selected
if (files.value().first.empty() == false)
setBootLoader(b, BL_RK05);
else
setBootLoader(b, BL_RL02);
break;
}
}
void set_hostname()
{
WiFi.setHostname("PDP-11");
}
void configure_network(console *const c)
{
WiFi.persistent(true);
WiFi.setAutoReconnect(true);
WiFi.mode(WIFI_STA);
c->put_string_lf("Scanning for wireless networks...");
int n_ssids = WiFi.scanNetworks();
c->put_string_lf("Wireless networks:");
for(int i=0; i<n_ssids; i++)
c->put_string_lf(format("\t%s", WiFi.SSID(i).c_str()));
c->flush_input();
std::string wifi_ap = c->read_line("Enter SSID[|PSK]: ");
auto parts = split(wifi_ap, "|");
if (parts.size() > 2) {
c->put_string_lf("Invalid SSID/PSK: should not contain '|'");
return;
}
set_hostname();
if (parts.size() == 1)
WiFi.begin(parts.at(0).c_str());
else
WiFi.begin(parts.at(0).c_str(), parts.at(1).c_str());
}
void start_network(console *const c)
{
WiFi.mode(WIFI_STA);
set_hostname();
WiFi.begin();
int i = 0;
while (WiFi.waitForConnectResult() != WL_CONNECTED && i < 10 * 3) {
c->put_string(".");
delay(1000 / 3);
i++;
}
c->put_string_lf("");
c->put_string_lf(format("Local IP address: %s", WiFi.localIP().toString().c_str()));
}
void setup() {
Serial.begin(115200);
@ -181,8 +316,6 @@ void setup() {
Serial.println(F("Connect CPU to BUS"));
b->add_cpu(c);
c->setEmulateMFPT(true);
Serial.println(F("Init console"));
cnsl = new console_esp32(&stop_event, b);
@ -205,20 +338,6 @@ void setup() {
// setup_wifi_stations();
Serial.println(F("Load RK05"));
auto disk_files = select_disk_files(cnsl);
if (disk_files.first.empty() == false)
b->add_rk05(new rk05(disk_files.first, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
if (disk_files.second.empty() == false)
b->add_rl02(new rl02(disk_files.second, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
if (disk_files.first.empty() == false)
setBootLoader(b, BL_RK05);
else
setBootLoader(b, BL_RL02);
Serial.print(F("Free RAM after init: "));
Serial.println(ESP.getFreeHeap());

37
bus.cpp
View file

@ -17,7 +17,7 @@
// see also https://github.com/espressif/esp-idf/issues/1934
constexpr int n_pages = 12;
#else
constexpr int n_pages = 128; // 1MB
constexpr int n_pages = 32; // 32=256kB (for EKBEEx.BIC)
#endif
constexpr uint16_t di_ena_mask[4] = { 4, 2, 0, 1 };
@ -309,8 +309,11 @@ uint16_t bus::read(const uint16_t a, const bool word_mode, const bool use_prev,
uint32_t m_offset = calculate_physical_address(run_mode, a, !peek_only, false, peek_only, space == d_space);
if (peek_only == false && m_offset >= n_pages * 8192) {
if (!peek_only) DOLOG(debug, false, "Read non existing mapped memory (%o >= %o)", m_offset, n_pages * 8192);
if (!peek_only) DOLOG(debug, true, "Read non existing mapped memory (%o >= %o)", m_offset, n_pages * 8192);
c->schedule_trap(004); // no such memory
throw 6;
}
if (word_mode)
@ -358,7 +361,8 @@ void bus::clearMMR0Bit(const int bit)
void bus::setMMR2(const uint16_t value)
{
MMR2 = value;
if ((MMR0 & 0xe000) == 0)
MMR2 = value;
}
void bus::check_odd_addressing(const uint16_t a, const int run_mode, const d_i_space_t space, const bool is_write)
@ -424,19 +428,26 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c
bool do_trap = false;
if (access_control == 0)
if (is_write && access_control != 6) // write
do_trap = true;
else if (is_write && access_control != 6) // write
do_trap = true;
else if (!is_write && (access_control == 0 || access_control == 1 || access_control == 3 || access_control == 4 || access_control == 7)) {
else if (!is_write && (access_control == 0 || access_control == 1 || access_control == 3 || access_control == 4 || access_control == 7)) { // read
do_trap = true;
}
if (do_trap) {
DOLOG(debug, true, "TRAP(0250) (throw 1) for access_control %d on address %06o", access_control, a);
bool do_trap_250 = false;
if (MMR0 & (1 << 9))
c->schedule_trap(0250); // invalid address
if ((MMR0 & (1 << 9)) && (MMR0 & 0xf000) == 0) {
DOLOG(debug, true, "TRAP(0250) (throw 1) for access_control %d on address %06o", access_control, a);
do_trap_250 = true;
}
else {
DOLOG(debug, true, "A.C.F. triggger for %d on address %06o", access_control, a);
}
if (access_control == 1 || access_control == 4 || access_control == 5)
MMR0 |= 1 << 12; // set trap-flag
if (is_write)
pages[run_mode][d][apf].pdr |= 1 << 7;
@ -457,7 +468,11 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c
DOLOG(debug, true, "MMR0: %06o", MMR0);
throw 1;
if (do_trap_250) {
c->schedule_trap(0250); // invalid address
throw 1;
}
}
}

28
cpu.cpp
View file

@ -1293,7 +1293,7 @@ bool cpu::single_operand_instructions(const uint16_t instr)
psw &= 0xff00; // only alter lower 8 bits
psw |= getGAM(dst_mode, dst_reg, word_mode, false).value.value() & 0xef; // can't change bit 4
#else
schedule_trap(010);
trap(010);
#endif
}
else {
@ -1324,7 +1324,7 @@ bool cpu::single_operand_instructions(const uint16_t instr)
setPSW_n(extend_b7);
}
#else
schedule_trap(010);
trap(010);
#endif
}
else { // SXT
@ -1438,7 +1438,7 @@ bool cpu::condition_code_operations(const uint16_t instr)
setPSW_spl(level);
// // trap via vector 010 only(?) on an 11/60 and not(?) on an 11/70
// schedule_trap(010);
// trap(010);
return true;
}
@ -1466,7 +1466,7 @@ void cpu::pushStack(const uint16_t v)
if (getRegister(6) == stackLimitRegister) {
DOLOG(debug, true, "stackLimitRegister reached %06o while pushing %06o", stackLimitRegister, v);
schedule_trap(123);
trap(123, 7); // TODO
}
else {
uint16_t a = addRegister(6, false, -2);
@ -1501,11 +1501,11 @@ bool cpu::misc_operations(const uint16_t instr)
return true;
case 0b0000000000000011: // BPT
schedule_trap(014);
trap(014);
return true;
case 0b0000000000000100: // IOT
schedule_trap(020);
trap(020);
return true;
case 0b0000000000000110: // RTT
@ -1515,7 +1515,7 @@ bool cpu::misc_operations(const uint16_t instr)
case 0b0000000000000111: // MFPT
//setRegister(0, 0);
schedule_trap(010); // does not exist on PDP-11/70
trap(010); // does not exist on PDP-11/70
return true;
case 0b0000000000000101: // RESET
@ -1525,12 +1525,12 @@ bool cpu::misc_operations(const uint16_t instr)
}
if ((instr >> 8) == 0b10001000) { // EMT
schedule_trap(030);
trap(030);
return true;
}
if ((instr >> 8) == 0b10001001) { // TRAP
schedule_trap(034);
trap(034);
return true;
}
@ -1583,7 +1583,7 @@ bool cpu::misc_operations(const uint16_t instr)
void cpu::schedule_trap(const uint16_t vector)
{
DOLOG(debug, false, "schedule_trap @ %06o", pc);
DOLOG(debug, true, "schedule_trap @ %06o", pc);
scheduled_trap = vector;
}
@ -2169,9 +2169,15 @@ void cpu::step_b()
uint16_t temp_pc = getPC();
if ((b->getMMR0() & 0160000) == 0)
b->setMMR2(temp_pc);
try {
uint16_t instr = b->readWord(temp_pc);
if (temp_pc == 025250)
DOLOG(debug, true, "GREP %06o %06o", temp_pc, instr);
addRegister(7, false, 2);
if (double_operand_instructions(instr))
@ -2188,7 +2194,7 @@ void cpu::step_b()
DOLOG(warning, true, "UNHANDLED instruction %06o @ %06o", instr, temp_pc);
schedule_trap(010);
trap(010);
}
catch(const int exception) {
DOLOG(debug, true, "bus-trap during execution of command (%d)", exception);

2
cpu.h
View file

@ -132,7 +132,7 @@ public:
void setStackLimitRegister(const uint16_t v) { stackLimitRegister = v; }
uint16_t getStackPointer(const int which) const { assert(which >= 0 && which < 4); return sp[which]; }
uint16_t getPC() const { b->setMMR2(pc); return pc; }
uint16_t getPC() const { return pc; }
void setRegister(const int nr, const bool reg_set, const bool prev_mode, const uint16_t value);
void setRegister(const int nr, const bool prev_mode, const uint16_t v) { setRegister(nr, (getPSW() >> 11) & 1, prev_mode, v); }

View file

@ -7,7 +7,14 @@
#if defined(ESP32)
#include "esp32.h"
void setBootLoader(bus *const b);
void configure_disk(console *const c);
void configure_network(console *const c);
void start_network(console *const c);
#endif
// returns size of instruction (in bytes)
@ -341,6 +348,23 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
#endif
continue;
}
#if defined(ESP32)
else if (cmd == "cfgdisk") {
configure_disk(cnsl);
continue;
}
else if (cmd == "cfgnet") {
configure_network(cnsl);
continue;
}
else if (cmd == "startnet") {
start_network(cnsl);
continue;
}
#endif
else if (cmd == "quit" || cmd == "q") {
#if defined(ESP32)
ESP.restart();
@ -363,6 +387,11 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("setpc - set PC to value");
cnsl->put_string_lf("setmem - set memory (a=) to value (v=), both in octal, one byte");
cnsl->put_string_lf("toggle - set switch (s=, 0...15 (decimal)) of the front panel to state (t=, 0 or 1)");
#if defined(ESP32)
cnsl->put_string_lf("cfgnet - configure network (e.g. WiFi)");
cnsl->put_string_lf("startnet - start network");
cnsl->put_string_lf("cfgdisk - configure disk");
#endif
continue;
}

9
disk_backend.cpp Normal file
View file

@ -0,0 +1,9 @@
#include "disk_backend.h"
disk_backend::disk_backend()
{
}
disk_backend::~disk_backend()
{
}

18
disk_backend.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include <sys/types.h>
class disk_backend
{
public:
disk_backend();
virtual ~disk_backend();
virtual bool begin() = 0;
virtual bool read(const off_t offset, const size_t n, uint8_t *const target) = 0;
virtual bool write(const off_t offset, const size_t n, const uint8_t *const from) = 0;
};

44
disk_backend_file.cpp Normal file
View file

@ -0,0 +1,44 @@
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "disk_backend_file.h"
#include "log.h"
disk_backend_file::disk_backend_file(const std::string & filename) :
filename(filename)
{
}
disk_backend_file::~disk_backend_file()
{
close(fd);
}
bool disk_backend_file::begin()
{
fd = open(filename.c_str(), O_RDWR);
if (fd == -1) {
DOLOG(ll_error, true, "disk_backend_file: cannot open \"%s\": %s", filename.c_str(), strerror(errno));
return false;
}
return true;
}
bool disk_backend_file::read(const off_t offset, const size_t n, uint8_t *const target)
{
DOLOG(debug, false, "disk_backend_file::read: read %zu bytes from offset %zu", n, offset);
return pread(fd, target, n, offset) == ssize_t(n);
}
bool disk_backend_file::write(const off_t offset, const size_t n, const uint8_t *const from)
{
DOLOG(debug, false, "disk_backend_file::write: write %zu bytes to offset %zu", n, offset);
return pwrite(fd, from, n, offset) == ssize_t(n);
}

22
disk_backend_file.h Normal file
View file

@ -0,0 +1,22 @@
#include <string>
#include "disk_backend.h"
class disk_backend_file : public disk_backend
{
private:
const std::string filename;
int fd { -1 };
public:
disk_backend_file(const std::string & filename);
virtual ~disk_backend_file();
bool begin() override;
bool read(const off_t offset, const size_t n, uint8_t *const target) override;
bool write(const off_t offset, const size_t n, const uint8_t *const from) override;
};

269
disk_backend_nbd.cpp Normal file
View file

@ -0,0 +1,269 @@
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include "disk_backend_nbd.h"
#include "log.h"
#include "utils.h"
#ifdef ESP32
#include <lwip/netdb.h>
#include <lwip/sockets.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#endif
#define HTONLL(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)))
#define NTOHLL(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)))
disk_backend_nbd::disk_backend_nbd(const std::string & host, const int port) :
host(host),
port(port)
{
}
disk_backend_nbd::~disk_backend_nbd()
{
close(fd);
}
bool disk_backend_nbd::begin()
{
if (!connect(false)) {
DOLOG(ll_error, true, "disk_backend_nbd: cannot connect to NBD server");
return false;
}
DOLOG(info, true, "disk_backend_nbd: connected to NBD server");
return true;
}
bool disk_backend_nbd::connect(const bool retry)
{
do {
// LOOP until connected, logging message, exponential backoff?
addrinfo *res = nullptr;
addrinfo hints { 0 };
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char port_str[8] { 0 };
snprintf(port_str, sizeof port_str, "%d", port);
int rc = getaddrinfo(host.c_str(), port_str, &hints, &res);
if (rc != 0) {
#ifdef ESP32
DOLOG(ll_error, true, "disk_backend_nbd: cannot resolve \"%s\":%s", host.c_str(), port_str);
#else
DOLOG(ll_error, true, "disk_backend_nbd: cannot resolve \"%s\":%s: %s", host.c_str(), port_str, gai_strerror(rc));
#endif
sleep(1);
continue;
}
for(addrinfo *p = res; p != NULL; p = p->ai_next) {
if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
continue;
}
if (::connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
close(fd);
fd = -1;
DOLOG(ll_error, true, "disk_backend_nbd: cannot connect");
continue;
}
break;
}
freeaddrinfo(res);
struct __attribute__ ((packed)) {
uint8_t magic1[8];
uint8_t magic2[8];
uint64_t size;
uint32_t flags;
uint8_t padding[124];
} nbd_hello;
if (fd != -1) {
if (READ(fd, reinterpret_cast<char *>(&nbd_hello), sizeof nbd_hello) != sizeof nbd_hello) {
close(fd);
fd = -1;
DOLOG(debug, false, "disk_backend_nbd::connect: connect short read");
}
}
if (memcmp(nbd_hello.magic1, "NBDMAGIC", 8) != 0) {
close(fd);
fd = -1;
DOLOG(debug, false, "disk_backend_nbd::connect: magic invalid");
}
DOLOG(info, false, "NBD size: %u", NTOHLL(nbd_hello.size));
}
while(fd == -1 && retry);
return fd != -1;
}
bool disk_backend_nbd::read(const off_t offset, const size_t n, uint8_t *const target)
{
DOLOG(debug, false, "disk_backend_nbd::read: read %zu bytes from offset %zu", n, offset);
if (n == 0)
return true;
do {
if (fd == -1 && !connect(true)) {
DOLOG(debug, false, "disk_backend_nbd::read: (re-)connect");
sleep(1);
continue;
}
struct __attribute__ ((packed)) {
uint32_t magic;
uint32_t type;
uint64_t handle;
uint64_t offset;
uint32_t length;
} nbd_request { 0 };
nbd_request.magic = ntohl(0x25609513);
nbd_request.type = 0; // READ
nbd_request.offset = HTONLL(uint64_t(offset));
nbd_request.length = htonl(n);
if (WRITE(fd, reinterpret_cast<const char *>(&nbd_request), sizeof nbd_request) != sizeof nbd_request) {
DOLOG(debug, false, "disk_backend_nbd::read: problem sending request");
close(fd);
fd = -1;
sleep(1);
continue;
}
struct __attribute__ ((packed)) {
uint32_t magic;
uint32_t error;
uint64_t handle;
} nbd_reply;
if (READ(fd, reinterpret_cast<char *>(&nbd_reply), sizeof nbd_reply) != sizeof nbd_reply) {
DOLOG(debug, false, "disk_backend_nbd::read: problem receiving reply header");
close(fd);
fd = -1;
sleep(1);
continue;
}
if (ntohl(nbd_reply.magic) != 0x67446698) {
DOLOG(debug, false, "disk_backend_nbd::read: bad reply header %08x", nbd_reply.magic);
close(fd);
fd = -1;
sleep(1);
continue;
}
int error = ntohl(nbd_reply.error);
if (error) {
DOLOG(debug, false, "disk_backend_nbd::read: NBD server indicated error: %d", error);
return false;
}
if (READ(fd, reinterpret_cast<char *>(target), n) != ssize_t(n)) {
DOLOG(debug, false, "disk_backend_nbd::read: problem receiving payload");
close(fd);
fd = -1;
sleep(1);
continue;
}
}
while(fd == -1);
return true;
}
bool disk_backend_nbd::write(const off_t offset, const size_t n, const uint8_t *const from)
{
DOLOG(debug, false, "disk_backend_nbd::write: write %zu bytes to offset %zu", n, offset);
if (n == 0)
return true;
do {
if (!connect(true)) {
DOLOG(debug, false, "disk_backend_nbd::write: (re-)connect");
sleep(1);
continue;
}
struct __attribute__ ((packed)) {
uint32_t magic;
uint32_t type;
uint64_t handle;
uint64_t offset;
uint32_t length;
} nbd_request { 0 };
nbd_request.magic = ntohl(0x25609513);
nbd_request.type = 1; // WRITE
nbd_request.offset = HTONLL(uint64_t(offset));
nbd_request.length = htonl(n);
if (WRITE(fd, reinterpret_cast<const char *>(&nbd_request), sizeof nbd_request) != sizeof nbd_request) {
DOLOG(debug, false, "disk_backend_nbd::write: problem sending request");
close(fd);
fd = -1;
sleep(1);
continue;
}
if (WRITE(fd, reinterpret_cast<const char *>(from), n) != ssize_t(n)) {
DOLOG(debug, false, "disk_backend_nbd::write: problem sending payload");
close(fd);
fd = -1;
sleep(1);
continue;
}
struct __attribute__ ((packed)) {
uint32_t magic;
uint32_t error;
uint64_t handle;
} nbd_reply;
if (READ(fd, reinterpret_cast<char *>(&nbd_reply), sizeof nbd_reply) != sizeof nbd_reply) {
DOLOG(debug, false, "disk_backend_nbd::write: problem receiving reply header");
close(fd);
fd = -1;
sleep(1);
continue;
}
if (ntohl(nbd_reply.magic) != 0x67446698) {
DOLOG(debug, false, "disk_backend_nbd::write: bad reply header %08x", nbd_reply.magic);
close(fd);
fd = -1;
sleep(1);
continue;
}
int error = ntohl(nbd_reply.error);
if (error) {
DOLOG(debug, false, "disk_backend_nbd::write: NBD server indicated error: %d", error);
return false;
}
}
while(fd == -1);
return true;
}

25
disk_backend_nbd.h Normal file
View file

@ -0,0 +1,25 @@
#include <string>
#include <sys/types.h>
#include "disk_backend.h"
class disk_backend_nbd : public disk_backend
{
private:
const std::string host;
const int port { 0 };
int fd { -1 };
bool connect(const bool retry);
public:
disk_backend_nbd(const std::string & host, const int port);
virtual ~disk_backend_nbd();
bool begin() override;
bool read(const off_t offset, const size_t n, uint8_t *const target) override;
bool write(const off_t offset, const size_t n, const uint8_t *const from) override;
};

View file

@ -5,6 +5,11 @@
#include "kw11-l.h"
#include "utils.h"
#if defined(ESP32)
#include "esp32.h"
#endif
#if defined(ESP32)
void thread_wrapper_kw11(void *p)
{

View file

@ -12,6 +12,9 @@
#include "console_posix.h"
#include "cpu.h"
#include "debugger.h"
#include "disk_backend.h"
#include "disk_backend_file.h"
#include "disk_backend_nbd.h"
#include "gen.h"
#include "kw11-l.h"
#include "loaders.h"
@ -45,6 +48,7 @@ void help()
printf("-T t.bin load file as a binary tape file (like simh \"load\" command)\n");
printf("-R d.rk load file as a RK05 disk device\n");
printf("-r d.rl load file as a RL02 disk device\n");
printf("-N host:port:type use NBD-server as disk device, type being either \"rk05\" or \"rl02\"\n");
printf("-p 123 set CPU start pointer to decimal(!) value\n");
printf("-b x enable bootloader (build-in), parameter must be \"rk05\" or \"rl02\"\n");
printf("-n ncurses UI\n");
@ -59,8 +63,8 @@ int main(int argc, char *argv[])
{
//setlocale(LC_ALL, "");
std::vector<std::string> rk05_files;
std::vector<std::string> rl02_files;
std::vector<disk_backend *> rk05_files;
std::vector<disk_backend *> rl02_files;
bool run_debugger = false;
bool tracing = false;
@ -80,8 +84,10 @@ int main(int argc, char *argv[])
std::string test;
disk_backend *temp_d = nullptr;
int opt = -1;
while((opt = getopt(argc, argv, "hm:T:r:R:p:ndtL:b:l:s:Q:")) != -1)
while((opt = getopt(argc, argv, "hm:T:r:R:p:ndtL:b:l:s:Q:N:")) != -1)
{
switch(opt) {
case 'h':
@ -132,13 +138,35 @@ int main(int argc, char *argv[])
break;
case 'R':
rk05_files.push_back(optarg);
temp_d = new disk_backend_file(optarg);
if (!temp_d->begin())
error_exit(false, "Cannot use file \"%s\" for RK05", optarg);
rk05_files.push_back(temp_d);
break;
case 'r':
rl02_files.push_back(optarg);
temp_d = new disk_backend_file(optarg);
if (!temp_d->begin())
error_exit(false, "Cannot use file \"%s\" for RL02", optarg);
rl02_files.push_back(temp_d);
break;
case 'N': {
auto parts = split(optarg, ":");
if (parts.size() != 3)
error_exit(false, "-N: parameter missing");
temp_d = new disk_backend_nbd(parts.at(0), atoi(parts.at(1).c_str()));
if (parts.at(2) == "rk05")
rk05_files.push_back(temp_d);
else if (parts.at(2) == "rl02")
rl02_files.push_back(temp_d);
else
error_exit(false, "\"%s\" is not recognized as a disk type", parts.at(2).c_str());
}
break;
case 'p':
start_addr = atoi(optarg);
sa_set = true;

View file

@ -22,42 +22,21 @@ static const char * const regnames[] = {
"RK05_DATABUF "
};
rk05::rk05(const std::vector<std::string> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
rk05::rk05(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
b(b),
disk_read_acitivity(disk_read_acitivity),
disk_write_acitivity(disk_write_acitivity)
{
memset(registers, 0x00, sizeof registers);
memset(registers, 0x00, sizeof registers );
memset(xfer_buffer, 0x00, sizeof xfer_buffer);
for(auto file : files) {
#if defined(ESP32)
File32 *fh = new File32();
if (!fh->open(file.c_str(), O_RDWR)) {
delete fh;
error_exit(true, "rk05: cannot open \"%s\"", file.c_str());
}
#else
FILE *fh = fopen(file.c_str(), "rb");
if (!fh)
error_exit(true, "rk05: cannot open \"%s\"", file.c_str());
#endif
fhs.push_back(fh);
}
fhs = files;
}
rk05::~rk05()
{
for(auto fh : fhs) {
#if defined(ESP32)
fh->close();
for(auto fh : fhs)
delete fh;
#else
fclose(fh);
#endif
}
}
uint8_t rk05::readByte(const uint16_t addr)
@ -117,10 +96,6 @@ void rk05::writeByte(const uint16_t addr, const uint8_t v)
void rk05::writeWord(const uint16_t addr, uint16_t v)
{
#if defined(ESP32)
digitalWrite(LED_BUILTIN, LOW);
#endif
const int reg = (addr - RK05_BASE) / 2;
registers[reg] = v;
@ -158,19 +133,8 @@ void rk05::writeWord(const uint16_t addr, uint16_t v)
for(size_t i=0; i<reclen; i++)
xfer_buffer[i] = b->readUnibusByte(memoff + i);
#if defined(ESP32)
File32 *fh = fhs.at(device);
if (!fh->seek(diskoffb))
DOLOG(ll_error, true, "RK05 seek error %s", strerror(errno));
if (fh->write(xfer_buffer, reclen) != reclen)
DOLOG(ll_error, true, "RK05 fwrite error %s", strerror(errno));
#else
FILE *fh = fhs.at(device);
if (fseek(fh, diskoffb, SEEK_SET) == -1)
DOLOG(ll_error, true, "RK05 seek error %s", strerror(errno));
if (fwrite(xfer_buffer, 1, reclen, fh) != reclen)
DOLOG(ll_error, true, "RK05 fwrite error %s", strerror(errno));
#endif
if (!fhs.at(device)->write(diskoffb, reclen, xfer_buffer))
DOLOG(ll_error, true, "RK05 pwrite error %s", strerror(errno));
if (v & 2048)
DOLOG(debug, true, "RK05 inhibit BA increase");
@ -194,43 +158,19 @@ void rk05::writeWord(const uint16_t addr, uint16_t v)
DOLOG(debug, true, "RK05 drive %d position sec %d surf %d cyl %d, reclen %zo, READ from %o, mem: %o", device, sector, surface, cylinder, reclen, diskoffb, memoff);
bool proceed = true;
#if defined(ESP32)
File32 *fh = fhs.at(device);
if (!fh->seek(diskoffb)) {
DOLOG(ll_error, true, "RK05 seek error %s", strerror(errno));
proceed = false;
}
#else
FILE *fh = nullptr;
if (device >= fhs.size())
proceed = false;
else {
fh = fhs.at(device);
if (fseek(fh, diskoffb, SEEK_SET) == -1) {
DOLOG(ll_error, true, "RK05 seek error %s", strerror(errno));
proceed = false;
}
}
#endif
int temp_diskoffb = diskoffb;
uint32_t temp = reclen;
uint32_t p = memoff;
while(proceed && temp > 0) {
while(temp > 0) {
uint32_t cur = std::min(uint32_t(sizeof xfer_buffer), temp);
#if defined(ESP32)
yield();
if (!fhs.at(device)->read(temp_diskoffb, cur, xfer_buffer)) {
DOLOG(ll_error, true, "RK05 read error %s", strerror(errno));
break;
}
if (fh->read(xfer_buffer, cur) != size_t(cur))
DOLOG(debug, true, "RK05 fread error: %s", strerror(errno));
#else
if (fread(xfer_buffer, 1, cur, fh) != size_t(cur))
DOLOG(debug, true, "RK05 fread error: %s", strerror(errno));
#endif
temp_diskoffb += cur;
for(uint32_t i=0; i<cur; i++) {
if (p < 0160000)
@ -284,8 +224,4 @@ void rk05::writeWord(const uint16_t addr, uint16_t v)
}
}
}
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
}

21
rk05.h
View file

@ -1,4 +1,4 @@
// (C) 2018-2022 by Folkert van Heusden
// (C) 2018-2023 by Folkert van Heusden
// Released under Apache License v2.0
#pragma once
@ -8,9 +8,7 @@
#include <string>
#include <vector>
#if defined(ESP32)
#include "esp32.h"
#endif
#include "disk_backend.h"
#define RK05_DS 0177400 // drive status
@ -28,19 +26,16 @@ class bus;
class rk05
{
private:
bus *const b;
uint16_t registers[7];
uint8_t xfer_buffer[512];
#if defined(ESP32)
std::vector<File32 *> fhs;
#else
std::vector<FILE *> fhs;
#endif
bus *const b { nullptr };
uint16_t registers [ 7];
uint8_t xfer_buffer[512];
std::vector<disk_backend *> fhs;
std::atomic_bool *const disk_read_acitivity { nullptr };
std::atomic_bool *const disk_write_acitivity { nullptr };
public:
rk05(const std::vector<std::string> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
rk05(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
virtual ~rk05();
uint8_t readByte(const uint16_t addr);

View file

@ -22,7 +22,7 @@ static const char * const regnames[] = {
"multipurpose "
};
rl02::rl02(const std::vector<std::string> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
rl02::rl02(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
b(b),
disk_read_acitivity(disk_read_acitivity),
disk_write_acitivity(disk_write_acitivity)
@ -30,34 +30,13 @@ rl02::rl02(const std::vector<std::string> & files, bus *const b, std::atomic_boo
memset(registers, 0x00, sizeof registers);
memset(xfer_buffer, 0x00, sizeof xfer_buffer);
for(auto file : files) {
#if defined(ESP32)
File32 *fh = new File32();
if (!fh->open(file.c_str(), O_RDWR)) {
delete fh;
error_exit(true, "rl02: cannot open \"%s\"", file.c_str());
}
#else
FILE *fh = fopen(file.c_str(), "rb");
if (!fh)
error_exit(true, "rl02: cannot open \"%s\"", file.c_str());
#endif
fhs.push_back(fh);
}
fhs = files;
}
rl02::~rl02()
{
for(auto fh : fhs) {
#if defined(ESP32)
fh->close();
for(auto fh : fhs)
delete fh;
#else
fclose(fh);
#endif
}
}
uint8_t rl02::readByte(const uint16_t addr)
@ -116,10 +95,6 @@ uint32_t rl02::calcOffset(const uint16_t da)
void rl02::writeWord(const uint16_t addr, uint16_t v)
{
#if defined(ESP32)
digitalWrite(LED_BUILTIN, LOW);
#endif
DOLOG(debug, true, "RL02 write %06o: %06o", addr, v);
const int reg = (addr - RL02_BASE) / 2;
@ -138,44 +113,13 @@ void rl02::writeWord(const uint16_t addr, uint16_t v)
if (command == 2) { // get status
registers[(RL02_MPR - RL02_BASE) / 2] = 0;
#if 0
if (registers[(RL02_DAR - RL02_BASE) / 2] & 2) { // get status -> load status word in MPR
registers[(RL02_MPR - RL02_BASE) / 2] = 5 | // lock on
(1 << 3) | // brushes are home
(1 << 7) | // this is an RL02
0;
DOLOG(debug, true, "RL02 set MPR for status to %06o", registers[(RL02_MPR - RL02_BASE) / 2]);
}
#endif
}
else if (command == 6 || command == 7) { // read data / read data without header check
*disk_read_acitivity = true;
bool proceed = true;
#if defined(ESP32)
File32 *fh = fhs.at(device);
if (!fh->seek(disk_offset)) {
DOLOG(ll_error, true, "RL02 seek to %d error %s", disk_offset, strerror(errno));
proceed = false;
}
#else
FILE *fh = nullptr;
if (size_t(device) >= fhs.size())
proceed = false;
else {
fh = fhs.at(device);
if (fseek(fh, disk_offset, SEEK_SET) == -1) {
DOLOG(ll_error, true, "RL02 seek error %s", strerror(errno));
proceed = false;
}
}
#endif
uint32_t temp_disk_offset = disk_offset;
uint32_t memory_address = registers[(RL02_BAR - RL02_BASE) / 2];
@ -187,19 +131,16 @@ void rl02::writeWord(const uint16_t addr, uint16_t v)
while(proceed && count > 0) {
uint32_t cur = std::min(uint32_t(sizeof xfer_buffer), count);
#if defined(ESP32)
yield();
if (fh->read(xfer_buffer, cur) != size_t(cur))
DOLOG(info, true, "RL02 fread error: %s", strerror(errno));
#else
if (fread(xfer_buffer, 1, cur, fh) != size_t(cur))
DOLOG(info, true, "RL02 fread error: %s", strerror(errno));
#endif
if (!fhs.at(device)->read(temp_disk_offset, cur, xfer_buffer)) {
DOLOG(ll_error, true, "RL02 read error %s", strerror(errno));
break;
}
for(uint32_t i=0; i<cur; i++, p++)
b->writeByte(p, xfer_buffer[i]);
temp_disk_offset += cur;
count -= cur;
}
@ -212,8 +153,4 @@ void rl02::writeWord(const uint16_t addr, uint16_t v)
*disk_read_acitivity = false;
}
}
#if defined(ESP32)
digitalWrite(LED_BUILTIN, HIGH);
#endif
}

16
rl02.h
View file

@ -1,4 +1,4 @@
// (C) 2022 by Folkert van Heusden
// (C) 2023 by Folkert van Heusden
// Released under Apache License v2.0
#pragma once
@ -8,9 +8,8 @@
#include <string>
#include <vector>
#if defined(ESP32)
#include "esp32.h"
#endif
#include "disk_backend.h"
#define RL02_CSR 0174400 // control status register
#define RL02_BAR 0174402 // bus address register
@ -27,12 +26,7 @@ private:
bus *const b;
uint16_t registers[4];
uint8_t xfer_buffer[512];
#if defined(ESP32)
std::vector<File32 *> fhs;
#else
std::vector<FILE *> fhs;
#endif
std::vector<disk_backend *> fhs;
std::atomic_bool *const disk_read_acitivity { nullptr };
std::atomic_bool *const disk_write_acitivity { nullptr };
@ -40,7 +34,7 @@ private:
uint32_t calcOffset(uint16_t);
public:
rl02(const std::vector<std::string> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
rl02(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
virtual ~rl02();
uint8_t readByte(const uint16_t addr);

View file

@ -10,6 +10,7 @@
#include <string>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#include <sys/time.h>
@ -144,3 +145,49 @@ void set_thread_name(std::string name)
pthread_setname_np(pthread_self(), name.c_str());
#endif
}
ssize_t WRITE(int fd, const char *whereto, size_t len)
{
ssize_t cnt=0;
while(len > 0)
{
ssize_t rc = write(fd, whereto, len);
if (rc == -1)
return -1;
else if (rc == 0)
return -1;
else
{
whereto += rc;
len -= rc;
cnt += rc;
}
}
return cnt;
}
ssize_t READ(int fd, char *whereto, size_t len)
{
ssize_t cnt=0;
while(len > 0)
{
ssize_t rc = read(fd, whereto, len);
if (rc == -1)
return -1;
else if (rc == 0)
break;
else
{
whereto += rc;
len -= rc;
cnt += rc;
}
}
return cnt;
}

View file

@ -18,3 +18,6 @@ uint64_t get_us();
void myusleep(uint64_t us);
void set_thread_name(std::string name);
ssize_t WRITE(int fd, const char *whereto, size_t len);
ssize_t READ(int fd, char *whereto, size_t len);