diff --git a/CMakeLists.txt b/CMakeLists.txt index 396612b..60626b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,3 +95,8 @@ pkg_check_modules(PANEL REQUIRED panel) target_link_libraries(kek ${PANEL_LIBRARIES}) target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS}) target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER}) + +pkg_check_modules(JANSSON REQUIRED jansson) +target_link_libraries(kek ${JANSSON_LIBRARIES}) +target_include_directories(kek PUBLIC ${JANSSON_INCLUDE_DIRS}) +target_compile_options(kek PUBLIC ${JANSSON_CFLAGS_OTHER}) diff --git a/cpu.cpp b/cpu.cpp index 543ef5e..a5d4b21 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -1637,7 +1637,6 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt) { DOLOG(debug, true, "*** CPU::TRAP %o, new-ipl: %d, is-interrupt: %d ***", vector, new_ipl, is_interrupt); - int processing_trap_depth = 0; uint16_t before_psw = 0; uint16_t before_pc = 0; @@ -1650,14 +1649,15 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt) if (processing_trap_depth >= 2) { DOLOG(debug, true, "Trap depth %d", processing_trap_depth); + if (processing_trap_depth >= 3) { + *event = EVENT_HALT; + break; + } + if (kernel_mode) vector = 4; setRegister(6, 04); - - if (processing_trap_depth >= 3) { - // TODO: halt? - } } else { before_psw = getPSW(); @@ -1689,6 +1689,8 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt) pushStack(before_psw); pushStack(before_pc); + processing_trap_depth = 0; + // if we reach this point then the trap was processed without causing // another trap break; diff --git a/cpu.h b/cpu.h index ff973d2..5c5f23a 100644 --- a/cpu.h +++ b/cpu.h @@ -35,6 +35,7 @@ private: uint16_t psw { 0 }; uint16_t fpsr { 0 }; uint16_t stackLimitRegister { 0377 }; + int processing_trap_depth { 0 }; uint64_t instruction_count { 0 }; uint64_t running_since { 0 }; diff --git a/debugger.h b/debugger.h index a1cff28..2a8392b 100644 --- a/debugger.h +++ b/debugger.h @@ -4,5 +4,6 @@ #include "bus.h" #include "console.h" +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 bool tracing); void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const bool tracing, const uint16_t bic_start); diff --git a/json/produce-json.py b/json/produce-json.py index 66e37da..686cc14 100755 --- a/json/produce-json.py +++ b/json/produce-json.py @@ -13,25 +13,29 @@ class PDP1170_wrapper(PDP1170): self.reset_mem_transactions_dict() def reset_mem_transactions_dict(self): - self.mem_transactions_w = dict() + self.mem_transactions = dict() def get_mem_transactions_dict(self): - return self.mem_transactions_w + return self.mem_transactions def physRW(self, physaddr, value=None): if value == None: # read - return super().physRW(physaddr, value) + self.mem_transactions[physaddr] = random.randint(0, 65536) + return super().physRW(physaddr, self.mem_transactions[physaddr]) - self.mem_transactions_w[physaddr] = value + self.mem_transactions[physaddr] = value super().physRW(physaddr, value) def physRW_N(self, physaddr, nwords, words=None): + temp_addr = physaddr if words == None: + for i in range(nwords): + physRW(temp_addr, random.randint(0, 65536)) + temp_addr += 2 return super().physRW_N(physaddr, nwords) - temp_addr = physaddr for w in words: - self.mem_transactions_w[temp_addr] = w + self.mem_transactions[temp_addr] = w temp_addr += 2 super().physRW_N(physaddr, nwords, words=words) @@ -40,8 +44,8 @@ class test_generator: def _invoke_bp(self, a, i): return True - def _run_test(self, mem_setup, reg_setup): - p = PDP1170_wrapper() + def _run_test(self, addr, mem_setup, reg_setup, psw): + p = PDP1170_wrapper(loglevel='DEBUG') for a, v in mem_setup: p.mmu.wordRW(a, v) @@ -51,19 +55,23 @@ class test_generator: p.reset_mem_transactions_dict() - p.run(breakpoint=self._invoke_bp) + p._syncregs() + p.run_steps(pc=addr, steps=1) + p._syncregs() return p def create_test(self): out = { } + addr = random.randint(0, 65536) & ~3 + # TODO what is the maximum size of an instruction? mem_kv = [] - mem_kv.append((0o1000, random.randint(0, 65536))) - mem_kv.append((0o1002, random.randint(0, 65536))) - mem_kv.append((0o1004, random.randint(0, 65536))) - mem_kv.append((0o1006, random.randint(0, 65536))) + mem_kv.append((addr + 0, random.randint(0, 65536))) + mem_kv.append((addr + 2, random.randint(0, 65536))) + mem_kv.append((addr + 4, random.randint(0, 65536))) + mem_kv.append((addr + 6, random.randint(0, 65536))) out['memory-before'] = dict() for a, v in mem_kv: @@ -72,18 +80,21 @@ class test_generator: reg_kv = [] for i in range(7): reg_kv.append((i, random.randint(0, 65536))) - reg_kv.append((7, 0o1000)) + reg_kv.append((7, addr)) out['registers-before'] = dict() for r, v in reg_kv: out['registers-before'][r] = v + out['registers-before']['psw'] = 0 # TODO random.randint(0, 65536) + try: - p = self._run_test(mem_kv, reg_kv) + p = self._run_test(addr, mem_kv, reg_kv, out['registers-before']['psw']) out['registers-after'] = dict() for r, v in reg_kv: out['registers-after'][r] = v + out['registers-after']['psw'] = p.psw out['memory-after'] = dict() for a, v in mem_kv: @@ -96,8 +107,9 @@ class test_generator: return out - except: - print('test failed') + except Exception as e: + # handle PDP11 traps; store them + print('test failed', e) return None fh = open(sys.argv[1], 'w') @@ -105,7 +117,7 @@ fh = open(sys.argv[1], 'w') t = test_generator() tests = [] -for i in range(0, 1024): +for i in range(0, 4096): test = t.create_test() if test != None: tests.append(test) diff --git a/log.cpp b/log.cpp index 9e73cb2..73e28fb 100644 --- a/log.cpp +++ b/log.cpp @@ -15,7 +15,7 @@ #include "win32.h" -static const char *logfile = strdup("/tmp/myip.log"); +static const char *logfile = strdup("/tmp/kek.log"); log_level_t log_level_file = warning; log_level_t log_level_screen = warning; FILE *lfh = nullptr; diff --git a/main.cpp b/main.cpp index 6379a42..d238076 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,8 @@ -// (C) 2018-2023 by Folkert van Heusden +// (C) 2018-2024 by Folkert van Heusden // Released under MIT license #include +#include #include #include #include @@ -49,6 +50,91 @@ void sw_handler(int s) } #endif +int run_cpu_validation(const std::string & filename) +{ + json_error_t error; + json_t *json = json_load_file(filename.c_str(), 0, &error); + if (!json) + error_exit(false, "%s", error.text); + + size_t n_ok = 0; + size_t array_size = json_array_size(json); + for(size_t i=0; iadd_cpu(c); + + // {"memory-before": {"512": 51435, "514": 45610, "516": 15091, "518": 43544}, "registers-before": {"0": 64423, "1": 1, "2": 41733, "3": 14269, "4": 48972, "5": 42770, "6": 57736, "7": 512}, "registers-after": {"0": 64423, "1": 1, "2": 41733, "3": 14269, "4": 48972, "5": 42770, "6": 57736, "7": 512}, "memory-after": {"512": 51435, "514": 45610, "516": 15091, "518": 43544}} + + // initialize + json_t *memory_before = json_object_get(test, "memory-before"); + const char *key = nullptr; + json_t *value = nullptr; + json_object_foreach(memory_before, key, value) { + b->writePhysical(atoi(key), json_integer_value(value)); + } + + json_t *registers_before = json_object_get(test, "registers-before"); + json_object_foreach(registers_before, key, value) { + if (strcmp(key, "psw") == 0) + c->setPSW(uint16_t(json_integer_value(value)), false); // TODO: without the AND + else + c->setRegister(atoi(key), json_integer_value(value), rm_cur); + } + + c->step_a(); + disassemble(c, nullptr, c->getPC(), false); + c->step_b(); + + // validate + bool err = false; + json_t *memory_after = json_object_get(test, "memory-after"); + json_object_foreach(memory_before, key, value) { + uint16_t mem_contains = b->readPhysical(atoi(key)); + uint16_t should_be = json_integer_value(value); + + if (mem_contains != should_be) { + DOLOG(warning, true, "memory address %s mismatch (is: %06o, should be: %06o)", key, mem_contains, should_be); + err = true; + } + } + + json_t *registers_after = json_object_get(test, "registers-after"); + json_object_foreach(registers_before, key, value) { + uint16_t register_is = 0; + + if (strcmp(key, "psw") == 0) + register_is = c->getPSW(); + else + register_is = c->getRegister(atoi(key), rm_cur); + uint16_t should_be = json_integer_value(value); + + if (register_is != should_be) { + DOLOG(warning, true, "register %s mismatch (is: %06o, should be: %06o)", key, register_is, should_be); + err = true; + } + } + + if (err) + DOLOG(warning, true, "%s", json_dumps(test, 0)); + else + n_ok++; + + // clean-up + delete b; + } + + json_decref(json); + + printf("# ok: %zu out of %zu\n", n_ok, array_size); + + return 0; +} + void help() { printf("-h this help\n"); @@ -65,6 +151,7 @@ void help() printf("-t enable tracing (disassemble to stderr, requires -d as well)\n"); printf("-l x log to file x\n"); printf("-L x,y set log level for screen (x) and file (y)\n"); + printf("-J x run validation suite x against the CPU emulation\n"); } int main(int argc, char *argv[]) @@ -95,14 +182,20 @@ int main(int argc, char *argv[]) disk_backend *temp_d = nullptr; + std::string validate_json; + int opt = -1; - while((opt = getopt(argc, argv, "hm:T:Br:R:p:ndtL:b:l:s:Q:N:")) != -1) + while((opt = getopt(argc, argv, "hm:T:Br:R:p:ndtL:b:l:s:Q:N:J:")) != -1) { switch(opt) { case 'h': help(); return 1; + case 'J': + validate_json = optarg; + break; + case 'Q': test = optarg; break; @@ -210,6 +303,9 @@ int main(int argc, char *argv[]) setlog(logfile, ll_file, ll_screen); + if (validate_json.empty() == false) + return run_cpu_validation(validate_json); + bus *b = new bus(); b->set_console_switches(console_switches);