split bus class into a mmu class
This commit is contained in:
parent
54836067b5
commit
9f2339cda7
10 changed files with 377 additions and 275 deletions
|
@ -37,6 +37,7 @@ add_executable(
|
||||||
log.cpp
|
log.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
memory.cpp
|
memory.cpp
|
||||||
|
mmu.cpp
|
||||||
rk05.cpp
|
rk05.cpp
|
||||||
rl02.cpp
|
rl02.cpp
|
||||||
terminal.cpp
|
terminal.cpp
|
||||||
|
|
|
@ -48,16 +48,16 @@ std::optional<std::string> breakpoint_register::is_triggered() const
|
||||||
|
|
||||||
switch(reg) {
|
switch(reg) {
|
||||||
case hr_mmr0:
|
case hr_mmr0:
|
||||||
v = b->getMMR0();
|
v = b->getMMU()->getMMR0();
|
||||||
break;
|
break;
|
||||||
case hr_mmr1:
|
case hr_mmr1:
|
||||||
v = b->getMMR1();
|
v = b->getMMU()->getMMR1();
|
||||||
break;
|
break;
|
||||||
case hr_mmr2:
|
case hr_mmr2:
|
||||||
v = b->getMMR2();
|
v = b->getMMU()->getMMR2();
|
||||||
break;
|
break;
|
||||||
case hr_mmr3:
|
case hr_mmr3:
|
||||||
v = b->getMMR3();
|
v = b->getMMU()->getMMR3();
|
||||||
break;
|
break;
|
||||||
case hr_psw:
|
case hr_psw:
|
||||||
v = c->getPSW();
|
v = c->getPSW();
|
||||||
|
|
310
bus.cpp
310
bus.cpp
|
@ -10,6 +10,7 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "mmu.h"
|
||||||
#include "tm-11.h"
|
#include "tm-11.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
@ -18,7 +19,6 @@
|
||||||
#include <esp_debug_helpers.h>
|
#include <esp_debug_helpers.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr const int di_ena_mask[4] = { 4, 2, 0, 1 };
|
|
||||||
|
|
||||||
bus::bus()
|
bus::bus()
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,7 @@ bus::~bus()
|
||||||
delete rk05_;
|
delete rk05_;
|
||||||
delete rl02_;
|
delete rl02_;
|
||||||
delete tty_;
|
delete tty_;
|
||||||
|
delete mmu_;
|
||||||
delete m;
|
delete m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +58,7 @@ void bus::reset()
|
||||||
{
|
{
|
||||||
m->reset();
|
m->reset();
|
||||||
|
|
||||||
memset(pages, 0x00, sizeof pages);
|
mmu_->reset();
|
||||||
|
|
||||||
CPUERR = MMR0 = MMR1 = MMR2 = MMR3 = PIR = CSR = 0;
|
|
||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
c->reset();
|
c->reset();
|
||||||
|
@ -105,38 +104,18 @@ void bus::add_tty(tty *const tty_)
|
||||||
|
|
||||||
void bus::init()
|
void bus::init()
|
||||||
{
|
{
|
||||||
MMR0 = 0;
|
mmu_->setMMR0(0);
|
||||||
MMR3 = 0;
|
mmu_->setMMR3(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bus::trap_odd(const uint16_t a)
|
void bus::trap_odd(const uint16_t a)
|
||||||
{
|
{
|
||||||
MMR0 &= ~(7 << 1);
|
uint16_t temp = mmu_->getMMR0();
|
||||||
MMR0 |= (a >> 13) << 1;
|
|
||||||
|
temp &= ~(7 << 1);
|
||||||
|
temp |= (a >> 13) << 1;
|
||||||
|
|
||||||
|
mmu_->setMMR0(temp);
|
||||||
|
|
||||||
c->trap(004); // invalid access
|
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
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O CPU error: %03o", temp);
|
||||||
return 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
|
if (a == ADDR_PIR || a == ADDR_PIR + 1) { // PIR
|
||||||
uint16_t temp = 0;
|
uint16_t temp = 0;
|
||||||
|
|
||||||
|
uint16_t PIR = mmu_->getPIR();
|
||||||
|
|
||||||
if (word_mode == wm_word)
|
if (word_mode == wm_word)
|
||||||
temp = PIR;
|
temp = PIR;
|
||||||
else
|
else
|
||||||
|
@ -262,17 +243,17 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm
|
||||||
|
|
||||||
/// MMU ///
|
/// MMU ///
|
||||||
if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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
|
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) {
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 LO: %03o", temp);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
if (a == ADDR_MMR0 + 1) {
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0 HI: %03o", temp);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (a == ADDR_MMR0) {
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR0: %06o", temp);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a == ADDR_MMR1) { // MMR1
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR1: %06o", temp);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a == ADDR_MMR2) { // MMR2
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR2: %06o", temp);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a == ADDR_MMR3) { // MMR3
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O MMR3: %06o", temp);
|
||||||
return 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
|
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);
|
if (!peek_only) DOLOG(debug, false, "READ-I/O CPUERR: %06o", temp);
|
||||||
return 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;
|
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)
|
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 (a & 1) {
|
||||||
if (is_write)
|
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);
|
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
|
const uint8_t apf = a >> 13; // active page field
|
||||||
|
|
||||||
if ((MMR0 & 1) == 0) {
|
if (mmu_->is_enabled() == false) {
|
||||||
bool is_psw = a == ADDR_PSW;
|
bool is_psw = a == ADDR_PSW;
|
||||||
return { a, apf, a, is_psw, a, is_psw };
|
return { a, apf, a, is_psw, a, is_psw };
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t physical_instruction = pages[run_mode][0][apf].par * 64;
|
uint32_t physical_instruction = mmu_->get_physical_memory_offset(run_mode, 0, apf);
|
||||||
uint32_t physical_data = pages[run_mode][1][apf].par * 64;
|
uint32_t physical_data = mmu_->get_physical_memory_offset(run_mode, 1, apf);
|
||||||
|
|
||||||
uint16_t p_offset = a & 8191; // page offset
|
uint16_t p_offset = a & 8191; // page offset
|
||||||
|
|
||||||
physical_instruction += p_offset;
|
physical_instruction += p_offset;
|
||||||
physical_data += 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_instruction &= 0x3ffff;
|
||||||
physical_data &= 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;
|
physical_data = physical_instruction;
|
||||||
|
|
||||||
uint32_t io_base = get_io_base();
|
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)
|
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;
|
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;
|
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
|
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
|
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;
|
m_offset += p_offset;
|
||||||
|
|
||||||
if ((MMR3 & 16) == 0) // off is 18bit
|
if ((mmu_->getMMR3() & 16) == 0) // off is 18bit
|
||||||
m_offset &= 0x3ffff;
|
m_offset &= 0x3ffff;
|
||||||
|
|
||||||
uint32_t io_base = get_io_base();
|
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 (trap_action != T_PROCEED) {
|
||||||
if (is_write)
|
if (is_write)
|
||||||
pages[run_mode][d][apf].pdr |= 1 << 7;
|
mmu_->set_page_trapped(run_mode, d, apf);
|
||||||
|
|
||||||
if ((MMR0 & 0160000) == 0) {
|
if (mmu_->is_locked() == false) {
|
||||||
MMR0 &= ~((1l << 15) | (1 << 14) | (1 << 13) | (1 << 12) | (3 << 5) | (7 << 1) | (1 << 4));
|
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)
|
if (is_write && access_control != 6)
|
||||||
MMR0 |= 1 << 13; // read-only
|
temp |= 1 << 13; // read-only
|
||||||
//
|
//
|
||||||
if (access_control == 0 || access_control == 4)
|
if (access_control == 0 || access_control == 4)
|
||||||
MMR0 |= 1l << 15; // not resident
|
temp |= 1l << 15; // not resident
|
||||||
else
|
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) {
|
if (trap_action == T_TRAP_250) {
|
||||||
DOLOG(debug, false, "Page access %d (for virtual address %06o): trap 0250", access_control, a);
|
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, !peek_only, "bus::calculate_physical_address %o >= %o", m_offset, n_pages * 8192l);
|
||||||
DOLOG(debug, false, "TRAP(04) (throw 6) on address %06o", a);
|
DOLOG(debug, false, "TRAP(04) (throw 6) on address %06o", a);
|
||||||
|
|
||||||
if ((MMR0 & 0160000) == 0) {
|
if (mmu_->is_locked() == false) {
|
||||||
MMR0 &= 017777;
|
uint16_t temp = mmu_->getMMR0();
|
||||||
MMR0 |= 1l << 15; // non-resident
|
|
||||||
|
|
||||||
MMR0 &= ~14; // add current page
|
temp &= 017777;
|
||||||
MMR0 |= apf << 1;
|
temp |= 1l << 15; // non-resident
|
||||||
|
|
||||||
MMR0 &= ~(3 << 5);
|
temp &= ~14; // add current page
|
||||||
MMR0 |= run_mode << 5;
|
temp |= apf << 1;
|
||||||
|
|
||||||
|
temp &= ~(3 << 5);
|
||||||
|
temp |= run_mode << 5;
|
||||||
|
|
||||||
|
mmu_->setMMR0(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_write)
|
if (is_write)
|
||||||
pages[run_mode][d][apf].pdr |= 1 << 7;
|
mmu_->set_page_trapped(run_mode, d, apf);
|
||||||
|
|
||||||
c->trap(04);
|
c->trap(04);
|
||||||
|
|
||||||
throw 6;
|
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;
|
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);
|
// 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);
|
DOLOG(debug, false, "TRAP(0250) (throw 7) on address %06o", a);
|
||||||
c->trap(0250); // invalid access
|
c->trap(0250); // invalid access
|
||||||
|
|
||||||
if ((MMR0 & 0160000) == 0) {
|
if (mmu_->is_locked() == false) {
|
||||||
MMR0 &= 017777;
|
uint16_t temp = mmu_->getMMR0();
|
||||||
MMR0 |= 1 << 14; // length
|
|
||||||
|
|
||||||
MMR0 &= ~14; // add current page
|
temp &= 017777;
|
||||||
MMR0 |= apf << 1;
|
temp |= 1 << 14; // length
|
||||||
|
|
||||||
MMR0 &= ~(3 << 5);
|
temp &= ~14; // add current page
|
||||||
MMR0 |= run_mode << 5;
|
temp |= apf << 1;
|
||||||
|
|
||||||
MMR0 &= ~(1 << 4);
|
temp &= ~(3 << 5);
|
||||||
MMR0 |= d << 4;
|
temp |= run_mode << 5;
|
||||||
|
|
||||||
|
temp &= ~(1 << 4);
|
||||||
|
temp |= d << 4;
|
||||||
|
|
||||||
|
mmu_->setMMR0(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_write)
|
if (is_write)
|
||||||
pages[run_mode][d][apf].pdr |= 1 << 7;
|
mmu_->set_page_trapped(run_mode, d, apf);
|
||||||
|
|
||||||
throw 7;
|
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 {
|
else {
|
||||||
// DOLOG(debug, false, "no MMU (read physical address %08o)", m_offset);
|
// 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;
|
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)
|
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();
|
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 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)
|
if (mmu_->is_enabled() && (addr_in & 1) == 0 /* TODO remove this? */ && addr_in != ADDR_MMR0)
|
||||||
pages[run_mode][d][apf].pdr |= 64; // set 'W' (written to) bit
|
mmu_->set_page_written_to(run_mode, d, apf);
|
||||||
|
|
||||||
uint32_t m_offset = calculate_physical_address(run_mode, addr_in, true, true, false, space);
|
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
|
if (a == ADDR_MMR0 || a == ADDR_MMR0 + 1) { // MMR0
|
||||||
DOLOG(debug, false, "WRITE-I/O MMR0 register %s: %03o", a & 1 ? "MSB" : "LSB", value);
|
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 };
|
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
|
if (a == ADDR_CPU_ERR) { // cpu error register
|
||||||
DOLOG(debug, false, "WRITE-I/O CPUERR: %06o", value);
|
DOLOG(debug, false, "WRITE-I/O CPUERR: %06o", value);
|
||||||
CPUERR = 0;
|
mmu_->setCPUERR(0);
|
||||||
return { false };
|
return { false };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a == ADDR_MMR3) { // MMR3
|
if (a == ADDR_MMR3) { // MMR3
|
||||||
DOLOG(debug, false, "WRITE-I/O set MMR3: %06o", value);
|
DOLOG(debug, false, "WRITE-I/O set MMR3: %06o", value);
|
||||||
MMR3 = value;
|
mmu_->setMMR3(value);
|
||||||
return { false };
|
return { false };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a == ADDR_MMR0) { // MMR0
|
if (a == ADDR_MMR0) { // MMR0
|
||||||
DOLOG(debug, false, "WRITE-I/O set MMR0: %06o", value);
|
DOLOG(debug, false, "WRITE-I/O set MMR0: %06o", value);
|
||||||
setMMR0(value);
|
mmu_->setMMR0(value);
|
||||||
return { false };
|
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;
|
bits >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PIR = value;
|
mmu_->setPIR(value);
|
||||||
|
|
||||||
return { false };
|
return { false };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,31 +893,31 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1
|
||||||
/// MMU ///
|
/// MMU ///
|
||||||
// supervisor
|
// supervisor
|
||||||
if (a >= ADDR_PDR_SV_START && a < ADDR_PDR_SV_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
if (a >= ADDR_PAR_SV_START && a < ADDR_PAR_SV_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// kernel
|
// kernel
|
||||||
if (a >= ADDR_PDR_K_START && a < ADDR_PDR_K_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
if (a >= ADDR_PAR_K_START && a < ADDR_PAR_K_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// user
|
// user
|
||||||
if (a >= ADDR_PDR_U_START && a < ADDR_PDR_U_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
if (a >= ADDR_PAR_U_START && a < ADDR_PAR_U_END) {
|
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 };
|
return { false };
|
||||||
}
|
}
|
||||||
////
|
////
|
||||||
|
|
48
bus.h
48
bus.h
|
@ -8,9 +8,11 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "tm-11.h"
|
#include "gen.h"
|
||||||
|
#include "mmu.h"
|
||||||
#include "rk05.h"
|
#include "rk05.h"
|
||||||
#include "rl02.h"
|
#include "rl02.h"
|
||||||
|
#include "tm-11.h"
|
||||||
|
|
||||||
#if defined(BUILD_FOR_RP2040)
|
#if defined(BUILD_FOR_RP2040)
|
||||||
#include "rp2040.h"
|
#include "rp2040.h"
|
||||||
|
@ -70,12 +72,6 @@ class cpu;
|
||||||
class memory;
|
class memory;
|
||||||
class tty;
|
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 enum { T_PROCEED, T_ABORT_4, T_TRAP_250 } trap_action_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -87,10 +83,6 @@ typedef struct {
|
||||||
bool physical_data_is_psw;
|
bool physical_data_is_psw;
|
||||||
} memory_addresses_t;
|
} memory_addresses_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t par, pdr;
|
|
||||||
} page_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool is_psw;
|
bool is_psw;
|
||||||
} write_rc_t;
|
} write_rc_t;
|
||||||
|
@ -104,14 +96,11 @@ private:
|
||||||
rl02 *rl02_ { nullptr };
|
rl02 *rl02_ { nullptr };
|
||||||
tty *tty_ { nullptr };
|
tty *tty_ { nullptr };
|
||||||
|
|
||||||
|
mmu *mmu_ { nullptr };
|
||||||
|
|
||||||
int n_pages { DEFAULT_N_PAGES };
|
int n_pages { DEFAULT_N_PAGES };
|
||||||
memory *m { nullptr };
|
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)
|
#if defined(BUILD_FOR_RP2040)
|
||||||
SemaphoreHandle_t lf_csr_lock { xSemaphoreCreateBinary() };
|
SemaphoreHandle_t lf_csr_lock { xSemaphoreCreateBinary() };
|
||||||
#else
|
#else
|
||||||
|
@ -124,11 +113,6 @@ private:
|
||||||
uint16_t console_switches { 0 };
|
uint16_t console_switches { 0 };
|
||||||
uint16_t console_leds { 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:
|
public:
|
||||||
bus();
|
bus();
|
||||||
~bus();
|
~bus();
|
||||||
|
@ -153,9 +137,11 @@ public:
|
||||||
void add_rl02(rl02 *const rl02_);
|
void add_rl02(rl02 *const rl02_);
|
||||||
void add_tty (tty *const tty_);
|
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
|
void init(); // invoked by 'RESET' command
|
||||||
|
|
||||||
|
@ -178,29 +164,15 @@ public:
|
||||||
|
|
||||||
void writeUnibusByte(const uint32_t a, const uint8_t value);
|
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 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);
|
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;
|
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);
|
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);
|
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;
|
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);
|
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
32
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)
|
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);
|
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, { }, { }, { }, { } };
|
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
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ always d_space here? TODO
|
||||||
|
|
||||||
g.space = isR7_space;
|
g.space = isR7_space;
|
||||||
|
@ -1737,8 +1737,8 @@ bool cpu::misc_operations(const uint16_t instr)
|
||||||
|
|
||||||
// PUSH link
|
// PUSH link
|
||||||
pushStack(getRegister(link_reg));
|
pushStack(getRegister(link_reg));
|
||||||
if (!b->isMMR1Locked()) {
|
if (!b->getMMU()->isMMR1Locked()) {
|
||||||
b->addToMMR1(-2, 6);
|
b->getMMU()->addToMMR1(-2, 6);
|
||||||
|
|
||||||
addToMMR1(a);
|
addToMMR1(a);
|
||||||
}
|
}
|
||||||
|
@ -1806,7 +1806,7 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
|
||||||
setRegister(6, 04);
|
setRegister(6, 04);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
b->clearMMR1();
|
b->getMMU()->clearMMR1();
|
||||||
|
|
||||||
before_psw = getPSW();
|
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));
|
work_values_str.push_back(format("%06o", v));
|
||||||
out.insert({ "work-values", work_values_str });
|
out.insert({ "work-values", work_values_str });
|
||||||
|
|
||||||
out.insert({ "MMR0", { format("%06o", b->getMMR0()) } });
|
out.insert({ "MMR0", { format("%06o", b->getMMU()->getMMR0()) } });
|
||||||
out.insert({ "MMR1", { format("%06o", b->getMMR1()) } });
|
out.insert({ "MMR1", { format("%06o", b->getMMU()->getMMR1()) } });
|
||||||
out.insert({ "MMR2", { format("%06o", b->getMMR2()) } });
|
out.insert({ "MMR2", { format("%06o", b->getMMU()->getMMR2()) } });
|
||||||
out.insert({ "MMR3", { format("%06o", b->getMMR3()) } });
|
out.insert({ "MMR3", { format("%06o", b->getMMU()->getMMR3()) } });
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -2355,12 +2355,12 @@ void cpu::step()
|
||||||
{
|
{
|
||||||
it_is_a_trap = false;
|
it_is_a_trap = false;
|
||||||
|
|
||||||
if (!b->isMMR1Locked())
|
if (!b->getMMU()->isMMR1Locked())
|
||||||
b->clearMMR1();
|
b->getMMU()->clearMMR1();
|
||||||
|
|
||||||
if (any_queued_interrupts && execute_any_pending_interrupt()) {
|
if (any_queued_interrupts && execute_any_pending_interrupt()) {
|
||||||
if (!b->isMMR1Locked())
|
if (!b->getMMU()->isMMR1Locked())
|
||||||
b->clearMMR1();
|
b->getMMU()->clearMMR1();
|
||||||
}
|
}
|
||||||
|
|
||||||
instruction_count++;
|
instruction_count++;
|
||||||
|
@ -2368,8 +2368,8 @@ void cpu::step()
|
||||||
try {
|
try {
|
||||||
instruction_start = getPC();
|
instruction_start = getPC();
|
||||||
|
|
||||||
if (!b->isMMR1Locked())
|
if (!b->getMMU()->isMMR1Locked())
|
||||||
b->setMMR2(instruction_start);
|
b->getMMU()->setMMR2(instruction_start);
|
||||||
|
|
||||||
uint16_t instr = b->readWord(instruction_start);
|
uint16_t instr = b->readWord(instruction_start);
|
||||||
|
|
||||||
|
|
12
debugger.cpp
12
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)
|
void mmu_dump(console *const cnsl, bus *const b, const bool verbose)
|
||||||
{
|
{
|
||||||
uint16_t mmr0 = b->getMMR0();
|
uint16_t mmr0 = b->getMMU()->getMMR0();
|
||||||
uint16_t mmr1 = b->getMMR1();
|
uint16_t mmr1 = b->getMMU()->getMMR1();
|
||||||
uint16_t mmr2 = b->getMMR2();
|
uint16_t mmr2 = b->getMMU()->getMMR2();
|
||||||
uint16_t mmr3 = b->getMMR3();
|
uint16_t mmr3 = b->getMMU()->getMMR3();
|
||||||
|
|
||||||
cnsl->put_string_lf(mmr0 & 1 ? "MMU enabled" : "MMU NOT enabled");
|
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)
|
void mmu_resolve(console *const cnsl, bus *const b, const uint16_t va)
|
||||||
{
|
{
|
||||||
int run_mode = b->getCpu()->getPSW_runmode();
|
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);
|
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. 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));
|
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) {
|
if (run_mode == 0) {
|
||||||
dump_par_pdr(cnsl, b, ADDR_PDR_K_START, ADDR_PAR_K_START, "kernel i-space", 0, data.apf);
|
dump_par_pdr(cnsl, b, ADDR_PDR_K_START, ADDR_PAR_K_START, "kernel i-space", 0, data.apf);
|
||||||
|
|
6
gen.h
6
gen.h
|
@ -6,3 +6,9 @@
|
||||||
typedef enum { EVENT_NONE = 0, EVENT_HALT, EVENT_INTERRUPT, EVENT_TERMINATE } stop_event_t;
|
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 { 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;
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -122,7 +122,7 @@ int run_cpu_validation(const std::string & filename)
|
||||||
{
|
{
|
||||||
json_t *a_mmr0 = json_object_get(test, "mmr0-before");
|
json_t *a_mmr0 = json_object_get(test, "mmr0-before");
|
||||||
assert(a_mmr0);
|
assert(a_mmr0);
|
||||||
b->setMMR0(json_integer_value(a_mmr0));
|
b->getMMU()->setMMR0(json_integer_value(a_mmr0));
|
||||||
}
|
}
|
||||||
|
|
||||||
disassemble(c, nullptr, start_pc, false);
|
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());
|
json_t *a_mmr = json_object_get(test, format("mmr%d-after", r).c_str());
|
||||||
assert(a_mmr);
|
assert(a_mmr);
|
||||||
uint16_t should_be_mmr = json_integer_value(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) {
|
if (should_be_mmr != is_mmr) {
|
||||||
int is_d1 = is_mmr >> 11;
|
int is_d1 = is_mmr >> 11;
|
||||||
if (is_d1 & 16)
|
if (is_d1 & 16)
|
||||||
|
|
160
mmu.cpp
Normal file
160
mmu.cpp
Normal 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
71
mmu.h
Normal 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);
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue