Compare commits

...
Sign in to create a new pull request.

30 commits

Author SHA1 Message Date
Folkert van Heusden
7a2d7ea779
set_memory_size wants page count
Some checks failed
CodeQL / Analyze (push) Has been cancelled
2025-04-16 07:56:59 +02:00
Folkert van Heusden
0001282505
keep TRAP flag in RTI
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-16 07:54:55 +02:00
Folkert van Heusden
1b09b335e0
631c251c08 broke interrupt handling
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-15 21:42:37 +02:00
Folkert van Heusden
3a5379997e
MMR1 fixes
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-15 21:13:02 +02:00
Folkert van Heusden
5c3ddfcf3a
code clean-up & tracing in mmu 2025-04-15 21:12:44 +02:00
Folkert van Heusden
631c251c08
double clearMM1 invocation
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-15 20:14:24 +02:00
Folkert van Heusden
df2d5f5ef4
MMU errors trigger trap 250
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-15 19:46:41 +02:00
Folkert van Heusden
0ab515678b
Revert "other modes with reg 7 shall not update MMR1"
Some checks are pending
CodeQL / Analyze (push) Waiting to run
This reverts commit 5b82ad260f and
2163d5ad58.
2025-04-15 19:42:36 +02:00
Folkert van Heusden
517db4657a
load init file
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-15 19:31:36 +02:00
Folkert van Heusden
7e976f0a78
benchmark 2025-04-14 10:31:49 +02:00
Folkert van Heusden
38bff121c2
RP07 support 2025-04-14 10:03:05 +02:00
Folkert van Heusden
b4a4606200
native build 2025-04-14 10:02:52 +02:00
Folkert van Heusden
ebad251ba4
borders are now not inverted 2025-04-14 09:19:08 +02:00
Folkert van Heusden
5b82ad260f
other modes with reg 7 shall not update MMR1 2025-04-12 12:30:43 +02:00
Folkert van Heusden
62c86dfccb
MMR1/2 verification
Some checks failed
CodeQL / Analyze (push) Has been cancelled
2025-04-12 12:26:07 +02:00
Folkert van Heusden
2163d5ad58
mode 2 reg 7 shall not update MMR1 2025-04-12 12:25:56 +02:00
Folkert van Heusden
9faba4a2b6
PSW magic
Some checks failed
CodeQL / Analyze (push) Has been cancelled
2025-04-09 08:52:59 +02:00
Folkert van Heusden
fc7de5a9da
clarified filename prompt
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-08 17:19:10 +02:00
Folkert van Heusden
2349ac8488
pin configuration ESP32-wemos
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-08 17:05:49 +02:00
Folkert van Heusden
9b1e552183
run_cpu_validation: disassemble after last instruction to show state afterwards
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-08 12:44:27 +02:00
Folkert van Heusden
b0c4553a82
json files are now produced by simh, see phys https://vanheusden.com/git/folkert/simh-testsetgenerator/
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-07 16:07:36 +02:00
Folkert van Heusden
b77c163cac
a test can now consist of multiple pdp instructions
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-07 09:30:38 +02:00
Folkert van Heusden
1bd06643b5
memory settings fixes (16b versus 8b)
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 23:00:40 +02:00
Folkert van Heusden
323008a1b0
Code-coverage target in cmake.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 18:45:50 +02:00
Folkert van Heusden
01b510df22
ADD/SUB disassembly
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 18:39:25 +02:00
Folkert van Heusden
068aa51560
explained "-1" parameter
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 15:27:10 +02:00
Folkert van Heusden
48b1da252e
The BSD2.11 RP06 bootloader requires 0176700 to be in R1 when invoked.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 11:12:38 +02:00
Folkert van Heusden
8a0cee1b96
disassembler: in addressing_to_string return the reason why a disassemble failed
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 09:09:46 +02:00
Folkert van Heusden
d71e9d6beb
disassembler fix for conditional jumps & single operand instructions
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 08:43:04 +02:00
Folkert van Heusden
ca17b9b9bf
spaces
Some checks are pending
CodeQL / Analyze (push) Waiting to run
2025-04-06 08:17:38 +02:00
16 changed files with 948 additions and 1327 deletions

View file

@ -16,10 +16,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
include(FindPkgConfig) include(FindPkgConfig)
if (NOT WIN32) set(APP_SOURCES
add_executable(
kek
breakpoint.cpp breakpoint.cpp
breakpoint_and.cpp breakpoint_and.cpp
breakpoint_memory.cpp breakpoint_memory.cpp
@ -57,58 +54,56 @@ add_executable(
utils.cpp utils.cpp
) )
if (NOT WIN32)
add_executable(
kek
${APP_SOURCES}
)
add_executable(
kek-codecoverage
${APP_SOURCES}
)
add_executable(
kek-native
${APP_SOURCES}
)
target_compile_options(kek-native PRIVATE -march=native -mtune=native -O3)
target_compile_options(kek-codecoverage PRIVATE -fprofile-arcs -ftest-coverage)
target_link_options(kek-codecoverage PRIVATE -fprofile-arcs -ftest-coverage)
pkg_check_modules(NCURSES REQUIRED ncurses) pkg_check_modules(NCURSES REQUIRED ncurses)
target_link_libraries(kek ${NCURSES_LIBRARIES}) target_link_libraries(kek ${NCURSES_LIBRARIES})
target_include_directories(kek PUBLIC ${NCURSES_INCLUDE_DIRS}) target_include_directories(kek PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek PUBLIC ${NCURSES_CFLAGS_OTHER}) target_compile_options(kek PUBLIC ${NCURSES_CFLAGS_OTHER})
target_link_libraries(kek-codecoverage ${NCURSES_LIBRARIES})
target_include_directories(kek-codecoverage PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek-codecoverage PUBLIC ${NCURSES_CFLAGS_OTHER})
target_link_libraries(kek-native ${NCURSES_LIBRARIES})
target_include_directories(kek-native PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek-native PUBLIC ${NCURSES_CFLAGS_OTHER})
pkg_check_modules(PANEL REQUIRED panel) pkg_check_modules(PANEL REQUIRED panel)
target_link_libraries(kek ${PANEL_LIBRARIES}) target_link_libraries(kek ${PANEL_LIBRARIES})
target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS}) target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER}) target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER})
target_link_libraries(kek-codecoverage ${PANEL_LIBRARIES})
target_include_directories(kek-codecoverage PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek-codecoverage PUBLIC ${PANEL_CFLAGS_OTHER})
target_link_libraries(kek-native ${PANEL_LIBRARIES})
target_include_directories(kek-native PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek-native PUBLIC ${PANEL_CFLAGS_OTHER})
endif (NOT WIN32) endif (NOT WIN32)
if (WIN32) if (WIN32)
add_executable( add_executable(
kek-win32 kek-win32
breakpoint.cpp ${APP_SOURCES}
breakpoint_and.cpp
breakpoint_memory.cpp
breakpoint_or.cpp
breakpoint_parser.cpp
breakpoint_register.cpp
bus.cpp
comm.cpp
comm_posix_tty.cpp
comm_tcp_socket_client.cpp
comm_tcp_socket_server.cpp
console.cpp
console_posix.cpp
cpu.cpp
dc11.cpp
debugger.cpp
device.cpp
disk_backend.cpp
disk_backend_file.cpp
disk_backend_nbd.cpp
error.cpp
kw11-l.cpp
loaders.cpp
log.cpp
main.cpp
memory.cpp
mmu.cpp
rk05.cpp
rl02.cpp
rp06.cpp
tm-11.cpp
tty.cpp
utils.cpp
win32.cpp win32.cpp
) )
endif (WIN32) endif (WIN32)
include(CheckIPOSupported) include(CheckIPOSupported)
@ -124,6 +119,7 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads) find_package(Threads)
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(kek Threads::Threads) target_link_libraries(kek Threads::Threads)
target_link_libraries(kek-codecoverage Threads::Threads)
else () else ()
target_link_libraries(kek-win32 Threads::Threads) target_link_libraries(kek-win32 Threads::Threads)
@ -132,7 +128,21 @@ endif ()
add_subdirectory(arduinojson) add_subdirectory(arduinojson)
target_link_libraries(kek ArduinoJson) target_link_libraries(kek ArduinoJson)
target_link_libraries(kek-codecoverage ArduinoJson)
target_link_libraries(kek-native ArduinoJson)
if (WIN32) if (WIN32)
target_link_libraries(kek-win32 ArduinoJson) target_link_libraries(kek-win32 ArduinoJson)
endif () endif ()
add_custom_target(coverage
COMMAND ${CMAKE_COMMAND} -E remove_directory coverage
COMMAND ${CMAKE_COMMAND} -E make_directory coverage
COMMAND lcov --capture --directory . --output-file coverage.info
COMMAND genhtml coverage.info --output-directory coverage
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(coverage_reset
COMMAND lcov --directory . --zerocounters
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View file

@ -17,7 +17,7 @@ board_build.filesystem = littlefs
lib_deps = greiman/SdFat@^2.1.2 lib_deps = greiman/SdFat@^2.1.2
adafruit/Adafruit NeoPixel adafruit/Adafruit NeoPixel
bblanchon/ArduinoJson@^7.0.4 bblanchon/ArduinoJson@^7.0.4
build_flags = -std=gnu++2a -DESP32=1 -ggdb3 -D_GLIBCXX_USE_C99 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DCONFIG_SPIRAM_USE_MALLOC -DRGBW_PIXELS -fexceptions build_flags = -std=gnu++2a -DESP32=1 -ggdb3 -D_GLIBCXX_USE_C99 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DCONFIG_SPIRAM_USE_MALLOC -DRGBW_PIXELS -fexceptions -DNEOPIXELS_PIN=25 -DTTY_SERIAL_TX=17 -DTTY_SERIAL_RX=18
build_unflags = -std=gnu++11 -std=gnu++17 build_unflags = -std=gnu++11 -std=gnu++17
extra_scripts = pre:prepare.py extra_scripts = pre:prepare.py

View file

@ -1,2 +1,5 @@
41ef37ce6b7f13f1cc2df0e6ac570647fe07fe12: 41ef37ce6b7f13f1cc2df0e6ac570647fe07fe12:
MIPS: 0.74, relative speed: 33.23%, instructions executed: 3692600 in 5.00 seconds MIPS: 0.74, relative speed: 33.23%, instructions executed: 3692600 in 5.00 seconds
38bff121c236e811335792d6e33ca33711daca9a:
MIPS: 0.34, relative speed: 15.32%, instructions executed: 1702381 in 5.00 seconds

View file

@ -139,7 +139,7 @@ void bus::set_memory_size(const int n_pages)
mmu_->begin(m, c); mmu_->begin(m, c);
TRACE("Memory is now %u kB in size", n_bytes / 1024); TRACE("Memory is now %u kB (%d pages)", n_bytes / 1024, n_pages);
} }
void bus::reset() void bus::reset()

200
cpu.cpp
View file

@ -316,11 +316,15 @@ int cpu::getPSW_spl() const
void cpu::setPSW(const uint16_t v, const bool limited) void cpu::setPSW(const uint16_t v, const bool limited)
{ {
if (limited) if (limited) {
psw = (psw & 0174340) | (v & 037); int cur_mode = std::max( v >> 14, psw >> 14);
else int prev_mode = std::max((v >> 12) & 3, (psw >> 12) & 3);
psw = (psw & 004340) | (v & 037) | (cur_mode << 14) | (prev_mode << 12);
}
else {
psw = v & 0174377; // mask off reserved bits psw = v & 0174377; // mask off reserved bits
} }
}
void cpu::setPSW_flags_nzv(const uint16_t value, const word_mode_t word_mode) void cpu::setPSW_flags_nzv(const uint16_t value, const word_mode_t word_mode)
{ {
@ -453,9 +457,9 @@ 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->getMMU()->isMMR1Locked() && g.mmr1_update.has_value()) { if (b->getMMU()->isMMR1Locked() == false && g.mmr1_update.has_value() == true) {
TRACE("MMR1: add %d to register R%d", g.mmr1_update.value().delta, g.mmr1_update.value().reg);
assert(g.mmr1_update.value().delta); assert(g.mmr1_update.value().delta);
b->getMMU()->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg); b->getMMU()->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg);
} }
} }
@ -484,17 +488,17 @@ gam_rc_t cpu::getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t wo
break; break;
case 2: // (Rn)+ / #n case 2: // (Rn)+ / #n
g.addr = get_register(reg); g.addr = get_register(reg);
if (read_value)
g.value = b->read(g.addr.value(), word_mode, rm_cur, isR7_space);
add_register(reg, word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1); add_register(reg, word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1);
g.mmr1_update = { word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1, reg }; g.mmr1_update = { word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1, reg };
if (read_value)
g.value = b->read(g.addr.value(), word_mode, rm_cur, isR7_space);
break; break;
case 3: // @(Rn)+ / @#a case 3: // @(Rn)+ / @#a
g.addr = b->read(get_register(reg), wm_word, rm_cur, isR7_space); g.addr = b->read(get_register(reg), wm_word, rm_cur, isR7_space);
// might be wrong: the adds should happen when the read is really performed, because of traps
add_register(reg, 2);
g.mmr1_update = { 2, reg }; g.mmr1_update = { 2, reg };
g.space = d_space; g.space = d_space;
// might be wrong: the adds should happen when the read is really performed(?), because of traps
add_register(reg, 2);
if (read_value) if (read_value)
g.value = b->read(g.addr.value(), word_mode, rm_cur, g.space); g.value = b->read(g.addr.value(), word_mode, rm_cur, g.space);
break; break;
@ -581,20 +585,20 @@ bool cpu::double_operand_instructions(const uint16_t instr)
case 0b001: { // MOV/MOVB Move Word/Byte case 0b001: { // MOV/MOVB Move Word/Byte
gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode); gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode);
bool set_flags = true; bool set_flags = true;
if (word_mode == wm_byte && dst_mode == 0) if (word_mode == wm_byte && dst_mode == 0) {
set_register(dst_reg, int8_t(g_src.value.value())); // int8_t: sign extension set_register(dst_reg, int8_t(g_src.value.value())); // int8_t: sign extension
addToMMR1(g_src);
}
else { else {
auto g_dst = getGAMAddress(dst_mode, dst_reg, word_mode); auto g_dst = getGAMAddress(dst_mode, dst_reg, word_mode);
addToMMR1(g_dst); addToMMR1(g_dst);
addToMMR1(g_src);
set_flags = putGAM(g_dst, g_src.value.value()); set_flags = putGAM(g_dst, g_src.value.value());
} }
addToMMR1(g_src);
if (set_flags) if (set_flags)
setPSW_flags_nzv(g_src.value.value(), word_mode); setPSW_flags_nzv(g_src.value.value(), word_mode);
@ -1654,7 +1658,6 @@ bool cpu::misc_operations(const uint16_t instr)
pop_from_stack_trace(); pop_from_stack_trace();
setPC(popStack()); setPC(popStack());
setPSW(popStack(), !!getPSW_runmode()); setPSW(popStack(), !!getPSW_runmode());
psw &= ~020; // disable TRAP flag
return true; return true;
case 0b0000000000000011: // BPT case 0b0000000000000011: // BPT
@ -1796,13 +1799,8 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
set_register(6, 04); set_register(6, 04);
} }
else { else {
b->getMMU()->clearMMR1();
before_psw = getPSW(); before_psw = getPSW();
before_pc = getPC(); before_pc = getPC();
// TODO set MMR2?
} }
if (debug_mode) if (debug_mode)
@ -1854,8 +1852,11 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
int run_mode = getPSW_runmode(); int run_mode = getPSW_runmode();
auto temp = b->peek_word(run_mode, pc & 65535); auto temp = b->peek_word(run_mode, pc & 65535);
if (temp.has_value() == false) if (temp.has_value() == false) {
return { }; operand_parameters out;
out.error = "cannot read from memory";
return out;
}
uint16_t next_word = temp.value(); uint16_t next_word = temp.value();
int reg = mode_register & 7; int reg = mode_register & 7;
uint16_t mask = word_mode == wm_byte ? 0xff : 0xffff; uint16_t mask = word_mode == wm_byte ? 0xff : 0xffff;
@ -1871,108 +1872,110 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
else else
reg_name = format("R%d", reg); reg_name = format("R%d", reg);
switch(mode_register >> 3) { std::optional<std::string> error;
int mode = mode_register >> 3;
switch(mode) {
case 0: case 0:
return { { reg_name, 2, -1, uint16_t(get_register(reg) & mask), true } }; return { { reg_name, 2, -1, uint16_t(get_register(reg) & mask), true, { } } };
case 1: case 1:
temp2 = b->peek_word(run_mode, get_register(reg)); temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } }; return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
case 2: case 2:
if (reg == 7) if (reg == 7)
return { { format("#%06o", next_word), 4, int(next_word), uint16_t(next_word & mask), true } }; return { { format("#%06o", next_word), 4, int(next_word), uint16_t(next_word & mask), true, { } } };
temp2 = b->peek_word(run_mode, get_register(reg)); temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } }; return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
case 3: case 3:
if (reg == 7) { if (reg == 7) {
temp2 = b->peek_word(run_mode, next_word); temp2 = b->peek_word(run_mode, next_word);
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", next_word);
return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } }; return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
} }
temp2 = b->peek_word(run_mode, get_register(reg)); temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
else {
uint16_t keep = temp2.value();
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", keep);
}
return { { format("@(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
case 4: {
uint16_t calculated_address = get_register(reg) - (word_mode == wm_word || reg >= 6 ? 2 : 1);
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
return { { format("-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
}
case 5: {
uint16_t calculated_address = get_register(reg) - 2;
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
else { else {
temp2 = b->peek_word(run_mode, temp2.value()); temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", temp2.value());
} }
return { { format("@(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } }; return { { format("@-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
case 4:
temp2 = b->peek_word(run_mode, get_register(reg) - (word_mode == wm_word || reg >= 6 ? 2 : 1));
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
return { { format("-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 5:
temp2 = b->peek_word(run_mode, get_register(reg) - 2);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
} }
return { { format("@-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 6: case 6:
if (reg == 7) { {
temp2 = b->peek_word(run_mode, get_register(reg) + next_word); uint16_t calculated_address = get_register(reg) + next_word;
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
return { { format("%06o", (pc + next_word + 2) & 65535), 4, int(next_word), uint16_t(temp2.value() & mask), valid } }; if (reg == 7)
return { { format("%06o", (pc + next_word + 2) & 65535), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
} }
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
return { { format("%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
case 7: case 7:
if (reg == 7) { {
temp2 = b->peek_word(run_mode, get_register(reg) + next_word); uint16_t calculated_address = get_register(reg) + next_word;
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
else { else {
temp2 = b->peek_word(run_mode, temp2.value()); temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false) if (temp2.has_value() == false)
temp2 = 0xffff, valid = false; temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", temp2.value());
} }
return { { format("@%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } }; if (reg == 7)
return { { format("@%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("@%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
}
} }
temp2 = b->peek_word(run_mode, get_register(reg) + next_word); operand_parameters out;
if (temp2.has_value() == false) out.error = format("unknown register mode %d", mode);
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
}
return { { format("@%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid } }; return out;
}
return { };
} }
std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t addr) const std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t addr) const
@ -2076,6 +2079,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
case 0b000110100: case 0b000110100:
if (word_mode == wm_byte) if (word_mode == wm_byte)
name = "MTPS"; name = "MTPS";
else
name = "MARK";
break; break;
case 0b000110111: case 0b000110111:
@ -2089,8 +2094,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
if (text.empty() && name.empty() == false) if (text.empty() && name.empty() == false)
text = name + word_mode_str + space + dst_text.operand; text = name + word_mode_str + space + dst_text.operand;
if (dst_text.valid == false) if (text.empty() == false && dst_text.valid == false)
text += " (INV)"; text += " (INV1)";
if (text.empty() == false && next_word != -1) if (text.empty() == false && next_word != -1)
instruction_words.push_back(next_word); instruction_words.push_back(next_word);
@ -2137,7 +2142,7 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
text = name + space + src_text + comma + dst_text.operand; // TODO: swap for ASH, ASHC text = name + space + src_text + comma + dst_text.operand; // TODO: swap for ASH, ASHC
if (dst_text.valid == false) if (dst_text.valid == false)
text += " (INV)"; text += " (INV2)";
if (text.empty() == false && next_word != -1) if (text.empty() == false && next_word != -1)
instruction_words.push_back(next_word); instruction_words.push_back(next_word);
@ -2194,8 +2199,10 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
work_values.push_back(dst_text.work_value); work_values.push_back(dst_text.work_value);
if (src_text.valid == false || dst_text.valid == false) if (src_text.valid == false || dst_text.valid == false)
text += " (INV)"; text += " (INV3)";
if (do_opcode == 0b110)
word_mode_str.clear();
text = name + word_mode_str + space + src_text.operand + comma + dst_text.operand; text = name + word_mode_str + space + src_text.operand + comma + dst_text.operand;
} }
@ -2351,7 +2358,10 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
text = std::string("JMP ") + dst_text.operand; text = std::string("JMP ") + dst_text.operand;
if (dst_text.valid == false) if (dst_text.valid == false)
text += " (INV)"; text += " (INV4)";
if (addressing.value().error.has_value())
text += " " + addressing.value().error.value();
} }
if ((instruction & 0b1111111000000000) == 0b0000100000000000) { if ((instruction & 0b1111111000000000) == 0b0000100000000000) {
@ -2434,13 +2444,8 @@ bool cpu::step()
{ {
it_is_a_trap = false; it_is_a_trap = false;
if (!b->getMMU()->isMMR1Locked()) if (any_queued_interrupts)
b->getMMU()->clearMMR1(); execute_any_pending_interrupt();
if (any_queued_interrupts && execute_any_pending_interrupt()) {
if (!b->getMMU()->isMMR1Locked())
b->getMMU()->clearMMR1();
}
instruction_count++; instruction_count++;
@ -2453,17 +2458,12 @@ bool cpu::step()
uint16_t instr = b->read_word(instruction_start); uint16_t instr = b->read_word(instruction_start);
add_register(7, 2); add_register(7, 2);
if (double_operand_instructions(instr)) if (double_operand_instructions(instr) || conditional_branch_instructions(instr) || condition_code_operations(instr) || misc_operations(instr)) {
return true; if (!b->getMMU()->isMMR1Locked())
b->getMMU()->clearMMR1();
if (conditional_branch_instructions(instr))
return true;
if (condition_code_operations(instr))
return true;
if (misc_operations(instr))
return true; return true;
}
DOLOG(warning, false, "UNHANDLED instruction %06o @ %06o", instr, instruction_start); DOLOG(warning, false, "UNHANDLED instruction %06o @ %06o", instr, instruction_start);
@ -2472,7 +2472,7 @@ bool cpu::step()
return false; return false;
} }
catch(const int exception_nr) { catch(const int exception_nr) {
TRACE("bus-trap during execution of command (%d)", exception_nr); TRACE("trap during execution of command (%d)", exception_nr);
} }
return true; return true;

1
cpu.h
View file

@ -109,6 +109,7 @@ private:
int instruction_part; int instruction_part;
uint16_t work_value; uint16_t work_value;
bool valid; bool valid;
std::optional<std::string> error;
}; };
std::optional<operand_parameters> addressing_to_string(const uint8_t mode_register, const uint16_t pc, const word_mode_t word_mode) const; std::optional<operand_parameters> addressing_to_string(const uint8_t mode_register, const uint16_t pc, const word_mode_t word_mode) const;

View file

@ -1,6 +1,7 @@
// (C) 2018-2024 by Folkert van Heusden // (C) 2018-2025 by Folkert van Heusden
// Released under MIT license // Released under MIT license
#include <fstream>
#include <optional> #include <optional>
#include "gen.h" #include "gen.h"
#if IS_POSIX || defined(_WIN32) #if IS_POSIX || defined(_WIN32)
@ -172,11 +173,12 @@ std::optional<std::string> select_host_file(console *const cnsl)
for(;;) { for(;;) {
cnsl->flush_input(); cnsl->flush_input();
std::string selected_file = cnsl->read_line("Enter filename (or empty to abort): "); std::string selected_file = cnsl->read_line("Enter filename (\"dir\" for listing or empty to abort): ");
if (selected_file.empty()) if (selected_file.empty())
return { }; return { };
if (selected_file != "dir") {
cnsl->put_string("Opening file: "); cnsl->put_string("Opening file: ");
cnsl->put_string_lf(selected_file.c_str()); cnsl->put_string_lf(selected_file.c_str());
@ -196,6 +198,7 @@ std::optional<std::string> select_host_file(console *const cnsl)
return selected_file; return selected_file;
cnsl->put_string_lf("open failed"); cnsl->put_string_lf("open failed");
}
ls_l(cnsl); ls_l(cnsl);
} }
@ -724,46 +727,38 @@ void set_kw11_l_interrupt_freq(console *const cnsl, bus *const b, const int freq
cnsl->put_string_lf("Frequency out of range"); cnsl->put_string_lf("Frequency out of range");
} }
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event) struct debugger_state {
{ int32_t trace_start_addr { -1 };
int32_t trace_start_addr = -1; int n_single_step { 1 };
int n_single_step = 1; bool turbo { false };
bool turbo = false; bool marker { false };
bool marker = false;
std::optional<int> t_rl; // trace runlevel std::optional<int> t_rl; // trace runlevel
bool single_step { false };
};
bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const std::string & cmd)
{
cpu *const c = b->getCpu(); cpu *const c = b->getCpu();
b->set_debug_mode();
bool single_step = false;
while(*stop_event != EVENT_TERMINATE) {
try {
if (marker)
cnsl->put_string_lf("---");
std::string cmd = cnsl->read_line(format("%d", stop_event->load()));
auto parts = split(cmd, " "); auto parts = split(cmd, " ");
auto kv = split(parts, "="); auto kv = split(parts, "=");
if (parts.empty()) if (parts.empty())
continue; return true;
if (cmd == "go") { if (cmd == "go") {
single_step = false; state->single_step = false;
*stop_event = EVENT_NONE; *stop_event = EVENT_NONE;
} }
else if (cmd == "marker") else if (cmd == "marker")
marker = !marker; state->marker = !state->marker;
else if (parts[0] == "single" || parts[0] == "s" || parts[0] == "step") { else if (parts[0] == "single" || parts[0] == "s" || parts[0] == "step") {
single_step = true; state->single_step = true;
if (parts.size() == 2) if (parts.size() == 2)
n_single_step = std::stoi(parts[1]); state->n_single_step = std::stoi(parts[1]);
else else
n_single_step = 1; state->n_single_step = 1;
*stop_event = EVENT_NONE; *stop_event = EVENT_NONE;
} }
@ -792,7 +787,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("Breakpoint not found"); cnsl->put_string_lf("Breakpoint not found");
} }
continue; return true;
} }
else if (cmd == "lbp") { else if (cmd == "lbp") {
auto bps = c->list_breakpoints(); auto bps = c->list_breakpoints();
@ -805,9 +800,9 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
if (bps.empty()) if (bps.empty())
cnsl->put_string_lf("(none)"); cnsl->put_string_lf("(none)");
continue; return true;
} }
else if (parts[0] == "disassemble" || parts[0] == "d") { else if (parts[0] == "disassemble" || parts[0] == "dis") {
uint16_t pc = kv.find("pc") != kv.end() ? std::stoi(kv.find("pc")->second, nullptr, 8) : c->getPC(); uint16_t pc = kv.find("pc") != kv.end() ? std::stoi(kv.find("pc")->second, nullptr, 8) : c->getPC();
int n = kv.find("n") != kv.end() ? std::stoi(kv.find("n") ->second, nullptr, 10) : 1; int n = kv.find("n") != kv.end() ? std::stoi(kv.find("n") ->second, nullptr, 10) : 1;
@ -821,7 +816,16 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
show_registers = false; show_registers = false;
} }
continue; return true;
}
else if (parts[0] == "D" && parts.size() == 3) { // SIMH compatibility
uint16_t v = std::stoi(parts.at(2), nullptr, 8);
if (parts[1] == "PC")
c->setPC(v);
else {
uint16_t a = std::stoi(parts.at(1), nullptr, 8);
c->getBus()->write_word(a, v);
}
} }
else if (parts[0] == "setpc") { else if (parts[0] == "setpc") {
if (parts.size() == 2) { if (parts.size() == 2) {
@ -834,12 +838,12 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("setpc requires an (octal address as) parameter"); cnsl->put_string_lf("setpc requires an (octal address as) parameter");
} }
continue; return true;
} }
else if (parts[0] == "getpc") { else if (parts[0] == "getpc") {
cnsl->put_string_lf(format("PC = %06o", c->getPC())); cnsl->put_string_lf(format("PC = %06o", c->getPC()));
continue; return true;
} }
else if (parts[0] == "setreg") { else if (parts[0] == "setreg") {
if (parts.size() == 3) { if (parts.size() == 3) {
@ -853,7 +857,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("setreg requires a register and an octal value"); cnsl->put_string_lf("setreg requires a register and an octal value");
} }
continue; return true;
} }
else if (parts[0] == "getreg") { else if (parts[0] == "getreg") {
if (parts.size() == 2) { if (parts.size() == 2) {
@ -864,7 +868,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("getreg requires a register"); cnsl->put_string_lf("getreg requires a register");
} }
continue; return true;
} }
else if (parts[0] == "setstack") { else if (parts[0] == "setstack") {
if (parts.size() == 3) { if (parts.size() == 3) {
@ -879,7 +883,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("setstack requires a register and an octal value"); cnsl->put_string_lf("setstack requires a register and an octal value");
} }
continue; return true;
} }
else if (parts[0] == "getstack") { else if (parts[0] == "getstack") {
if (parts.size() == 2) { if (parts.size() == 2) {
@ -890,7 +894,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("getreg requires a stack register"); cnsl->put_string_lf("getreg requires a stack register");
} }
continue; return true;
} }
else if (parts[0] == "setpsw") { else if (parts[0] == "setpsw") {
if (parts.size() == 2) { if (parts.size() == 2) {
@ -903,12 +907,12 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("setpsw requires an octal value"); cnsl->put_string_lf("setpsw requires an octal value");
} }
continue; return true;
} }
else if (parts[0] == "getpsw") { else if (parts[0] == "getpsw") {
cnsl->put_string_lf(format("PSW = %06o", c->getPSW())); cnsl->put_string_lf(format("PSW = %06o", c->getPSW()));
continue; return true;
} }
else if (parts[0] == "getmem") { else if (parts[0] == "getmem") {
auto a_it = kv.find("a"); auto a_it = kv.find("a");
@ -920,7 +924,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(format("MEM %06o = %03o", a, c->getBus()->read_byte(a))); cnsl->put_string_lf(format("MEM %06o = %03o", a, c->getBus()->read_byte(a)));
} }
continue; return true;
} }
else if (parts[0] == "setmem") { else if (parts[0] == "setmem") {
auto a_it = kv.find("a"); auto a_it = kv.find("a");
@ -937,7 +941,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(format("Set %06o to %03o", a, v)); cnsl->put_string_lf(format("Set %06o to %03o", a, v));
} }
continue; return true;
} }
else if (parts[0] == "toggle") { else if (parts[0] == "toggle") {
auto s_it = kv.find("s"); auto s_it = kv.find("s");
@ -954,14 +958,14 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(format("Set switch %d to %d", s, t)); cnsl->put_string_lf(format("Set switch %d to %d", s, t));
} }
continue; return true;
} }
else if (parts[0] == "trace" || parts[0] == "t") { else if (parts[0] == "trace" || parts[0] == "t") {
settrace(!gettrace()); settrace(!gettrace());
cnsl->put_string_lf(format("Tracing set to %s", gettrace() ? "ON" : "OFF")); cnsl->put_string_lf(format("Tracing set to %s", gettrace() ? "ON" : "OFF"));
continue; return true;
} }
else if (parts[0] == "state") { else if (parts[0] == "state") {
if (parts[1] == "rl02") if (parts[1] == "rl02")
@ -976,12 +980,12 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
b->getTM11()->show_state(cnsl); b->getTM11()->show_state(cnsl);
else if (parts[1] == "kw11l") else if (parts[1] == "kw11l")
b->getKW11_L()->show_state(cnsl); b->getKW11_L()->show_state(cnsl);
else if (parts[1] == "rp06") else if (parts[1] == "rp06" || parts[1] == "rp07")
b->getRP06()->show_state(cnsl); b->getRP06()->show_state(cnsl);
else else
cnsl->put_string_lf(format("Device \"%s\" is not known", parts[1].c_str())); cnsl->put_string_lf(format("Device \"%s\" is not known", parts[1].c_str()));
continue; return true;
} }
else if (parts[0] == "mmures") { else if (parts[0] == "mmures") {
if (parts.size() == 2) if (parts.size() == 2)
@ -989,26 +993,26 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
else else
cnsl->put_string_lf("Parameter missing"); cnsl->put_string_lf("Parameter missing");
continue; return true;
} }
else if (parts[0] == "regdump") { else if (parts[0] == "regdump") {
reg_dump(cnsl, c); reg_dump(cnsl, c);
continue; return true;
} }
else if (parts[0] == "strace") { else if (parts[0] == "strace") {
if (parts.size() != 2) { if (parts.size() != 2) {
trace_start_addr = -1; state->trace_start_addr = -1;
cnsl->put_string_lf("Tracing start address reset"); cnsl->put_string_lf("Tracing start address reset");
} }
else { else {
trace_start_addr = std::stoi(parts[1], nullptr, 8); state->trace_start_addr = std::stoi(parts[1], nullptr, 8);
cnsl->put_string_lf(format("Tracing start address set to %06o", trace_start_addr)); cnsl->put_string_lf(format("Tracing start address set to %06o", state->trace_start_addr));
} }
continue; return true;
} }
else if (parts[0] == "examine" || parts[0] == "e") { else if (parts[0] == "examine" || parts[0] == "e") {
if (parts.size() < 3) if (parts.size() < 3)
@ -1020,7 +1024,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
if (parts[2] != "p" && parts[2] != "v") { if (parts[2] != "p" && parts[2] != "v") {
cnsl->put_string_lf("expected p (physical address) or v (virtual address)"); cnsl->put_string_lf("expected p (physical address) or v (virtual address)");
continue; return true;
} }
std::string out; std::string out;
@ -1058,45 +1062,45 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(out); cnsl->put_string_lf(out);
} }
continue; return true;
} }
else if (cmd == "reset" || cmd == "r") { else if (cmd == "reset" || cmd == "r") {
*stop_event = EVENT_NONE; *stop_event = EVENT_NONE;
b->reset(); b->reset();
cnsl->put_string_lf("resetted"); cnsl->put_string_lf("resetted");
continue; return true;
} }
else if (cmd == "cfgdisk") { else if (cmd == "cfgdisk") {
configure_disk(b, cnsl); configure_disk(b, cnsl);
continue; return true;
} }
#if defined(ESP32) #if defined(ESP32)
else if (cmd == "cfgnet") { else if (cmd == "cfgnet") {
configure_network(cnsl); configure_network(cnsl);
continue; return true;
} }
else if (cmd == "chknet") { else if (cmd == "chknet") {
check_network(cnsl); check_network(cnsl);
continue; return true;
} }
else if (cmd == "startnet") { else if (cmd == "startnet") {
start_network(cnsl); start_network(cnsl);
continue; return true;
} }
else if (parts[0] == "pm" && parts.size() == 2) { else if (parts[0] == "pm" && parts.size() == 2) {
reinterpret_cast<console_esp32 *>(cnsl)->set_panel_mode(parts[1] == "bits" ? console_esp32::PM_BITS : console_esp32::PM_POINTER); reinterpret_cast<console_esp32 *>(cnsl)->set_panel_mode(parts[1] == "bits" ? console_esp32::PM_BITS : console_esp32::PM_POINTER);
continue; return true;
} }
#endif #endif
else if (cmd == "stats") { else if (cmd == "stats") {
show_run_statistics(cnsl, c); show_run_statistics(cnsl, c);
continue; return true;
} }
else if (parts[0] == "ramsize") { else if (parts[0] == "ramsize") {
if (parts.size() == 2) if (parts.size() == 2)
@ -1107,44 +1111,44 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(format("Memory size: %u pages or %u kB (decimal)", n_pages, n_pages * 8192 / 1024)); cnsl->put_string_lf(format("Memory size: %u pages or %u kB (decimal)", n_pages, n_pages * 8192 / 1024));
} }
continue; return true;
} }
else if (parts[0] == "bl" && parts.size() == 2) { else if (parts[0] == "bl" && parts.size() == 2) {
if (parts.at(1) == "rk05") if (parts.at(1) == "rk05")
set_boot_loader(b, BL_RK05); set_boot_loader(b, BL_RK05);
else if (parts.at(1) == "rl02") else if (parts.at(1) == "rl02")
set_boot_loader(b, BL_RL02); set_boot_loader(b, BL_RL02);
else if (parts.at(1) == "rp06") else if (parts.at(1) == "rp06" || parts[1] == "rp07")
set_boot_loader(b, BL_RP06); set_boot_loader(b, BL_RP06);
else else
cnsl->put_string_lf("???"); cnsl->put_string_lf("???");
continue; return true;
} }
else if (parts[0] == "trl") { else if (parts[0] == "trl") {
if (parts.size() == 1) if (parts.size() == 1)
t_rl.reset(); state->t_rl.reset();
else else
t_rl = std::stoi(parts.at(1)); state->t_rl = std::stoi(parts.at(1));
continue; return true;
} }
else if (cmd == "cls") { else if (cmd == "cls") {
const char cls[] = { 27, '[', '2', 'J', 12, 0 }; const char cls[] = { 27, '[', '2', 'J', 27, '[', 'H', 12, 0 };
cnsl->put_string_lf(cls); cnsl->put_string_lf(cls);
continue; return true;
} }
else if (cmd == "turbo") { else if (cmd == "turbo") {
turbo = !turbo; state->turbo = !state->turbo;
if (turbo) if (state->turbo)
c->set_debug(false); c->set_debug(false);
cnsl->put_string_lf(format("Turbo set to %s", turbo ? "ON" : "OFF")); cnsl->put_string_lf(format("Turbo set to %s", state->turbo ? "ON" : "OFF"));
continue; return true;
} }
else if (cmd == "debug") { else if (cmd == "debug") {
bool new_mode = !c->get_debug(); bool new_mode = !c->get_debug();
@ -1152,7 +1156,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf(format("Debug mode set to %s", new_mode ? "ON" : "OFF")); cnsl->put_string_lf(format("Debug mode set to %s", new_mode ? "ON" : "OFF"));
continue; return true;
} }
else if (parts[0] == "setll" && parts.size() == 2) { else if (parts[0] == "setll" && parts.size() == 2) {
auto ll_parts = split(parts[1], ","); auto ll_parts = split(parts[1], ",");
@ -1178,17 +1182,17 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
setll(ll_screen, ll_file); setll(ll_screen, ll_file);
} }
continue; return true;
} }
#if IS_POSIX #if IS_POSIX
else if (parts[0] == "ser" && parts.size() == 2) { else if (parts[0] == "ser" && parts.size() == 2) {
serialize_state(cnsl, b, parts.at(1)); serialize_state(cnsl, b, parts.at(1));
continue; return true;
} }
#endif #endif
else if (parts[0] == "setinthz" && parts.size() == 2) { else if (parts[0] == "setinthz" && parts.size() == 2) {
set_kw11_l_interrupt_freq(cnsl, b, std::stoi(parts.at(1))); set_kw11_l_interrupt_freq(cnsl, b, std::stoi(parts.at(1)));
continue; return true;
} }
else if (parts[0] == "setsl" && parts.size() == 3) { else if (parts[0] == "setsl" && parts.size() == 3) {
if (setloghost(parts.at(1).c_str(), parse_ll(parts[2])) == false) if (setloghost(parts.at(1).c_str(), parse_ll(parts[2])) == false)
@ -1196,22 +1200,22 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
else else
send_syslog(info, "Hello, world!"); send_syslog(info, "Hello, world!");
continue; return true;
} }
else if (parts[0] == "pts" && parts.size() == 2) { else if (parts[0] == "pts" && parts.size() == 2) {
cnsl->enable_timestamp(std::stoi(parts[1])); cnsl->enable_timestamp(std::stoi(parts[1]));
continue; return true;
} }
else if (cmd == "qi") { else if (cmd == "qi") {
show_queued_interrupts(cnsl, c); show_queued_interrupts(cnsl, c);
continue; return true;
} }
else if (parts[0] == "log") { else if (parts[0] == "log") {
DOLOG(info, true, cmd.c_str()); DOLOG(info, true, cmd.c_str());
continue; return true;
} }
else if (parts[0] == "bic" && parts.size() == 2) { else if (parts[0] == "bic" && parts.size() == 2) {
auto rc = load_tape(b, parts[1].c_str()); auto rc = load_tape(b, parts[1].c_str());
@ -1224,7 +1228,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("BIC/LDA failed to load"); cnsl->put_string_lf("BIC/LDA failed to load");
} }
continue; return true;
} }
else if (parts[0] == "lt") { else if (parts[0] == "lt") {
if (parts.size() == 2) if (parts.size() == 2)
@ -1232,42 +1236,42 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
else else
tm11_load_tape(cnsl, b, { }); tm11_load_tape(cnsl, b, { });
continue; return true;
} }
else if (cmd == "dir" || cmd == "ls") { else if (cmd == "dir" || cmd == "ls") {
ls_l(cnsl); ls_l(cnsl);
continue; return true;
} }
else if (cmd == "ult") { else if (cmd == "ult") {
tm11_unload_tape(b); tm11_unload_tape(b);
continue; return true;
} }
else if (parts[0] == "testdc11") { else if (parts[0] == "testdc11") {
b->getDC11()->test_ports(cmd); b->getDC11()->test_ports(cmd);
continue; return true;
} }
else if (cmd == "dp") { else if (cmd == "dp") {
cnsl->stop_panel_thread(); cnsl->stop_panel_thread();
continue; return true;
} }
else if (cmd == "cdc11") { else if (cmd == "cdc11") {
configure_comm(cnsl, *b->getDC11()->get_comm_interfaces()); configure_comm(cnsl, *b->getDC11()->get_comm_interfaces());
continue; return true;
} }
else if (cmd == "serdc11") { else if (cmd == "serdc11") {
serdc11(cnsl, b); serdc11(cnsl, b);
continue; return true;
} }
else if (cmd == "dserdc11") { else if (cmd == "dserdc11") {
deserdc11(cnsl, b); deserdc11(cnsl, b);
continue; return true;
} }
else if (cmd == "bt") { else if (cmd == "bt") {
if (c->get_debug() == false) if (c->get_debug() == false)
@ -1278,17 +1282,17 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
for(auto & element: backtrace) for(auto & element: backtrace)
cnsl->put_string_lf(format("%06o %s", element.first, element.second.c_str())); cnsl->put_string_lf(format("%06o %s", element.first, element.second.c_str()));
continue; return true;
} }
else if (cmd == "quit" || cmd == "q") { else if (cmd == "quit" || cmd == "q") {
#if defined(ESP32) #if defined(ESP32)
ESP.restart(); ESP.restart();
#endif #endif
break; return false;
} }
else if (cmd == "help" || cmd == "h" || cmd == "?") { else if (cmd == "help" || cmd == "h" || cmd == "?") {
constexpr const char *const help[] = { constexpr const char *const help[] = {
"disassemble/d - show current instruction (pc=/n=)", "dis[assemble] - show current instruction (pc=/n=)",
"go - run until trap or ^e", "go - run until trap or ^e",
#if !defined(ESP32) #if !defined(ESP32)
"quit/q - stop emulator", "quit/q - stop emulator",
@ -1311,7 +1315,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
"strace x - start tracing from address - invoke without address to disable", "strace x - start tracing from address - invoke without address to disable",
"trl x - set trace run-level (0...3), empty for all", "trl x - set trace run-level (0...3), empty for all",
"regdump - dump register contents", "regdump - dump register contents",
"state x - dump state of a device: rl02, rk05, rp06, mmu, tm11, kw11l or dc11", "state x - dump state of a device: rl02, rk05, rp06, rp07, mmu, tm11, kw11l or dc11",
"mmures x - resolve a virtual address", "mmures x - resolve a virtual address",
"qi - show queued interrupts", "qi - show queued interrupts",
"setpc x - set PC to value (octal)", "setpc x - set PC to value (octal)",
@ -1333,7 +1337,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
"ult - unload tape", "ult - unload tape",
"stats - show run statistics", "stats - show run statistics",
"ramsize x - set ram size (page (8 kB) count, decimal)", "ramsize x - set ram size (page (8 kB) count, decimal)",
"bl - set bootloader (rl02, rk05 or rp06)", "bl - set bootloader (rl02, rk05, rp06 or rp07)",
"cdc11 - configure DC11 device", "cdc11 - configure DC11 device",
"serdc11 - store DC11 device settings", "serdc11 - store DC11 device settings",
"dserdc11 - load DC11 device settings", "dserdc11 - load DC11 device settings",
@ -1357,11 +1361,11 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
size_t i=0; size_t i=0;
while(help[i]) while(help[i])
cnsl->put_string_lf(help[i++]); cnsl->put_string_lf(help[i++]);
continue; return true;
} }
else { else {
cnsl->put_string_lf("?"); cnsl->put_string_lf("?");
continue; return true;
} }
c->emulation_start(); c->emulation_start();
@ -1370,7 +1374,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
bool reset_cpu = true; bool reset_cpu = true;
if (turbo) { if (state->turbo) {
while(*stop_event == EVENT_NONE) while(*stop_event == EVENT_NONE)
c->step(); c->step();
} }
@ -1378,25 +1382,25 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
reset_cpu = false; reset_cpu = false;
while(*stop_event == EVENT_NONE) { while(*stop_event == EVENT_NONE) {
if (trace_start_addr != -1 && c->getPC() == trace_start_addr) if (state->trace_start_addr != -1 && c->getPC() == state->trace_start_addr)
settrace(true); settrace(true);
if ((gettrace() || single_step) && (t_rl.has_value() == false || t_rl.value() == c->getPSW_runmode())) { if ((gettrace() || state->single_step) && (state->t_rl.has_value() == false || state->t_rl.value() == c->getPSW_runmode())) {
if (!single_step) if (!state->single_step)
TRACE("---"); TRACE("---");
disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false); disassemble(c, state->single_step ? cnsl : nullptr, c->getPC(), false);
} }
auto bp_result = c->check_breakpoint(); auto bp_result = c->check_breakpoint();
if (bp_result.has_value() && !single_step) { if (bp_result.has_value() && !state->single_step) {
cnsl->put_string_lf("Breakpoint: " + bp_result.value()); cnsl->put_string_lf("Breakpoint: " + bp_result.value());
break; break;
} }
c->step(); c->step();
if (single_step && --n_single_step == 0) if (state->single_step && --state->n_single_step == 0)
break; break;
} }
} }
@ -1405,6 +1409,35 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
if (reset_cpu) if (reset_cpu)
c->reset(); c->reset();
return true;
}
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const std::optional<std::string> & init)
{
debugger_state state;
b->set_debug_mode();
if (init.has_value()) {
std::string line;
std::ifstream fh;
fh.open(init.value());
while(std::getline(fh, line)) {
if (debugger_do(&state, cnsl, b, stop_event, line) == false)
return;
}
}
while(*stop_event != EVENT_TERMINATE) {
try {
if (state.marker)
cnsl->put_string_lf("---");
std::string cmd = cnsl->read_line(format("%d", stop_event->load()));
if (debugger_do(&state, cnsl, b, stop_event, cmd) == false)
break;
} }
catch(const std::exception & e) { catch(const std::exception & e) {
cnsl->put_string_lf(format("Exception caught: %s", e.what())); cnsl->put_string_lf(format("Exception caught: %s", e.what()));

View file

@ -7,6 +7,6 @@
int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool instruction_only); int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool instruction_only);
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event); void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const std::optional<std::string> & init);
void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const uint16_t bic_start); void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const uint16_t bic_start);

View file

@ -1,185 +0,0 @@
#! /usr/bin/python3
import copy
import json
from machine import PDP1170
from mmio import MMIO
from pdptraps import PDPTrap, PDPTraps
import random
import sys
ignore_traps = True
class MMIO_wrapper(MMIO):
def register(self, iofunc, offsetaddr, nwords, *, byte_writes=False, reset=False):
pass
def register_simpleattr(self, obj, attrname, addr, reset=False):
pass
def devicereset_register(self, func):
pass
class PDP1170_wrapper(PDP1170):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger.info('')
self.reset_mem_transactions_dict()
def reset_mem_transactions_dict(self):
self.mem_transactions = dict()
self.before = dict()
def get_mem_before(self):
return self.before
def get_mem_transactions_dict(self):
return self.mem_transactions
def put(self, physaddr, value):
self.before[physaddr] = value
super().physRW(physaddr, value)
def physRW(self, physaddr, value=None):
if value == None: # read
self.logger.info(f'Read from {physaddr:08o} (phys)')
if not physaddr in self.mem_transactions and not physaddr in self.before:
self.before[physaddr] = random.randint(0, 65536)
return super().physRW(physaddr, self.before[physaddr])
self.logger.info(f'Write to {physaddr:08o}: {value:06o} (phys)')
self.mem_transactions[physaddr] = value
return super().physRW(physaddr, value)
def physRW_N(self, physaddr, nwords, words=None):
temp_addr = physaddr
if words == None:
self.logger.info(f'Read {nwords} words from {physaddr:08o} (phys)')
for i in range(nwords):
self.physRW(temp_addr, random.randint(0, 65536))
temp_addr += 2
return super().physRW_N(physaddr, nwords)
self.logger.info(f'Write {nwords} ({len(words)}) words to {physaddr:08o} (phys)')
for w in words:
self.mem_transactions[temp_addr] = w
temp_addr += 2
return super().physRW_N(physaddr, nwords, words=words)
class test_generator:
def _invoke_bp(self, a, i):
return True
def put_registers(self, p, target, tag):
target[tag] = dict()
target[tag][0] = dict()
target[tag][1] = dict()
for set_ in range(0, 2):
for reg_ in range(0, 6):
target[tag][set_][reg_] = p.registerfiles[set_][reg_]
target[tag]['sp'] = copy.deepcopy(p.stackpointers)
target[tag]['pc'] = p.r[p.PC]
def create_test(self):
out = { }
p = PDP1170_wrapper(loglevel='DEBUG')
p.ub.mmio = MMIO_wrapper(p)
# TODO what is the maximum size of an instruction?
# non-mmu thus shall be below device range
addr = random.randint(0, 0o160000 - 8) & ~1
mem_kv = []
while True:
instr = random.randint(0, 65536 - 8)
if instr != 1: # TODO ignore 'WAIT' instruction
break
p.logger.info(f'emulating {instr:06o}')
mem_kv.append((addr + 0, instr))
mem_kv.append((addr + 2, random.randint(0, 65536 - 8)))
mem_kv.append((addr + 4, random.randint(0, 65536 - 8)))
mem_kv.append((addr + 6, random.randint(0, 65536 - 8)))
out['memory-before'] = dict()
for a, v in mem_kv:
p.put(a, v)
try:
# generate & set PSW
while True:
try:
p.psw = random.randint(0, 65536)
break
except PDPTraps.ReservedInstruction as ri:
pass
# generate other registers
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65536)))
reg_kv.append((7, addr))
# set registers
set_ = (p.psw >> 11) & 1
for r, v in reg_kv:
p.registerfiles[set_][r] = v
p.registerfiles[1 - set_][r] = (~v) & 65535 # make sure it triggers errors
assert p.registerfiles[set_][r] == p.r[r]
p.r[6] = p.registerfiles[set_][6]
p.r[p.PC] = addr
p._syncregs()
self.put_registers(p, out, 'registers-before')
out['registers-before']['psw'] = p.psw
# run instruction
p.run_steps(pc=addr, steps=1)
if p.straps and ignore_traps:
return None
self.put_registers(p, out, 'registers-after')
out['registers-after']['psw'] = p.psw
mb = p.get_mem_before()
for a in mb:
out['memory-before'][a] = mb[a]
out['memory-after'] = dict()
mem_transactions = p.get_mem_transactions_dict()
for a in mem_transactions:
out['memory-after'][a] = mem_transactions[a]
# TODO originele geheugeninhouden checken
return out
except PDPTraps.ReservedInstruction as pri:
return None
except Exception as e:
# handle PDP11 traps; store them
print(f'test failed {e}, line number: {e.__traceback__.tb_lineno}')
return None
fh = open(sys.argv[1], 'w')
t = test_generator()
tests = []
try:
for i in range(32768):
if (i & 63) == 0:
print(f'{i}\r', end='')
test = t.create_test()
if test != None:
tests.append(test)
except KeyboardInterrupt as ki:
pass
fh.write(json.dumps(tests, indent=4))
fh.close()

