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)
|
include(FindPkgConfig)
|
||||||
|
|
||||||
set(APP_SOURCES
|
if (NOT WIN32)
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
kek
|
||||||
breakpoint.cpp
|
breakpoint.cpp
|
||||||
breakpoint_and.cpp
|
breakpoint_and.cpp
|
||||||
breakpoint_memory.cpp
|
breakpoint_memory.cpp
|
||||||
|
@ -54,56 +57,58 @@ set(APP_SOURCES
|
||||||
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
|
||||||
${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
|
win32.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
include(CheckIPOSupported)
|
include(CheckIPOSupported)
|
||||||
|
@ -119,7 +124,6 @@ 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)
|
||||||
|
|
||||||
|
@ -128,21 +132,7 @@ 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}
|
|
||||||
)
|
|
||||||
|
|
|
@ -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 -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
|
build_unflags = -std=gnu++11 -std=gnu++17
|
||||||
extra_scripts = pre:prepare.py
|
extra_scripts = pre:prepare.py
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,2 @@
|
||||||
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
|
|
||||||
|
|
2
bus.cpp
2
bus.cpp
|
@ -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 (%d pages)", n_bytes / 1024, n_pages);
|
TRACE("Memory is now %u kB in size", n_bytes / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bus::reset()
|
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)
|
void cpu::setPSW(const uint16_t v, const bool limited)
|
||||||
{
|
{
|
||||||
if (limited) {
|
if (limited)
|
||||||
int cur_mode = std::max( v >> 14, psw >> 14);
|
psw = (psw & 0174340) | (v & 037);
|
||||||
int prev_mode = std::max((v >> 12) & 3, (psw >> 12) & 3);
|
else
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -457,9 +453,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() == false && g.mmr1_update.has_value() == true) {
|
if (!b->getMMU()->isMMR1Locked() && g.mmr1_update.has_value()) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,17 +484,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);
|
||||||
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)
|
if (read_value)
|
||||||
g.value = b->read(g.addr.value(), word_mode, rm_cur, isR7_space);
|
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;
|
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;
|
||||||
|
@ -585,20 +581,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);
|
||||||
|
|
||||||
|
@ -1658,6 +1654,7 @@ 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
|
||||||
|
@ -1799,8 +1796,13 @@ 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)
|
||||||
|
@ -1852,11 +1854,8 @@ 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)
|
||||||
operand_parameters out;
|
return { };
|
||||||
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;
|
||||||
|
@ -1872,110 +1871,108 @@ 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);
|
||||||
|
|
||||||
std::optional<std::string> error;
|
switch(mode_register >> 3) {
|
||||||
|
|
||||||
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, 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:
|
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, 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:
|
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, 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));
|
temp2 = b->peek_word(run_mode, get_register(reg));
|
||||||
if (temp2.has_value() == false)
|
if (temp2.has_value() == false)
|
||||||
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
|
temp2 = 0xffff, valid = false;
|
||||||
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, 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:
|
case 6:
|
||||||
{
|
if (reg == 7) {
|
||||||
uint16_t calculated_address = get_register(reg) + next_word;
|
temp2 = b->peek_word(run_mode, 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, 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 } };
|
||||||
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 } };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 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, 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 {
|
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, 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 } };
|
||||||
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 } };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operand_parameters out;
|
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
|
||||||
out.error = format("unknown register mode %d", mode);
|
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
|
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:
|
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:
|
||||||
|
@ -2094,8 +2089,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 (text.empty() == false && dst_text.valid == false)
|
if (dst_text.valid == false)
|
||||||
text += " (INV1)";
|
text += " (INV)";
|
||||||
|
|
||||||
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);
|
||||||
|
@ -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
|
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 += " (INV2)";
|
text += " (INV)";
|
||||||
|
|
||||||
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);
|
||||||
|
@ -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);
|
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 += " (INV3)";
|
text += " (INV)";
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2358,10 +2351,7 @@ 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 += " (INV4)";
|
text += " (INV)";
|
||||||
|
|
||||||
if (addressing.value().error.has_value())
|
|
||||||
text += " " + addressing.value().error.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((instruction & 0b1111111000000000) == 0b0000100000000000) {
|
if ((instruction & 0b1111111000000000) == 0b0000100000000000) {
|
||||||
|
@ -2444,8 +2434,13 @@ bool cpu::step()
|
||||||
{
|
{
|
||||||
it_is_a_trap = false;
|
it_is_a_trap = false;
|
||||||
|
|
||||||
if (any_queued_interrupts)
|
if (!b->getMMU()->isMMR1Locked())
|
||||||
execute_any_pending_interrupt();
|
b->getMMU()->clearMMR1();
|
||||||
|
|
||||||
|
if (any_queued_interrupts && execute_any_pending_interrupt()) {
|
||||||
|
if (!b->getMMU()->isMMR1Locked())
|
||||||
|
b->getMMU()->clearMMR1();
|
||||||
|
}
|
||||||
|
|
||||||
instruction_count++;
|
instruction_count++;
|
||||||
|
|
||||||
|
@ -2458,12 +2453,17 @@ 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) || conditional_branch_instructions(instr) || condition_code_operations(instr) || misc_operations(instr)) {
|
if (double_operand_instructions(instr))
|
||||||
if (!b->getMMU()->isMMR1Locked())
|
return true;
|
||||||
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("trap during execution of command (%d)", exception_nr);
|
TRACE("bus-trap during execution of command (%d)", exception_nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
1
cpu.h
1
cpu.h
|
@ -109,7 +109,6 @@ 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;
|
||||||
|
|
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
|
// 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)
|
||||||
|
@ -173,12 +172,11 @@ 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 (\"dir\" for listing or empty to abort): ");
|
std::string selected_file = cnsl->read_line("Enter filename (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());
|
||||||
|
|
||||||
|
@ -198,7 +196,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -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");
|
cnsl->put_string_lf("Frequency out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct debugger_state {
|
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
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
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();
|
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())
|
||||||
return true;
|
continue;
|
||||||
|
|
||||||
if (cmd == "go") {
|
if (cmd == "go") {
|
||||||
state->single_step = false;
|
single_step = false;
|
||||||
|
|
||||||
*stop_event = EVENT_NONE;
|
*stop_event = EVENT_NONE;
|
||||||
}
|
}
|
||||||
else if (cmd == "marker")
|
else if (cmd == "marker")
|
||||||
state->marker = !state->marker;
|
marker = !marker;
|
||||||
else if (parts[0] == "single" || parts[0] == "s" || parts[0] == "step") {
|
else if (parts[0] == "single" || parts[0] == "s" || parts[0] == "step") {
|
||||||
state->single_step = true;
|
single_step = true;
|
||||||
|
|
||||||
if (parts.size() == 2)
|
if (parts.size() == 2)
|
||||||
state->n_single_step = std::stoi(parts[1]);
|
n_single_step = std::stoi(parts[1]);
|
||||||
else
|
else
|
||||||
state->n_single_step = 1;
|
n_single_step = 1;
|
||||||
|
|
||||||
*stop_event = EVENT_NONE;
|
*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");
|
cnsl->put_string_lf("Breakpoint not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "lbp") {
|
else if (cmd == "lbp") {
|
||||||
auto bps = c->list_breakpoints();
|
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())
|
if (bps.empty())
|
||||||
cnsl->put_string_lf("(none)");
|
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();
|
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;
|
||||||
|
|
||||||
|
@ -816,16 +821,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
show_registers = false;
|
show_registers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
|
||||||
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) {
|
||||||
|
@ -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");
|
cnsl->put_string_lf("setpc requires an (octal address as) parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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()));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "setreg") {
|
else if (parts[0] == "setreg") {
|
||||||
if (parts.size() == 3) {
|
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");
|
cnsl->put_string_lf("setreg requires a register and an octal value");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "getreg") {
|
else if (parts[0] == "getreg") {
|
||||||
if (parts.size() == 2) {
|
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");
|
cnsl->put_string_lf("getreg requires a register");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "setstack") {
|
else if (parts[0] == "setstack") {
|
||||||
if (parts.size() == 3) {
|
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");
|
cnsl->put_string_lf("setstack requires a register and an octal value");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "getstack") {
|
else if (parts[0] == "getstack") {
|
||||||
if (parts.size() == 2) {
|
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");
|
cnsl->put_string_lf("getreg requires a stack register");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "setpsw") {
|
else if (parts[0] == "setpsw") {
|
||||||
if (parts.size() == 2) {
|
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");
|
cnsl->put_string_lf("setpsw requires an octal value");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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()));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "getmem") {
|
else if (parts[0] == "getmem") {
|
||||||
auto a_it = kv.find("a");
|
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)));
|
cnsl->put_string_lf(format("MEM %06o = %03o", a, c->getBus()->read_byte(a)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "setmem") {
|
else if (parts[0] == "setmem") {
|
||||||
auto a_it = kv.find("a");
|
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));
|
cnsl->put_string_lf(format("Set %06o to %03o", a, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "toggle") {
|
else if (parts[0] == "toggle") {
|
||||||
auto s_it = kv.find("s");
|
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));
|
cnsl->put_string_lf(format("Set switch %d to %d", s, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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"));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "state") {
|
else if (parts[0] == "state") {
|
||||||
if (parts[1] == "rl02")
|
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);
|
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" || parts[1] == "rp07")
|
else if (parts[1] == "rp06")
|
||||||
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()));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "mmures") {
|
else if (parts[0] == "mmures") {
|
||||||
if (parts.size() == 2)
|
if (parts.size() == 2)
|
||||||
|
@ -993,26 +989,26 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
else
|
else
|
||||||
cnsl->put_string_lf("Parameter missing");
|
cnsl->put_string_lf("Parameter missing");
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "regdump") {
|
else if (parts[0] == "regdump") {
|
||||||
reg_dump(cnsl, c);
|
reg_dump(cnsl, c);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "strace") {
|
else if (parts[0] == "strace") {
|
||||||
if (parts.size() != 2) {
|
if (parts.size() != 2) {
|
||||||
state->trace_start_addr = -1;
|
trace_start_addr = -1;
|
||||||
|
|
||||||
cnsl->put_string_lf("Tracing start address reset");
|
cnsl->put_string_lf("Tracing start address reset");
|
||||||
}
|
}
|
||||||
else {
|
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") {
|
else if (parts[0] == "examine" || parts[0] == "e") {
|
||||||
if (parts.size() < 3)
|
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") {
|
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)");
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string out;
|
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);
|
cnsl->put_string_lf(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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");
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "cfgdisk") {
|
else if (cmd == "cfgdisk") {
|
||||||
configure_disk(b, cnsl);
|
configure_disk(b, cnsl);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
else if (cmd == "cfgnet") {
|
else if (cmd == "cfgnet") {
|
||||||
configure_network(cnsl);
|
configure_network(cnsl);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "chknet") {
|
else if (cmd == "chknet") {
|
||||||
check_network(cnsl);
|
check_network(cnsl);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "startnet") {
|
else if (cmd == "startnet") {
|
||||||
start_network(cnsl);
|
start_network(cnsl);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (cmd == "stats") {
|
else if (cmd == "stats") {
|
||||||
show_run_statistics(cnsl, c);
|
show_run_statistics(cnsl, c);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "ramsize") {
|
else if (parts[0] == "ramsize") {
|
||||||
if (parts.size() == 2)
|
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));
|
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) {
|
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" || parts[1] == "rp07")
|
else if (parts.at(1) == "rp06")
|
||||||
set_boot_loader(b, BL_RP06);
|
set_boot_loader(b, BL_RP06);
|
||||||
else
|
else
|
||||||
cnsl->put_string_lf("???");
|
cnsl->put_string_lf("???");
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "trl") {
|
else if (parts[0] == "trl") {
|
||||||
if (parts.size() == 1)
|
if (parts.size() == 1)
|
||||||
state->t_rl.reset();
|
t_rl.reset();
|
||||||
else
|
else
|
||||||
state->t_rl = std::stoi(parts.at(1));
|
t_rl = std::stoi(parts.at(1));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "cls") {
|
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);
|
cnsl->put_string_lf(cls);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "turbo") {
|
else if (cmd == "turbo") {
|
||||||
state->turbo = !state->turbo;
|
turbo = !turbo;
|
||||||
|
|
||||||
if (state->turbo)
|
if (turbo)
|
||||||
c->set_debug(false);
|
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") {
|
else if (cmd == "debug") {
|
||||||
bool new_mode = !c->get_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"));
|
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) {
|
else if (parts[0] == "setll" && parts.size() == 2) {
|
||||||
auto ll_parts = split(parts[1], ",");
|
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);
|
setll(ll_screen, ll_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
#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));
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
#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)));
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
@ -1200,22 +1196,22 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
else
|
else
|
||||||
send_syslog(info, "Hello, world!");
|
send_syslog(info, "Hello, world!");
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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]));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "qi") {
|
else if (cmd == "qi") {
|
||||||
show_queued_interrupts(cnsl, c);
|
show_queued_interrupts(cnsl, c);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "log") {
|
else if (parts[0] == "log") {
|
||||||
DOLOG(info, true, cmd.c_str());
|
DOLOG(info, true, cmd.c_str());
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
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());
|
||||||
|
@ -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");
|
cnsl->put_string_lf("BIC/LDA failed to load");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "lt") {
|
else if (parts[0] == "lt") {
|
||||||
if (parts.size() == 2)
|
if (parts.size() == 2)
|
||||||
|
@ -1236,42 +1232,42 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
else
|
else
|
||||||
tm11_load_tape(cnsl, b, { });
|
tm11_load_tape(cnsl, b, { });
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "dir" || cmd == "ls") {
|
else if (cmd == "dir" || cmd == "ls") {
|
||||||
ls_l(cnsl);
|
ls_l(cnsl);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "ult") {
|
else if (cmd == "ult") {
|
||||||
tm11_unload_tape(b);
|
tm11_unload_tape(b);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "testdc11") {
|
else if (parts[0] == "testdc11") {
|
||||||
b->getDC11()->test_ports(cmd);
|
b->getDC11()->test_ports(cmd);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "dp") {
|
else if (cmd == "dp") {
|
||||||
cnsl->stop_panel_thread();
|
cnsl->stop_panel_thread();
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "cdc11") {
|
else if (cmd == "cdc11") {
|
||||||
configure_comm(cnsl, *b->getDC11()->get_comm_interfaces());
|
configure_comm(cnsl, *b->getDC11()->get_comm_interfaces());
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "serdc11") {
|
else if (cmd == "serdc11") {
|
||||||
serdc11(cnsl, b);
|
serdc11(cnsl, b);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "dserdc11") {
|
else if (cmd == "dserdc11") {
|
||||||
deserdc11(cnsl, b);
|
deserdc11(cnsl, b);
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "bt") {
|
else if (cmd == "bt") {
|
||||||
if (c->get_debug() == false)
|
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)
|
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()));
|
||||||
|
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else if (cmd == "quit" || cmd == "q") {
|
else if (cmd == "quit" || cmd == "q") {
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
#endif
|
#endif
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
else if (cmd == "help" || cmd == "h" || cmd == "?") {
|
else if (cmd == "help" || cmd == "h" || cmd == "?") {
|
||||||
constexpr const char *const help[] = {
|
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",
|
"go - run until trap or ^e",
|
||||||
#if !defined(ESP32)
|
#if !defined(ESP32)
|
||||||
"quit/q - stop emulator",
|
"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",
|
"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, 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",
|
"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)",
|
||||||
|
@ -1337,7 +1333,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
"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, rp06 or rp07)",
|
"bl - set bootloader (rl02, rk05 or rp06)",
|
||||||
"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",
|
||||||
|
@ -1361,11 +1357,11 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
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++]);
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cnsl->put_string_lf("?");
|
cnsl->put_string_lf("?");
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->emulation_start();
|
c->emulation_start();
|
||||||
|
@ -1374,7 +1370,7 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
|
|
||||||
bool reset_cpu = true;
|
bool reset_cpu = true;
|
||||||
|
|
||||||
if (state->turbo) {
|
if (turbo) {
|
||||||
while(*stop_event == EVENT_NONE)
|
while(*stop_event == EVENT_NONE)
|
||||||
c->step();
|
c->step();
|
||||||
}
|
}
|
||||||
|
@ -1382,25 +1378,25 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
reset_cpu = false;
|
reset_cpu = false;
|
||||||
|
|
||||||
while(*stop_event == EVENT_NONE) {
|
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);
|
settrace(true);
|
||||||
|
|
||||||
if ((gettrace() || state->single_step) && (state->t_rl.has_value() == false || state->t_rl.value() == c->getPSW_runmode())) {
|
if ((gettrace() || single_step) && (t_rl.has_value() == false || t_rl.value() == c->getPSW_runmode())) {
|
||||||
if (!state->single_step)
|
if (!single_step)
|
||||||
TRACE("---");
|
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();
|
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());
|
cnsl->put_string_lf("Breakpoint: " + bp_result.value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->step();
|
c->step();
|
||||||
|
|
||||||
if (state->single_step && --state->n_single_step == 0)
|
if (single_step && --n_single_step == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1409,35 +1405,6 @@ bool debugger_do(debugger_state *const state, console *const cnsl, bus *const b,
|
||||||
|
|
||||||
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()));
|
||||||
|
|
|
@ -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, 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);
|
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
|
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]);
|
||||||
|
@ -112,7 +144,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[] = {
|
||||||
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>()) {
|
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 });
|
out.push_back({ a, v & 255 });
|
||||||
if (a == 0 && v == 0)
|
out.push_back({ a + 1, v >> 8 });
|
||||||
printf("Suspect\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
@ -113,7 +112,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);
|
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
|
||||||
cpu *c = new cpu(b, &event);
|
cpu *c = new cpu(b, &event);
|
||||||
b->add_cpu(c);
|
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++)
|
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++)
|
||||||
|
@ -144,27 +140,20 @@ 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_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;
|
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
|
||||||
if (cur_n_errors == 0) {
|
else {
|
||||||
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" );
|
||||||
|
@ -173,9 +162,6 @@ 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));
|
||||||
|
@ -253,12 +239,11 @@ 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, 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("-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");
|
||||||
|
@ -267,7 +252,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 (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[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -278,7 +263,6 @@ 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;
|
||||||
|
@ -313,17 +297,13 @@ 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: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) {
|
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;
|
||||||
|
@ -391,7 +371,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" && disk_type != "rp07")
|
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06")
|
||||||
error_exit(false, "Disk type not known");
|
error_exit(false, "Disk type not known");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -480,7 +460,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);
|
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
|
||||||
|
|
||||||
b->set_console_switches(console_switches);
|
b->set_console_switches(console_switches);
|
||||||
|
|
||||||
|
@ -495,7 +475,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(), disk_type == "rp07");
|
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
|
||||||
rp06_dev->begin();
|
rp06_dev->begin();
|
||||||
b->add_RP06(rp06_dev);
|
b->add_RP06(rp06_dev);
|
||||||
|
|
||||||
|
@ -511,7 +491,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" || disk_type == "rp07") {
|
else if (disk_type == "rp06") {
|
||||||
bootloader = BL_RP06;
|
bootloader = BL_RP06;
|
||||||
|
|
||||||
for(auto & file: disk_files)
|
for(auto & file: disk_files)
|
||||||
|
@ -618,7 +598,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_init);
|
debugger(cnsl, b, &event);
|
||||||
else if (benchmark) {
|
else if (benchmark) {
|
||||||
// FILL MEMORY
|
// FILL MEMORY
|
||||||
memory *m = b->getRAM();
|
memory *m = b->getRAM();
|
||||||
|
|
21
mmu.cpp
21
mmu.cpp
|
@ -126,11 +126,6 @@ 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;
|
||||||
|
@ -150,7 +145,6 @@ bool mmu::get_use_data_space(const int run_mode) const
|
||||||
|
|
||||||
void mmu::clearMMR1()
|
void mmu::clearMMR1()
|
||||||
{
|
{
|
||||||
TRACE("clear MMR1");
|
|
||||||
MMR1 = 0;
|
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);
|
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)
|
||||||
{
|
{
|
||||||
|
@ -405,7 +410,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(0250);
|
c->trap(04);
|
||||||
|
|
||||||
throw 6;
|
throw 6;
|
||||||
}
|
}
|
||||||
|
|
16
rp06.cpp
16
rp06.cpp
|
@ -14,22 +14,18 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
||||||
unsigned NSECT = 22; // sectors per track
|
constexpr const unsigned NSECT = 22; // sectors per track
|
||||||
unsigned NTRAC = 19; // tracks per cylinder
|
constexpr const unsigned NTRAC = 19; // tracks per cylinder
|
||||||
unsigned SECTOR_SIZE = 512;
|
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 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, 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),
|
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()
|
||||||
|
@ -55,13 +51,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, j["is-rp07"].as<bool>());
|
rp06 *r = new rp06(b, nullptr, nullptr);
|
||||||
r->begin();
|
r->begin();
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|
6
rp06.h
6
rp06.h
|
@ -38,8 +38,8 @@ class bus;
|
||||||
class rp06: public disk_device
|
class rp06: public disk_device
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bus *const b { nullptr };
|
bus *const b;
|
||||||
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, const bool is_rp07);
|
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity);
|
||||||
virtual ~rp06();
|
virtual ~rp06();
|
||||||
|
|
||||||
void begin() override;
|
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++)
|
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
|
||||||
|
@ -182,7 +183,8 @@ 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
|
||||||
|
@ -190,11 +192,13 @@ 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,8 +215,10 @@ 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
|
||||||
|
@ -220,7 +226,8 @@ 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
|
||||||
|
@ -228,7 +235,8 @@ 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
*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);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue