diff --git a/CMakeLists.txt b/CMakeLists.txt index 32b5926..e20e176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ add_executable( main.cpp memory.cpp rk05.cpp - rx02.cpp + rl02.cpp terminal.cpp tests.cpp tm-11.cpp diff --git a/bus.cpp b/bus.cpp index e08e8c8..4730419 100644 --- a/bus.cpp +++ b/bus.cpp @@ -1,4 +1,4 @@ -// (C) 2018 by Folkert van Heusden +// (C) 2018-2022 by Folkert van Heusden // Released under Apache License v2.0 #include <assert.h> #include <stdio.h> @@ -19,6 +19,7 @@ constexpr int n_pages = 12; constexpr int n_pages = 16; #endif + bus::bus() { m = new memory(n_pages * 8192); @@ -33,7 +34,7 @@ bus::~bus() delete c; delete tm11; delete rk05_; - delete rx02_; + delete rl02_; delete tty_; delete m; } @@ -280,6 +281,9 @@ uint16_t bus::read(const uint16_t a, const bool word_mode, const bool use_prev, if (rk05_ && a >= RK05_BASE && a < RK05_END) return word_mode ? rk05_ -> readByte(a) : rk05_ -> readWord(a); + if (rl02_ && a >= RL02_BASE && a < RL02_END) + return word_mode ? rl02_ -> readByte(a) : rl02_ -> readWord(a); + if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END) return word_mode ? tty_ -> readByte(a) : tty_ -> readWord(a); @@ -546,6 +550,11 @@ uint16_t bus::write(const uint16_t a, const bool word_mode, uint16_t value, cons return value; } + if (rl02_ && a >= RL02_BASE && a < RL02_END) { + word_mode ? rl02_ -> writeByte(a, value) : rl02_ -> writeWord(a, value); + return value; + } + if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END) { word_mode ? tty_ -> writeByte(a, value) : tty_ -> writeWord(a, value); return value; diff --git a/bus.h b/bus.h index 0ce7138..a1c1260 100644 --- a/bus.h +++ b/bus.h @@ -7,7 +7,7 @@ #include "tm-11.h" #include "rk05.h" -#include "rx02.h" +#include "rl02.h" class cpu; class memory; @@ -24,7 +24,7 @@ private: cpu *c { nullptr }; tm_11 *tm11 { nullptr }; rk05 *rk05_ { nullptr }; - rx02 *rx02_ { nullptr }; + rl02 *rl02_ { nullptr }; tty *tty_ { nullptr }; memory *m { nullptr }; @@ -51,7 +51,7 @@ public: void add_cpu(cpu *const c) { this -> c = c; } void add_tm11(tm_11 *tm11) { this -> tm11 = tm11; } void add_rk05(rk05 *rk05_) { this -> rk05_ = rk05_; } - void add_rx02(rx02 *rx02_) { this -> rx02_ = rx02_; } + void add_rl02(rl02 *rl02_) { this -> rl02_ = rl02_; } void add_tty(tty *tty_) { this -> tty_ = tty_; } cpu *getCpu() { return this->c; } diff --git a/main.cpp b/main.cpp index 508aa7a..5408588 100644 --- a/main.cpp +++ b/main.cpp @@ -21,6 +21,8 @@ #include "utils.h" +typedef enum { BL_NONE, BL_RK05, BL_RL02 } bootloader_t; + bool withUI { false }; uint32_t event { 0 }; std::atomic_bool terminate { false }; @@ -37,32 +39,85 @@ void loadbin(bus *const b, uint16_t base, const char *const file) fclose(fh); } -void setBootLoader(bus *const b) +void setBootLoader(bus *const b, const bootloader_t which) { - cpu *const c = b -> getCpu(); + cpu *const c = b -> getCpu(); - const uint16_t offset = 01000; - constexpr uint16_t bootrom[] = { - 0012700, - 0177406, - 0012710, - 0177400, - 0012740, - 0000005, - 0105710, - 0100376, - 0005007 - }; + uint16_t offset = 0; + const uint16_t *bl = nullptr; + int size = 0; - FILE *fh = fopen("boot.dat", "wb"); + if (which == BL_RK05) { + offset = 01000; - for(size_t i=0; i<sizeof bootrom / 2; i++) { - b -> writeWord(offset + i * 2, bootrom[i]); - fputc(bootrom[i] & 255, fh); - fputc(bootrom[i] >> 8, fh); + static uint16_t rk05_code[] = { + 0012700, + 0177406, + 0012710, + 0177400, + 0012740, + 0000005, + 0105710, + 0100376, + 0005007 + }; + + bl = rk05_code; + + size = 9; + } + else if (which == BL_RL02) { + offset = 01000; + + /* from https://www.pdp-11.nl/peripherals/disk/rl-info.html + static uint16_t rl02_code[] = { + 0012701, + 0174400, + 0012761, + 0000013, + 0000004, + 0012711, + 0000004, + 0105711, + 0100376, + 0005061, + 0000002, + 0005061, + 0000004, + 0012761, + 0177400, + 0000006, + 0012711, + 0000014, + 0105711, + 0100376, + 0005007 + }; + + size = 21; + */ + + // from http://gunkies.org/wiki/RL11_disk_controller + static uint16_t rl02_code[] = { + 0012700, + 0174400, + 0012760, + 0177400, + 0000006, + 0012710, + 0000014, + 0105710, + 0100376, + 0005007, + }; + + size = 10; + + bl = rl02_code; } - fclose(fh); + for(int i=0; i<size; i++) + b -> writeWord(offset + i * 2, bl[i]); c -> setRegister(7, offset); } @@ -130,6 +185,44 @@ uint16_t loadTape(bus *const b, const char *const file) return start; } +void load_p11_x11(bus *const b, const std::string & file) +{ + FILE *fh = fopen(file.c_str(), "rb"); + if (!fh) + error_exit(true, "Cannot open %s", file.c_str()); + + uint16_t addr = 0; + int n = 0; + + while(!feof(fh)) { + char buffer[4096]; + + if (!fgets(buffer, sizeof buffer, fh)) + break; + + if (n) { + uint8_t byte = strtol(buffer, NULL, 16); + + b->writeByte(addr, byte); + + n--; + + addr++; + } + else { + std::vector<std::string> parts = split(buffer, " "); + + addr = strtol(parts[0].c_str(), NULL, 16); + n = strtol(parts[1].c_str(), NULL, 16); + } + } + + fclose(fh); + + cpu *const c = b -> getCpu(); + c -> setRegister(7, 0); +} + volatile bool sw = false; void sw_handler(int s) { @@ -150,7 +243,7 @@ void help() printf("-R d.rk load file as a RK05 disk device\n"); printf("-p 123 set CPU start pointer to decimal(!) value\n"); printf("-L f.bin load file into memory at address given by -p (and run it)\n"); - printf("-b enable bootloader (build-in)\n"); + printf("-b x enable bootloader (build-in), parameter must be \"rk05\" or \"rl02\"\n"); printf("-n ncurses UI\n"); printf("-d enable debugger\n"); printf("-t enable tracing (disassemble to stderr, requires -d as well)\n"); @@ -170,12 +263,16 @@ int main(int argc, char *argv[]) c -> setEmulateMFPT(true); std::vector<std::string> rk05_files; + std::vector<std::string> rl02_files; + bool testCases = false; bool run_debugger = false; bool tracing = false; - bool bootloader = false; + + bootloader_t bootloader = BL_NONE; + int opt = -1; - while((opt = getopt(argc, argv, "hm:T:R:p:ndtL:b")) != -1) + while((opt = getopt(argc, argv, "hm:T:r:R:p:ndtL:b:")) != -1) { switch(opt) { case 'h': @@ -183,7 +280,13 @@ int main(int argc, char *argv[]) return 1; case 'b': - bootloader = true; + if (strcasecmp(optarg, "rk05") == 0) + bootloader = BL_RK05; + else if (strcasecmp(optarg, "rl02") == 0) + bootloader = BL_RL02; + else + error_exit(false, "Bootload \"%s\" not recognized", optarg); + break; case 'd': @@ -216,6 +319,10 @@ int main(int argc, char *argv[]) rk05_files.push_back(optarg); break; + case 'r': + rl02_files.push_back(optarg); + break; + case 'p': c->setRegister(7, atoi(optarg)); break; @@ -247,8 +354,11 @@ int main(int argc, char *argv[]) if (rk05_files.empty() == false) b->add_rk05(new rk05(rk05_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag())); - if (bootloader) - setBootLoader(b); + if (rl02_files.empty() == false) + b->add_rl02(new rl02(rl02_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag())); + + if (bootloader != BL_NONE) + setBootLoader(b, bootloader); running = cnsl->get_running_flag(); @@ -275,6 +385,8 @@ int main(int argc, char *argv[]) // loadbin(b, 0, "test.dat"); // c->setRegister(7, 0); +//load_p11_x11(b, "/home/folkert/Projects/PDP-11/work/p11-2.10i/Tests/mtpi.x11"); + cnsl->start_thread(); if (run_debugger) @@ -306,5 +418,7 @@ int main(int argc, char *argv[]) delete b; + delete lf; + return 0; } diff --git a/rk05.cpp b/rk05.cpp index eb30ba5..cb76b86 100644 --- a/rk05.cpp +++ b/rk05.cpp @@ -11,7 +11,7 @@ #include "utils.h" -const char * const regnames[] = { +static const char * const regnames[] = { "RK05_DS drivestatus", "RK05_ERROR ", "RK05_CS ctrlstatus", diff --git a/rl02.cpp b/rl02.cpp new file mode 100644 index 0000000..9048057 --- /dev/null +++ b/rl02.cpp @@ -0,0 +1,218 @@ +// (C) 2022 by Folkert van Heusden +// Released under Apache License v2.0 +#include <errno.h> +#include <string.h> + +#include "bus.h" +#include "cpu.h" +#include "error.h" +#include "gen.h" +#include "rl02.h" +#include "utils.h" + + +constexpr int sectors_per_track = 40; +constexpr int bytes_per_sector = 256; + +static const char * const regnames[] = { + "control status", + "bus address ", + "disk address ", + "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) : + b(b), + disk_read_acitivity(disk_read_acitivity), + disk_write_acitivity(disk_write_acitivity) +{ + 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); + } +} + +rl02::~rl02() +{ + for(auto fh : fhs) { +#if defined(ESP32) + fh->close(); + delete fh; +#else + fclose(fh); +#endif + } +} + +uint8_t rl02::readByte(const uint16_t addr) +{ + uint16_t v = readWord(addr & ~1); + + if (addr & 1) + return v >> 8; + + return v; +} + +uint16_t rl02::readWord(const uint16_t addr) +{ + const int reg = (addr - RL02_BASE) / 2; + + if (addr == RL02_CSR) { // control status + setBit(registers[reg], 0, true); // drive ready (DRDY) + setBit(registers[reg], 7, true); // controller ready (CRDY) + } + + uint16_t value = registers[reg]; + + // TODO + + D(fprintf(stderr, "RL02 read %s/%o: %06o\n", regnames[reg], addr, value);) + + return value; +} + +void rl02::writeByte(const uint16_t addr, const uint8_t v) +{ + uint16_t vtemp = registers[(addr - RL02_BASE) / 2]; + + if (addr & 1) { + vtemp &= ~0xff00; + vtemp |= v << 8; + } + else { + vtemp &= ~0x00ff; + vtemp |= v; + } + + writeWord(addr, vtemp); +} + +uint32_t rl02::calcOffset(const uint16_t da) +{ + int sector = da & 63; + int track = (da >> 6) & 1023; + + uint32_t offset = (sectors_per_track * track + sector) * bytes_per_sector; + + return offset; +} + +void rl02::writeWord(const uint16_t addr, uint16_t v) +{ +#if defined(ESP32) + digitalWrite(LED_BUILTIN, LOW); +#endif + + fprintf(stderr, "RL02 write %06o: %06o\n", addr, v); + + const int reg = (addr - RL02_BASE) / 2; + + registers[reg] = v; + + if (addr == RL02_CSR) { // control status + const uint8_t command = (v >> 1) & 7; + + const bool do_exec = !(v & 128); + + fprintf(stderr, "RL02 set command %d, exec: %d\n", command, do_exec); + + uint32_t disk_offset = calcOffset(registers[(RL02_DAR - RL02_BASE) / 2] & ~1); + int device = 0; // TODO + + 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; + + fprintf(stderr, "RL02 set MPR for status to %06o\n", 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)) { + fprintf(stderr, "RL02 seek to %d error %s\n", 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) { + fprintf(stderr, "RL02 seek error %s\n", strerror(errno)); + proceed = false; + } + } +#endif + + uint32_t memory_address = registers[(RL02_BAR - RL02_BASE) / 2]; + + uint32_t count = (65536 - registers[(RL02_MPR - RL02_BASE) / 2]) * 2; + + fprintf(stderr, "RL02 read %d bytes (dec) from %d (dec) to %06o (oct)\n", count, disk_offset, memory_address); + + uint32_t p = memory_address; + 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)) + D(fprintf(stderr, "RL02 fread error: %s\n", strerror(errno));) +#else + if (fread(xfer_buffer, 1, cur, fh) != size_t(cur)) + D(fprintf(stderr, "RL02 fread error: %s\n", strerror(errno));) +#endif + + for(uint32_t i=0; i<cur; i++, p++) + b->writeByte(p, xfer_buffer[i]); + + count -= cur; + } + + if (registers[(RL02_CSR - RL02_BASE) / 2] & 64) { // interrupt enable? + fprintf(stderr, "RL02 triggering interrupt\n"); + + b->getCpu()->queue_interrupt(5, 0254); + } + + *disk_read_acitivity = false; + } + } + +#if defined(ESP32) + digitalWrite(LED_BUILTIN, HIGH); +#endif +} diff --git a/rl02.h b/rl02.h new file mode 100644 index 0000000..d0da43e --- /dev/null +++ b/rl02.h @@ -0,0 +1,51 @@ +// (C) 2022 by Folkert van Heusden +// Released under Apache License v2.0 +#pragma once + +#include <atomic> +#include <stdint.h> +#include <stdio.h> +#include <string> +#include <vector> + +#if defined(ESP32) +#include "esp32.h" +#endif + +#define RL02_CSR 0174400 // control status register +#define RL02_BAR 0174402 // bus address register +#define RL02_DAR 0174404 // disk address register +#define RL02_MPR 0174406 // multi purpose register +#define RL02_BASE RL02_CSR +#define RL02_END (RL02_MPR + 2) + +class bus; + +class rl02 +{ +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::atomic_bool *const disk_read_acitivity { nullptr }; + std::atomic_bool *const disk_write_acitivity { nullptr }; + + 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); + virtual ~rl02(); + + uint8_t readByte(const uint16_t addr); + uint16_t readWord(const uint16_t addr); + + void writeByte(const uint16_t addr, const uint8_t v); + void writeWord(const uint16_t addr, const uint16_t v); +}; diff --git a/rx02.cpp b/rx02.cpp deleted file mode 100644 index 6b5767f..0000000 --- a/rx02.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// (C) 2018 by Folkert van Heusden -// Released under Apache License v2.0 -#include <errno.h> -#include <string.h> - -#include "rx02.h" -#include "gen.h" -#include "memory.h" -#include "utils.h" - -rx02::rx02(const std::string & file, memory *const m) : m(m) -{ - offset = 0; - memset(registers, 0x00, sizeof registers); - - fh = fopen(file.c_str(), "rb"); -} - -rx02::~rx02() -{ - fclose(fh); -} - -uint8_t rx02::readByte(const uint16_t addr) -{ - uint16_t v = readWord(addr & ~1); - - if (addr & 1) - return v >> 8; - - return v; -} - -uint16_t rx02::readWord(const uint16_t addr) -{ - const int reg = (addr - RX02_BASE) / 2; - uint16_t vtemp = registers[reg]; - - D(printf("RX02 read addr %o: ", addr);) - - // FIXME - - D(printf("%o\n", vtemp);) - - return vtemp; -} - -void rx02::writeByte(const uint16_t addr, const uint8_t v) -{ - uint16_t vtemp = registers[(addr - RX02_BASE) / 2]; - - if (addr & 1) { - vtemp &= ~0xff00; - vtemp |= v << 8; - } - else { - vtemp &= ~0x00ff; - vtemp |= v; - } - - writeWord(addr, vtemp); -} - -void rx02::writeWord(const uint16_t addr, uint16_t v) -{ - D(printf("RX02 write %o: %o\n", addr, v);) - - // FIXME - - D(printf("set register %o to %o\n", addr, v);) - registers[(addr - RX02_BASE) / 2] = v; -} diff --git a/rx02.h b/rx02.h deleted file mode 100644 index 36146db..0000000 --- a/rx02.h +++ /dev/null @@ -1,33 +0,0 @@ -// (C) 2018 by Folkert van Heusden -// Released under Apache License v2.0 -#pragma once - -#include <stdint.h> -#include <stdio.h> -#include <string> - -// FIXME namen van defines -#define RX02_BASE 0 -#define RX02_END (1 + 2) - -class memory; - -class rx02 -{ -private: - memory *const m; - uint16_t registers[7]; - uint8_t xfer_buffer[512]; - int offset; - FILE *fh; - -public: - rx02(const std::string & file, memory *const m); - virtual ~rx02(); - - uint8_t readByte(const uint16_t addr); - uint16_t readWord(const uint16_t addr); - - void writeByte(const uint16_t addr, const uint8_t v); - void writeWord(const uint16_t addr, uint16_t v); -};