View file

@ -1,218 +0,0 @@
#! /usr/bin/python3
import copy
import json
from machine import PDP1170
from mmio import MMIO
from op4 import op4_dispatch_table
import os
from pdptraps import PDPTrap, PDPTraps
import random
import sys
ignore_traps = True
class MMIO_wrapper(MMIO):
def register(self, iofunc, offsetaddr, nwords, *, byte_writes=False, reset=False):
pass
def register_simpleattr(self, obj, attrname, addr, reset=False):
pass
def devicereset_register(self, func):
pass
class PDP1170_wrapper(PDP1170):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger.info('')
self.reset_mem_transactions_dict()
def reset_mem_transactions_dict(self):
self.mem_transactions = dict()
self.before = dict()
def get_mem_before(self):
return self.before
def get_mem_transactions_dict(self):
return self.mem_transactions
def put(self, physaddr, value):
self.before[physaddr] = value
super().physRW(physaddr, value)
def physRW(self, physaddr, value=None):
if value == None: # read
self.logger.info(f'Read from {physaddr:08o} (phys)')
if not physaddr in self.mem_transactions and not physaddr in self.before:
self.before[physaddr] = random.randint(0, 65535)
return super().physRW(physaddr, self.before[physaddr])
self.logger.info(f'Write to {physaddr:08o}: {value:06o} (phys)')
self.mem_transactions[physaddr] = value
return super().physRW(physaddr, value)
def physRW_N(self, physaddr, nwords, words=None):
temp_addr = physaddr
if words == None:
self.logger.info(f'Read {nwords} words from {physaddr:08o} (phys)')
for i in range(nwords):
self.physRW(temp_addr, random.randint(0, 65535))
temp_addr += 2
return super().physRW_N(physaddr, nwords)
self.logger.info(f'Write {nwords} ({len(words)}) words to {physaddr:08o} (phys)')
for w in words:
self.mem_transactions[temp_addr] = w
temp_addr += 2
return super().physRW_N(physaddr, nwords, words=words)
def simple_run_1(self, pc):
thisPC = pc
self.r[self.PC] = (thisPC + 2) & 0o177777 # "could" wrap
try:
inst = self.mmu.wordRW(thisPC)
op4_dispatch_table[inst >> 12](self, inst)
return True
except PDPTrap as trap:
return False
class test_generator:
def _invoke_bp(self, a, i):
return True
def put_registers(self, p, target, tag):
target[tag] = dict()
target[tag][0] = dict()
target[tag][1] = dict()
for set_ in range(0, 2):
for reg_ in range(0, 6):
target[tag][set_][reg_] = p.registerfiles[set_][reg_]
target[tag]['sp'] = copy.deepcopy(p.stackpointers)
target[tag]['pc'] = p.r[p.PC]
def create_test(self, instr, psw):
try:
out = { }
p = PDP1170_wrapper(loglevel='ERROR')
p.ub.mmio = MMIO_wrapper(p)
# TODO what is the maximum size of an instruction?
# non-mmu thus shall be below device range
addr = random.randint(0, 0o160000 - 8) & ~1
mem_kv = []
if instr == 1: # TODO ignore 'WAIT' instruction
return None
p.logger.info(f'emulating {instr:06o}')
mem_kv.append((addr + 0, instr))
mem_kv.append((addr + 2, random.randint(0, 65535 - 8) & ~1))
mem_kv.append((addr + 4, random.randint(0, 65535 - 8) & ~1))
mem_kv.append((addr + 6, random.randint(0, 65535 - 8) & ~1))
out['memory-before'] = dict()
for a, v in mem_kv:
p.put(a, v)
p.psw = psw
# generate other registers
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65535) & ~1))
reg_kv.append((7, addr))
# set registers
set_ = (p.psw >> 11) & 1
for r, v in reg_kv:
p.registerfiles[set_][r] = v
p.registerfiles[1 - set_][r] = (~v) & 65535 # make sure it triggers errors
assert p.registerfiles[set_][r] == p.r[r]
p.r[6] = p.registerfiles[set_][6]
p.r[p.PC] = addr
p._syncregs()
self.put_registers(p, out, 'registers-before')
out['registers-before']['psw'] = p.psw
# run instruction
p.run_steps(pc=addr, steps=1)
#if p.simple_run_1(addr) == False:
# return None
#if (p.straps or p.issues) and ignore_traps:
if p.straps and ignore_traps:
return None
p._syncregs()
self.put_registers(p, out, 'registers-after')
out['registers-after']['psw'] = p.psw
mb = p.get_mem_before()
for a in mb:
out['memory-before'][a] = mb[a]
out['memory-after'] = dict()
mem_transactions = p.get_mem_transactions_dict()
# verify original values
for a, v in mem_kv:
if not a in mem_transactions:
mem_transactions[a] = v
for a in mem_transactions:
out['memory-after'][a] = mem_transactions[a]
out['mmr0-after'] = p.mmu.MMR0
p.mmu._MMR1commit()
out['mmr1-after'] = p.mmu.MMR1
out['mmr2-after'] = p.mmu.MMR2
out['mmr3-after'] = p.mmu.MMR3
return out
except PDPTraps.ReservedInstruction as pri:
return None
except Exception as e:
# handle PDP11 traps; store them
print(f'test failed {e}, line number: {e.__traceback__.tb_lineno}')
return None
stop = False
while True:
psw = random.randint(0, 65535) & 0o174377
name = f'/data/data/temp2/0008-{psw:06o}.json'
if os.path.isfile(name):
print(f'skipping {name}')
continue
print(name)
fh = open(name, 'w')
t = test_generator()
tests = []
try:
for lb in range(65535):
if (lb & 63) == 0:
print(f'{lb} {len(tests)}\r', end='')
test = t.create_test(lb, psw)
if test != None:
tests.append(test)
except KeyboardInterrupt as ki:
stop = True
fh.write(json.dumps(tests, indent=4))
fh.close()
if stop:
break

View file

@ -53,38 +53,6 @@ void set_boot_loader(bus *const b, const bootloader_t which)
0005007 0005007
}; };
#if 0
// from https://github.com/amakukha/PyPDP11.git
offset = 02000;
start = 02002;
static uint16_t rk05_code[] = {
0042113, // "KD"
0012706, 02000, // MOV #boot_start, SP
0012700, 0000000, // MOV #unit, R0 ; unit number
0010003, // MOV R0, R3
0000303, // SWAB R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0012701, 0177412, // MOV #RKDA, R1 ; csr
0010311, // MOV R3, (R1) ; load da
0005041, // CLR -(R1) ; clear ba
0012741, 0177000, // MOV #-256.*2, -(R1) ; load wc
0012741, 0000005, // MOV #READ+GO, -(R1) ; read & go
0005002, // CLR R2
0005003, // CLR R3
0012704, 02020, // MOV #START+20, R4
0005005, // CLR R5
0105711, // TSTB (R1)
0100376, // BPL .-2
0105011, // CLRB (R1)
0005007 // CLR PC
};
#endif
bl = rk05_code; bl = rk05_code;
size = sizeof(rk05_code)/sizeof(rk05_code[0]); size = sizeof(rk05_code)/sizeof(rk05_code[0]);
@ -144,7 +112,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
start = offset = 02000; start = offset = 02000;
static const uint16_t rp06_code[] = { static const uint16_t rp06_code[] = {
012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000 012701, 0176700, 012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000
}; };

