diff --git a/CMakeLists.txt b/CMakeLists.txt index 60626b9..e6008af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ cmake_minimum_required(VERSION 3.9) add_compile_options(-Wall -pedantic -Wextra) +add_compile_options(-fsanitize=address) +add_link_options(-fsanitize=address) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -69,9 +72,9 @@ endif (WIN32) include(CheckIPOSupported) check_ipo_supported(RESULT supported) -set(CMAKE_BUILD_TYPE RelWithDebInfo) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) -#set(CMAKE_BUILD_TYPE Debug) +#set(CMAKE_BUILD_TYPE RelWithDebInfo) +#set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) +set(CMAKE_BUILD_TYPE Debug) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) diff --git a/cpu.cpp b/cpu.cpp index a5d4b21..72aaa9e 100644 --- a/cpu.cpp +++ b/cpu.cpp @@ -1,4 +1,4 @@ -// (C) 2018-2023 by Folkert van Heusden +// (C) 2018-2024 by Folkert van Heusden // Released under MIT license #include @@ -179,6 +179,27 @@ uint16_t cpu::addRegister(const int nr, const rm_selection_t mode_selection, con return pc += value; } +void cpu::lowlevel_register_set(const uint8_t set, const uint8_t reg, const uint16_t value) +{ + if (reg < 6) + regs0_5[set][reg] = value; + else if (reg == 6) + sp[set == 0 ? 0 : 3] = value; + else + pc = value; +} + +uint16_t cpu::lowlevel_register_get(const uint8_t set, const uint8_t reg) +{ + if (reg < 6) + return regs0_5[set][reg]; + + if (reg == 6) + return sp[set == 0 ? 0 : 3]; + + return pc; +} + bool cpu::getBitPSW(const int bit) const { return (psw >> bit) & 1; diff --git a/cpu.h b/cpu.h index 5c5f23a..48ff371 100644 --- a/cpu.h +++ b/cpu.h @@ -1,4 +1,4 @@ -// (C) 2018-2023 by Folkert van Heusden +// (C) 2018-2024 by Folkert van Heusden // Released under MIT license #pragma once @@ -64,7 +64,6 @@ private: void addToMMR1(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode); - gam_rc_t getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode, const rm_selection_t mode_selection, const bool read_value = true); gam_rc_t getGAMAddress(const uint8_t mode, const int reg, const word_mode_t word_mode); bool putGAM(const gam_rc_t & g, const uint16_t value); // returns false when flag registers should not be updated @@ -143,8 +142,11 @@ public: uint16_t getPC() const { return pc; } void setRegister(const int nr, const uint16_t value, const rm_selection_t mode_selection = rm_cur); - void setRegisterLowByte(const int nr, const word_mode_t word_mode, const uint16_t value); + // used by 'main' for json-validation + void lowlevel_register_set(const uint8_t set, const uint8_t reg, const uint16_t value); + uint16_t lowlevel_register_get(const uint8_t set, const uint8_t reg); + void lowlevel_psw_set(const uint16_t value) { psw = value; } void setStackPointer(const int which, const uint16_t value) { assert(which >= 0 && which < 4); sp[which] = value; } void setPC(const uint16_t value) { pc = value; } diff --git a/json/produce-json.py b/json/produce-json.py index 686cc14..0bbc31b 100755 --- a/json/produce-json.py +++ b/json/produce-json.py @@ -2,6 +2,7 @@ import json from machine import PDP1170 +from pdptraps import PDPTrap, PDPTraps import random import sys @@ -14,23 +15,32 @@ class PDP1170_wrapper(PDP1170): 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.mem_transactions[physaddr] = random.randint(0, 65536) - return super().physRW(physaddr, self.mem_transactions[physaddr]) + 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.mem_transactions[physaddr] = value - super().physRW(physaddr, value) + return 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)) + self.physRW(temp_addr, random.randint(0, 65536)) temp_addr += 2 return super().physRW_N(physaddr, nwords) @@ -38,32 +48,28 @@ class PDP1170_wrapper(PDP1170): self.mem_transactions[temp_addr] = w temp_addr += 2 - super().physRW_N(physaddr, nwords, words=words) + return super().physRW_N(physaddr, nwords, words=words) class test_generator: def _invoke_bp(self, a, i): return True - def _run_test(self, addr, mem_setup, reg_setup, psw): - p = PDP1170_wrapper(loglevel='DEBUG') + 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_] - for a, v in mem_setup: - p.mmu.wordRW(a, v) - - for r, v in reg_setup: - p.r[r] = v - - p.reset_mem_transactions_dict() - - p._syncregs() - p.run_steps(pc=addr, steps=1) - p._syncregs() - - return p + target[tag]['sp'] = p.stackpointers + target[tag]['pc'] = p.PC def create_test(self): out = { } + p = PDP1170_wrapper(loglevel='DEBUG') + addr = random.randint(0, 65536) & ~3 # TODO what is the maximum size of an instruction? @@ -72,41 +78,59 @@ class test_generator: 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: - out['memory-before'][a] = v - - reg_kv = [] - for i in range(7): - reg_kv.append((i, random.randint(0, 65536))) - 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) + p.put(a, v) try: - p = self._run_test(addr, mem_kv, reg_kv, out['registers-before']['psw']) + # generate & set PSW + while True: + try: + p.psw = random.randint(0, 65536) + break + except PDPTraps.ReservedInstruction as ri: + pass - out['registers-after'] = dict() + # 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: - out['registers-after'][r] = v + 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._syncregs() + + self.put_registers(p, out, 'registers-before') + out['registers-before']['psw'] = p.psw + + p.run_steps(pc=addr, steps=1) + + 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() - for a, v in mem_kv: - out['memory-after'][a] = v mem_transactions = p.get_mem_transactions_dict() for a in mem_transactions: out['memory-after'][a] = mem_transactions[a] + # TODO originele geheugeninhouden checken # TODO check if mem_transactions affects I/O, then return None return out + except PDPTraps.ReservedInstruction as pri: + return None + except Exception as e: # handle PDP11 traps; store them print('test failed', e) @@ -122,5 +146,5 @@ for i in range(0, 4096): if test != None: tests.append(test) -fh.write(json.dumps(tests)) +fh.write(json.dumps(tests, indent=4)) fh.close() diff --git a/main.cpp b/main.cpp index d238076..147d4bf 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ // (C) 2018-2024 by Folkert van Heusden // Released under MIT license +#include #include #include #include @@ -70,20 +71,40 @@ int run_cpu_validation(const std::string & filename) // {"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)); + { + // 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); + json_t *const registers_before = json_object_get(test, "registers-before"); + + { + const char *key = nullptr; + json_t *value = nullptr; + json_t *set0 = json_object_get(registers_before, "0"); + json_object_foreach(set0, key, value) { + c->lowlevel_register_set(0, atoi(key), json_integer_value(value)); + } + json_t *set1 = json_object_get(registers_before, "1"); + json_object_foreach(set1, key, value) { + c->lowlevel_register_set(1, atoi(key), json_integer_value(value)); + } + } + { + json_t *psw_reg = json_object_get(registers_before, "psw"); + assert(psw_reg); + c->lowlevel_psw_set(json_integer_value(psw_reg)); + } + { + json_t *b_pc = json_object_get(registers_before, "pc"); + assert(b_pc); + c->setPC(json_integer_value(b_pc)); + // TODO PS } c->step_a(); @@ -91,39 +112,74 @@ int run_cpu_validation(const std::string & filename) 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); + { + bool err = false; + { + json_t *memory_after = json_object_get(test, "memory-after"); + const char *key = nullptr; + json_t *value = nullptr; + json_object_foreach(memory_after, key, value) { + int key_v = atoi(key); + uint16_t mem_contains = b->readPhysical(key_v); + 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; + if (mem_contains != should_be) { + DOLOG(warning, true, "memory address %06o (%d) mismatch (is: %06o (%d), should be: %06o (%d))", key_v, key_v, mem_contains, mem_contains, should_be, 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; + uint16_t psw = c->getPSW(); - if (strcmp(key, "psw") == 0) - register_is = c->getPSW(); + json_t *const registers_after = json_object_get(test, "registers-after"); + + { + int set_nr = (psw >> 11) & 1; + char set[] = { char('0' + set_nr), 0x00 }; + + json_t *a_set = json_object_get(registers_after, set); + const char *key = nullptr; + json_t *value = nullptr; + json_object_foreach(a_set, key, value) { + uint16_t register_is = c->lowlevel_register_get(set_nr, atoi(key)); + uint16_t should_be = json_integer_value(value); + + if (register_is != should_be) { + DOLOG(warning, true, "set %d register %s mismatch (is: %06o, should be: %06o)", set_nr, key, register_is, should_be); + err = true; + } + } + } + + { + json_t *a_pc = json_object_get(registers_after, "pc"); + assert(a_pc); + uint16_t should_be_pc = json_integer_value(a_pc); + if (c->getPC() != should_be_pc) { + DOLOG(warning, true, "PC register mismatch (is: %06o, should be: %06o)", c->getPC(), should_be_pc); + err = true; + } + } + + // TODO check PS + + { + json_t *a_psw = json_object_get(registers_after, "psw"); + assert(a_psw); + uint16_t should_be_psw = json_integer_value(a_psw); + if (should_be_psw != psw) { + DOLOG(warning, true, "PSW register mismatch (is: %06o, should be: %06o)", psw, should_be_psw); + err = true; + } + } + + if (err) + DOLOG(warning, true, "%s\n", json_dumps(test, 0)); // also emit empty line(!) 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; - } + n_ok++; } - if (err) - DOLOG(warning, true, "%s", json_dumps(test, 0)); - else - n_ok++; - // clean-up delete b; }