// (C) 2022 by Folkert van Heusden // Released under Apache License v2.0 #include #include #include "bus.h" #include "cpu.h" #include "error.h" #include "gen.h" #include "log.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 & 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 DOLOG(debug, true, "RL02 read %s/%o: %06o", 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 DOLOG(debug, true, "RL02 write %06o: %06o", 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); DOLOG(debug, true, "RL02 set command %d, exec: %d", 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; 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 memory_address = registers[(RL02_BAR - RL02_BASE) / 2]; uint32_t count = (65536 - registers[(RL02_MPR - RL02_BASE) / 2]) * 2; DOLOG(debug, true, "RL02 read %d bytes (dec) from %d (dec) to %06o (oct)", 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)) 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 for(uint32_t i=0; iwriteByte(p, xfer_buffer[i]); count -= cur; } if (registers[(RL02_CSR - RL02_BASE) / 2] & 64) { // interrupt enable? DOLOG(debug, true, "RL02 triggering interrupt"); b->getCpu()->queue_interrupt(5, 0254); } *disk_read_acitivity = false; } } #if defined(ESP32) digitalWrite(LED_BUILTIN, HIGH); #endif }