View file

@ -65,8 +65,9 @@ std::vector<std::pair<uint32_t, uint8_t> > get_memory_settings(const JsonArrayCo
for(const auto & kv: kv_dict.as<JsonObjectConst>()) { for(const auto & kv: kv_dict.as<JsonObjectConst>()) {
uint32_t a = std::stoi(kv.key().c_str(), nullptr, 8); uint32_t a = std::stoi(kv.key().c_str(), nullptr, 8);
uint16_t v = kv.value().as<int>(); uint16_t v = kv.value().as<int>();
out.push_back({ a, v & 255 }); out.push_back({ a, v });
out.push_back({ a + 1, v >> 8 }); if (a == 0 && v == 0)
printf("Suspect\n");
} }
} }
return out; return out;
@ -112,7 +113,7 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
// create environment // create environment
event = 0; event = 0;
bus *b = new bus(); bus *b = new bus();
b->set_memory_size(DEFAULT_N_PAGES * 8192l); b->set_memory_size(DEFAULT_N_PAGES);
cpu *c = new cpu(b, &event); cpu *c = new cpu(b, &event);
b->add_cpu(c); b->add_cpu(c);
@ -130,6 +131,9 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
for(int i=0; i<4; i++) for(int i=0; i<4; i++)
c->set_stackpointer(i, get_register_value(before, format("stack-%d", i))); c->set_stackpointer(i, get_register_value(before, format("stack-%d", i)));
b->getMMU()->setMMR1(get_register_value(before, "mmr1"));
b->getMMU()->setMMR2(get_register_value(before, "mmr2"));
// registers // registers
for(int set=0; set<2; set++) { for(int set=0; set<2; set++) {
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
@ -140,20 +144,27 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
auto memory_before = before["memory"]; auto memory_before = before["memory"];
auto memory_before_settings = get_memory_settings(memory_before); auto memory_before_settings = get_memory_settings(memory_before);
for(auto & element: memory_before_settings) for(auto & element: memory_before_settings)
b->write_physical(element.first, element.second); b->write_unibus_byte(element.first, element.second);
} }
int run_n_instructions = test["before"]["run-n-instructions"].as<int>();
int cur_n_errors = 0; int cur_n_errors = 0;
// DO! // DO!
c->emulation_start(); c->emulation_start();
for(int k=0; k<run_n_instructions; k++) {
disassemble(c, nullptr, c->getPC(), false); disassemble(c, nullptr, c->getPC(), false);
if (c->step() == false) { if (c->step() == false) {
cnsl->put_string_lf("Treated as an invalid instruction"); cnsl->put_string_lf("Treated as an invalid instruction");
cur_n_errors++; cur_n_errors++;
break;
} }
}
disassemble(c, nullptr, c->getPC(), false);
// VERIFY // VERIFY
else { if (cur_n_errors == 0) {
auto after = test["after"]; auto after = test["after"];
cur_n_errors += !compare_values(cnsl, c->getPC(), get_register_value(after, "PC" ), "PC" ); cur_n_errors += !compare_values(cnsl, c->getPC(), get_register_value(after, "PC" ), "PC" );
@ -162,6 +173,9 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
for(int i=0; i<4; i++) for(int i=0; i<4; i++)
cur_n_errors += !compare_values(cnsl, c->get_stackpointer(i), get_register_value(after, format("stack-%d", i)), format("Stack pointer %d", i)); cur_n_errors += !compare_values(cnsl, c->get_stackpointer(i), get_register_value(after, format("stack-%d", i)), format("Stack pointer %d", i));
cur_n_errors += !compare_values(cnsl, b->getMMU()->getMMR1(), get_register_value(after, "mmr1"), "MMR1");
cur_n_errors += !compare_values(cnsl, b->getMMU()->getMMR2(), get_register_value(after, "mmr2"), "MMR2");
for(int set=0; set<2; set++) { for(int set=0; set<2; set++) {
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
cur_n_errors += !compare_values(cnsl, c->lowlevel_register_get(set, i), get_register_value(after, format("reg-%d.%d", i, set)), format("Register %d", i)); cur_n_errors += !compare_values(cnsl, c->lowlevel_register_get(set, i), get_register_value(after, format("reg-%d.%d", i, set)), format("Register %d", i));
@ -239,11 +253,12 @@ void help()
printf("-B run tape file as a unit test (for .BIC files)\n"); printf("-B run tape file as a unit test (for .BIC files)\n");
printf("-r d.img load file as a disk device\n"); printf("-r d.img load file as a disk device\n");
printf("-N host:port use NBD-server as disk device (like -r)\n"); printf("-N host:port use NBD-server as disk device (like -r)\n");
printf("-R x select disk type (rk05, rl02 or rp06)\n"); printf("-R x select disk type (rk05, rl02, rp06 or rp07)\n");
printf("-p 123 set CPU start pointer to decimal(!) value\n"); printf("-p 123 set CPU start pointer to decimal(!) value\n");
printf("-b enable bootloader (builtin)\n"); printf("-b enable bootloader (builtin)\n");
printf("-n ncurses UI\n"); printf("-n ncurses UI\n");
printf("-d enable debugger\n"); printf("-d enable debugger\n");
printf("-f x first process the commands from file x before entering the debugger\n");
printf("-S x set ram size (in number of 8 kB pages)\n"); printf("-S x set ram size (in number of 8 kB pages)\n");
printf("-s x,y set console switche state: set bit x (0...15) to y (0/1)\n"); printf("-s x,y set console switche state: set bit x (0...15) to y (0/1)\n");
printf("-t enable tracing (disassemble to stderr, requires -d as well)\n"); printf("-t enable tracing (disassemble to stderr, requires -d as well)\n");
@ -252,7 +267,7 @@ void help()
printf("-X do not include timestamp in logging\n"); printf("-X do not include timestamp in logging\n");
printf("-J x run validation suite x against the CPU emulation\n"); printf("-J x run validation suite x against the CPU emulation\n");
printf("-M log metrics\n"); printf("-M log metrics\n");
printf("-1 x use x as device for DC-11\n"); printf("-1 x use x as device for DC-11 (instead of 4 tcp-sockets starting at port 1100)\n");
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -263,6 +278,7 @@ int main(int argc, char *argv[])
std::string disk_type = "rk05"; std::string disk_type = "rk05";
bool run_debugger = false; bool run_debugger = false;
std::optional<std::string> debugger_init;
bool enable_bootloader = false; bool enable_bootloader = false;
bootloader_t bootloader = BL_NONE; bootloader_t bootloader = BL_NONE;
@ -297,13 +313,17 @@ int main(int argc, char *argv[])
std::optional<std::string> dc11_device; std::optional<std::string> dc11_device;
int opt = -1; int opt = -1;
while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndtL:bl:s:Q:N:J:XS:P1:")) != -1) while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndf:tL:bl:s:Q:N:J:XS:P1:")) != -1)
{ {
switch(opt) { switch(opt) {
case 'h': case 'h':
help(); help();
return 1; return 1;
case 'f':
debugger_init = optarg;
break;
case '1': case '1':
dc11_device = optarg; dc11_device = optarg;
break; break;
@ -371,7 +391,7 @@ int main(int argc, char *argv[])
case 'R': case 'R':
disk_type = optarg; disk_type = optarg;
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06") if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06" && disk_type != "rp07")
error_exit(false, "Disk type not known"); error_exit(false, "Disk type not known");
break; break;
@ -460,7 +480,7 @@ int main(int argc, char *argv[])
if (set_ram_size.has_value()) if (set_ram_size.has_value())
b->set_memory_size(set_ram_size.value()); b->set_memory_size(set_ram_size.value());
else else
b->set_memory_size(DEFAULT_N_PAGES * 8192l); b->set_memory_size(DEFAULT_N_PAGES);
b->set_console_switches(console_switches); b->set_console_switches(console_switches);
@ -475,7 +495,7 @@ int main(int argc, char *argv[])
rl02_dev->begin(); rl02_dev->begin();
b->add_rl02(rl02_dev); b->add_rl02(rl02_dev);
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()); auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag(), disk_type == "rp07");
rp06_dev->begin(); rp06_dev->begin();
b->add_RP06(rp06_dev); b->add_RP06(rp06_dev);
@ -491,7 +511,7 @@ int main(int argc, char *argv[])
for(auto & file: disk_files) for(auto & file: disk_files)
rl02_dev->access_disk_backends()->push_back(file); rl02_dev->access_disk_backends()->push_back(file);
} }
else if (disk_type == "rp06") { else if (disk_type == "rp06" || disk_type == "rp07") {
bootloader = BL_RP06; bootloader = BL_RP06;
for(auto & file: disk_files) for(auto & file: disk_files)
@ -598,7 +618,7 @@ int main(int argc, char *argv[])
if (is_bic) if (is_bic)
run_bic(cnsl, b, &event, bic_start.value()); run_bic(cnsl, b, &event, bic_start.value());
else if (run_debugger || (bootloader == BL_NONE && test.empty() && tape.empty())) else if (run_debugger || (bootloader == BL_NONE && test.empty() && tape.empty()))
debugger(cnsl, b, &event); debugger(cnsl, b, &event, debugger_init);
else if (benchmark) { else if (benchmark) {
// FILL MEMORY // FILL MEMORY
memory *m = b->getRAM(); memory *m = b->getRAM();

21
mmu.cpp
View file

@ -126,6 +126,11 @@ void mmu::clearMMR0Bit(const int bit)
MMR0 &= ~(1 << bit); MMR0 &= ~(1 << bit);
} }
void mmu::setMMR1(const uint16_t value)
{
MMR1 = value;
}
void mmu::setMMR2(const uint16_t value) void mmu::setMMR2(const uint16_t value)
{ {
MMR2 = value; MMR2 = value;
@ -145,6 +150,7 @@ bool mmu::get_use_data_space(const int run_mode) const
void mmu::clearMMR1() void mmu::clearMMR1()
{ {
TRACE("clear MMR1");
MMR1 = 0; MMR1 = 0;
} }
@ -371,21 +377,10 @@ void mmu::verify_page_access(const uint16_t virt_addr, const int run_mode, const
TRACE("MMR0: %06o", temp); TRACE("MMR0: %06o", temp);
} }
if (trap_action == T_TRAP_250) {
TRACE("Page access %d (for virtual address %06o): trap 0250", access_control, virt_addr); TRACE("Page access %d (for virtual address %06o): trap 0250", access_control, virt_addr);
c->trap(0250); // abort
c->trap(0250); // trap
throw 5; throw 5;
} }
else { // T_ABORT_4
TRACE("Page access %d (for virtual address %06o): trap 004", access_control, virt_addr);
c->trap(004); // abort
throw 5;
}
}
void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const bool d, const int apf, const bool is_io, const bool is_write) void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const bool d, const int apf, const bool is_io, const bool is_write)
{ {
@ -410,7 +405,7 @@ void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const
if (is_write) if (is_write)
set_page_trapped(run_mode, d, apf); set_page_trapped(run_mode, d, apf);
c->trap(04); c->trap(0250);
throw 6; throw 6;
} }

