split bus class into a mmu class

This commit is contained in:
folkert van heusden 2024-04-25 09:45:55 +02:00
parent 54836067b5
commit 9f2339cda7
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
10 changed files with 377 additions and 275 deletions

View file

@ -37,6 +37,7 @@ add_executable(
log.cpp
main.cpp
memory.cpp
mmu.cpp
rk05.cpp
rl02.cpp
terminal.cpp

View file

@ -48,16 +48,16 @@ std::optional<std::string> 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();

310
bus.cpp
View file

@ -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 <esp_debug_helpers.h>
#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<trap_action_t, int> 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 };
}
////

48
bus.h
View file

@ -8,9 +8,11 @@
#include <stdint.h>
#include <stdio.h>
#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<trap_action_t, int> 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);
};

32
cpu.cpp
View file

@ -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<std::string, std::vector<std::string> > 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);

View file

@ -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);

6
gen.h
View file

@ -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;

View file

@ -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)

160
mmu.cpp Normal file
View file

@ -0,0 +1,160 @@
#include <cassert>
#include <cstring>
#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);
}

71
mmu.h Normal file
View file

@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
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);
};