diff --git a/pymlac/CPU.test b/pymlac/CPU.test index 56d7134..481f6c8 100644 --- a/pymlac/CPU.test +++ b/pymlac/CPU.test @@ -1,26 +1,2 @@ -# LAW -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [LAW 0]; RUN - checkcycles 1; checkreg pc 0101; checkreg ac 0 -setreg ac 012345; setreg l 0; setreg pc 0100; setmem 0100 [LAW 0]; RUN - checkcycles 1; checkreg pc 0101; checkreg ac 0 -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [LAW 0377]; RUN - checkcycles 1; checkreg pc 0101; checkreg ac 0377 -setreg ac 012345; setreg l 0; setreg pc 0100; setmem 0100 [LAW 0377]; RUN - checkcycles 1; checkreg pc 0101; checkreg ac 0377 -# LWC -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [LWC 0]; RUN; checkcycles 1; checkreg pc 0101; checkreg ac 0177777 -setreg ac 012345; setreg l 0; setreg pc 0100; setmem 0100 [LWC 0]; RUN; checkcycles 1; checkreg pc 0101; checkreg ac 0177777 -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [LWC 1]; RUN; checkcycles 1; checkreg pc 0101; checkreg ac 0177776 -setreg ac 012345; setreg l 0; setreg pc 0100; setmem 0100 [LWC 1]; RUN; checkcycles 1; checkreg pc 0101; checkreg ac 0177776 -# JMP -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [JMP 0200]; RUN; checkcycles 2; checkreg pc 0200 -setreg ac 012345; setreg l 0; setreg pc 0100; setmem 0100 [JMP 0110]; RUN; checkcycles 2; checkreg pc 0110 -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [JMP *0110]; setmem 0110 0120; RUN; checkcycles 3; checkreg pc 0120 -setreg ac 012345; setreg l 1; setreg pc 0100; setmem 0100 [JMP *010]; setmem 010 0120; RUN checkcycles 3; checkreg pc 0120; checkmem 010 0121 -# DAC -setreg ac 1; setreg l 1; setreg pc 0100; setmem 0100 [DAC 0110]; RUN - checkcycles 2; checkreg pc 0101; checkmem 0110 1 -setreg ac 0177777; setreg l 0; setreg pc 0100; setmem 0100 [DAC *0110]; setmem 0110 0120; RUN - checkcycles 3; checkreg pc 0101; checkmem 0120 0177777 setreg ac 0177777; setreg l 1; setreg pc 0100; setmem 0100 [DAC *010]; setmem 010 0120; RUN - checkcycles 3; checkreg pc 0101; checkmem 0120 0177777; checkmem 010 0121 + checkcycles 3; checkreg pc 0101; checkmem 0121 0177777; checkmem 010 0121 diff --git a/pymlac/Memory.py b/pymlac/Memory.py index f8e424e..4e62061 100644 --- a/pymlac/Memory.py +++ b/pymlac/Memory.py @@ -221,7 +221,6 @@ def fetch(address, indirect): return memory[address] def eff_address(address, indirect): -#def get(address, indirect): """Get an effective memory address. The address can be indirect, and may be through an diff --git a/pymlac/test_CPU.py b/pymlac/test_CPU.py index d3e398f..567f06b 100644 --- a/pymlac/test_CPU.py +++ b/pymlac/test_CPU.py @@ -61,10 +61,56 @@ where is a file of test instructions and # If any errors are found, print line followed by all errors. +import os + from Globals import * import MainCPU import Memory +import log +log = log.Log('test_CPU.log', log.Log.DEBUG) + + +# number of cycles used in previous instruction +UsedCycles = 0 + +# register+value explicitly set by test +RegValues = {} + +# memory address+value explicitly set by test +MemValues = {} + +# memory value all explicitly set to (shouldn't change) +MemAllValue = None + +# registers value all explicitly set to (shouldn't change) +RegAllValue = None + +# temporary assembler file and listfile prefix +AsmFilename = '_#ASM#_' + + +def assemble(addr, opcode): + """Assemble a single instruction, return opcode.""" + + # create ASM file with instruction + with open(AsmFilename+'.asm', 'wb') as fd: + fd.write('\torg\t%07o\n' % addr) + fd.write('\t%s\n' % opcode[1:-1]) + fd.write('\tend\n') + + # now assemble file + os.system('../iasm/iasm -l %s.lst %s.asm' % (AsmFilename, AsmFilename)) + + # read the listing file to get assembled opcode + with open(AsmFilename+'.lst', 'rb') as fd: + lines = fd.readlines() + line = lines[1] + (opcode, _) = line.split(None, 1) + print('opcode=%07o' % int(opcode, base=8)) + + return int(opcode, base=8) + def setreg(name, value): """Set register to a value. @@ -72,20 +118,44 @@ def setreg(name, value): Remember value to check later. """ + log.debug('setreg: name=%s, value=%s' % (name, oct(value))) + global RegValues RegValues[name] = value + log.debug('setreg: After, RegValues=%s' % str(RegValues)) - exec "MainCPU.%s = %07o" % (name, value) + if name == 'ac': + MainCPU.AC = value + log.debug('setreg: After, AC=%s' % oct(MainCPU.AC)) + elif name == 'l': + MainCPU.L = value & 1 + log.debug('setreg: After, L=%s' % oct(MainCPU.L)) + elif name == 'pc': + MainCPU.PC = value + log.debug('setreg: After, PC=%s' % oct(MainCPU.PC)) def setmem(addr, value): """Set memory location to a value.""" + if isinstance(value, basestring): + log.debug('setmem: addr=%s, value=%s' % (oct(addr), value)) + else: + log.debug('setmem: addr=%s, value=%s' % (oct(addr), oct(value))) + global MemValues - MemValues[addr] = value + # check if we must assemble var2 + if isinstance(value, basestring) and value[0] == '[': + # assemble an instruction + value = assemble(addr, value) + log.debug('setmem: assembled opcode=%07o' % value) - Memory.memory[addr] = value + MemValues[addr] = value + log.debug('setmem: After, MemValues=%s' % str(MemValues)) + + Memory.put(value, addr, False) + log.debug('setmem: After, Memory at %07o is %07o' % (addr, Memory.fetch(addr, False))) def allmem(value): @@ -98,9 +168,8 @@ def allmem(value): MemAllValue = value - for mem in range(PCMASK): - print str(mem) - Memory.memory[mem] = value + for mem in range(MEMORY_SIZE): + Memory.put(mem, value, False) def allreg(value): """Set all registers to a value.""" @@ -113,12 +182,105 @@ def allreg(value): MainCPU.L = value & 1 MainCPU.PC = value +def check_all_mem(): + """Check memory for unwanted changes.""" + + for mem in range(MEMORY_SIZE): + if mem in MemValues: + continue + value = Memory.fetch(mem, False) + if value != MemAllValue: + return ('Changed memory at address %07o, is %07o, should be %07o' + % (mem, value, MemAllValue)) + +def check_all_regs(): + """Check registers for unwanted changes.""" + + result = [] + + if 'ac' not in RegValues: + if MainCPU.AC != RegAllValue: + result.append('AC changed, is %07o, should be %07o' + % (MainCPU.AC, RegAllValue)) + + if 'l' not in RegValues: + if MainCPU.L != RegAllValue & 1: + result.append('L changed, is %02o, should be %02o' + % (MainCPU.L, RegAllValue & 1)) + + if 'pc' not in RegValues: + if MainCPU.PC != RegAllValue: + result.append('PC changed, is %07o, should be %07o' + % (MainCPU.PC, RegAllValue)) + + if result: + return result.join('\n') + +def checkreg(reg, value): + """Check register is as it should be.""" + + global RegValues + + RegValues[reg] = value + log.debug('checkreg: After, RegValues=%s' % str(RegValues)) + + if reg == 'ac': + if MainCPU.AC != value: + return 'AC wrong, is %07o, should be %07o' % (MainCPU.AC, value) + elif reg == 'l': + if MainCPU.L != value: + return 'L wrong, is %07o, should be %07o' % (MainCPU.L, value) + elif reg == 'pc': + if MainCPU.PC != value: + return 'AC wrong, is %07o, should be %07o' % (MainCPU.PC, value) + else: + raise Exception('checkreg: bad register name: %s' % name) + +def checkmem(addr, value): + """Check a memory location is as it should be.""" + + global MemValues + + MemValues[addr] = value + log.debug('checkmem: After, MemValues=%s' % str(MemValues)) + + memvalue = Memory.fetch(addr, False) + if memvalue != value: + return 'Memory wrong at address %07o, is %07o, should be %07o' % (addr, memvalue, value) + +def checkcycles(cycles, var2=None): + """Check that opcode cycles used is correct.""" + + if cycles != UsedCycles: + return 'Opcode used %d cycles, expected %d' % (UsedCycles, cycles) + +def run(addr, var2): + """Execute instruction.""" + + global UsedCycles + + if addr is not None: + # force PC to given address + setreg('pc', addr) + + UsedCycles = MainCPU.execute_one_instruction() + +def debug_operation(op, var1, var2): + """Write operation to log file.""" + + if var1: + if var2: + log.debug('Operation: %s %s %s' % (op, var1, var2)) + else: + log.debug('Operation: %s %s' % (op, var1)) + else: + log.debug('Operation: %s' % op) + def execute(test): """Execute test string in 'test'.""" global RegValues, MemValues global RegAllValue, MemAllValue - global Test # set globals RegValues = {} @@ -126,11 +288,15 @@ def execute(test): RegAllValue = {} MemAllValue = {} - Test = test + result = [] + + MainCPU.init() + MainCPU.running = True + Memory.init() # clear memory and registers to 0 first -# allmem(0) -# allreg(0) + allmem(0) + allreg(0) # interpret the test instructions instructions = test.split(';') @@ -146,35 +312,77 @@ def execute(test): except IndexError: var2 = None - try: - if op == 'setreg': - setreg(var1, var2) - elif op == 'setmem': - setmem(var1, var2) - elif op == 'run': - run(var1, var2) - elif op == 'checkcycles': - checkcycles(var1, var2) - elif op == 'checkreg': - checkreg(var1, var2) - elif op == 'checkmem': - checkmem(var1, var2) - elif op == 'allreg': - allreg(var1, var2) - elif op == 'allmem': - allmem(var1, var2) + debug_operation(op, var1, var2) + + # change var strings into numeric values + if var1 and var1[0] in '0123456789': + if var1[0] == '0': + var1 = int(var1, base=8) else: - error('Unrecognized operation: %s' % test) - except CPUError: - pass + var1 = int(var1) + + if var2 and var2[0] in '0123456789': + if var2[0] == '0': + var2 = int(var2, base=8) + else: + var2 = int(var2) + + if op == 'setreg': + r = setreg(var1, var2) + elif op == 'setmem': + r = setmem(var1, var2) + elif op == 'run': + r = run(var1, var2) + elif op == 'checkcycles': + r = checkcycles(var1, var2) + elif op == 'checkreg': + r = checkreg(var1, var2) + elif op == 'checkmem': + r = checkmem(var1, var2) + elif op == 'allreg': + r = allreg(var1, var2) + elif op == 'allmem': + r = allmem(var1, var2) + else: + error('Unrecognized operation: %s' % test) + + if r is not None: + result.append(r) + # now check all memory and regs for changes - check_all_mem() - check_all_regs() + r = check_all_mem() + if r: + result.append(r) + + r = check_all_regs() + if r: + result.append(r) + + print(test) + if result: + print('\t' + '\n\t'.join(result)) + + memdump('core.txt', 0, 0200) + +def memdump(filename, start, number): + """Dump memory from 'start' into 'filename', 'number' words dumped.""" + + with open(filename, 'wb') as fd: + for addr in range(start, start+number, 8): + a = addr + llen = min(8, start+number - addr) + line = '%04o ' % addr + for _ in range(llen): + line += '%06o ' % Memory.fetch(a, False) + a += 1 + fd.write('%s\n' % line) def main(filename): """Execute CPU tests from 'filename'.""" + log.debug("Running test file '%s'" % filename) + # get all tests from file with open(filename, 'rb') as fd: lines = fd.readlines() @@ -203,6 +411,7 @@ def main(filename): # now do each test for test in tests: + log.debug('Executing test: %s' % test) execute(test)