View file

@ -14,18 +14,22 @@
#include "utils.h" #include "utils.h"
constexpr const unsigned NSECT = 22; // sectors per track unsigned NSECT = 22; // sectors per track
constexpr const unsigned NTRAC = 19; // tracks per cylinder unsigned NTRAC = 19; // tracks per cylinder
constexpr const unsigned SECTOR_SIZE = 512; unsigned SECTOR_SIZE = 512;
constexpr const uint16_t default_DS = uint16_t(rp06::ds_bits::DPR) /* drive present */ | uint16_t(rp06::ds_bits::MOL) /* medium on-line */ | uint16_t(rp06::ds_bits::VV) /* volume valid */ | uint16_t(rp06::ds_bits::DRY) /* drive ready */; constexpr const uint16_t default_DS = uint16_t(rp06::ds_bits::DPR) /* drive present */ | uint16_t(rp06::ds_bits::MOL) /* medium on-line */ | uint16_t(rp06::ds_bits::VV) /* volume valid */ | uint16_t(rp06::ds_bits::DRY) /* drive ready */;
constexpr const char *regnames[] { "Control", "Status", "Error register 1", "Maintenance", "Attention summary", "Desired sector/track address", "Error register 1", "Look ahead", "Drive type", "Serial no", "Offset", "Desired cylinder address", "Current cylinder address", "Error register 2", "Error register 3", "ECC position", "ECC pattern" }; constexpr const char *regnames[] { "Control", "Status", "Error register 1", "Maintenance", "Attention summary", "Desired sector/track address", "Error register 1", "Look ahead", "Drive type", "Serial no", "Offset", "Desired cylinder address", "Current cylinder address", "Error register 2", "Error register 3", "ECC position", "ECC pattern" };
rp06::rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity) : rp06::rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity, const bool is_rp07) :
b(b), b(b),
disk_read_activity (disk_read_activity ), disk_read_activity (disk_read_activity ),
disk_write_activity(disk_write_activity) disk_write_activity(disk_write_activity)
{ {
if (is_rp07) {
NSECT = 50;
NTRAC = 32;
}
} }
rp06::~rp06() rp06::~rp06()
@ -51,13 +55,13 @@ void rp06::show_state(console *const cnsl) const
JsonDocument rp06::serialize() const JsonDocument rp06::serialize() const
{ {
JsonDocument j; JsonDocument j;
j["is-rp07"] = is_rp07;
return j; return j;
} }
rp06 *rp06::deserialize(const JsonVariantConst j, bus *const b) rp06 *rp06::deserialize(const JsonVariantConst j, bus *const b)
{ {
rp06 *r = new rp06(b, nullptr, nullptr); rp06 *r = new rp06(b, nullptr, nullptr, j["is-rp07"].as<bool>());
r->begin(); r->begin();
return r; return r;

6
rp06.h
View file

@ -38,8 +38,8 @@ class bus;
class rp06: public disk_device class rp06: public disk_device
{ {
private: private:
bus *const b; bus *const b { nullptr };
bool is_rp07 { false };
uint16_t registers[32] { }; uint16_t registers[32] { };
std::atomic_bool *const disk_read_activity { nullptr }; std::atomic_bool *const disk_read_activity { nullptr };
@ -50,7 +50,7 @@ private:
uint32_t compute_offset() const; uint32_t compute_offset() const;
public: public:
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity); rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity, const bool is_rp07);
virtual ~rp06(); virtual ~rp06();
void begin() override; void begin() override;

View file

@ -174,8 +174,7 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
for(loop=0; loop<len; loop++) for(loop=0; loop<len; loop++)
{ {
if (str[loop] == '^') if (str[loop] == '^') {
{
if (!inv) if (!inv)
mywattron(win -> win, A_REVERSE); mywattron(win -> win, A_REVERSE);
else else
@ -183,8 +182,7 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
inv = 1 - inv; inv = 1 - inv;
} }
else if (str[loop] == '_') else if (str[loop] == '_') {
{
if (!underline) if (!underline)
mywattron(win -> win, A_UNDERLINE); mywattron(win -> win, A_UNDERLINE);
else else
@ -192,13 +190,11 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
underline = 1 - underline; underline = 1 - underline;
} }
else if (str[loop] == '\n') else if (str[loop] == '\n') {
{
cursor_x = 0; cursor_x = 0;
y++; y++;
} }
else else {
{
mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]); mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]);
} }
} }
@ -215,10 +211,8 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
int loop, len = strlen(str); int loop, len = strlen(str);
bool inv = false, underline = false; bool inv = false, underline = false;
for(loop=0; loop<len; loop++) for(loop=0; loop<len; loop++) {
{ if (str[loop] == rev) {
if (str[loop] == rev)
{
if (!inv) if (!inv)
mywattron(win -> win, A_REVERSE); mywattron(win -> win, A_REVERSE);
else else
@ -226,8 +220,7 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
inv = 1 - inv; inv = 1 - inv;
} }
else if (str[loop] == un) else if (str[loop] == un) {
{
if (!underline) if (!underline)
mywattron(win -> win, A_UNDERLINE); mywattron(win -> win, A_UNDERLINE);
else else
@ -235,8 +228,7 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
underline = 1 - underline; underline = 1 - underline;
} }
else else {
{
waddch(win -> win, str[loop]); waddch(win -> win, str[loop]);
} }
} }
@ -260,9 +252,7 @@ void create_win_border(int x, int y, int width, int height, const char *title, N
*bwin = create_window_xy(y + 0, x + 0, height + 2 + wbb * 2, width + 2 + wbb * 2); *bwin = create_window_xy(y + 0, x + 0, height + 2 + wbb * 2, width + 2 + wbb * 2);
*win = create_window_xy(y + 1 + wbb, x + 1 + wbb, height + 0, width + 0); *win = create_window_xy(y + 1 + wbb, x + 1 + wbb, height + 0, width + 0);
mywattron((*bwin) -> win, A_REVERSE);
box((*bwin) -> win, 0, 0); box((*bwin) -> win, 0, 0);
mywattroff((*bwin) -> win, A_REVERSE);
mywattron((*bwin) -> win, A_STANDOUT); mywattron((*bwin) -> win, A_STANDOUT);