Compare commits
No commits in common. "master" and "jsonsimh" have entirely different histories.
16 changed files with 1329 additions and 950 deletions
|
@ -16,7 +16,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|||
|
||||
include(FindPkgConfig)
|
||||
|
||||
set(APP_SOURCES
|
||||
if (NOT WIN32)
|
||||
|
||||
add_executable(
|
||||
kek
|
||||
breakpoint.cpp
|
||||
breakpoint_and.cpp
|
||||
breakpoint_memory.cpp
|
||||
|
@ -54,56 +57,58 @@ set(APP_SOURCES
|
|||
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)
|
||||
target_link_libraries(kek ${NCURSES_LIBRARIES})
|
||||
target_include_directories(kek PUBLIC ${NCURSES_INCLUDE_DIRS})
|
||||
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)
|
||||
target_link_libraries(kek ${PANEL_LIBRARIES})
|
||||
target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS})
|
||||
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)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
add_executable(
|
||||
kek-win32
|
||||
${APP_SOURCES}
|
||||
breakpoint.cpp
|
||||
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
|
||||
)
|
||||
|
||||
endif (WIN32)
|
||||
|
||||
include(CheckIPOSupported)
|
||||
|
@ -119,7 +124,6 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
|||
find_package(Threads)
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(kek Threads::Threads)
|
||||
target_link_libraries(kek-codecoverage Threads::Threads)
|
||||
else ()
|
||||
target_link_libraries(kek-win32 Threads::Threads)
|
||||
|
||||
|
@ -128,21 +132,7 @@ endif ()
|
|||
|
||||
add_subdirectory(arduinojson)
|
||||
target_link_libraries(kek ArduinoJson)
|
||||
target_link_libraries(kek-codecoverage ArduinoJson)
|
||||
target_link_libraries(kek-native ArduinoJson)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(kek-win32 ArduinoJson)
|
||||
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}
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@ board_build.filesystem = littlefs
|
|||
lib_deps = greiman/SdFat@^2.1.2
|
||||
adafruit/Adafruit NeoPixel
|
||||
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 -DNEOPIXELS_PIN=25 -DTTY_SERIAL_TX=17 -DTTY_SERIAL_RX=18
|
||||
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_unflags = -std=gnu++11 -std=gnu++17
|
||||
extra_scripts = pre:prepare.py
|
||||
|
||||
|
|
|
@ -1,5 +1,2 @@
|
|||
41ef37ce6b7f13f1cc2df0e6ac570647fe07fe12:
|
||||
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
|
||||
|
|
2
bus.cpp
2
bus.cpp
|
@ -139,7 +139,7 @@ void bus::set_memory_size(const int n_pages)
|
|||
|
||||
mmu_->begin(m, c);
|
||||
|
||||
TRACE("Memory is now %u kB (%d pages)", n_bytes / 1024, n_pages);
|
||||
TRACE("Memory is now %u kB in size", n_bytes / 1024);
|
||||
}
|
||||
|
||||
void bus::reset()
|
||||
|
|
204
cpu.cpp
204
cpu.cpp
|
@ -316,15 +316,11 @@ int cpu::getPSW_spl() const
|
|||
|
||||
void cpu::setPSW(const uint16_t v, const bool limited)
|
||||
{
|
||||
if (limited) {
|
||||
int cur_mode = std::max( v >> 14, psw >> 14);
|
||||
int prev_mode = std::max((v >> 12) & 3, (psw >> 12) & 3);
|
||||
psw = (psw & 004340) | (v & 037) | (cur_mode << 14) | (prev_mode << 12);
|
||||
}
|
||||
else {
|
||||
if (limited)
|
||||
psw = (psw & 0174340) | (v & 037);
|
||||
else
|
||||
psw = v & 0174377; // mask off reserved bits
|
||||
}
|
||||
}
|
||||
|
||||
void cpu::setPSW_flags_nzv(const uint16_t value, const word_mode_t word_mode)
|
||||
{
|
||||
|
@ -457,9 +453,9 @@ void cpu::queue_interrupt(const uint8_t level, const uint8_t vector)
|
|||
|
||||
void cpu::addToMMR1(const gam_rc_t & g)
|
||||
{
|
||||
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);
|
||||
if (!b->getMMU()->isMMR1Locked() && g.mmr1_update.has_value()) {
|
||||
assert(g.mmr1_update.value().delta);
|
||||
|
||||
b->getMMU()->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg);
|
||||
}
|
||||
}
|
||||
|
@ -488,17 +484,17 @@ gam_rc_t cpu::getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t wo
|
|||
break;
|
||||
case 2: // (Rn)+ / #n
|
||||
g.addr = get_register(reg);
|
||||
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 };
|
||||
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);
|
||||
g.mmr1_update = { word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1, reg };
|
||||
break;
|
||||
case 3: // @(Rn)+ / @#a
|
||||
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.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)
|
||||
g.value = b->read(g.addr.value(), word_mode, rm_cur, g.space);
|
||||
break;
|
||||
|
@ -585,20 +581,20 @@ bool cpu::double_operand_instructions(const uint16_t instr)
|
|||
|
||||
case 0b001: { // MOV/MOVB Move Word/Byte
|
||||
gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode);
|
||||
|
||||
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
|
||||
addToMMR1(g_src);
|
||||
}
|
||||
else {
|
||||
auto g_dst = getGAMAddress(dst_mode, dst_reg, word_mode);
|
||||
addToMMR1(g_dst);
|
||||
addToMMR1(g_src);
|
||||
|
||||
set_flags = putGAM(g_dst, g_src.value.value());
|
||||
}
|
||||
|
||||
addToMMR1(g_src);
|
||||
|
||||
if (set_flags)
|
||||
setPSW_flags_nzv(g_src.value.value(), word_mode);
|
||||
|
||||
|
@ -1658,6 +1654,7 @@ bool cpu::misc_operations(const uint16_t instr)
|
|||
pop_from_stack_trace();
|
||||
setPC(popStack());
|
||||
setPSW(popStack(), !!getPSW_runmode());
|
||||
psw &= ~020; // disable TRAP flag
|
||||
return true;
|
||||
|
||||
case 0b0000000000000011: // BPT
|
||||
|
@ -1799,8 +1796,13 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
|
|||
set_register(6, 04);
|
||||
}
|
||||
else {
|
||||
b->getMMU()->clearMMR1();
|
||||
|
||||
before_psw = getPSW();
|
||||
|
||||
before_pc = getPC();
|
||||
|
||||
// TODO set MMR2?
|
||||
}
|
||||
|
||||
if (debug_mode)
|
||||
|
@ -1852,11 +1854,8 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
|
|||
|
||||
int run_mode = getPSW_runmode();
|
||||
auto temp = b->peek_word(run_mode, pc & 65535);
|
||||
if (temp.has_value() == false) {
|
||||
operand_parameters out;
|
||||
out.error = "cannot read from memory";
|
||||
return out;
|
||||
}
|
||||
if (temp.has_value() == false)
|
||||
return { };
|
||||
uint16_t next_word = temp.value();
|
||||
int reg = mode_register & 7;
|
||||
uint16_t mask = word_mode == wm_byte ? 0xff : 0xffff;
|
||||
|
@ -1872,110 +1871,108 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
|
|||
else
|
||||
reg_name = format("R%d", reg);
|
||||
|
||||
std::optional<std::string> error;
|
||||
|
||||
int mode = mode_register >> 3;
|
||||
switch(mode) {
|
||||
switch(mode_register >> 3) {
|
||||
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:
|
||||
temp2 = b->peek_word(run_mode, get_register(reg));
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
|
||||
temp2 = 0xffff, valid = false;
|
||||
|
||||
return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
|
||||
return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
|
||||
|
||||
case 2:
|
||||
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));
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
|
||||
temp2 = 0xffff, valid = false;
|
||||
|
||||
return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
|
||||
return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
|
||||
|
||||
case 3:
|
||||
if (reg == 7) {
|
||||
temp2 = b->peek_word(run_mode, next_word);
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", next_word);
|
||||
temp2 = 0xffff, valid = false;
|
||||
|
||||
return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
|
||||
return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
|
||||
}
|
||||
|
||||
temp2 = b->peek_word(run_mode, get_register(reg));
|
||||
if (temp2.has_value() == 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);
|
||||
temp2 = 0xffff, valid = false;
|
||||
else {
|
||||
temp2 = b->peek_word(run_mode, temp2.value());
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", temp2.value());
|
||||
temp2 = 0xffff, valid = false;
|
||||
}
|
||||
|
||||
return { { format("@-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
|
||||
return { { format("@(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
|
||||
|
||||
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:
|
||||
{
|
||||
uint16_t calculated_address = get_register(reg) + next_word;
|
||||
temp2 = b->peek_word(run_mode, calculated_address);
|
||||
if (reg == 7) {
|
||||
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
|
||||
temp2 = 0xffff, valid = false;
|
||||
|
||||
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 } };
|
||||
return { { format("%06o", (pc + next_word + 2) & 65535), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
|
||||
}
|
||||
|
||||
case 7:
|
||||
{
|
||||
uint16_t calculated_address = get_register(reg) + next_word;
|
||||
temp2 = b->peek_word(run_mode, calculated_address);
|
||||
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
|
||||
if (temp2.has_value() == false)
|
||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
|
||||
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:
|
||||
if (reg == 7) {
|
||||
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
|
||||
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, error = format("cannot fetch memory from %o", temp2.value());
|
||||
temp2 = 0xffff, valid = false;
|
||||
}
|
||||
|
||||
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 } };
|
||||
}
|
||||
return { { format("@%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
|
||||
}
|
||||
|
||||
operand_parameters out;
|
||||
out.error = format("unknown register mode %d", mode);
|
||||
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
|
||||
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 out;
|
||||
return { { format("@%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t addr) const
|
||||
|
@ -2079,8 +2076,6 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
|
|||
case 0b000110100:
|
||||
if (word_mode == wm_byte)
|
||||
name = "MTPS";
|
||||
else
|
||||
name = "MARK";
|
||||
break;
|
||||
|
||||
case 0b000110111:
|
||||
|
@ -2094,8 +2089,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
|
|||
if (text.empty() && name.empty() == false)
|
||||
text = name + word_mode_str + space + dst_text.operand;
|
||||
|
||||
if (text.empty() == false && dst_text.valid == false)
|
||||
text += " (INV1)";
|
||||
if (dst_text.valid == false)
|
||||
text += " (INV)";
|
||||
|
||||
if (text.empty() == false && next_word != -1)
|
||||
instruction_words.push_back(next_word);
|
||||
|
@ -2142,7 +2137,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
|
||||
|
||||
if (dst_text.valid == false)
|
||||
text += " (INV2)";
|
||||
text += " (INV)";
|
||||
|
||||
if (text.empty() == false && next_word != -1)
|
||||
instruction_words.push_back(next_word);
|
||||
|
@ -2199,10 +2194,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
|
|||
work_values.push_back(dst_text.work_value);
|
||||
|
||||
if (src_text.valid == false || dst_text.valid == false)
|
||||
text += " (INV3)";
|
||||
text += " (INV)";
|
||||
|
||||
if (do_opcode == 0b110)
|
||||
word_mode_str.clear();
|
||||
text = name + word_mode_str + space + src_text.operand + comma + dst_text.operand;
|
||||
}
|
||||
|
||||
|
@ -2358,10 +2351,7 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
|
|||
text = std::string("JMP ") + dst_text.operand;
|
||||
|
||||
if (dst_text.valid == false)
|
||||
text += " (INV4)";
|
||||
|
||||
if (addressing.value().error.has_value())
|
||||
text += " " + addressing.value().error.value();
|
||||
text += " (INV)";
|
||||
}
|
||||
|
||||
if ((instruction & 0b1111111000000000) == 0b0000100000000000) {
|
||||
|
@ -2444,8 +2434,13 @@ bool cpu::step()
|
|||
{
|
||||
it_is_a_trap = false;
|
||||
|
||||
if (any_queued_interrupts)
|
||||
execute_any_pending_interrupt();
|
||||
if (!b->getMMU()->isMMR1Locked())
|
||||
b->getMMU()->clearMMR1();
|
||||
|
||||
if (any_queued_interrupts && execute_any_pending_interrupt()) {
|
||||
if (!b->getMMU()->isMMR1Locked())
|
||||
b->getMMU()->clearMMR1();
|
||||
}
|
||||
|
||||
instruction_count++;
|
||||
|
||||
|
@ -2458,12 +2453,17 @@ bool cpu::step()
|
|||
uint16_t instr = b->read_word(instruction_start);
|
||||
add_register(7, 2);
|
||||
|
||||
if (double_operand_instructions(instr) || conditional_branch_instructions(instr) || condition_code_operations(instr) || misc_operations(instr)) {
|
||||
if (!b->getMMU()->isMMR1Locked())
|
||||
b->getMMU()->clearMMR1();
|
||||
|
||||
if (double_operand_instructions(instr))
|
||||
return true;
|
||||
|
||||
if (conditional_branch_instructions(instr))
|
||||
return true;
|
||||
|
||||
if (condition_code_operations(instr))
|
||||
return true;
|
||||
|
||||
if (misc_operations(instr))
|
||||
return true;
|
||||
}
|
||||
|
||||
DOLOG(warning, false, "UNHANDLED instruction %06o @ %06o", instr, instruction_start);
|
||||
|
||||
|
@ -2472,7 +2472,7 @@ bool cpu::step()
|
|||
return false;
|
||||
}
|
||||
catch(const int exception_nr) {
|
||||
TRACE("trap during execution of command (%d)", exception_nr);
|
||||
TRACE("bus-trap during execution of command (%d)", exception_nr);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
1
cpu.h
1
cpu.h
|
@ -109,7 +109,6 @@ private:
|
|||
int instruction_part;
|
||||
uint16_t work_value;
|
||||
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;
|
||||
|
|
237
debugger.cpp
237
debugger.cpp
|
@ -1,7 +1,6 @@
|
|||
// (C) 2018-2025 by Folkert van Heusden
|
||||
// (C) 2018-2024 by Folkert van Heusden
|
||||
// Released under MIT license
|
||||
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include "gen.h"
|
||||
#if IS_POSIX || defined(_WIN32)
|
||||
|
@ -173,12 +172,11 @@ std::optional<std::string> select_host_file(console *const cnsl)
|
|||
for(;;) {
|
||||
cnsl->flush_input();
|
||||
|
||||
std::string selected_file = cnsl->read_line("Enter filename (\"dir\" for listing or empty to abort): ");
|
||||
std::string selected_file = cnsl->read_line("Enter filename (or empty to abort): ");
|
||||
|
||||
if (selected_file.empty())
|
||||
return { };
|
||||
|
||||
if (selected_file != "dir") {
|
||||
cnsl->put_string("Opening file: ");
|
||||
cnsl->put_string_lf(selected_file.c_str());
|
||||
|
||||
|
@ -198,7 +196,6 @@ std::optional<std::string> select_host_file(console *const cnsl)
|
|||
return selected_file;
|
||||
|
||||
cnsl->put_string_lf("open failed");
|
||||
}
|
||||
|
||||
ls_l(cnsl);
|
||||
}
|
||||
|
@ -727,38 +724,46 @@ void set_kw11_l_interrupt_freq(console *const cnsl, bus *const b, const int freq
|
|||
cnsl->put_string_lf("Frequency out of range");
|
||||
}
|
||||
|
||||
struct debugger_state {
|
||||
int32_t trace_start_addr { -1 };
|
||||
int n_single_step { 1 };
|
||||
bool turbo { false };
|
||||
bool marker { false };
|
||||
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)
|
||||
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event)
|
||||
{
|
||||
int32_t trace_start_addr = -1;
|
||||
int n_single_step = 1;
|
||||
bool turbo = false;
|
||||
bool marker = false;
|
||||
std::optional<int> t_rl; // trace runlevel
|
||||
|
||||
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 kv = split(parts, "=");
|
||||
|
||||
if (parts.empty())
|
||||
return true;
|
||||
continue;
|
||||
|
||||
if (cmd == "go") {
|
||||
state->single_step = false;
|
||||
single_step = false;
|
||||
|
||||
*stop_event = EVENT_NONE;
|
||||
}
|
||||
else if (cmd == "marker")
|
||||
state->marker = !state->marker;
|
||||
marker = !marker;
|
||||
else if (parts[0] == "single" || parts[0] == "s" || parts[0] == "step") {
|
||||
state->single_step = true;
|
||||
single_step = true;
|
||||
|
||||
if (parts.size() == 2)
|
||||
state->n_single_step = std::stoi(parts[1]);
|
||||
n_single_step = std::stoi(parts[1]);
|
||||
else
|
||||
state->n_single_step = 1;
|
||||
n_single_step = 1;
|
||||
|
||||
*stop_event = EVENT_NONE;
|
||||
}
|
||||
|
@ -787,7 +792,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("Breakpoint not found");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "lbp") {
|
||||
auto bps = c->list_breakpoints();
|
||||
|
@ -800,9 +805,9 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
if (bps.empty())
|
||||
cnsl->put_string_lf("(none)");
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "disassemble" || parts[0] == "dis") {
|
||||
else if (parts[0] == "disassemble" || parts[0] == "d") {
|
||||
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;
|
||||
|
||||
|
@ -816,16 +821,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
show_registers = false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setpc") {
|
||||
if (parts.size() == 2) {
|
||||
|
@ -838,12 +834,12 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("setpc requires an (octal address as) parameter");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "getpc") {
|
||||
cnsl->put_string_lf(format("PC = %06o", c->getPC()));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setreg") {
|
||||
if (parts.size() == 3) {
|
||||
|
@ -857,7 +853,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("setreg requires a register and an octal value");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "getreg") {
|
||||
if (parts.size() == 2) {
|
||||
|
@ -868,7 +864,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("getreg requires a register");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setstack") {
|
||||
if (parts.size() == 3) {
|
||||
|
@ -883,7 +879,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("setstack requires a register and an octal value");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "getstack") {
|
||||
if (parts.size() == 2) {
|
||||
|
@ -894,7 +890,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("getreg requires a stack register");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setpsw") {
|
||||
if (parts.size() == 2) {
|
||||
|
@ -907,12 +903,12 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("setpsw requires an octal value");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "getpsw") {
|
||||
cnsl->put_string_lf(format("PSW = %06o", c->getPSW()));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "getmem") {
|
||||
auto a_it = kv.find("a");
|
||||
|
@ -924,7 +920,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf(format("MEM %06o = %03o", a, c->getBus()->read_byte(a)));
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setmem") {
|
||||
auto a_it = kv.find("a");
|
||||
|
@ -941,7 +937,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf(format("Set %06o to %03o", a, v));
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "toggle") {
|
||||
auto s_it = kv.find("s");
|
||||
|
@ -958,14 +954,14 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf(format("Set switch %d to %d", s, t));
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "trace" || parts[0] == "t") {
|
||||
settrace(!gettrace());
|
||||
|
||||
cnsl->put_string_lf(format("Tracing set to %s", gettrace() ? "ON" : "OFF"));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "state") {
|
||||
if (parts[1] == "rl02")
|
||||
|
@ -980,12 +976,12 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
b->getTM11()->show_state(cnsl);
|
||||
else if (parts[1] == "kw11l")
|
||||
b->getKW11_L()->show_state(cnsl);
|
||||
else if (parts[1] == "rp06" || parts[1] == "rp07")
|
||||
else if (parts[1] == "rp06")
|
||||
b->getRP06()->show_state(cnsl);
|
||||
else
|
||||
cnsl->put_string_lf(format("Device \"%s\" is not known", parts[1].c_str()));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "mmures") {
|
||||
if (parts.size() == 2)
|
||||
|
@ -993,26 +989,26 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
else
|
||||
cnsl->put_string_lf("Parameter missing");
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "regdump") {
|
||||
reg_dump(cnsl, c);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "strace") {
|
||||
if (parts.size() != 2) {
|
||||
state->trace_start_addr = -1;
|
||||
trace_start_addr = -1;
|
||||
|
||||
cnsl->put_string_lf("Tracing start address reset");
|
||||
}
|
||||
else {
|
||||
state->trace_start_addr = std::stoi(parts[1], nullptr, 8);
|
||||
trace_start_addr = std::stoi(parts[1], nullptr, 8);
|
||||
|
||||
cnsl->put_string_lf(format("Tracing start address set to %06o", state->trace_start_addr));
|
||||
cnsl->put_string_lf(format("Tracing start address set to %06o", trace_start_addr));
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "examine" || parts[0] == "e") {
|
||||
if (parts.size() < 3)
|
||||
|
@ -1024,7 +1020,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
if (parts[2] != "p" && parts[2] != "v") {
|
||||
cnsl->put_string_lf("expected p (physical address) or v (virtual address)");
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string out;
|
||||
|
@ -1062,45 +1058,45 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf(out);
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "reset" || cmd == "r") {
|
||||
*stop_event = EVENT_NONE;
|
||||
b->reset();
|
||||
cnsl->put_string_lf("resetted");
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "cfgdisk") {
|
||||
configure_disk(b, cnsl);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
#if defined(ESP32)
|
||||
else if (cmd == "cfgnet") {
|
||||
configure_network(cnsl);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "chknet") {
|
||||
check_network(cnsl);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "startnet") {
|
||||
start_network(cnsl);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
else if (cmd == "stats") {
|
||||
show_run_statistics(cnsl, c);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "ramsize") {
|
||||
if (parts.size() == 2)
|
||||
|
@ -1111,44 +1107,44 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf(format("Memory size: %u pages or %u kB (decimal)", n_pages, n_pages * 8192 / 1024));
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "bl" && parts.size() == 2) {
|
||||
if (parts.at(1) == "rk05")
|
||||
set_boot_loader(b, BL_RK05);
|
||||
else if (parts.at(1) == "rl02")
|
||||
set_boot_loader(b, BL_RL02);
|
||||
else if (parts.at(1) == "rp06" || parts[1] == "rp07")
|
||||
else if (parts.at(1) == "rp06")
|
||||
set_boot_loader(b, BL_RP06);
|
||||
else
|
||||
cnsl->put_string_lf("???");
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "trl") {
|
||||
if (parts.size() == 1)
|
||||
state->t_rl.reset();
|
||||
t_rl.reset();
|
||||
else
|
||||
state->t_rl = std::stoi(parts.at(1));
|
||||
t_rl = std::stoi(parts.at(1));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "cls") {
|
||||
const char cls[] = { 27, '[', '2', 'J', 27, '[', 'H', 12, 0 };
|
||||
const char cls[] = { 27, '[', '2', 'J', 12, 0 };
|
||||
|
||||
cnsl->put_string_lf(cls);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "turbo") {
|
||||
state->turbo = !state->turbo;
|
||||
turbo = !turbo;
|
||||
|
||||
if (state->turbo)
|
||||
if (turbo)
|
||||
c->set_debug(false);
|
||||
|
||||
cnsl->put_string_lf(format("Turbo set to %s", state->turbo ? "ON" : "OFF"));
|
||||
cnsl->put_string_lf(format("Turbo set to %s", turbo ? "ON" : "OFF"));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "debug") {
|
||||
bool new_mode = !c->get_debug();
|
||||
|
@ -1156,7 +1152,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
|
||||
cnsl->put_string_lf(format("Debug mode set to %s", new_mode ? "ON" : "OFF"));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setll" && parts.size() == 2) {
|
||||
auto ll_parts = split(parts[1], ",");
|
||||
|
@ -1182,17 +1178,17 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
setll(ll_screen, ll_file);
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
#if IS_POSIX
|
||||
else if (parts[0] == "ser" && parts.size() == 2) {
|
||||
serialize_state(cnsl, b, parts.at(1));
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
else if (parts[0] == "setinthz" && parts.size() == 2) {
|
||||
set_kw11_l_interrupt_freq(cnsl, b, std::stoi(parts.at(1)));
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "setsl" && parts.size() == 3) {
|
||||
if (setloghost(parts.at(1).c_str(), parse_ll(parts[2])) == false)
|
||||
|
@ -1200,22 +1196,22 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
else
|
||||
send_syslog(info, "Hello, world!");
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "pts" && parts.size() == 2) {
|
||||
cnsl->enable_timestamp(std::stoi(parts[1]));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "qi") {
|
||||
show_queued_interrupts(cnsl, c);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "log") {
|
||||
DOLOG(info, true, cmd.c_str());
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "bic" && parts.size() == 2) {
|
||||
auto rc = load_tape(b, parts[1].c_str());
|
||||
|
@ -1228,7 +1224,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
cnsl->put_string_lf("BIC/LDA failed to load");
|
||||
}
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "lt") {
|
||||
if (parts.size() == 2)
|
||||
|
@ -1236,42 +1232,42 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
else
|
||||
tm11_load_tape(cnsl, b, { });
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "dir" || cmd == "ls") {
|
||||
ls_l(cnsl);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "ult") {
|
||||
tm11_unload_tape(b);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (parts[0] == "testdc11") {
|
||||
b->getDC11()->test_ports(cmd);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "dp") {
|
||||
cnsl->stop_panel_thread();
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "cdc11") {
|
||||
configure_comm(cnsl, *b->getDC11()->get_comm_interfaces());
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "serdc11") {
|
||||
serdc11(cnsl, b);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "dserdc11") {
|
||||
deserdc11(cnsl, b);
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "bt") {
|
||||
if (c->get_debug() == false)
|
||||
|
@ -1282,17 +1278,17 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
for(auto & element: backtrace)
|
||||
cnsl->put_string_lf(format("%06o %s", element.first, element.second.c_str()));
|
||||
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "quit" || cmd == "q") {
|
||||
#if defined(ESP32)
|
||||
ESP.restart();
|
||||
#endif
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
else if (cmd == "help" || cmd == "h" || cmd == "?") {
|
||||
constexpr const char *const help[] = {
|
||||
"dis[assemble] - show current instruction (pc=/n=)",
|
||||
"disassemble/d - show current instruction (pc=/n=)",
|
||||
"go - run until trap or ^e",
|
||||
#if !defined(ESP32)
|
||||
"quit/q - stop emulator",
|
||||
|
@ -1315,7 +1311,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
"strace x - start tracing from address - invoke without address to disable",
|
||||
"trl x - set trace run-level (0...3), empty for all",
|
||||
"regdump - dump register contents",
|
||||
"state x - dump state of a device: rl02, rk05, rp06, rp07, mmu, tm11, kw11l or dc11",
|
||||
"state x - dump state of a device: rl02, rk05, rp06, mmu, tm11, kw11l or dc11",
|
||||
"mmures x - resolve a virtual address",
|
||||
"qi - show queued interrupts",
|
||||
"setpc x - set PC to value (octal)",
|
||||
|
@ -1337,7 +1333,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
"ult - unload tape",
|
||||
"stats - show run statistics",
|
||||
"ramsize x - set ram size (page (8 kB) count, decimal)",
|
||||
"bl - set bootloader (rl02, rk05, rp06 or rp07)",
|
||||
"bl - set bootloader (rl02, rk05 or rp06)",
|
||||
"cdc11 - configure DC11 device",
|
||||
"serdc11 - store DC11 device settings",
|
||||
"dserdc11 - load DC11 device settings",
|
||||
|
@ -1361,11 +1357,11 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
size_t i=0;
|
||||
while(help[i])
|
||||
cnsl->put_string_lf(help[i++]);
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
cnsl->put_string_lf("?");
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
c->emulation_start();
|
||||
|
@ -1374,7 +1370,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
|
||||
bool reset_cpu = true;
|
||||
|
||||
if (state->turbo) {
|
||||
if (turbo) {
|
||||
while(*stop_event == EVENT_NONE)
|
||||
c->step();
|
||||
}
|
||||
|
@ -1382,25 +1378,25 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
reset_cpu = false;
|
||||
|
||||
while(*stop_event == EVENT_NONE) {
|
||||
if (state->trace_start_addr != -1 && c->getPC() == state->trace_start_addr)
|
||||
if (trace_start_addr != -1 && c->getPC() == trace_start_addr)
|
||||
settrace(true);
|
||||
|
||||
if ((gettrace() || state->single_step) && (state->t_rl.has_value() == false || state->t_rl.value() == c->getPSW_runmode())) {
|
||||
if (!state->single_step)
|
||||
if ((gettrace() || single_step) && (t_rl.has_value() == false || t_rl.value() == c->getPSW_runmode())) {
|
||||
if (!single_step)
|
||||
TRACE("---");
|
||||
|
||||
disassemble(c, state->single_step ? cnsl : nullptr, c->getPC(), false);
|
||||
disassemble(c, single_step ? cnsl : nullptr, c->getPC(), false);
|
||||
}
|
||||
|
||||
auto bp_result = c->check_breakpoint();
|
||||
if (bp_result.has_value() && !state->single_step) {
|
||||
if (bp_result.has_value() && !single_step) {
|
||||
cnsl->put_string_lf("Breakpoint: " + bp_result.value());
|
||||
break;
|
||||
}
|
||||
|
||||
c->step();
|
||||
|
||||
if (state->single_step && --state->n_single_step == 0)
|
||||
if (single_step && --n_single_step == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1409,35 +1405,6 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
|||
|
||||
if (reset_cpu)
|
||||
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) {
|
||||
cnsl->put_string_lf(format("Exception caught: %s", e.what()));
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
|
||||
|
||||
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, const std::optional<std::string> & init);
|
||||
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event);
|
||||
|
||||
void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const uint16_t bic_start);
|
||||
|
|
185
json/produce-json.py
Executable file
185
json/produce-json.py
Executable file
|
@ -0,0 +1,185 @@
|
|||
#! /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()
|
218
json/produce-json2.py
Executable file
218
json/produce-json2.py
Executable file
|
@ -0,0 +1,218 @@
|
|||
#! /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
|
34
loaders.cpp
34
loaders.cpp
|
@ -53,6 +53,38 @@ void set_boot_loader(bus *const b, const bootloader_t which)
|
|||
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;
|
||||
|
||||
size = sizeof(rk05_code)/sizeof(rk05_code[0]);
|
||||
|
@ -112,7 +144,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
|
|||
start = offset = 02000;
|
||||
|
||||
static const uint16_t rp06_code[] = {
|
||||
012701, 0176700, 012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000
|
||||
012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000
|
||||
|
||||
};
|
||||
|
||||
|
|
46
main.cpp
46
main.cpp
|
@ -65,9 +65,8 @@ std::vector<std::pair<uint32_t, uint8_t> > get_memory_settings(const JsonArrayCo
|
|||
for(const auto & kv: kv_dict.as<JsonObjectConst>()) {
|
||||
uint32_t a = std::stoi(kv.key().c_str(), nullptr, 8);
|
||||
uint16_t v = kv.value().as<int>();
|
||||
out.push_back({ a, v });
|
||||
if (a == 0 && v == 0)
|
||||
printf("Suspect\n");
|
||||
out.push_back({ a, v & 255 });
|
||||
out.push_back({ a + 1, v >> 8 });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
@ -113,7 +112,7 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
|
|||
// create environment
|
||||
event = 0;
|
||||
bus *b = new bus();
|
||||
b->set_memory_size(DEFAULT_N_PAGES);
|
||||
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
|
||||
cpu *c = new cpu(b, &event);
|
||||
b->add_cpu(c);
|
||||
|
||||
|
@ -131,9 +130,6 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
|
|||
for(int i=0; i<4; 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
|
||||
for(int set=0; set<2; set++) {
|
||||
for(int i=0; i<6; i++)
|
||||
|
@ -144,27 +140,20 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
|
|||
auto memory_before = before["memory"];
|
||||
auto memory_before_settings = get_memory_settings(memory_before);
|
||||
for(auto & element: memory_before_settings)
|
||||
b->write_unibus_byte(element.first, element.second);
|
||||
b->write_physical(element.first, element.second);
|
||||
}
|
||||
|
||||
int run_n_instructions = test["before"]["run-n-instructions"].as<int>();
|
||||
|
||||
int cur_n_errors = 0;
|
||||
|
||||
// DO!
|
||||
c->emulation_start();
|
||||
for(int k=0; k<run_n_instructions; k++) {
|
||||
disassemble(c, nullptr, c->getPC(), false);
|
||||
if (c->step() == false) {
|
||||
cnsl->put_string_lf("Treated as an invalid instruction");
|
||||
cur_n_errors++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
disassemble(c, nullptr, c->getPC(), false);
|
||||
|
||||
// VERIFY
|
||||
if (cur_n_errors == 0) {
|
||||
else {
|
||||
auto after = test["after"];
|
||||
|
||||
cur_n_errors += !compare_values(cnsl, c->getPC(), get_register_value(after, "PC" ), "PC" );
|
||||
|
@ -173,9 +162,6 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
|
|||
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, 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 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));
|
||||
|
@ -253,12 +239,11 @@ void help()
|
|||
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("-N host:port use NBD-server as disk device (like -r)\n");
|
||||
printf("-R x select disk type (rk05, rl02, rp06 or rp07)\n");
|
||||
printf("-R x select disk type (rk05, rl02 or rp06)\n");
|
||||
printf("-p 123 set CPU start pointer to decimal(!) value\n");
|
||||
printf("-b enable bootloader (builtin)\n");
|
||||
printf("-n ncurses UI\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,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");
|
||||
|
@ -267,7 +252,7 @@ void help()
|
|||
printf("-X do not include timestamp in logging\n");
|
||||
printf("-J x run validation suite x against the CPU emulation\n");
|
||||
printf("-M log metrics\n");
|
||||
printf("-1 x use x as device for DC-11 (instead of 4 tcp-sockets starting at port 1100)\n");
|
||||
printf("-1 x use x as device for DC-11\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -278,7 +263,6 @@ int main(int argc, char *argv[])
|
|||
std::string disk_type = "rk05";
|
||||
|
||||
bool run_debugger = false;
|
||||
std::optional<std::string> debugger_init;
|
||||
|
||||
bool enable_bootloader = false;
|
||||
bootloader_t bootloader = BL_NONE;
|
||||
|
@ -313,17 +297,13 @@ int main(int argc, char *argv[])
|
|||
std::optional<std::string> dc11_device;
|
||||
|
||||
int opt = -1;
|
||||
while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndf:tL:bl:s:Q:N:J:XS:P1:")) != -1)
|
||||
while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndtL:bl:s:Q:N:J:XS:P1:")) != -1)
|
||||
{
|
||||
switch(opt) {
|
||||
case 'h':
|
||||
help();
|
||||
return 1;
|
||||
|
||||
case 'f':
|
||||
debugger_init = optarg;
|
||||
break;
|
||||
|
||||
case '1':
|
||||
dc11_device = optarg;
|
||||
break;
|
||||
|
@ -391,7 +371,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
case 'R':
|
||||
disk_type = optarg;
|
||||
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06" && disk_type != "rp07")
|
||||
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06")
|
||||
error_exit(false, "Disk type not known");
|
||||
break;
|
||||
|
||||
|
@ -480,7 +460,7 @@ int main(int argc, char *argv[])
|
|||
if (set_ram_size.has_value())
|
||||
b->set_memory_size(set_ram_size.value());
|
||||
else
|
||||
b->set_memory_size(DEFAULT_N_PAGES);
|
||||
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
|
||||
|
||||
b->set_console_switches(console_switches);
|
||||
|
||||
|
@ -495,7 +475,7 @@ int main(int argc, char *argv[])
|
|||
rl02_dev->begin();
|
||||
b->add_rl02(rl02_dev);
|
||||
|
||||
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag(), disk_type == "rp07");
|
||||
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
|
||||
rp06_dev->begin();
|
||||
b->add_RP06(rp06_dev);
|
||||
|
||||
|
@ -511,7 +491,7 @@ int main(int argc, char *argv[])
|
|||
for(auto & file: disk_files)
|
||||
rl02_dev->access_disk_backends()->push_back(file);
|
||||
}
|
||||
else if (disk_type == "rp06" || disk_type == "rp07") {
|
||||
else if (disk_type == "rp06") {
|
||||
bootloader = BL_RP06;
|
||||
|
||||
for(auto & file: disk_files)
|
||||
|
@ -618,7 +598,7 @@ int main(int argc, char *argv[])
|
|||
if (is_bic)
|
||||
run_bic(cnsl, b, &event, bic_start.value());
|
||||
else if (run_debugger || (bootloader == BL_NONE && test.empty() && tape.empty()))
|
||||
debugger(cnsl, b, &event, debugger_init);
|
||||
debugger(cnsl, b, &event);
|
||||
else if (benchmark) {
|
||||
// FILL MEMORY
|
||||
memory *m = b->getRAM();
|
||||
|
|
21
mmu.cpp
21
mmu.cpp
|
@ -126,11 +126,6 @@ void mmu::clearMMR0Bit(const int bit)
|
|||
MMR0 &= ~(1 << bit);
|
||||
}
|
||||
|
||||
void mmu::setMMR1(const uint16_t value)
|
||||
{
|
||||
MMR1 = value;
|
||||
}
|
||||
|
||||
void mmu::setMMR2(const uint16_t value)
|
||||
{
|
||||
MMR2 = value;
|
||||
|
@ -150,7 +145,6 @@ bool mmu::get_use_data_space(const int run_mode) const
|
|||
|
||||
void mmu::clearMMR1()
|
||||
{
|
||||
TRACE("clear MMR1");
|
||||
MMR1 = 0;
|
||||
}
|
||||
|
||||
|
@ -377,10 +371,21 @@ void mmu::verify_page_access(const uint16_t virt_addr, const int run_mode, const
|
|||
TRACE("MMR0: %06o", temp);
|
||||
}
|
||||
|
||||
if (trap_action == T_TRAP_250) {
|
||||
TRACE("Page access %d (for virtual address %06o): trap 0250", access_control, virt_addr);
|
||||
c->trap(0250); // abort
|
||||
|
||||
c->trap(0250); // trap
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -405,7 +410,7 @@ void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const
|
|||
if (is_write)
|
||||
set_page_trapped(run_mode, d, apf);
|
||||
|
||||
c->trap(0250);
|
||||
c->trap(04);
|
||||
|
||||
throw 6;
|
||||
}
|
||||
|
|
16
rp06.cpp
16
rp06.cpp
|
@ -14,22 +14,18 @@
|
|||
#include "utils.h"
|
||||
|
||||
|
||||
unsigned NSECT = 22; // sectors per track
|
||||
unsigned NTRAC = 19; // tracks per cylinder
|
||||
unsigned SECTOR_SIZE = 512;
|
||||
constexpr const unsigned NSECT = 22; // sectors per track
|
||||
constexpr const unsigned NTRAC = 19; // tracks per cylinder
|
||||
constexpr const 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 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, const bool is_rp07) :
|
||||
rp06::rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity) :
|
||||
b(b),
|
||||
disk_read_activity (disk_read_activity ),
|
||||
disk_write_activity(disk_write_activity)
|
||||
{
|
||||
if (is_rp07) {
|
||||
NSECT = 50;
|
||||
NTRAC = 32;
|
||||
}
|
||||
}
|
||||
|
||||
rp06::~rp06()
|
||||
|
@ -55,13 +51,13 @@ void rp06::show_state(console *const cnsl) const
|
|||
JsonDocument rp06::serialize() const
|
||||
{
|
||||
JsonDocument j;
|
||||
j["is-rp07"] = is_rp07;
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
rp06 *rp06::deserialize(const JsonVariantConst j, bus *const b)
|
||||
{
|
||||
rp06 *r = new rp06(b, nullptr, nullptr, j["is-rp07"].as<bool>());
|
||||
rp06 *r = new rp06(b, nullptr, nullptr);
|
||||
r->begin();
|
||||
|
||||
return r;
|
||||
|
|
6
rp06.h
6
rp06.h
|
@ -38,8 +38,8 @@ class bus;
|
|||
class rp06: public disk_device
|
||||
{
|
||||
private:
|
||||
bus *const b { nullptr };
|
||||
bool is_rp07 { false };
|
||||
bus *const b;
|
||||
|
||||
uint16_t registers[32] { };
|
||||
|
||||
std::atomic_bool *const disk_read_activity { nullptr };
|
||||
|
@ -50,7 +50,7 @@ private:
|
|||
uint32_t compute_offset() const;
|
||||
|
||||
public:
|
||||
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity, const bool is_rp07);
|
||||
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity);
|
||||
virtual ~rp06();
|
||||
|
||||
void begin() override;
|
||||
|
|
26
terminal.cpp
26
terminal.cpp
|
@ -174,7 +174,8 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
|
|||
|
||||
for(loop=0; loop<len; loop++)
|
||||
{
|
||||
if (str[loop] == '^') {
|
||||
if (str[loop] == '^')
|
||||
{
|
||||
if (!inv)
|
||||
mywattron(win -> win, A_REVERSE);
|
||||
else
|
||||
|
@ -182,7 +183,8 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
|
|||
|
||||
inv = 1 - inv;
|
||||
}
|
||||
else if (str[loop] == '_') {
|
||||
else if (str[loop] == '_')
|
||||
{
|
||||
if (!underline)
|
||||
mywattron(win -> win, A_UNDERLINE);
|
||||
else
|
||||
|
@ -190,11 +192,13 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
|
|||
|
||||
underline = 1 - underline;
|
||||
}
|
||||
else if (str[loop] == '\n') {
|
||||
else if (str[loop] == '\n')
|
||||
{
|
||||
cursor_x = 0;
|
||||
y++;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]);
|
||||
}
|
||||
}
|
||||
|
@ -211,8 +215,10 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
|
|||
int loop, len = strlen(str);
|
||||
bool inv = false, underline = false;
|
||||
|
||||
for(loop=0; loop<len; loop++) {
|
||||
if (str[loop] == rev) {
|
||||
for(loop=0; loop<len; loop++)
|
||||
{
|
||||
if (str[loop] == rev)
|
||||
{
|
||||
if (!inv)
|
||||
mywattron(win -> win, A_REVERSE);
|
||||
else
|
||||
|
@ -220,7 +226,8 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
|
|||
|
||||
inv = 1 - inv;
|
||||
}
|
||||
else if (str[loop] == un) {
|
||||
else if (str[loop] == un)
|
||||
{
|
||||
if (!underline)
|
||||
mywattron(win -> win, A_UNDERLINE);
|
||||
else
|
||||
|
@ -228,7 +235,8 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
|
|||
|
||||
underline = 1 - underline;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
waddch(win -> win, str[loop]);
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +260,9 @@ 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);
|
||||
*win = create_window_xy(y + 1 + wbb, x + 1 + wbb, height + 0, width + 0);
|
||||
|
||||
mywattron((*bwin) -> win, A_REVERSE);
|
||||
box((*bwin) -> win, 0, 0);
|
||||
mywattroff((*bwin) -> win, A_REVERSE);
|
||||
|
||||
mywattron((*bwin) -> win, A_STANDOUT);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue