diff --git a/bus.cpp b/bus.cpp index d9a4741..d24b3b2 100644 --- a/bus.cpp +++ b/bus.cpp @@ -123,6 +123,289 @@ void bus::trap_odd(const uint16_t a) c->trap(004); // invalid access } +uint16_t bus::read_io(const uint16_t addr_in, const word_mode_t word_mode, const bool peek_only) +{ + //// REGISTERS //// + if (addr_in >= ADDR_KERNEL_R && addr_in <= ADDR_KERNEL_R + 5) { // kernel R0-R5 + uint16_t temp = c->getRegister(addr_in - ADDR_KERNEL_R) & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O kernel R%d: %06o", addr_in - ADDR_KERNEL_R, temp); + return temp; + } + if (addr_in >= ADDR_USER_R && addr_in <= ADDR_USER_R + 5) { // user R0-R5 + uint16_t temp = c->getRegister(addr_in - ADDR_USER_R) & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O user R%d: %06o", addr_in - ADDR_USER_R, temp); + return temp; + } + if (addr_in == ADDR_KERNEL_SP) { // kernel SP + uint16_t temp = c->getStackPointer(0) & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O kernel SP: %06o", temp); + return temp; + } + if (addr_in == ADDR_PC) { // PC + uint16_t temp = c->getPC() & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O PC: %06o", temp); + return temp; + } + if (addr_in == ADDR_SV_SP) { // supervisor SP + uint16_t temp = c->getStackPointer(1) & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O supervisor SP: %06o", temp); + return temp; + } + if (addr_in == ADDR_USER_SP) { // user SP + uint16_t temp = c->getStackPointer(3) & (word_mode ? 0xff : 0xffff); + if (!peek_only) DOLOG(debug, false, "READ-I/O user SP: %06o", temp); + return temp; + } + ///^ registers ^/// + + if (!peek_only) { + if ((addr_in & 1) && word_mode == wm_word) { + DOLOG(debug, true, "READ-I/O odd address %06o UNHANDLED", addr_in); + trap_odd(addr_in); + throw 0; + return 0; + } + } + + if (addr_in == ADDR_CPU_ERR) { // cpu error register + uint16_t temp = CPUERR & 0xff; + if (!peek_only) DOLOG(debug, false, "READ-I/O CPU error: %03o", temp); + return temp; + } + + if (addr_in == ADDR_MAINT) { // MAINT + uint16_t temp = 1; // POWER OK + if (!peek_only) DOLOG(debug, false, "READ-I/O MAINT: %o", temp); + return temp; + } + + if (addr_in == ADDR_CONSW) { // console switch & display register + uint16_t temp = console_switches; + if (!peek_only) DOLOG(debug, false, "READ-I/O console switch: %o", temp); + return temp; + } + + if (addr_in == ADDR_KW11P) { // KW11P programmable clock + uint16_t temp = 128; + if (!peek_only) DOLOG(debug, false, "READ-I/O programmable clock: %o", temp); + return temp; + } + + if (addr_in == ADDR_PIR || addr_in == ADDR_PIR + 1) { // PIR + uint16_t temp = 0; + + if (word_mode == wm_word) + temp = PIR; + else + temp = addr_in == ADDR_PIR ? PIR & 255 : PIR >> 8; + + if (!peek_only) DOLOG(debug, false, "READ-I/O PIR: %o", temp); + return temp; + } + + if (addr_in == ADDR_SYSTEM_ID) { + uint16_t temp = 011064; + if (!peek_only) DOLOG(debug, false, "READ-I/O system id: %o", temp); + return temp; + } + + if (addr_in == ADDR_LFC) { // line frequency clock and status register +#if defined(BUILD_FOR_RP2040) + xSemaphoreTake(lf_csr_lock, portMAX_DELAY); +#else + std::unique_lock lck(lf_csr_lock); +#endif + + uint16_t temp = lf_csr; + if (!peek_only) DOLOG(debug, false, "READ-I/O line frequency clock: %o", temp); + +#if defined(BUILD_FOR_RP2040) + xSemaphoreGive(lf_csr_lock); +#endif + + return temp; + } + + if (addr_in == ADDR_LP11CSR) { // printer, CSR register, LP11 + uint16_t temp = 0x80; + if (!peek_only) DOLOG(debug, false, "READ-I/O LP11 CSR: %o", temp); + return temp; + } + + /// MMU /// + if (addr_in >= ADDR_PDR_SV_START && addr_in < ADDR_PDR_SV_END) + return read_pdr(addr_in, 1, word_mode, peek_only); + else if (addr_in >= ADDR_PAR_SV_START && addr_in < ADDR_PAR_SV_END) + return read_par(addr_in, 1, word_mode, peek_only); + else if (addr_in >= ADDR_PDR_K_START && addr_in < ADDR_PDR_K_END) + return read_pdr(addr_in, 0, word_mode, peek_only); + else if (addr_in >= ADDR_PAR_K_START && addr_in < ADDR_PAR_K_END) + return read_par(addr_in, 0, word_mode, peek_only); + else if (addr_in >= ADDR_PDR_U_START && addr_in < ADDR_PDR_U_END) + return read_pdr(addr_in, 3, word_mode, peek_only); + else if (addr_in >= ADDR_PAR_U_START && addr_in < ADDR_PAR_U_END) + return read_par(addr_in, 3, word_mode, peek_only); + /////////// + + if (addr_in >= 0177740 && addr_in <= 0177753) { // cache control register and others + if (!peek_only) DOLOG(debug, false, "READ-I/O cache control register/others (%06o): %o", addr_in, 0); + // TODO + return 0; + } + + if (addr_in >= 0170200 && addr_in <= 0170377) { // unibus map + if (!peek_only) DOLOG(debug, false, "READ-I/O unibus map (%06o): %o", addr_in, 0); + // TODO + return 0; + } + + if (word_mode) { + if (addr_in == ADDR_PSW) { // PSW + uint8_t temp = c->getPSW(); + if (!peek_only) DOLOG(debug, false, "READ-I/O PSW LSB: %03o", temp); + return temp; + } + + if (addr_in == ADDR_PSW + 1) { + uint8_t temp = c->getPSW() >> 8; + if (!peek_only) DOLOG(debug, false, "READ-I/O PSW MSB: %03o", temp); + return temp; + } + if (addr_in == ADDR_STACKLIM) { // stack limit register + uint8_t temp = c->getStackLimitRegister(); + if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register (low): %03o", temp); + return temp; + } + if (addr_in == ADDR_STACKLIM + 1) { // stack limit register + uint8_t temp = c->getStackLimitRegister() >> 8; + if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register (high): %03o", temp); + return temp; + } + + if (addr_in == ADDR_MICROPROG_BREAK_REG) { // microprogram break register + uint8_t temp = microprogram_break_register; + if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register (low): %03o", temp); + return temp; + } + if (addr_in == ADDR_MICROPROG_BREAK_REG + 1) { // microprogram break register + uint8_t temp = microprogram_break_register >> 8; + if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register (high): %03o", temp); + return temp; + } + + if (addr_in == ADDR_MMR0) { + uint8_t temp = MMR0; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 LO: %03o", temp); + return temp; + } + if (addr_in == ADDR_MMR0 + 1) { + uint8_t temp = MMR0 >> 8; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 HI: %03o", temp); + return temp; + } + } + else { + if (addr_in == ADDR_MMR0) { + uint16_t temp = MMR0; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0: %06o", temp); + return temp; + } + + if (addr_in == ADDR_MMR1) { // MMR1 + uint16_t temp = MMR1; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR1: %06o", temp); + return temp; + } + + if (addr_in == ADDR_MMR2) { // MMR2 + uint16_t temp = MMR2; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR2: %06o", temp); + return temp; + } + + if (addr_in == ADDR_MMR3) { // MMR3 + uint16_t temp = MMR3; + if (!peek_only) DOLOG(debug, false, "READ-I/O MMR3: %06o", temp); + return temp; + } + + if (addr_in == ADDR_PSW) { // PSW + uint16_t temp = c->getPSW(); + if (!peek_only) DOLOG(debug, false, "READ-I/O PSW: %06o", temp); + return temp; + } + + if (addr_in == ADDR_STACKLIM) { // stack limit register + uint16_t temp = c->getStackLimitRegister(); + if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register: %06o", temp); + return temp; + } + + if (addr_in == ADDR_CPU_ERR) { // cpu error register + uint16_t temp = CPUERR; + if (!peek_only) DOLOG(debug, false, "READ-I/O CPUERR: %06o", temp); + return temp; + } + + if (addr_in == ADDR_MICROPROG_BREAK_REG) { // microprogram break register + uint16_t temp = microprogram_break_register; + if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register: %06o", temp); + return temp; + } + } + + if (tm11 && addr_in >= TM_11_BASE && addr_in < TM_11_END && !peek_only) { + DOLOG(debug, false, "READ-I/O TM11 register %d", (addr_in - TM_11_BASE) / 2); + + return word_mode ? tm11->readByte(addr_in) : tm11->readWord(addr_in); + } + + if (rk05_ && addr_in >= RK05_BASE && addr_in < RK05_END && !peek_only) { + DOLOG(debug, false, "READ-I/O RK05 register %d", (addr_in - RK05_BASE) / 2); + + return word_mode ? rk05_->readByte(addr_in) : rk05_->readWord(addr_in); + } + + if (rl02_ && addr_in >= RL02_BASE && addr_in < RL02_END && !peek_only) { + DOLOG(debug, false, "READ-I/O RL02 register %d", (addr_in - RL02_BASE) / 2); + + return word_mode ? rl02_->readByte(addr_in) : rl02_->readWord(addr_in); + } + + if (tty_ && addr_in >= PDP11TTY_BASE && addr_in < PDP11TTY_END && !peek_only) { + DOLOG(debug, false, "READ-I/O TTY register %d", (addr_in - PDP11TTY_BASE) / 2); + + return word_mode ? tty_->readByte(addr_in) : tty_->readWord(addr_in); + } + + // LO size register field must be all 1s, so subtract 1 + constexpr uint32_t system_size = n_pages * 8192l / 64 - 1; + + if (addr_in == ADDR_SYSSIZE + 2) { // system size HI + uint16_t temp = system_size >> 16; + if (!peek_only) DOLOG(debug, false, "READ-I/O accessing system size HI: %06o", temp); + return temp; + } + + if (addr_in == ADDR_SYSSIZE) { // system size LO + uint16_t temp = system_size; + if (!peek_only) DOLOG(debug, false, "READ-I/O accessing system size LO: %06o", temp); + return temp; + } + + if (!peek_only) { + uint32_t m_offset = addr_in - 0160000 + get_io_base(); + + DOLOG(debug, true, "READ-I/O UNHANDLED read %08o (%c), (base: %o)", m_offset, word_mode ? 'B' : ' ', get_io_base()); + + c->trap(004); // no such i/o + + throw 1; + } + + return -1; +} + uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm_selection_t mode_selection, const bool peek_only, const d_i_space_t space) { int run_mode = mode_selection == rm_cur ? c->getPSW_runmode() : c->getPSW_prev_runmode(); @@ -135,283 +418,7 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm if (is_io) { uint16_t a = m_offset - io_base + 0160000; // TODO - //// REGISTERS //// - if (a >= ADDR_KERNEL_R && a <= ADDR_KERNEL_R + 5) { // kernel R0-R5 - uint16_t temp = c->getRegister(a - ADDR_KERNEL_R) & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O kernel R%d: %06o", a - ADDR_KERNEL_R, temp); - return temp; - } - if (a >= ADDR_USER_R && a <= ADDR_USER_R + 5) { // user R0-R5 - uint16_t temp = c->getRegister(a - ADDR_USER_R) & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O user R%d: %06o", a - ADDR_USER_R, temp); - return temp; - } - if (a == ADDR_KERNEL_SP) { // kernel SP - uint16_t temp = c->getStackPointer(0) & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O kernel SP: %06o", temp); - return temp; - } - if (a == ADDR_PC) { // PC - uint16_t temp = c->getPC() & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O PC: %06o", temp); - return temp; - } - if (a == ADDR_SV_SP) { // supervisor SP - uint16_t temp = c->getStackPointer(1) & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O supervisor SP: %06o", temp); - return temp; - } - if (a == ADDR_USER_SP) { // user SP - uint16_t temp = c->getStackPointer(3) & (word_mode ? 0xff : 0xffff); - if (!peek_only) DOLOG(debug, false, "READ-I/O user SP: %06o", temp); - return temp; - } - ///^ registers ^/// - - if (!peek_only) { - if ((a & 1) && word_mode == wm_word) { - DOLOG(debug, true, "READ-I/O odd address %06o UNHANDLED", a); - trap_odd(a); - throw 0; - return 0; - } - } - - if (a == ADDR_CPU_ERR) { // cpu error register - uint16_t temp = CPUERR & 0xff; - if (!peek_only) DOLOG(debug, false, "READ-I/O CPU error: %03o", temp); - return temp; - } - - if (a == ADDR_MAINT) { // MAINT - uint16_t temp = 1; // POWER OK - if (!peek_only) DOLOG(debug, false, "READ-I/O MAINT: %o", temp); - return temp; - } - - if (a == ADDR_CONSW) { // console switch & display register - uint16_t temp = console_switches; - if (!peek_only) DOLOG(debug, false, "READ-I/O console switch: %o", temp); - return temp; - } - - if (a == ADDR_KW11P) { // KW11P programmable clock - uint16_t temp = 128; - if (!peek_only) DOLOG(debug, false, "READ-I/O programmable clock: %o", temp); - return temp; - } - - if (a == ADDR_PIR || a == ADDR_PIR + 1) { // PIR - uint16_t temp = 0; - - if (word_mode == wm_word) - temp = PIR; - else - temp = a == ADDR_PIR ? PIR & 255 : PIR >> 8; - - if (!peek_only) DOLOG(debug, false, "READ-I/O PIR: %o", temp); - return temp; - } - - if (a == ADDR_SYSTEM_ID) { - uint16_t temp = 011064; - if (!peek_only) DOLOG(debug, false, "READ-I/O system id: %o", temp); - return temp; - } - - if (a == ADDR_LFC) { // line frequency clock and status register -#if defined(BUILD_FOR_RP2040) - xSemaphoreTake(lf_csr_lock, portMAX_DELAY); -#else - std::unique_lock lck(lf_csr_lock); -#endif - - uint16_t temp = lf_csr; - if (!peek_only) DOLOG(debug, false, "READ-I/O line frequency clock: %o", temp); - -#if defined(BUILD_FOR_RP2040) - xSemaphoreGive(lf_csr_lock); -#endif - - return temp; - } - - if (a == ADDR_LP11CSR) { // printer, CSR register, LP11 - uint16_t temp = 0x80; - if (!peek_only) DOLOG(debug, false, "READ-I/O LP11 CSR: %o", temp); - return temp; - } - - /// MMU /// - if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END) - return read_pdr(a, 1, word_mode, peek_only); - else if (a >= ADDR_PAR_SV_START && a < ADDR_PAR_SV_END) - return read_par(a, 1, word_mode, peek_only); - else if (a >= ADDR_PDR_K_START && a < ADDR_PDR_K_END) - return read_pdr(a, 0, word_mode, peek_only); - else if (a >= ADDR_PAR_K_START && a < ADDR_PAR_K_END) - return read_par(a, 0, word_mode, peek_only); - else if (a >= ADDR_PDR_U_START && a < ADDR_PDR_U_END) - return read_pdr(a, 3, word_mode, peek_only); - else if (a >= ADDR_PAR_U_START && a < ADDR_PAR_U_END) - return read_par(a, 3, word_mode, peek_only); - /////////// - - if (a >= 0177740 && a <= 0177753) { // cache control register and others - if (!peek_only) DOLOG(debug, false, "READ-I/O cache control register/others (%06o): %o", a, 0); - // TODO - return 0; - } - - if (a >= 0170200 && a <= 0170377) { // unibus map - if (!peek_only) DOLOG(debug, false, "READ-I/O unibus map (%06o): %o", a, 0); - // TODO - return 0; - } - - if (word_mode) { - if (a == ADDR_PSW) { // PSW - uint8_t temp = c->getPSW(); - if (!peek_only) DOLOG(debug, false, "READ-I/O PSW LSB: %03o", temp); - return temp; - } - - if (a == ADDR_PSW + 1) { - uint8_t temp = c->getPSW() >> 8; - if (!peek_only) DOLOG(debug, false, "READ-I/O PSW MSB: %03o", temp); - return temp; - } - if (a == ADDR_STACKLIM) { // stack limit register - uint8_t temp = c->getStackLimitRegister(); - if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register (low): %03o", temp); - return temp; - } - if (a == ADDR_STACKLIM + 1) { // stack limit register - uint8_t temp = c->getStackLimitRegister() >> 8; - if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register (high): %03o", temp); - return temp; - } - - if (a == ADDR_MICROPROG_BREAK_REG) { // microprogram break register - uint8_t temp = microprogram_break_register; - if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register (low): %03o", temp); - return temp; - } - if (a == ADDR_MICROPROG_BREAK_REG + 1) { // microprogram break register - uint8_t temp = microprogram_break_register >> 8; - if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register (high): %03o", temp); - return temp; - } - - if (a == ADDR_MMR0) { - uint8_t temp = MMR0; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 LO: %03o", temp); - return temp; - } - if (a == ADDR_MMR0 + 1) { - uint8_t temp = MMR0 >> 8; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 HI: %03o", temp); - return temp; - } - } - else { - if (a == ADDR_MMR0) { - uint16_t temp = MMR0; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0: %06o", temp); - return temp; - } - - if (a == ADDR_MMR1) { // MMR1 - uint16_t temp = MMR1; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR1: %06o", temp); - return temp; - } - - if (a == ADDR_MMR2) { // MMR2 - uint16_t temp = MMR2; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR2: %06o", temp); - return temp; - } - - if (a == ADDR_MMR3) { // MMR3 - uint16_t temp = MMR3; - if (!peek_only) DOLOG(debug, false, "READ-I/O MMR3: %06o", temp); - return temp; - } - - if (a == ADDR_PSW) { // PSW - uint16_t temp = c->getPSW(); - if (!peek_only) DOLOG(debug, false, "READ-I/O PSW: %06o", temp); - return temp; - } - - if (a == ADDR_STACKLIM) { // stack limit register - uint16_t temp = c->getStackLimitRegister(); - if (!peek_only) DOLOG(debug, false, "READ-I/O stack limit register: %06o", temp); - return temp; - } - - if (a == ADDR_CPU_ERR) { // cpu error register - uint16_t temp = CPUERR; - if (!peek_only) DOLOG(debug, false, "READ-I/O CPUERR: %06o", temp); - return temp; - } - - if (a == ADDR_MICROPROG_BREAK_REG) { // microprogram break register - uint16_t temp = microprogram_break_register; - if (!peek_only) DOLOG(debug, false, "READ-I/O microprogram break register: %06o", temp); - return temp; - } - } - - if (tm11 && a >= TM_11_BASE && a < TM_11_END && !peek_only) { - DOLOG(debug, false, "READ-I/O TM11 register %d", (a - TM_11_BASE) / 2); - - return word_mode ? tm11->readByte(a) : tm11->readWord(a); - } - - if (rk05_ && a >= RK05_BASE && a < RK05_END && !peek_only) { - DOLOG(debug, false, "READ-I/O RK05 register %d", (a - RK05_BASE) / 2); - - return word_mode ? rk05_->readByte(a) : rk05_->readWord(a); - } - - if (rl02_ && a >= RL02_BASE && a < RL02_END && !peek_only) { - DOLOG(debug, false, "READ-I/O RL02 register %d", (a - RL02_BASE) / 2); - - return word_mode ? rl02_->readByte(a) : rl02_->readWord(a); - } - - if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END && !peek_only) { - DOLOG(debug, false, "READ-I/O TTY register %d", (a - PDP11TTY_BASE) / 2); - - return word_mode ? tty_->readByte(a) : tty_->readWord(a); - } - - // LO size register field must be all 1s, so subtract 1 - constexpr uint32_t system_size = n_pages * 8192l / 64 - 1; - - if (a == ADDR_SYSSIZE + 2) { // system size HI - uint16_t temp = system_size >> 16; - if (!peek_only) DOLOG(debug, false, "READ-I/O accessing system size HI: %06o", temp); - return temp; - } - - if (a == ADDR_SYSSIZE) { // system size LO - uint16_t temp = system_size; - if (!peek_only) DOLOG(debug, false, "READ-I/O accessing system size LO: %06o", temp); - return temp; - } - - if (!peek_only) { - DOLOG(debug, true, "READ-I/O UNHANDLED read %08o (%c), (base: %o)", m_offset, word_mode ? 'B' : ' ', get_io_base()); - - c->trap(004); // no such i/o - - throw 1; - } - - return -1; + return read_io(a, word_mode, peek_only); } if (peek_only == false && word_mode == wm_word && (addr_in & 1)) { @@ -736,6 +743,264 @@ void bus::write_par(const uint32_t a, const int run_mode, const uint16_t value, DOLOG(debug, true, "WRITE-I/O PAR run-mode %d: %c for %d: %o (%07o)", run_mode, is_d ? 'D' : 'I', page, word_mode ? value & 0xff : value, pages[run_mode][is_d][page].par * 64); } +void bus::write_io(const uint16_t addr_in, const word_mode_t word_mode, const uint16_t value_in) +{ + uint16_t value = value_in; + + if (word_mode) { + if (addr_in == ADDR_PSW || addr_in == ADDR_PSW + 1) { // PSW + DOLOG(debug, true, "WRITE-I/O PSW %s: %03o", addr_in & 1 ? "MSB" : "LSB", value); + + uint16_t vtemp = c->getPSW(); + + if (addr_in == ADDR_PSW) + vtemp = (vtemp & 0xff00) | value; + else + vtemp = (vtemp & 0x00ff) | (value << 8); + + vtemp &= ~16; // cannot set T bit via this + + c->setPSW(vtemp, false); + + return; + } + + if (addr_in == ADDR_STACKLIM || addr_in == ADDR_STACKLIM + 1) { // stack limit register + DOLOG(debug, true, "WRITE-I/O stack limit register %s: %03o", addr_in & 1 ? "MSB" : "LSB", value); + + uint16_t v = c->getStackLimitRegister(); + + if (addr_in == ADDR_STACKLIM) + v = (v & 0xff00) | value; + else + v = (v & 0x00ff) | (value << 8); + + v |= 0377; + + c->setStackLimitRegister(v); + + return; + } + + if (addr_in == ADDR_MICROPROG_BREAK_REG || addr_in == ADDR_MICROPROG_BREAK_REG + 1) { // microprogram break register + DOLOG(debug, false, "WRITE-I/O micropram break register %s: %03o", addr_in & 1 ? "MSB" : "LSB", value); + + if (addr_in == ADDR_MICROPROG_BREAK_REG) + microprogram_break_register = (microprogram_break_register & 0xff00) | value; + else + microprogram_break_register = (microprogram_break_register & 0x00ff) | (value << 8); + + return; + } + + if (addr_in == ADDR_MMR0 || addr_in == ADDR_MMR0 + 1) { // MMR0 + DOLOG(debug, true, "WRITE-I/O MMR0 register %s: %03o", addr_in & 1 ? "MSB" : "LSB", value); + + if (addr_in == ADDR_MMR0) + MMR0 = (MMR0 & 0xff00) | value; + else + MMR0 = (MMR0 & 0x00ff) | (value << 8); + + return; + } + } + else { + if (addr_in == ADDR_PSW) { // PSW + DOLOG(debug, true, "WRITE-I/O PSW: %06o", value); + c->setPSW(value & ~16, false); + return; + } + + if (addr_in == ADDR_STACKLIM) { // stack limit register + DOLOG(debug, true, "WRITE-I/O stack limit register: %06o", value); + c->setStackLimitRegister(value & 0xff00); + return; + } + + if (addr_in >= ADDR_KERNEL_R && addr_in <= ADDR_KERNEL_R + 5) { // kernel R0-R5 + int reg = addr_in - ADDR_KERNEL_R; + DOLOG(debug, true, "WRITE-I/O kernel R%d: %06o", reg, value); + c->setRegister(reg, value); + return; + } + if (addr_in >= ADDR_USER_R && addr_in <= ADDR_USER_R + 5) { // user R0-R5 + int reg = addr_in - ADDR_USER_R; + DOLOG(debug, true, "WRITE-I/O user R%d: %06o", reg, value); + c->setRegister(reg, value); + return; + } + if (addr_in == ADDR_KERNEL_SP) { // kernel SP + DOLOG(debug, true, "WRITE-I/O kernel SP: %06o", value); + c->setStackPointer(0, value); + return; + } + if (addr_in == ADDR_PC) { // PC + DOLOG(debug, true, "WRITE-I/O PC: %06o", value); + c->setPC(value); + return; + } + if (addr_in == ADDR_SV_SP) { // supervisor SP + DOLOG(debug, true, "WRITE-I/O supervisor sp: %06o", value); + c->setStackPointer(1, value); + return; + } + if (addr_in == ADDR_USER_SP) { // user SP + DOLOG(debug, true, "WRITE-I/O user sp: %06o", value); + c->setStackPointer(3, value); + return; + } + + if (addr_in == ADDR_MICROPROG_BREAK_REG) { // microprogram break register + DOLOG(debug, false, "WRITE-I/O microprogram break register: %06o", value); + microprogram_break_register = value & 0xff; // only 8b on 11/70? + return; + } + } + + if (addr_in == ADDR_CPU_ERR) { // cpu error register + DOLOG(debug, true, "WRITE-I/O CPUERR: %06o", value); + CPUERR = 0; + return; + } + + if (addr_in == ADDR_MMR3) { // MMR3 + DOLOG(debug, true, "WRITE-I/O set MMR3: %06o", value); + MMR3 = value; + return; + } + + if (addr_in == ADDR_MMR0) { // MMR0 + DOLOG(debug, true, "WRITE-I/O set MMR0: %06o", value); + setMMR0(value); + return; + } + + if (addr_in == ADDR_PIR) { // PIR + DOLOG(debug, true, "WRITE-I/O set PIR: %06o", value); + + value &= 0177000; + + int bits = value >> 9; + + while(bits) { + value += 042; // bit 1...3 and 5...7 + bits >>= 1; + } + + PIR = value; + return; + } + + if (addr_in == ADDR_LFC) { // line frequency clock and status register +#if defined(BUILD_FOR_RP2040) + xSemaphoreTake(lf_csr_lock, portMAX_DELAY); +#else + std::unique_lock lck(lf_csr_lock); +#endif + + DOLOG(debug, true, "WRITE-I/O set line frequency clock/status register: %06o", value); + lf_csr = value; +#if defined(BUILD_FOR_RP2040) + xSemaphoreGive(lf_csr_lock); +#endif + return; + } + + if (tm11 && addr_in >= TM_11_BASE && addr_in < TM_11_END) { + DOLOG(debug, false, "WRITE-I/O TM11 register %d: %06o", (addr_in - TM_11_BASE) / 2, value); + word_mode ? tm11->writeByte(addr_in, value) : tm11->writeWord(addr_in, value); + return; + } + + if (rk05_ && addr_in >= RK05_BASE && addr_in < RK05_END) { + DOLOG(debug, false, "WRITE-I/O RK05 register %d: %06o", (addr_in - RK05_BASE) / 2, value); + word_mode ? rk05_->writeByte(addr_in, value) : rk05_->writeWord(addr_in, value); + return; + } + + if (rl02_ && addr_in >= RL02_BASE && addr_in < RL02_END) { + DOLOG(debug, false, "WRITE-I/O RL02 register %d: %06o", (addr_in - RL02_BASE) / 2, value); + word_mode ? rl02_->writeByte(addr_in, value) : rl02_->writeWord(addr_in, value); + return; + } + + if (tty_ && addr_in >= PDP11TTY_BASE && addr_in < PDP11TTY_END) { + DOLOG(debug, false, "WRITE-I/O TTY register %d: %06o", (addr_in - PDP11TTY_BASE) / 2, value); + word_mode ? tty_->writeByte(addr_in, value) : tty_->writeWord(addr_in, value); + return; + } + + /// MMU /// + // supervisor + if (addr_in >= ADDR_PDR_SV_START && addr_in < ADDR_PDR_SV_END) { + write_pdr(addr_in, 1, value, word_mode); + return; + } + if (addr_in >= ADDR_PAR_SV_START && addr_in < ADDR_PAR_SV_END) { + write_par(addr_in, 1, value, word_mode); + return; + } + + // kernel + if (addr_in >= ADDR_PDR_K_START && addr_in < ADDR_PDR_K_END) { + write_pdr(addr_in, 0, value, word_mode); + return; + } + if (addr_in >= ADDR_PAR_K_START && addr_in < ADDR_PAR_K_END) { + write_par(addr_in, 0, value, word_mode); + return; + } + + // user + if (addr_in >= ADDR_PDR_U_START && addr_in < ADDR_PDR_U_END) { + write_pdr(addr_in, 3, value, word_mode); + return; + } + if (addr_in >= ADDR_PAR_U_START && addr_in < ADDR_PAR_U_END) { + write_par(addr_in, 3, value, word_mode); + return; + } + //// + + if (addr_in >= 0177740 && addr_in <= 0177753) { // cache control register and others + // TODO + return; + } + + if (addr_in >= 0170200 && addr_in <= 0170377) { // unibus map + DOLOG(debug, false, "writing %06o to unibus map (%06o)", value, addr_in); + // TODO + return; + } + + if (addr_in == ADDR_CONSW) { // switch register + console_leds = value; + return; + } + + if (addr_in == ADDR_SYSSIZE || addr_in == ADDR_SYSSIZE + 2) // system size (is read-only) + return; + + if (addr_in == ADDR_SYSTEM_ID) // is r/o + return; + + /////////// + + uint32_t m_offset = addr_in - 0160000 + get_io_base(); + + DOLOG(debug, true, "WRITE-I/O UNHANDLED %08o(%c): %06o (base: %o)", m_offset, word_mode ? 'B' : 'W', value, get_io_base()); + + if (word_mode == wm_word && (addr_in & 1)) { + DOLOG(debug, true, "WRITE-I/O to %08o (value: %06o) - odd address!", m_offset, value); + + trap_odd(addr_in); + throw 8; + } + + c->trap(004); // no such i/o + throw 9; +} + void bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint16_t value, const rm_selection_t mode_selection, const d_i_space_t space) { int run_mode = mode_selection == rm_cur ? c->getPSW_runmode() : c->getPSW_prev_runmode(); @@ -757,257 +1022,7 @@ void bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint16_t va if (is_io) { uint16_t a = m_offset - io_base + 0160000; // TODO - if (word_mode) { - if (a == ADDR_PSW || a == ADDR_PSW + 1) { // PSW - DOLOG(debug, true, "WRITE-I/O PSW %s: %03o", a & 1 ? "MSB" : "LSB", value); - - uint16_t vtemp = c->getPSW(); - - if (a == ADDR_PSW) - vtemp = (vtemp & 0xff00) | value; - else - vtemp = (vtemp & 0x00ff) | (value << 8); - - vtemp &= ~16; // cannot set T bit via this - - c->setPSW(vtemp, false); - - return; - } - - if (a == ADDR_STACKLIM || a == ADDR_STACKLIM + 1) { // stack limit register - DOLOG(debug, true, "WRITE-I/O stack limit register %s: %03o", a & 1 ? "MSB" : "LSB", value); - - uint16_t v = c->getStackLimitRegister(); - - if (a == ADDR_STACKLIM) - v = (v & 0xff00) | value; - else - v = (v & 0x00ff) | (value << 8); - - v |= 0377; - - c->setStackLimitRegister(v); - - return; - } - - if (a == ADDR_MICROPROG_BREAK_REG || a == ADDR_MICROPROG_BREAK_REG + 1) { // microprogram break register - DOLOG(debug, false, "WRITE-I/O micropram break register %s: %03o", a & 1 ? "MSB" : "LSB", value); - - if (a == ADDR_MICROPROG_BREAK_REG) - microprogram_break_register = (microprogram_break_register & 0xff00) | value; - else - microprogram_break_register = (microprogram_break_register & 0x00ff) | (value << 8); - - return; - } - - if (a == ADDR_MMR0 || a == ADDR_MMR0 + 1) { // MMR0 - DOLOG(debug, true, "WRITE-I/O MMR0 register %s: %03o", a & 1 ? "MSB" : "LSB", value); - - if (a == ADDR_MMR0) - MMR0 = (MMR0 & 0xff00) | value; - else - MMR0 = (MMR0 & 0x00ff) | (value << 8); - - return; - } - } - else { - if (a == ADDR_PSW) { // PSW - DOLOG(debug, true, "WRITE-I/O PSW: %06o", value); - c->setPSW(value & ~16, false); - return; - } - - if (a == ADDR_STACKLIM) { // stack limit register - DOLOG(debug, true, "WRITE-I/O stack limit register: %06o", value); - c->setStackLimitRegister(value & 0xff00); - return; - } - - if (a >= ADDR_KERNEL_R && a <= ADDR_KERNEL_R + 5) { // kernel R0-R5 - int reg = a - ADDR_KERNEL_R; - DOLOG(debug, true, "WRITE-I/O kernel R%d: %06o", reg, value); - c->setRegister(reg, value); - return; - } - if (a >= ADDR_USER_R && a <= ADDR_USER_R + 5) { // user R0-R5 - int reg = a - ADDR_USER_R; - DOLOG(debug, true, "WRITE-I/O user R%d: %06o", reg, value); - c->setRegister(reg, value); - return; - } - if (a == ADDR_KERNEL_SP) { // kernel SP - DOLOG(debug, true, "WRITE-I/O kernel SP: %06o", value); - c->setStackPointer(0, value); - return; - } - if (a == ADDR_PC) { // PC - DOLOG(debug, true, "WRITE-I/O PC: %06o", value); - c->setPC(value); - return; - } - if (a == ADDR_SV_SP) { // supervisor SP - DOLOG(debug, true, "WRITE-I/O supervisor sp: %06o", value); - c->setStackPointer(1, value); - return; - } - if (a == ADDR_USER_SP) { // user SP - DOLOG(debug, true, "WRITE-I/O user sp: %06o", value); - c->setStackPointer(3, value); - return; - } - - if (a == ADDR_MICROPROG_BREAK_REG) { // microprogram break register - DOLOG(debug, false, "WRITE-I/O microprogram break register: %06o", value); - microprogram_break_register = value & 0xff; // only 8b on 11/70? - return; - } - } - - if (a == ADDR_CPU_ERR) { // cpu error register - DOLOG(debug, true, "WRITE-I/O CPUERR: %06o", value); - CPUERR = 0; - return; - } - - if (a == ADDR_MMR3) { // MMR3 - DOLOG(debug, true, "WRITE-I/O set MMR3: %06o", value); - MMR3 = value; - return; - } - - if (a == ADDR_MMR0) { // MMR0 - DOLOG(debug, true, "WRITE-I/O set MMR0: %06o", value); - setMMR0(value); - return; - } - - if (a == ADDR_PIR) { // PIR - DOLOG(debug, true, "WRITE-I/O set PIR: %06o", value); - - value &= 0177000; - - int bits = value >> 9; - - while(bits) { - value += 042; // bit 1...3 and 5...7 - bits >>= 1; - } - - PIR = value; - return; - } - - if (a == ADDR_LFC) { // line frequency clock and status register -#if defined(BUILD_FOR_RP2040) - xSemaphoreTake(lf_csr_lock, portMAX_DELAY); -#else - std::unique_lock lck(lf_csr_lock); -#endif - - DOLOG(debug, true, "WRITE-I/O set line frequency clock/status register: %06o", value); - lf_csr = value; -#if defined(BUILD_FOR_RP2040) - xSemaphoreGive(lf_csr_lock); -#endif - return; - } - - if (tm11 && a >= TM_11_BASE && a < TM_11_END) { - DOLOG(debug, false, "WRITE-I/O TM11 register %d: %06o", (a - TM_11_BASE) / 2, value); - word_mode ? tm11->writeByte(a, value) : tm11->writeWord(a, value); - return; - } - - if (rk05_ && a >= RK05_BASE && a < RK05_END) { - DOLOG(debug, false, "WRITE-I/O RK05 register %d: %06o", (a - RK05_BASE) / 2, value); - word_mode ? rk05_->writeByte(a, value) : rk05_->writeWord(a, value); - return; - } - - if (rl02_ && a >= RL02_BASE && a < RL02_END) { - DOLOG(debug, false, "WRITE-I/O RL02 register %d: %06o", (a - RL02_BASE) / 2, value); - word_mode ? rl02_->writeByte(a, value) : rl02_->writeWord(a, value); - return; - } - - if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END) { - DOLOG(debug, false, "WRITE-I/O TTY register %d: %06o", (a - PDP11TTY_BASE) / 2, value); - word_mode ? tty_->writeByte(a, value) : tty_->writeWord(a, value); - return; - } - - /// MMU /// - // supervisor - if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END) { - write_pdr(a, 1, value, word_mode); - return; - } - if (a >= ADDR_PAR_SV_START && a < ADDR_PAR_SV_END) { - write_par(a, 1, value, word_mode); - return; - } - - // kernel - if (a >= ADDR_PDR_K_START && a < ADDR_PDR_K_END) { - write_pdr(a, 0, value, word_mode); - return; - } - if (a >= ADDR_PAR_K_START && a < ADDR_PAR_K_END) { - write_par(a, 0, value, word_mode); - return; - } - - // user - if (a >= ADDR_PDR_U_START && a < ADDR_PDR_U_END) { - write_pdr(a, 3, value, word_mode); - return; - } - if (a >= ADDR_PAR_U_START && a < ADDR_PAR_U_END) { - write_par(a, 3, value, word_mode); - return; - } - //// - - if (a >= 0177740 && a <= 0177753) { // cache control register and others - // TODO - return; - } - - if (a >= 0170200 && a <= 0170377) { // unibus map - DOLOG(debug, false, "writing %06o to unibus map (%06o)", value, a); - // TODO - return; - } - - if (a == ADDR_CONSW) { // switch register - console_leds = value; - return; - } - - if (a == ADDR_SYSSIZE || a == ADDR_SYSSIZE + 2) // system size (is read-only) - return; - - if (a == ADDR_SYSTEM_ID) // is r/o - return; - - /////////// - - DOLOG(debug, true, "WRITE-I/O UNHANDLED %08o(%c): %06o (base: %o)", m_offset, word_mode ? 'B' : 'W', value, get_io_base()); - - if (word_mode == wm_word && (a & 1)) { - DOLOG(debug, true, "WRITE-I/O to %08o (value: %06o) - odd address!", m_offset, value); - - trap_odd(a); - throw 8; - } - - c->trap(004); // no such i/o - - throw 9; + write_io(a, word_mode, value); } if (word_mode == wm_word && (addr_in & 1)) { diff --git a/bus.h b/bus.h index 714e455..ae1808c 100644 --- a/bus.h +++ b/bus.h @@ -149,12 +149,14 @@ public: uint16_t readByte(const uint16_t a) { return read(a, wm_byte, rm_cur); } uint16_t readWord(const uint16_t a, const d_i_space_t s = i_space); uint16_t peekWord(const uint16_t a); + uint16_t read_io(const uint16_t addr_in, const word_mode_t word_mode, const bool peek_only); uint16_t readUnibusByte(const uint16_t a); void write(const uint16_t a, const word_mode_t word_mode, uint16_t value, const rm_selection_t mode_selection, const d_i_space_t s = i_space); void writeByte(const uint16_t a, const uint8_t value) { return write(a, wm_byte, value, rm_cur); } void writeWord(const uint16_t a, const uint16_t value, const d_i_space_t s = i_space); + void write_io(const uint16_t addr_in, const word_mode_t word_mode, const uint16_t value); uint16_t readPhysical(const uint32_t a); void writePhysical(const uint32_t a, const uint16_t value); diff --git a/cpu.cpp b/cpu.cpp index 705ba69..eaff233 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -682,6 +682,8 @@ bool cpu::additional_double_operand_instructions(const uint16_t instr) bool sign = SIGN(R, wm_word); + DOLOG(debug, true, "ASH shift %d, value %08o", shift, oldR); + if (shift == 0) { setPSW_c(false); setPSW_v(false); @@ -733,6 +735,8 @@ bool cpu::additional_double_operand_instructions(const uint16_t instr) bool sign = R0R1 & 0x80000000; + DOLOG(debug, true, "ASH shift %d, value %08o", shift, R0R1); + setPSW_v(false); if (shift == 0) @@ -1290,13 +1294,15 @@ bool cpu::single_operand_instructions(const uint16_t instr) } } - if (set_flags) - setPSW_flags_nzv(v, wm_word); + if (!instruction_aborted) { + if (set_flags) + setPSW_flags_nzv(v, wm_word); - // put on current stack - pushStack(v); + // put on current stack + pushStack(v); - b->addToMMR1(-2, 6); + b->addToMMR1(-2, 6); + } break; } @@ -1319,23 +1325,27 @@ bool cpu::single_operand_instructions(const uint16_t instr) uint32_t phys_a = word_mode == wm_byte ? phys.physical_data : phys.physical_instruction; bool phys_psw = word_mode == wm_byte ? phys.physical_data_is_psw : phys.physical_instruction_is_psw; + mtpi_count++; + if (phys_a >= b->get_io_base()) { b->write(a.addr.value(), wm_word, v, rm_prev); // put in '13/12' address space set_flags = phys_psw; } else { - mtpi_count++; - DOLOG(debug, true, "%lu %06o MTP%c %06o: %06o (physical: %o)", mtpi_count, pc-2, word_mode == wm_byte ? 'D' : 'I', a.addr.value(), v, phys_a); b->check_odd_addressing(phys_a, prev_run_mode, word_mode == wm_byte ? d_space : i_space, true); // TODO d/i space must depend on the check done in calculate_physical_address b->writePhysical(phys_a, v); } + + DOLOG(debug, true, "%lu %06o MTP%c %06o: %06o (physical: %o)", mtpi_count, pc-2, word_mode == wm_byte ? 'D' : 'I', a.addr.value(), v, phys_a); } - if (set_flags) - setPSW_flags_nzv(v, wm_word); + if (!instruction_aborted) { + if (set_flags) + setPSW_flags_nzv(v, wm_word); - b->addToMMR1(2, 6); + b->addToMMR1(2, 6); + } break; } @@ -1671,6 +1681,7 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt) uint16_t before_pc = 0; it_is_a_trap = true; + instruction_aborted = true; do { try { @@ -2230,6 +2241,7 @@ std::map > cpu::disassemble(const uint16_t void cpu::step_a() { it_is_a_trap = false; + instruction_aborted = false; if ((b->getMMR0() & 0160000) == 0) b->clearMMR1(); diff --git a/cpu.h b/cpu.h index afbee62..05b43ab 100644 --- a/cpu.h +++ b/cpu.h @@ -41,6 +41,7 @@ private: uint64_t wait_time { 0 }; bool it_is_a_trap { false }; uint64_t mtpi_count { 0 }; + bool instruction_aborted{ false }; // level, vector std::map > queued_interrupts;