From 9f2339cda73a62a27100277db6a54d233b8eb13d Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Thu, 25 Apr 2024 09:45:55 +0200 Subject: [PATCH] split bus class into a mmu class --- CMakeLists.txt | 1 + breakpoint_register.cpp | 8 +- bus.cpp | 310 +++++++++++++--------------------------- bus.h | 48 ++----- cpu.cpp | 32 ++--- debugger.cpp | 12 +- gen.h | 6 + main.cpp | 4 +- mmu.cpp | 160 +++++++++++++++++++++ mmu.h | 71 +++++++++ 10 files changed, 377 insertions(+), 275 deletions(-) create mode 100644 mmu.cpp create mode 100644 mmu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a9016a5..bdb83e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable( log.cpp main.cpp memory.cpp + mmu.cpp rk05.cpp rl02.cpp terminal.cpp diff --git a/breakpoint_register.cpp b/breakpoint_register.cpp index 20b2cc2..9a94f63 100644 --- a/breakpoint_register.cpp +++ b/breakpoint_register.cpp @@ -48,16 +48,16 @@ std::optional breakpoint_register::is_triggered() const switch(reg) { case hr_mmr0: - v = b->getMMR0(); + v = b->getMMU()->getMMR0(); break; case hr_mmr1: - v = b->getMMR1(); + v = b->getMMU()->getMMR1(); break; case hr_mmr2: - v = b->getMMR2(); + v = b->getMMU()->getMMR2(); break; case hr_mmr3: - v = b->getMMR3(); + v = b->getMMU()->getMMR3(); break; case hr_psw: v = c->getPSW(); diff --git a/bus.cpp b/bus.cpp index 6913a36..41932b2 100644 --- a/bus.cpp +++ b/bus.cpp @@ -10,6 +10,7 @@ #include "cpu.h" #include "log.h" #include "memory.h" +#include "mmu.h" #include "tm-11.h" #include "tty.h" #include "utils.h" @@ -18,7 +19,6 @@ #include #endif -constexpr const int di_ena_mask[4] = { 4, 2, 0, 1 }; bus::bus() { @@ -38,6 +38,7 @@ bus::~bus() delete rk05_; delete rl02_; delete tty_; + delete mmu_; delete m; } @@ -57,9 +58,7 @@ void bus::reset() { m->reset(); - memset(pages, 0x00, sizeof pages); - - CPUERR = MMR0 = MMR1 = MMR2 = MMR3 = PIR = CSR = 0; + mmu_->reset(); if (c) c->reset(); @@ -105,38 +104,18 @@ void bus::add_tty(tty *const tty_) void bus::init() { - MMR0 = 0; - MMR3 = 0; -} - -uint16_t bus::read_pdr(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only) -{ - int page = (a >> 1) & 7; - bool is_d = a & 16; - uint16_t t = pages[run_mode][is_d][page].pdr; - - if (!peek_only) - DOLOG(debug, false, "READ-I/O PDR run-mode %d: %c for %d: %o", run_mode, is_d ? 'D' : 'I', page, t); - - return word_mode ? (a & 1 ? t >> 8 : t & 255) : t; -} - -uint16_t bus::read_par(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only) -{ - int page = (a >> 1) & 7; - bool is_d = a & 16; - uint16_t t = pages[run_mode][is_d][page].par; - - if (!peek_only) - DOLOG(debug, false, "READ-I/O PAR run-mode %d: %c for %d: %o (phys: %07o)", run_mode, is_d ? 'D' : 'I', page, t, t * 64); - - return word_mode ? (a & 1 ? t >> 8 : t & 255) : t; + mmu_->setMMR0(0); + mmu_->setMMR3(0); } void bus::trap_odd(const uint16_t a) { - MMR0 &= ~(7 << 1); - MMR0 |= (a >> 13) << 1; + uint16_t temp = mmu_->getMMR0(); + + temp &= ~(7 << 1); + temp |= (a >> 13) << 1; + + mmu_->setMMR0(temp); c->trap(004); // invalid access } @@ -196,7 +175,7 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm } if (a == ADDR_CPU_ERR) { // cpu error register - uint16_t temp = CPUERR & 0xff; + uint16_t temp = mmu_->getCPUERR() & 0xff; if (!peek_only) DOLOG(debug, false, "READ-I/O CPU error: %03o", temp); return temp; } @@ -222,6 +201,8 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm if (a == ADDR_PIR || a == ADDR_PIR + 1) { // PIR uint16_t temp = 0; + uint16_t PIR = mmu_->getPIR(); + if (word_mode == wm_word) temp = PIR; else @@ -262,17 +243,17 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm /// MMU /// if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END) - return read_pdr(a, 1, word_mode, peek_only); + return mmu_->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); + return mmu_->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); + return mmu_->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); + return mmu_->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); + return mmu_->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); + return mmu_->read_par(a, 3, word_mode, peek_only); /////////// if (a >= 0177740 && a <= 0177753) { // cache control register and others @@ -327,37 +308,37 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm } if (a == ADDR_MMR0) { - uint8_t temp = MMR0; + uint8_t temp = mmu_->getMMR0(); 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; + uint8_t temp = mmu_->getMMR0() >> 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; + uint16_t temp = mmu_->getMMR0(); if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0: %06o", temp); return temp; } if (a == ADDR_MMR1) { // MMR1 - uint16_t temp = MMR1; + uint16_t temp = mmu_->getMMR1(); if (!peek_only) DOLOG(debug, false, "READ-I/O MMR1: %06o", temp); return temp; } if (a == ADDR_MMR2) { // MMR2 - uint16_t temp = MMR2; + uint16_t temp = mmu_->getMMR2(); if (!peek_only) DOLOG(debug, false, "READ-I/O MMR2: %06o", temp); return temp; } if (a == ADDR_MMR3) { // MMR3 - uint16_t temp = MMR3; + uint16_t temp = mmu_->getMMR3(); if (!peek_only) DOLOG(debug, false, "READ-I/O MMR3: %06o", temp); return temp; } @@ -375,7 +356,7 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm } if (a == ADDR_CPU_ERR) { // cpu error register - uint16_t temp = CPUERR; + uint16_t temp = mmu_->getCPUERR(); if (!peek_only) DOLOG(debug, false, "READ-I/O CPUERR: %06o", temp); return temp; } @@ -464,49 +445,11 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm return temp; } -void bus::setMMR0(uint16_t value) -{ - value &= ~(3 << 10); // bit 10 & 11 always read as 0 - - if (value & 1) - value &= ~(7l << 13); // reset error bits - - if (MMR0 & 0160000) { - if ((value & 1) == 0) - value &= 254; // bits 7...1 are protected - } - -// TODO if bit 15/14/13 are set (either of them), then do not modify bit 1...7 - - MMR0 = value; -} - -void bus::setMMR0Bit(const int bit) -{ - assert(bit != 10 && bit != 11); - assert(bit < 16 && bit >= 0); - - MMR0 |= 1 << bit; -} - -void bus::clearMMR0Bit(const int bit) -{ - assert(bit != 10 && bit != 11); - assert(bit < 16 && bit >= 0); - - MMR0 &= ~(1 << bit); -} - -void bus::setMMR2(const uint16_t value) -{ - 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) { if (a & 1) { if (is_write) - pages[run_mode][space == d_space][a >> 13].pdr |= 1 << 7; + mmu_->set_page_trapped(run_mode, space == d_space, a >> 13); trap_odd(a); @@ -518,25 +461,25 @@ memory_addresses_t bus::calculate_physical_address(const int run_mode, const uin { const uint8_t apf = a >> 13; // active page field - if ((MMR0 & 1) == 0) { + if (mmu_->is_enabled() == false) { bool is_psw = a == ADDR_PSW; return { a, apf, a, is_psw, a, is_psw }; } - uint32_t physical_instruction = pages[run_mode][0][apf].par * 64; - uint32_t physical_data = pages[run_mode][1][apf].par * 64; + uint32_t physical_instruction = mmu_->get_physical_memory_offset(run_mode, 0, apf); + uint32_t physical_data = mmu_->get_physical_memory_offset(run_mode, 1, apf); uint16_t p_offset = a & 8191; // page offset physical_instruction += p_offset; physical_data += p_offset; - if ((MMR3 & 16) == 0) { // offset is 18bit + if ((mmu_->getMMR3() & 16) == 0) { // offset is 18bit physical_instruction &= 0x3ffff; physical_data &= 0x3ffff; } - if (get_use_data_space(run_mode) == false) + if (mmu_->get_use_data_space(run_mode) == false) physical_data = physical_instruction; uint32_t io_base = get_io_base(); @@ -568,14 +511,9 @@ void bus::mmudebug(const uint16_t a) } } -bool bus::get_use_data_space(const int run_mode) const -{ - return !!(MMR3 & di_ena_mask[run_mode]); -} - std::pair bus::get_trap_action(const int run_mode, const bool d, const int apf, const bool is_write) { - const int access_control = pages[run_mode][d][apf].pdr & 7; + const int access_control = mmu_->get_access_control(run_mode, d, apf); trap_action_t trap_action = T_PROCEED; @@ -609,18 +547,18 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c { uint32_t m_offset = a; - if ((MMR0 & 1 /* mmu enabled */) || (is_write && (MMR0 & (1 << 8 /* maintenance check */)))) { + if (mmu_->is_enabled() || (is_write && (mmu_->getMMR0() & (1 << 8 /* maintenance check */)))) { const uint8_t apf = a >> 13; // active page field - bool d = space == d_space && get_use_data_space(run_mode) ? space == d_space : false; + bool d = space == d_space && mmu_->get_use_data_space(run_mode) ? space == d_space : false; uint16_t p_offset = a & 8191; // page offset - m_offset = pages[run_mode][d][apf].par * 64; // memory offset + m_offset = mmu_->get_physical_memory_offset(run_mode, d, apf); m_offset += p_offset; - if ((MMR3 & 16) == 0) // off is 18bit + if ((mmu_->getMMR3() & 16) == 0) // off is 18bit m_offset &= 0x3ffff; uint32_t io_base = get_io_base(); @@ -634,28 +572,32 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c if (trap_action != T_PROCEED) { if (is_write) - pages[run_mode][d][apf].pdr |= 1 << 7; + mmu_->set_page_trapped(run_mode, d, apf); - if ((MMR0 & 0160000) == 0) { - MMR0 &= ~((1l << 15) | (1 << 14) | (1 << 13) | (1 << 12) | (3 << 5) | (7 << 1) | (1 << 4)); + if (mmu_->is_locked() == false) { + uint16_t temp = mmu_->getMMR0(); + + temp &= ~((1l << 15) | (1 << 14) | (1 << 13) | (1 << 12) | (3 << 5) | (7 << 1) | (1 << 4)); if (is_write && access_control != 6) - MMR0 |= 1 << 13; // read-only + temp |= 1 << 13; // read-only // if (access_control == 0 || access_control == 4) - MMR0 |= 1l << 15; // not resident + temp |= 1l << 15; // not resident else - MMR0 |= 1 << 13; // read-only + temp |= 1 << 13; // read-only - MMR0 |= run_mode << 5; // TODO: kernel-mode or user-mode when a trap occurs in user-mode? + temp |= run_mode << 5; // TODO: kernel-mode or user-mode when a trap occurs in user-mode? - MMR0 |= apf << 1; // add current page + temp |= apf << 1; // add current page - MMR0 |= d << 4; + temp |= d << 4; + + mmu_->setMMR0(temp); + + DOLOG(debug, false, "MMR0: %06o", temp); } - DOLOG(debug, false, "MMR0: %06o", MMR0); - if (trap_action == T_TRAP_250) { DOLOG(debug, false, "Page access %d (for virtual address %06o): trap 0250", access_control, a); @@ -677,29 +619,33 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c DOLOG(debug, !peek_only, "bus::calculate_physical_address %o >= %o", m_offset, n_pages * 8192l); DOLOG(debug, false, "TRAP(04) (throw 6) on address %06o", a); - if ((MMR0 & 0160000) == 0) { - MMR0 &= 017777; - MMR0 |= 1l << 15; // non-resident + if (mmu_->is_locked() == false) { + uint16_t temp = mmu_->getMMR0(); - MMR0 &= ~14; // add current page - MMR0 |= apf << 1; + temp &= 017777; + temp |= 1l << 15; // non-resident - MMR0 &= ~(3 << 5); - MMR0 |= run_mode << 5; + temp &= ~14; // add current page + temp |= apf << 1; + + temp &= ~(3 << 5); + temp |= run_mode << 5; + + mmu_->setMMR0(temp); } if (is_write) - pages[run_mode][d][apf].pdr |= 1 << 7; + mmu_->set_page_trapped(run_mode, d, apf); c->trap(04); throw 6; } - uint16_t pdr_len = (pages[run_mode][d][apf].pdr >> 8) & 127; + uint16_t pdr_len = mmu_->get_pdr_len(run_mode, d, apf); uint16_t pdr_cmp = (a >> 6) & 127; - bool direction = pages[run_mode][d][apf].pdr & 8; + bool direction = mmu_->get_pdr_direction(run_mode, d, apf); // DOLOG(debug, false, "p_offset %06o pdr_len %06o direction %d, run_mode %d, apf %d, pdr: %06o", p_offset, pdr_len, direction, run_mode, apf, pages[run_mode][d][apf].pdr); @@ -708,28 +654,34 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c DOLOG(debug, false, "TRAP(0250) (throw 7) on address %06o", a); c->trap(0250); // invalid access - if ((MMR0 & 0160000) == 0) { - MMR0 &= 017777; - MMR0 |= 1 << 14; // length + if (mmu_->is_locked() == false) { + uint16_t temp = mmu_->getMMR0(); - MMR0 &= ~14; // add current page - MMR0 |= apf << 1; + temp &= 017777; + temp |= 1 << 14; // length - MMR0 &= ~(3 << 5); - MMR0 |= run_mode << 5; + temp &= ~14; // add current page + temp |= apf << 1; - MMR0 &= ~(1 << 4); - MMR0 |= d << 4; + temp &= ~(3 << 5); + temp |= run_mode << 5; + + temp &= ~(1 << 4); + temp |= d << 4; + + mmu_->setMMR0(temp); } if (is_write) - pages[run_mode][d][apf].pdr |= 1 << 7; + mmu_->set_page_trapped(run_mode, d, apf); throw 7; } } - DOLOG(debug, false, "virtual address %06o maps to physical address %08o (run_mode: %d, apf: %d, par: %08o, poff: %o, AC: %d, %s)", a, m_offset, run_mode, apf, pages[run_mode][d][apf].par * 64, p_offset, pages[run_mode][d][apf].pdr & 7, d ? "D" : "I"); + DOLOG(debug, false, "virtual address %06o maps to physical address %08o (run_mode: %d, apf: %d, par: %08o, poff: %o, AC: %d, %s)", a, m_offset, run_mode, apf, + mmu_->get_physical_memory_offset(run_mode, d, apf), + p_offset, mmu_->get_access_control(run_mode, d, apf), d ? "D" : "I"); } else { // DOLOG(debug, false, "no MMU (read physical address %08o)", m_offset); @@ -738,69 +690,6 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c return m_offset; } -void bus::clearMMR1() -{ - MMR1 = 0; -} - -void bus::addToMMR1(const int8_t delta, const uint8_t reg) -{ - assert(reg >= 0 && reg <= 7); - assert(delta >= -2 && delta <= 2); - - assert((getMMR0() & 0160000) == 0); // MMR1 should not be locked - -#if defined(ESP32) -// if (MMR1 > 255) -// esp_backtrace_print(32); -#else - if (MMR1 > 255) { - extern FILE *lfh; - fflush(lfh); - } - assert(MMR1 < 256); -#endif - - MMR1 <<= 8; - - MMR1 |= (delta & 31) << 3; - MMR1 |= reg; -} - -void bus::write_pdr(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode) -{ - bool is_d = a & 16; - int page = (a >> 1) & 7; - - if (word_mode == wm_byte) { - assert(a != 0 || value < 256); - - update_word(&pages[run_mode][is_d][page].pdr, a & 1, value); - } - else { - pages[run_mode][is_d][page].pdr = value; - } - - pages[run_mode][is_d][page].pdr &= ~(32768 + 128 /*A*/ + 64 /*W*/ + 32 + 16); // set bit 4, 5 & 15 to 0 as they are unused and A/W are set to 0 by writes - - DOLOG(debug, false, "WRITE-I/O PDR run-mode %d: %c for %d: %o [%d]", run_mode, is_d ? 'D' : 'I', page, value, word_mode); -} - -void bus::write_par(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode) -{ - bool is_d = a & 16; - int page = (a >> 1) & 7; - - if (word_mode == wm_byte) - update_word(&pages[run_mode][is_d][page].par, a & 1, value); - else - pages[run_mode][is_d][page].par = value; - - pages[run_mode][is_d][page].pdr &= ~(128 /*A*/ + 64 /*W*/); // reset PDR A/W when PAR is written to - - DOLOG(debug, false, "WRITE-I/O PAR run-mode %d: %c for %d: %o (%07o)", run_mode, is_d ? 'D' : 'I', page, word_mode == wm_byte ? value & 0xff : value, pages[run_mode][is_d][page].par * 64); -} - write_rc_t 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(); @@ -809,10 +698,10 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1 bool is_data = space == d_space; - bool d = is_data && get_use_data_space(run_mode) ? is_data : false; + bool d = is_data && mmu_->get_use_data_space(run_mode) ? is_data : false; - if ((MMR0 & 1) == 1 && (addr_in & 1) == 0 && addr_in != ADDR_MMR0) - pages[run_mode][d][apf].pdr |= 64; // set 'W' (written to) bit + if (mmu_->is_enabled() && (addr_in & 1) == 0 /* TODO remove this? */ && addr_in != ADDR_MMR0) + mmu_->set_page_written_to(run_mode, d, apf); uint32_t m_offset = calculate_physical_address(run_mode, addr_in, true, true, false, space); @@ -862,7 +751,9 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1 if (a == ADDR_MMR0 || a == ADDR_MMR0 + 1) { // MMR0 DOLOG(debug, false, "WRITE-I/O MMR0 register %s: %03o", a & 1 ? "MSB" : "LSB", value); - update_word(&MMR0, a & 1, value); + uint16_t temp = mmu_->getMMR0(); + update_word(&temp, a & 1, value); + mmu_->setMMR0(temp); return { false }; } @@ -922,19 +813,19 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1 if (a == ADDR_CPU_ERR) { // cpu error register DOLOG(debug, false, "WRITE-I/O CPUERR: %06o", value); - CPUERR = 0; + mmu_->setCPUERR(0); return { false }; } if (a == ADDR_MMR3) { // MMR3 DOLOG(debug, false, "WRITE-I/O set MMR3: %06o", value); - MMR3 = value; + mmu_->setMMR3(value); return { false }; } if (a == ADDR_MMR0) { // MMR0 DOLOG(debug, false, "WRITE-I/O set MMR0: %06o", value); - setMMR0(value); + mmu_->setMMR0(value); return { false }; } @@ -950,7 +841,8 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1 bits >>= 1; } - PIR = value; + mmu_->setPIR(value); + return { false }; } @@ -1001,31 +893,31 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1 /// MMU /// // supervisor if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END) { - write_pdr(a, 1, value, word_mode); + mmu_->write_pdr(a, 1, value, word_mode); return { false }; } if (a >= ADDR_PAR_SV_START && a < ADDR_PAR_SV_END) { - write_par(a, 1, value, word_mode); + mmu_->write_par(a, 1, value, word_mode); return { false }; } // kernel if (a >= ADDR_PDR_K_START && a < ADDR_PDR_K_END) { - write_pdr(a, 0, value, word_mode); + mmu_->write_pdr(a, 0, value, word_mode); return { false }; } if (a >= ADDR_PAR_K_START && a < ADDR_PAR_K_END) { - write_par(a, 0, value, word_mode); + mmu_->write_par(a, 0, value, word_mode); return { false }; } // user if (a >= ADDR_PDR_U_START && a < ADDR_PDR_U_END) { - write_pdr(a, 3, value, word_mode); + mmu_->write_pdr(a, 3, value, word_mode); return { false }; } if (a >= ADDR_PAR_U_START && a < ADDR_PAR_U_END) { - write_par(a, 3, value, word_mode); + mmu_->write_par(a, 3, value, word_mode); return { false }; } //// diff --git a/bus.h b/bus.h index fb3eac0..eab0947 100644 --- a/bus.h +++ b/bus.h @@ -8,9 +8,11 @@ #include #include -#include "tm-11.h" +#include "gen.h" +#include "mmu.h" #include "rk05.h" #include "rl02.h" +#include "tm-11.h" #if defined(BUILD_FOR_RP2040) #include "rp2040.h" @@ -70,12 +72,6 @@ class cpu; class memory; class tty; -typedef enum { d_space, i_space } d_i_space_t; - -typedef enum { wm_word = 0, wm_byte = 1 } word_mode_t; - -typedef enum { rm_prev, rm_cur } rm_selection_t; - typedef enum { T_PROCEED, T_ABORT_4, T_TRAP_250 } trap_action_t; typedef struct { @@ -87,10 +83,6 @@ typedef struct { bool physical_data_is_psw; } memory_addresses_t; -typedef struct { - uint16_t par, pdr; -} page_t; - typedef struct { bool is_psw; } write_rc_t; @@ -104,14 +96,11 @@ private: rl02 *rl02_ { nullptr }; tty *tty_ { nullptr }; + mmu *mmu_ { nullptr }; + int n_pages { DEFAULT_N_PAGES }; memory *m { nullptr }; - // 8 pages, D/I, 3 modes and 1 invalid mode - page_t pages[4][2][8]; - - uint16_t MMR0 { 0 }, MMR1 { 0 }, MMR2 { 0 }, MMR3 { 0 }, CPUERR { 0 }, PIR { 0 }, CSR { 0 }; - #if defined(BUILD_FOR_RP2040) SemaphoreHandle_t lf_csr_lock { xSemaphoreCreateBinary() }; #else @@ -124,11 +113,6 @@ private: uint16_t console_switches { 0 }; uint16_t console_leds { 0 }; - uint16_t read_pdr (const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only); - uint16_t read_par (const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only); - void write_pdr(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode); - void write_par(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode); - public: bus(); ~bus(); @@ -153,9 +137,11 @@ public: void add_rl02(rl02 *const rl02_); void add_tty (tty *const tty_); - cpu *getCpu() { return this->c; } + cpu *getCpu() { return c; } - tty *getTty() { return this->tty_; } + tty *getTty() { return tty_; } + + mmu *getMMU() { return mmu_; } void init(); // invoked by 'RESET' command @@ -178,29 +164,15 @@ public: void writeUnibusByte(const uint32_t a, const uint8_t value); - uint16_t getMMR0() const { return MMR0; } - uint16_t getMMR1() const { return MMR1; } - uint16_t getMMR2() const { return MMR2; } - uint16_t getMMR3() const { return MMR3; } - uint16_t getMMR(int nr) const { const uint16_t *const mmrs[] { &MMR0, &MMR1, &MMR2, &MMR3 }; return *mmrs[nr]; } - bool isMMR1Locked() const { return !!(MMR0 & 0160000); } - void clearMMR1(); - void addToMMR1(const int8_t delta, const uint8_t reg); - void setMMR0(const uint16_t value); - void setMMR0Bit(const int bit); - void clearMMR0Bit(const int bit); - void setMMR2(uint16_t value); - void check_odd_addressing(const uint16_t a, const int run_mode, const d_i_space_t space, const bool is_write); void trap_odd(const uint16_t a); - uint32_t get_io_base() const { return MMR0 & 1 ? (MMR3 & 16 ? 017760000 : 0760000) : 0160000; } + uint32_t get_io_base() const { return mmu_->getMMR0() & 1 ? (mmu_->getMMR3() & 16 ? 017760000 : 0760000) : 0160000; } bool is_psw(const uint16_t addr, const int run_mode, const d_i_space_t space) const; std::pair get_trap_action(const int run_mode, const bool d, const int apf, const bool is_write); uint32_t calculate_physical_address(const int run_mode, const uint16_t a, const bool trap_on_failure, const bool is_write, const bool peek_only, const d_i_space_t space); - bool get_use_data_space(const int run_mode) const; memory_addresses_t calculate_physical_address(const int run_mode, const uint16_t a) const; void check_address(const bool trap_on_failure, const bool is_write, const memory_addresses_t & addr, const word_mode_t word_mode, const bool is_data, const int run_mode); }; diff --git a/cpu.cpp b/cpu.cpp index 8ae9891..d5e9582 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -460,10 +460,10 @@ void cpu::queue_interrupt(const uint8_t level, const uint8_t vector) void cpu::addToMMR1(const gam_rc_t & g) { - if (!b->isMMR1Locked() && g.mmr1_update.has_value()) { + if (!b->getMMU()->isMMR1Locked() && g.mmr1_update.has_value()) { assert(g.mmr1_update.value().delta); - b->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg); + b->getMMU()->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg); } } @@ -472,7 +472,7 @@ gam_rc_t cpu::getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t wo { gam_rc_t g { word_mode, mode_selection, i_space, mode, { }, { }, { }, { } }; - d_i_space_t isR7_space = reg == 7 ? i_space : (b->get_use_data_space(getPSW_runmode()) ? d_space : i_space); + d_i_space_t isR7_space = reg == 7 ? i_space : (b->getMMU()->get_use_data_space(getPSW_runmode()) ? d_space : i_space); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ always d_space here? TODO g.space = isR7_space; @@ -1737,8 +1737,8 @@ bool cpu::misc_operations(const uint16_t instr) // PUSH link pushStack(getRegister(link_reg)); - if (!b->isMMR1Locked()) { - b->addToMMR1(-2, 6); + if (!b->getMMU()->isMMR1Locked()) { + b->getMMU()->addToMMR1(-2, 6); addToMMR1(a); } @@ -1806,7 +1806,7 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt) setRegister(6, 04); } else { - b->clearMMR1(); + b->getMMU()->clearMMR1(); before_psw = getPSW(); @@ -2343,10 +2343,10 @@ std::map > cpu::disassemble(const uint16_t work_values_str.push_back(format("%06o", v)); out.insert({ "work-values", work_values_str }); - out.insert({ "MMR0", { format("%06o", b->getMMR0()) } }); - out.insert({ "MMR1", { format("%06o", b->getMMR1()) } }); - out.insert({ "MMR2", { format("%06o", b->getMMR2()) } }); - out.insert({ "MMR3", { format("%06o", b->getMMR3()) } }); + out.insert({ "MMR0", { format("%06o", b->getMMU()->getMMR0()) } }); + out.insert({ "MMR1", { format("%06o", b->getMMU()->getMMR1()) } }); + out.insert({ "MMR2", { format("%06o", b->getMMU()->getMMR2()) } }); + out.insert({ "MMR3", { format("%06o", b->getMMU()->getMMR3()) } }); return out; } @@ -2355,12 +2355,12 @@ void cpu::step() { it_is_a_trap = false; - if (!b->isMMR1Locked()) - b->clearMMR1(); + if (!b->getMMU()->isMMR1Locked()) + b->getMMU()->clearMMR1(); if (any_queued_interrupts && execute_any_pending_interrupt()) { - if (!b->isMMR1Locked()) - b->clearMMR1(); + if (!b->getMMU()->isMMR1Locked()) + b->getMMU()->clearMMR1(); } instruction_count++; @@ -2368,8 +2368,8 @@ void cpu::step() try { instruction_start = getPC(); - if (!b->isMMR1Locked()) - b->setMMR2(instruction_start); + if (!b->getMMU()->isMMR1Locked()) + b->getMMU()->setMMR2(instruction_start); uint16_t instr = b->readWord(instruction_start); diff --git a/debugger.cpp b/debugger.cpp index 857c02a..d78df6b 100644 --- a/debugger.cpp +++ b/debugger.cpp @@ -588,10 +588,10 @@ void dump_range_as_instructions(console *const cnsl, bus *const b, const uint16_ void mmu_dump(console *const cnsl, bus *const b, const bool verbose) { - uint16_t mmr0 = b->getMMR0(); - uint16_t mmr1 = b->getMMR1(); - uint16_t mmr2 = b->getMMR2(); - uint16_t mmr3 = b->getMMR3(); + uint16_t mmr0 = b->getMMU()->getMMR0(); + uint16_t mmr1 = b->getMMU()->getMMR1(); + uint16_t mmr2 = b->getMMU()->getMMR2(); + uint16_t mmr3 = b->getMMU()->getMMR3(); cnsl->put_string_lf(mmr0 & 1 ? "MMU enabled" : "MMU NOT enabled"); @@ -639,7 +639,7 @@ const char *trap_action_to_str(const trap_action_t ta) void mmu_resolve(console *const cnsl, bus *const b, const uint16_t va) { int run_mode = b->getCpu()->getPSW_runmode(); - cnsl->put_string_lf(format("Run mode: %d, use data space: %d", run_mode, b->get_use_data_space(run_mode))); + cnsl->put_string_lf(format("Run mode: %d, use data space: %d", run_mode, b->getMMU()->get_use_data_space(run_mode))); auto data = b->calculate_physical_address(run_mode, va); @@ -648,7 +648,7 @@ void mmu_resolve(console *const cnsl, bus *const b, const uint16_t va) cnsl->put_string_lf(format("Phys. addr. instruction: %08o (psw: %d)", data.physical_instruction, data.physical_instruction_is_psw)); cnsl->put_string_lf(format("Phys. addr. data: %08o (psw: %d)", data.physical_data, data.physical_data_is_psw)); - uint16_t mmr3 = b->getMMR3(); + uint16_t mmr3 = b->getMMU()->getMMR3(); if (run_mode == 0) { dump_par_pdr(cnsl, b, ADDR_PDR_K_START, ADDR_PAR_K_START, "kernel i-space", 0, data.apf); diff --git a/gen.h b/gen.h index 6dda84e..73e00ad 100644 --- a/gen.h +++ b/gen.h @@ -6,3 +6,9 @@ typedef enum { EVENT_NONE = 0, EVENT_HALT, EVENT_INTERRUPT, EVENT_TERMINATE } stop_event_t; typedef enum { DT_RK05, DT_RL02, DT_TAPE } disk_type_t; + +typedef enum { d_space, i_space } d_i_space_t; + +typedef enum { wm_word = 0, wm_byte = 1 } word_mode_t; + +typedef enum { rm_prev, rm_cur } rm_selection_t; diff --git a/main.cpp b/main.cpp index f935b58..71fa638 100644 --- a/main.cpp +++ b/main.cpp @@ -122,7 +122,7 @@ int run_cpu_validation(const std::string & filename) { json_t *a_mmr0 = json_object_get(test, "mmr0-before"); assert(a_mmr0); - b->setMMR0(json_integer_value(a_mmr0)); + b->getMMU()->setMMR0(json_integer_value(a_mmr0)); } disassemble(c, nullptr, start_pc, false); @@ -209,7 +209,7 @@ int run_cpu_validation(const std::string & filename) json_t *a_mmr = json_object_get(test, format("mmr%d-after", r).c_str()); assert(a_mmr); uint16_t should_be_mmr = json_integer_value(a_mmr); - uint16_t is_mmr = b->getMMR(r); + uint16_t is_mmr = b->getMMU()->getMMR(r); if (should_be_mmr != is_mmr) { int is_d1 = is_mmr >> 11; if (is_d1 & 16) diff --git a/mmu.cpp b/mmu.cpp new file mode 100644 index 0000000..2cdb12c --- /dev/null +++ b/mmu.cpp @@ -0,0 +1,160 @@ +#include +#include + +#include "gen.h" +#include "log.h" +#include "mmu.h" +#include "utils.h" + + +mmu::mmu() +{ +} + +mmu::~mmu() +{ +} + +void mmu::reset() +{ + memset(pages, 0x00, sizeof pages); + + CPUERR = MMR0 = MMR1 = MMR2 = MMR3 = PIR = CSR = 0; +} + +uint16_t mmu::read_pdr(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only) +{ + int page = (a >> 1) & 7; + bool is_d = a & 16; + uint16_t t = pages[run_mode][is_d][page].pdr; + + if (!peek_only) + DOLOG(debug, false, "MMU READ-I/O PDR run-mode %d: %c for %d: %o", run_mode, is_d ? 'D' : 'I', page, t); + + return word_mode ? (a & 1 ? t >> 8 : t & 255) : t; +} + +uint16_t mmu::read_par(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only) +{ + int page = (a >> 1) & 7; + bool is_d = a & 16; + uint16_t t = pages[run_mode][is_d][page].par; + + if (!peek_only) + DOLOG(debug, false, "MMU READ-I/O PAR run-mode %d: %c for %d: %o (phys: %07o)", run_mode, is_d ? 'D' : 'I', page, t, t * 64); + + return word_mode ? (a & 1 ? t >> 8 : t & 255) : t; +} + +void mmu::setMMR0(uint16_t value) +{ + value &= ~(3 << 10); // bit 10 & 11 always read as 0 + + if (value & 1) + value &= ~(7l << 13); // reset error bits + + if (MMR0 & 0160000) { + if ((value & 1) == 0) + value &= 254; // bits 7...1 are protected + } + +// TODO if bit 15/14/13 are set (either of them), then do not modify bit 1...7 + + MMR0 = value; +} + +void mmu::setMMR0Bit(const int bit) +{ + assert(bit != 10 && bit != 11); + assert(bit < 16 && bit >= 0); + + MMR0 |= 1 << bit; +} + +void mmu::clearMMR0Bit(const int bit) +{ + assert(bit != 10 && bit != 11); + assert(bit < 16 && bit >= 0); + + MMR0 &= ~(1 << bit); +} + +void mmu::setMMR2(const uint16_t value) +{ + MMR2 = value; +} + +void mmu::setMMR3(const uint16_t value) +{ + MMR3 = value; +} + +bool mmu::get_use_data_space(const int run_mode) const +{ + constexpr const int di_ena_mask[4] = { 4, 2, 0, 1 }; + + return !!(MMR3 & di_ena_mask[run_mode]); +} + +void mmu::clearMMR1() +{ + MMR1 = 0; +} + +void mmu::addToMMR1(const int8_t delta, const uint8_t reg) +{ + assert(reg >= 0 && reg <= 7); + assert(delta >= -2 && delta <= 2); + + assert((getMMR0() & 0160000) == 0); // MMR1 should not be locked + +#if defined(ESP32) +// if (MMR1 > 255) +// esp_backtrace_print(32); +#else + if (MMR1 > 255) { + extern FILE *lfh; + fflush(lfh); + } + assert(MMR1 < 256); +#endif + + MMR1 <<= 8; + + MMR1 |= (delta & 31) << 3; + MMR1 |= reg; +} + +void mmu::write_pdr(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode) +{ + bool is_d = a & 16; + int page = (a >> 1) & 7; + + if (word_mode == wm_byte) { + assert(a != 0 || value < 256); + + update_word(&pages[run_mode][is_d][page].pdr, a & 1, value); + } + else { + pages[run_mode][is_d][page].pdr = value; + } + + pages[run_mode][is_d][page].pdr &= ~(32768 + 128 /*A*/ + 64 /*W*/ + 32 + 16); // set bit 4, 5 & 15 to 0 as they are unused and A/W are set to 0 by writes + + DOLOG(debug, false, "mmu WRITE-I/O PDR run-mode %d: %c for %d: %o [%d]", run_mode, is_d ? 'D' : 'I', page, value, word_mode); +} + +void mmu::write_par(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode) +{ + bool is_d = a & 16; + int page = (a >> 1) & 7; + + if (word_mode == wm_byte) + update_word(&pages[run_mode][is_d][page].par, a & 1, value); + else + pages[run_mode][is_d][page].par = value; + + pages[run_mode][is_d][page].pdr &= ~(128 /*A*/ + 64 /*W*/); // reset PDR A/W when PAR is written to + + DOLOG(debug, false, "mmu WRITE-I/O PAR run-mode %d: %c for %d: %o (%07o)", run_mode, is_d ? 'D' : 'I', page, word_mode == wm_byte ? value & 0xff : value, pages[run_mode][is_d][page].par * 64); +} diff --git a/mmu.h b/mmu.h new file mode 100644 index 0000000..f508b69 --- /dev/null +++ b/mmu.h @@ -0,0 +1,71 @@ +#pragma once + +#include + + +typedef struct { + uint16_t par; + uint16_t pdr; +} page_t; + +class mmu +{ +private: + // 8 pages, D/I, 3 modes and 1 invalid mode + page_t pages[4][2][8]; + + uint16_t MMR0 { 0 }; + uint16_t MMR1 { 0 }; + uint16_t MMR2 { 0 }; + uint16_t MMR3 { 0 }; + uint16_t CPUERR { 0 }; + uint16_t PIR { 0 }; + uint16_t CSR { 0 }; + +public: + mmu(); + virtual ~mmu(); + + void reset(); + + bool is_enabled() const { return MMR0 & 1; } + bool is_locked() const { return !!(MMR0 & 0160000); } + + void set_page_trapped (const int run_mode, const bool d, const int apf) { pages[run_mode][d][apf].pdr |= 1 << 7; } + void set_page_written_to(const int run_mode, const bool d, const int apf) { pages[run_mode][d][apf].pdr |= 1 << 6; } + int get_access_control (const int run_mode, const bool d, const int apf) { return pages[run_mode][d][apf].pdr & 7; } + int get_pdr_len (const int run_mode, const bool d, const int apf) { return (pages[run_mode][d][apf].pdr >> 8) & 127; } + int get_pdr_direction (const int run_mode, const bool d, const int apf) { return pages[run_mode][d][apf].pdr & 8; } + uint32_t get_physical_memory_offset(const int run_mode, const bool d, const int apf) const { return pages[run_mode][d][apf].par * 64; } + bool get_use_data_space(const int run_mode) const; + + uint16_t getMMR0() const { return MMR0; } + uint16_t getMMR1() const { return MMR1; } + uint16_t getMMR2() const { return MMR2; } + uint16_t getMMR3() const { return MMR3; } + uint16_t getMMR(int nr) const { const uint16_t *const mmrs[] { &MMR0, &MMR1, &MMR2, &MMR3 }; return *mmrs[nr]; } + + void setMMR0(const uint16_t value); + void setMMR1(const uint16_t value); + void setMMR2(const uint16_t value); + void setMMR3(const uint16_t value); + + bool isMMR1Locked() const { return !!(MMR0 & 0160000); } + void clearMMR1(); + void addToMMR1(const int8_t delta, const uint8_t reg); + + void setMMR0Bit(const int bit); + void clearMMR0Bit(const int bit); + + uint16_t getCPUERR() const { return CPUERR; } + void setCPUERR(const uint16_t v) { CPUERR = v; } + + uint16_t getPIR() const { return PIR; }; + void setPIR(const uint16_t v) { PIR = v; } + + uint16_t read_par(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only); + uint16_t read_pdr(const uint32_t a, const int run_mode, const word_mode_t word_mode, const bool peek_only); + + void write_pdr(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode); + void write_par(const uint32_t a, const int run_mode, const uint16_t value, const word_mode_t word_mode); +};