From 2f45cc3996cd0b8f632f5821bc1e8d41eb626ee9 Mon Sep 17 00:00:00 2001 From: Ross Wilson Date: Sun, 21 Jun 2015 21:52:27 +0700 Subject: [PATCH] Main CPU and test moved to a class --- pymlac/MainCPU.py | 1439 ++++++++++++++++++++------------------------ pymlac/test_CPU.py | 715 +++++++++++----------- 2 files changed, 1007 insertions(+), 1147 deletions(-) diff --git a/pymlac/MainCPU.py b/pymlac/MainCPU.py index c8c0874..4e760a1 100644 --- a/pymlac/MainCPU.py +++ b/pymlac/MainCPU.py @@ -8,803 +8,698 @@ The Imlac main CPU. import sys from Globals import * -import DisplayCPU -#import Display -import Memory -import Ptr -import Ptp -import TtyIn -import TtyOut -import Kbd +#import DisplayCPU +#import Memory +#import Ptr +#import Ptp +#import TtyIn +#import TtyOut +#import Kbd import Trace -###### -# The main CPU registers -###### +class MainCPU(object): -PC = None # main CPU program counter -L = 0 # main CPU link register -AC = 0 # main CPU accumulator -Sync40Hz = 1 # main CPU 40Hz flag register -DS = 0 # dataswitches value + ###### + # The main CPU registers + ###### -# address of base of local code block -BlockBase = 0 + PC = None # main CPU program counter + L = 0 # main CPU link register + AC = 0 # main CPU accumulator + Sync40Hz = 1 # main CPU 40Hz flag register + DS = 0 # dataswitches value -# decode dictionaries (initialized in init()) -main_decode = None -page_00_decode = None -page02_decode = None -micro_opcodes = None -micro_singles = None + # address of base of local code block + BlockBase = 0 -# module-level state variables -running = False # True if CPU running + # decode dictionaries (initialized in __init__()) + main_decode = None + page_00_decode = None + page02_decode = None + micro_opcodes = None + micro_singles = None -Display = None # reference to the Display object + # module-level state variables + running = False # True if CPU running -def init(display): - global running, main_decode, page_00_decode, page02_decode, micro_opcodes - global micro_singles - global Display + def __init__(self, memory, display, displaycpu, kbd, ttyin, ttyout, ptp, ptr): + """Initialize the main CPU.""" - Display = display + self.memory = memory + self.display = display + self.displaycpu = displaycpu + self.kbd = kbd + self.ttyin = ttyin + self.ttyout = ttyout + self.ptp = ptp + self.ptr = ptr - # main dispatch dictionary for decoding opcodes in bits 1-4 - main_decode = {000: page_00, # secondary decode - 001: i_LAW_LWC, - 002: i_JMP, - # 003: illegal, - 004: i_DAC, - 005: i_XAM, - 006: i_ISZ, - 007: i_JMS, - # 010: illegal - 011: i_AND, - 012: i_IOR, - 013: i_XOR, - 014: i_LAC, - 015: i_ADD, - 016: i_SUB, - 017: i_SAM} + # main dispatch dictionary for decoding opcodes in bits 1-4 + self.main_decode = {000: self.page_00, # secondary decode + 001: self.i_LAW_LWC, + 002: self.i_JMP, + # 003: self.illegal, + 004: self.i_DAC, + 005: self.i_XAM, + 006: self.i_ISZ, + 007: self.i_JMS, + # 010: self.illegal + 011: self.i_AND, + 012: self.i_IOR, + 013: self.i_XOR, + 014: self.i_LAC, + 015: self.i_ADD, + 016: self.i_SUB, + 017: self.i_SAM} - # page_00 dispatch dictionary for decoding opcodes - # HLT may be handled specially - page_00_decode = {001003: i_DLA, - 001011: i_CTB, - 001012: i_DOF, - 001021: i_KRB, - 001022: i_KCF, - 001023: i_KRC, - 001031: i_RRB, - 001032: i_RCF, - 001033: i_RRC, - 001041: i_TPR, - 001042: i_TCF, - 001043: i_TPC, - 001051: i_HRB, - 001052: i_HOF, - 001061: i_HON, - 001062: i_STB, - 001071: i_SCF, - 001072: i_IOS, - 001101: i_IOT101, - 001111: i_IOT111, - 001131: i_IOT131, - 001132: i_IOT132, - 001134: i_IOT134, - 001141: i_IOT141, - 001161: i_IOF, - 001162: i_ION, - 001271: i_PUN, - 001274: i_PSF, - # 003000: illegal RAL0 - 003001: i_RAL1, - 003002: i_RAL2, - 003003: i_RAL3, - # 003020: illegal RAR0, - 003021: i_RAR1, - 003022: i_RAR2, - 003023: i_RAR3, - # 003040: illegal SAL0, - 003041: i_SAL1, - 003042: i_SAL2, - 003043: i_SAL3, - # 003060: illegal SAR0, - 003061: i_SAR1, - 003062: i_SAR2, - 003063: i_SAR3, - 003100: i_DON} - - page02_decode = {0002001: i_ASZ, - 0102001: i_ASN, - 0002002: i_ASP, - 0102002: i_ASM, - 0002004: i_LSZ, - 0102004: i_LSN, - 0002010: i_DSF, - 0102010: i_DSN, - 0002020: i_KSF, - 0102020: i_KSN, - 0002040: i_RSF, - 0102040: i_RSN, - 0002100: i_TSF, - 0102100: i_TSN, - 0002200: i_SSF, - 0102200: i_SSN, - 0002400: i_HSF, - 0102400: i_HSN} + # page_00 dispatch dictionary for decoding opcodes + # HLT may be handled specially + self.page_00_decode = {001003: self.i_DLA, + 001011: self.i_CTB, + 001012: self.i_DOF, + 001021: self.i_KRB, + 001022: self.i_KCF, + 001023: self.i_KRC, + 001031: self.i_RRB, + 001032: self.i_RCF, + 001033: self.i_RRC, + 001041: self.i_TPR, + 001042: self.i_TCF, + 001043: self.i_TPC, + 001051: self.i_HRB, + 001052: self.i_HOF, + 001061: self.i_HON, + 001062: self.i_STB, + 001071: self.i_SCF, + 001072: self.i_IOS, + 001101: self.i_IOT101, + 001111: self.i_IOT111, + 001131: self.i_IOT131, + 001132: self.i_IOT132, + 001134: self.i_IOT134, + 001141: self.i_IOT141, + 001161: self.i_IOF, + 001162: self.i_ION, + 001271: self.i_PUN, + 001274: self.i_PSF, + # 003000: self.illegal RAL0 + 003001: self.i_RAL1, + 003002: self.i_RAL2, + 003003: self.i_RAL3, + # 003020: self.illegal RAR0, + 003021: self.i_RAR1, + 003022: self.i_RAR2, + 003023: self.i_RAR3, + # 003040: self.illegal SAL0, + 003041: self.i_SAL1, + 003042: self.i_SAL2, + 003043: self.i_SAL3, + # 003060: self.illegal SAR0, + 003061: self.i_SAR1, + 003062: self.i_SAR2, + 003063: self.i_SAR3, + 003100: self.i_DON} - micro_opcodes = {0100000: 'NOP', - 0100001: 'CLA', - 0100002: 'CMA', - 0100003: 'STA', - 0100004: 'IAC', - 0100005: 'COA', - 0100006: 'CIA', - 0100010: 'CLL', - 0100011: 'CAL', - 0100020: 'CML', - 0100030: 'STL', - 0100040: 'ODA', - 0100041: 'LDA'} + self.page02_decode = {0002001: self.i_ASZ, + 0102001: self.i_ASN, + 0002002: self.i_ASP, + 0102002: self.i_ASM, + 0002004: self.i_LSZ, + 0102004: self.i_LSN, + 0002010: self.i_DSF, + 0102010: self.i_DSN, + 0002020: self.i_KSF, + 0102020: self.i_KSN, + 0002040: self.i_RSF, + 0102040: self.i_RSN, + 0002100: self.i_TSF, + 0102100: self.i_TSN, + 0002200: self.i_SSF, + 0102200: self.i_SSN, + 0002400: self.i_HSF, + 0102400: self.i_HSN} - micro_singles = {0100001: 'CLA', - 0100002: 'CMA', - 0100004: 'IAC', - 0100010: 'CLL', - 0100020: 'CML', - 0100040: 'ODA'} + self.micro_opcodes = {0100000: 'NOP', + 0100001: 'CLA', + 0100002: 'CMA', + 0100003: 'STA', + 0100004: 'IAC', + 0100005: 'COA', + 0100006: 'CIA', + 0100010: 'CLL', + 0100011: 'CAL', + 0100020: 'CML', + 0100030: 'STL', + 0100040: 'ODA', + 0100041: 'LDA'} + + self.micro_singles = {0100001: 'CLA', + 0100002: 'CMA', + 0100004: 'IAC', + 0100010: 'CLL', + 0100020: 'CML', + 0100040: 'ODA'} - running = False + self.running = False -def BLOCKADDR(address): - return BlockBase | address + def BLOCKADDR(self, address): + """Get address WITHIN THE BLOCK.""" -def execute_one_instruction(): - """Execute one MAIN instruction, return # cycles used""" + return self.BlockBase | address - global main_decode - global PC, BlockBase + def execute_one_instruction(self): + """Execute one MAIN instruction, return # cycles used""" - if not running: - return 0 + if not self.running: + return 0 - # get instruction word to execute, advance PC - instruction = Memory.fetch(PC, False) - BlockBase = PC & ADDRHIGHMASK - PC = MASK_MEM(PC + 1) + # get instruction word to execute, advance PC + instruction = self.memory.fetch(self.PC, False) + self.BlockBase = self.PC & ADDRHIGHMASK + self.PC = MASK_MEM(self.PC + 1) - # get instruction opcode, indirect bit and address - opcode = (instruction >> 11) & 017 - indirect = bool(instruction & 0100000) - address = (instruction & 03777) + # get instruction opcode, indirect bit and address + opcode = (instruction >> 11) & 017 + indirect = bool(instruction & 0100000) + address = (instruction & 03777) - return main_decode.get(opcode, illegal)(indirect, address, instruction) + return self.main_decode.get(opcode, self.illegal)(indirect, + address, + instruction) -def illegal(indirect, address, instruction): - if instruction: - msg = ('Illegal instruction (%6.6o) at address %6.6o' - % (instruction, PC-1)) - else: - msg = 'Illegal instruction at address %6.6o' % (PC-1) - raise RuntimeError(msg) + def illegal(self, indirect, address, instruction): + """Handle an illegal instruction.""" -def page_00(indirect, address, instruction): - if (instruction & 0077700) == 000000: - return microcode(instruction) - elif (instruction & 0077000) == 002000: - return page02_decode.get(instruction, illegal)() - - return page_00_decode.get(instruction, illegal)(indirect, - address, instruction) - -def i_LAW_LWC(indirect, address, instruction): - global AC - - if indirect: - AC = ~address & WORDMASK - Trace.itrace('LWC', False, address) - else: - AC = address - Trace.itrace('LAW', False, address) - return 1 - -def i_JMP(indirect, address, instruction): - global PC - - address = Memory.eff_address(address, indirect) - PC = address & PCMASK - Trace.itrace('JMP', indirect, address) - return 3 if indirect else 2 - -def i_DAC(indirect, address, instruction): - address = Memory.eff_address(address, indirect) - Memory.put(AC, address, False) - Trace.itrace('DAC', indirect, address) - return 3 if indirect else 2 - -def i_XAM(indirect, address, instruction): - global AC - - address = Memory.eff_address(address, indirect) - tmp = Memory.fetch(address, False) - Memory.put(AC, address, False) - AC = tmp - Trace.itrace('XAM', indirect, address) - return 3 if indirect else 2 - -def i_ISZ(indirect, address, instruction): - global PC - - address = Memory.eff_address(address, indirect) - value = (Memory.fetch(address, False) + 1) & WORDMASK - Memory.put(value, address, False) - if value == 0: - PC = (PC + 1) & WORDMASK - Trace.itrace('ISZ', indirect, address) - return 3 if indirect else 2 - -def i_JMS(indirect, address, instruction): - global PC - - address = Memory.eff_address(address, indirect) - Memory.put(PC, address, False) - PC = (address + 1) & PCMASK - Trace.itrace('JMS', indirect, address) - return 3 if indirect else 2 - -def i_AND(indirect, address, instruction): - global AC - - AC &= Memory.fetch(address, indirect) - Trace.itrace('AND', indirect, address) - return 3 if indirect else 2 - -def i_IOR(indirect, address, instruction): - global AC - - AC |= Memory.fetch(address, indirect) - Trace.itrace('IOR', indirect, address) - return 3 if indirect else 2 - -def i_XOR(indirect, address, instruction): - global AC - - AC ^= Memory.fetch(address, indirect) - Trace.itrace('XOR', indirect, address) - return 3 if indirect else 2 - -def i_LAC(indirect, address, instruction): - global AC - - AC = Memory.fetch(address, indirect) - Trace.itrace('LAC', indirect, address) - return 3 if indirect else 2 - -def i_ADD(indirect, address, instruction): - global AC, L - - AC += Memory.fetch(BLOCKADDR(address), indirect) - if AC & OVERFLOWMASK: - L = (~L) & 01 - AC &= WORDMASK - Trace.itrace('ADD', indirect, address) - return 3 if indirect else 2 - -def i_SUB(indirect, address, instruction): - global AC, L - - addit = Memory.fetch(BLOCKADDR(address), indirect) - addit = (~addit + 1) & WORDMASK - AC += addit - if AC & OVERFLOWMASK: - L = not L - AC &= WORDMASK - Trace.itrace('SUB', indirect, address) - return 3 if indirect else 2 - -def i_SAM(indirect, address, instruction): - global PC - - samaddr = BLOCKADDR(address) - - if AC == Memory.fetch(samaddr, indirect): - PC = (PC + 1) & PCMASK - Trace.itrace('SAM', indirect, address) - return 3 if indirect else 2 - -def microcode(instruction): - global AC, L, PC, DS, running - - # T1 - if instruction & 001: - AC = 0 - if instruction & 010: - L = 0 - - # T2 - if instruction & 002: - AC = (~AC) & WORDMASK - if instruction & 020: - L = (~L) & 01 - - # T3 - if instruction & 004: - AC += 1 - if AC & OVERFLOWMASK: - L = (~L) & 1 - AC &= WORDMASK - if instruction & 040: - AC |= DS - - # do some sort of trace - combine = [] - opcode = micro_opcodes.get(instruction, None) - if opcode: - combine.append(opcode) - - if not combine: - # nothing so far, we have HLT or unknown microcode - if not instruction & 0100000: - # bit 0 is clear, it's HLT - running = False - combine.append('HLT') + if instruction: + msg = ('Illegal instruction (%6.6o) at address %6.6o' + % (instruction, self.PC-1)) else: - for (k, op) in micro_singles.items(): - if instruction & k: - combine.append(op) - - Trace.itrace('+'.join(combine), False) - return 1 - -def i_DLA(indirect, address, instruction): - DisplayCPU.DPC = AC - Trace.itrace('DLA') - return 1 - -def i_CTB(indirect, address, instruction): - Trace.itrace('CTB') - return 1 - -def i_DOF(indirect, address, instruction): - DisplayCPU.stop() - Trace.itrace('DOF') - return 1 - -def i_KRB(indirect, address, instruction): - global AC - - AC |= Kbd.read() - Trace.itrace('KRB') - return 1 - -def i_KCF(indirect, address, instruction): - Kbd.clear() - Trace.itrace('KCF') - return 1 - -def i_KRC(indirect, address, instruction): - global AC - - AC |= Kbd.read() - Kbd.clear() - Trace.itrace('KRC') - return 1 - -def i_RRB(indirect, address, instruction): - global AC - - AC |= TtyIn.read() - Trace.itrace('RRB') - return 1 - -def i_RCF(indirect, address, instruction): - TtyIn.clear() - Trace.itrace('RCF') - return 1 - -def i_RRC(indirect, address, instruction): - global AC - - AC |= TtyIn.read() - TtyIn.clear() - Trace.itrace('RRC') - return 1 - -def i_TPR(indirect, address, instruction): - TtyOut.write(AC & 0xff) - Trace.itrace('TPR') - return 1 - -def i_TCF(indirect, address, instruction): - TtyOut.clear() - Trace.itrace('TCF') - return 1 - -def i_TPC(indirect, address, instruction): - TtyOut.write(AC & 0xff) - TtyOut.clear() - Trace.itrace('TPC') - return 1 - -def i_HRB(indirect, address, instruction): - global AC - - AC |= Ptr.read() - Trace.itrace('HRB') - return 1 - -def i_HOF(indirect, address, instruction): - Ptr.stop() - Trace.itrace('HOF') - return 1 - -def i_HON(indirect, address, instruction): - Ptr.start() - Trace.itrace('HON') - return 1 - -def i_STB(indirect, address, instruction): - Trace.itrace('STB') - return 1 - -def i_SCF(indirect, address, instruction): - global Sync40Hz - - Sync40Hz = 0 - Trace.itrace('SCF') - return 1 - -def i_IOS(indirect, address, instruction): - Trace.itrace('IOS') - return 1 - -def i_IOT101(indirect, address, instruction): - Trace.itrace('IOT101') - return 1 - -def i_IOT111(indirect, address, instruction): - Trace.itrace('IOT111') - return 1 - -def i_IOT131(indirect, address, instruction): - Trace.itrace('IOT131') - return 1 - -def i_IOT132(indirect, address, instruction): - Trace.itrace('IOT132') - return 1 - -def i_IOT134(indirect, address, instruction): - Trace.itrace('IOT134') - return 1 - -def i_IOT141(indirect, address, instruction): - Trace.itrace('IOT141') - return 1 - -def i_IOF(indirect, address, instruction): - Trace.itrace('IOF') - return 1 - -def i_ION(indirect, address, instruction): - Trace.itrace('ION') - return 1 - -def i_PUN(indirect, address, instruction): - Ptp.write(PC & 0xff) - Trace.itrace('PUN') - return 1 - -def i_PSF(indirect, address, instruction): - global PC - - if Ptp.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('PSF') - return 1 - -def i_RAL1(indirect, address, instruction): - global AC, L - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAL', False, 1) - return 1 - -def i_RAL2(indirect, address, instruction): - global AC, L - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAL', False, 2) - return 1 - -def i_RAL3(indirect, address, instruction): - global AC, L - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - newl = AC >> 15 - newac = (AC << 1) | L - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAL', False, 3) - return 1 - -def i_RAR1(indirect, address, instruction): - global AC, L - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAR', False, 1) - return 1 - -def i_RAR2(indirect, address, instruction): - global AC, L - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAR', False, 2) - return 1 - -def i_RAR3(indirect, address, instruction): - global AC, L - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - newl = AC & 1 - newac = (AC >> 1) | (L << 15) - L = newl - AC = newac & WORDMASK - - Trace.itrace('RAR', False, 3) - return 1 - -def i_SAL1(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - value = AC & 037777 - AC = (value << 1) | high_bit - Trace.itrace('SAL', False, 1) - return 1 - -def i_SAL2(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - value = AC & 017777 - AC = (value << 2) | high_bit - Trace.itrace('SAL', False, 2) - return 1 - -def i_SAL3(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - value = AC & 007777 - AC = (value << 3) | high_bit - Trace.itrace('SAL', False, 3) - return 1 - -def i_SAR1(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - AC = (AC >> 1) | high_bit - Trace.itrace('SAR', False, 1) - return 1 - -def i_SAR2(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - AC = (AC >> 1) | high_bit - AC = (AC >> 1) | high_bit - Trace.itrace('SAR', False, 2) - return 1 - -def i_SAR3(indirect, address, instruction): - global AC - - high_bit = AC & HIGHBITMASK - AC = (AC >> 1) | high_bit - AC = (AC >> 1) | high_bit - AC = (AC >> 1) | high_bit - Trace.itrace('SAR', False, 3) - return 1 - -def i_DON(indirect, address, instruction): - DisplayCPU.DRSindex = 0 - DisplayCPU.start() - Trace.itrace('DON') - return 1 - -def i_ASZ(): - global PC - - if AC == 0: - PC = (PC + 1) & WORDMASK - Trace.itrace('ASZ') - return 1 - -def i_ASN(): - global PC - - if AC != 0: - PC = (PC + 1) & WORDMASK - Trace.itrace('ASN') - return 1 - -def i_ASP(): - global PC - - if not (AC & HIGHBITMASK): - PC = (PC + 1) & WORDMASK - Trace.itrace('ASP') - return 1 - -def i_ASM(): - global PC - - if (AC & HIGHBITMASK): - PC = (PC + 1) & WORDMASK - Trace.itrace('ASM') - return 1 - -def i_LSZ(): - global PC - - if L == 0: - PC = (PC + 1) & WORDMASK - Trace.itrace('LSZ') - return 1 - -def i_LSN(): - global PC - - if L != 0: - PC = (PC + 1) & WORDMASK - Trace.itrace('LSN') - return 1 - -def i_DSF(): - global PC - - if DisplayCPU.ison(): - PC = (PC + 1) & WORDMASK - Trace.itrace('DSF') - return 1 - -def i_DSN(): - global PC - - if not DisplayCPU.ison(): - PC = (PC + 1) & WORDMASK - Trace.itrace('DSN') - return 1 - -def i_KSF(): - global PC - - if Kbd.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('KSF') - return 1 - -def i_KSN(): - global PC - - if not Kbd.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('KSN') - return 1 - -def i_RSF(): - global PC - - if TtyIn.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('RSF') - return 1 - -def i_RSN(): - global PC - - if not TtyIn.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('RSN') - return 1 - -def i_TSF(): - global PC - - if TtyOut.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('TSF') - return 1 - -def i_TSN(): - global PC - - if not TtyOut.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('TSN') - return 1 - -def i_SSF(): - global PC - - if Display.ready(): # skip if 40Hz sync on - PC = (PC + 1) & WORDMASK - Trace.itrace('SSF') - return 1 - -def i_SSN(): - global PC - - if not Display.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('SSN') - return 1 - -def i_HSF(): - global PC - - if Ptr.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('HSF') - return 1 - -def i_HSN(): - global PC - - if not Ptr.ready(): - PC = (PC + 1) & WORDMASK - Trace.itrace('HSN') - return 1 + msg = 'Illegal instruction at address %6.6o' % (PC-1) + raise RuntimeError(msg) + + def page_00(self, indirect, address, instruction): + if (instruction & 0077700) == 000000: + return self.microcode(instruction) + elif (instruction & 0077000) == 002000: + return self.page02_decode.get(instruction, self.illegal)() + + return self.page_00_decode.get(instruction, self.illegal)(indirect, + address, + instruction) + + def i_LAW_LWC(self, indirect, address, instruction): + if indirect: + self.AC = ~address & WORDMASK + Trace.itrace('LWC', False, address) + else: + self.AC = address + Trace.itrace('LAW', False, address) + return 1 + + def i_JMP(self, indirect, address, instruction): + address = self.memory.eff_address(address, indirect) + self.PC = address & PCMASK + Trace.itrace('JMP', indirect, address) + return 3 if indirect else 2 + + def i_DAC(self, indirect, address, instruction): + address = self.memory.eff_address(address, indirect) + self.memory.put(self.AC, address, False) + Trace.itrace('DAC', indirect, address) + return 3 if indirect else 2 + + def i_XAM(self, indirect, address, instruction): + address = self.memory.eff_address(address, indirect) + tmp = self.memory.fetch(address, False) + self.memory.put(self.AC, address, False) + self.AC = tmp + Trace.itrace('XAM', indirect, address) + return 3 if indirect else 2 + + def i_ISZ(self, indirect, address, instruction): + address = self.memory.eff_address(address, indirect) + value = (self.memory.fetch(address, False) + 1) & WORDMASK + self.memory.put(value, address, False) + if value == 0: + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('ISZ', indirect, address) + return 3 if indirect else 2 + + def i_JMS(self, indirect, address, instruction): + address = self.memory.eff_address(address, indirect) + self.memory.put(self.PC, address, False) + self.PC = (address + 1) & PCMASK + Trace.itrace('JMS', indirect, address) + return 3 if indirect else 2 + + def i_AND(self, indirect, address, instruction): + self.AC &= self.memory.fetch(address, indirect) + Trace.itrace('AND', indirect, address) + return 3 if indirect else 2 + + def i_IOR(self, indirect, address, instruction): + self.AC |= self.memory.fetch(address, indirect) + Trace.itrace('IOR', indirect, address) + return 3 if indirect else 2 + + def i_XOR(self, indirect, address, instruction): + self.AC ^= self.memory.fetch(address, indirect) + Trace.itrace('XOR', indirect, address) + return 3 if indirect else 2 + + def i_LAC(self, indirect, address, instruction): + self.AC = self.memory.fetch(address, indirect) + Trace.itrace('LAC', indirect, address) + return 3 if indirect else 2 + + def i_ADD(self, indirect, address, instruction): + self.AC += self.memory.fetch(self.BLOCKADDR(address), indirect) + if self.AC & OVERFLOWMASK: + self.L = (~self.L) & 01 + self.AC &= WORDMASK + Trace.itrace('ADD', indirect, address) + return 3 if indirect else 2 + + def i_SUB(self, indirect, address, instruction): + addit = self.memory.fetch(self.BLOCKADDR(address), indirect) + addit = (~addit + 1) & WORDMASK + self.AC += addit + if self.AC & OVERFLOWMASK: + self.L = ~self.L + self.AC &= WORDMASK + Trace.itrace('SUB', indirect, address) + return 3 if indirect else 2 + + def i_SAM(self, indirect, address, instruction): + samaddr = self.BLOCKADDR(address) + if self.AC == self.memory.fetch(samaddr, indirect): + self.PC = (self.PC + 1) & PCMASK + Trace.itrace('SAM', indirect, address) + return 3 if indirect else 2 + + def microcode(self, instruction): + # T1 + if instruction & 001: + self.AC = 0 + if instruction & 010: + self.L = 0 + + # T2 + if instruction & 002: + self.AC = (~self.AC) & WORDMASK + if instruction & 020: + self.L = (~self.L) & 01 + + # T3 + if instruction & 004: + self.AC += 1 + if self.AC & OVERFLOWMASK: + self.L = (~self.L) & 1 + self.AC &= WORDMASK + if instruction & 040: + self.AC |= self.DS + + # do some sort of trace + combine = [] + opcode = self.micro_opcodes.get(instruction, None) + if opcode: + combine.append(opcode) + + if not combine: + # nothing so far, we have HLT or unknown microcode + if not instruction & 0100000: + # bit 0 is clear, it's HLT + self.running = False + combine.append('HLT') + else: + for (k, op) in self.micro_singles.items(): + if instruction & k: + combine.append(op) + + Trace.itrace('+'.join(combine), False) + return 1 + + def i_DLA(self, indirect, address, instruction): + self.displaycpu.DPC = self.AC + Trace.itrace('DLA') + return 1 + + def i_CTB(self, indirect, address, instruction): + Trace.itrace('CTB') + return 1 + + def i_DOF(self, indirect, address, instruction): + self.displaycpu.stop() + Trace.itrace('DOF') + return 1 + + def i_KRB(self, indirect, address, instruction): + self.AC |= self.kbd.read() + Trace.itrace('KRB') + return 1 + + def i_KCF(self, indirect, address, instruction): + self.kbd.clear() + Trace.itrace('KCF') + return 1 + + def i_KRC(self, indirect, address, instruction): + self.AC |= self.kbd.read() + self.kbd.clear() + Trace.itrace('KRC') + return 1 + + def i_RRB(self, indirect, address, instruction): + self.AC |= self.ttyin.read() + Trace.itrace('RRB') + return 1 + + def i_RCF(self, indirect, address, instruction): + self.ttyin.clear() + Trace.itrace('RCF') + return 1 + + def i_RRC(self, indirect, address, instruction): + self.AC |= self.ttyin.read() + self.ttyin.clear() + Trace.itrace('RRC') + return 1 + + def i_TPR(self, indirect, address, instruction): + self.ttyout.write(self.AC & 0xff) + Trace.itrace('TPR') + return 1 + + def i_TCF(self, indirect, address, instruction): + self.ttyout.clear() + Trace.itrace('TCF') + return 1 + + def i_TPC(self, indirect, address, instruction): + self.ttyout.write(self.AC & 0xff) + self.ttyout.clear() + Trace.itrace('TPC') + return 1 + + def i_HRB(self, indirect, address, instruction): + self.AC |= self.ptr.read() + Trace.itrace('HRB') + return 1 + + def i_HOF(self, indirect, address, instruction): + self.ptr.stop() + Trace.itrace('HOF') + return 1 + + def i_HON(self, indirect, address, instruction): + self.ptr.start() + Trace.itrace('HON') + return 1 + + def i_STB(self, indirect, address, instruction): + Trace.itrace('STB') + return 1 + + def i_SCF(self, indirect, address, instruction): + self.Sync40Hz = 0 + Trace.itrace('SCF') + return 1 + + def i_IOS(self, indirect, address, instruction): + Trace.itrace('IOS') + return 1 + + def i_IOT101(self, indirect, address, instruction): + Trace.itrace('IOT101') + return 1 + + def i_IOT111(self, indirect, address, instruction): + Trace.itrace('IOT111') + return 1 + + def i_IOT131(self, indirect, address, instruction): + Trace.itrace('IOT131') + return 1 + + def i_IOT132(self, indirect, address, instruction): + Trace.itrace('IOT132') + return 1 + + def i_IOT134(self, indirect, address, instruction): + Trace.itrace('IOT134') + return 1 + + def i_IOT141(self, indirect, address, instruction): + Trace.itrace('IOT141') + return 1 + + def i_IOF(self, indirect, address, instruction): + Trace.itrace('IOF') + return 1 + + def i_ION(self, indirect, address, instruction): + Trace.itrace('ION') + return 1 + + def i_PUN(self, indirect, address, instruction): + self.ptp.write(self.PC & 0xff) + Trace.itrace('PUN') + return 1 + + def i_PSF(self, indirect, address, instruction): + if self.ptp.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('PSF') + return 1 + + def i_RAL1(self, indirect, address, instruction): + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAL', False, 1) + return 1 + + def i_RAL2(self, indirect, address, instruction): + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAL', False, 2) + return 1 + + def i_RAL3(self, indirect, address, instruction): + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC >> 15 + newac = (self.AC << 1) | self.L + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAL', False, 3) + return 1 + + def i_RAR1(self, indirect, address, instruction): + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAR', False, 1) + return 1 + + def i_RAR2(self, indirect, address, instruction): + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAR', False, 2) + return 1 + + def i_RAR3(self, indirect, address, instruction): + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + newl = self.AC & 1 + newac = (self.AC >> 1) | (self.L << 15) + self.L = newl + self.AC = newac & WORDMASK + Trace.itrace('RAR', False, 3) + return 1 + + def i_SAL1(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + value = self.AC & 037777 + self.AC = (value << 1) | high_bit + Trace.itrace('SAL', False, 1) + return 1 + + def i_SAL2(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + value = self.AC & 017777 + self.AC = (value << 2) | high_bit + Trace.itrace('SAL', False, 2) + return 1 + + def i_SAL3(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + value = self.AC & 007777 + self.AC = (value << 3) | high_bit + Trace.itrace('SAL', False, 3) + return 1 + + def i_SAR1(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + self.AC = (self.AC >> 1) | high_bit + Trace.itrace('SAR', False, 1) + return 1 + + def i_SAR2(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + self.AC = (self.AC >> 1) | high_bit + self.AC = (self.AC >> 1) | high_bit + Trace.itrace('SAR', False, 2) + return 1 + + def i_SAR3(self, indirect, address, instruction): + high_bit = self.AC & HIGHBITMASK + self.AC = (self.AC >> 1) | high_bit + self.AC = (self.AC >> 1) | high_bit + self.AC = (self.AC >> 1) | high_bit + Trace.itrace('SAR', False, 3) + return 1 + + def i_DON(self, indirect, address, instruction): + self.displaycpu.DRSindex = 0 + self.displaycpu.start() + Trace.itrace('DON') + return 1 + + def i_ASZ(self): + if self.AC == 0: + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('ASZ') + return 1 + + def i_ASN(self): + if self.AC != 0: + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('ASN') + return 1 + + def i_ASP(self): + if not (self.AC & HIGHBITMASK): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('ASP') + return 1 + + def i_ASM(self): + if (self.AC & HIGHBITMASK): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('ASM') + return 1 + + def i_LSZ(self): + if self.L == 0: + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('LSZ') + return 1 + + def i_LSN(self): + if self.L != 0: + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('LSN') + return 1 + + def i_DSF(self): + if self.displaycpu.ison(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('DSF') + return 1 + + def i_DSN(self): + if not self.displaycpu.ison(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('DSN') + return 1 + + def i_KSF(self): + if self.kbd.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('KSF') + return 1 + + def i_KSN(self): + if not self.kbd.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('KSN') + return 1 + + def i_RSF(self): + if self.ttyin.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('RSF') + return 1 + + def i_RSN(self): + if not self.ttyin.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('RSN') + return 1 + + def i_TSF(self): + if self.ttyout.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('TSF') + return 1 + + def i_TSN(self): + if not self.ttyout.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('TSN') + return 1 + + def i_SSF(self): + if self.display.ready(): # skip if 40Hz sync on + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('SSF') + return 1 + + def i_SSN(self): + if not self.display.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('SSN') + return 1 + + def i_HSF(self): + if self.ptr.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('HSF') + return 1 + + def i_HSN(self): + if not self.ptr.ready(): + self.PC = (self.PC + 1) & WORDMASK + Trace.itrace('HSN') + return 1 diff --git a/pymlac/test_CPU.py b/pymlac/test_CPU.py index 8ded335..4cd079f 100644 --- a/pymlac/test_CPU.py +++ b/pymlac/test_CPU.py @@ -14,7 +14,7 @@ where is a file of test instructions and # We implement a small interpreter to test the CPU. The test code is read in # from a file: # -# # LAW +# # 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; setmem 0100 [LAW 0]; RUN 0100 # checkcycles 1; checkreg pc 0101; checkreg ac 0 @@ -70,423 +70,387 @@ import Memory import log log = log.Log('test_CPU.log', log.Log.DEBUG) -# fake a Display instance -DisplayState = False + +class TestCPU(object): + + # temporary assembler file and listfile prefix + AsmFilename = '_#ASM#_' -# number of cycles used in previous instruction -UsedCycles = 0 + def __init__(self): + """Initialize the test.""" -# register+value explicitly set by test -RegValues = {} + pass -# memory address+value explicitly set by test -MemValues = {} + def assemble(self, addr, opcode): + """Assemble a single instruction, return opcode.""" -# memory value all explicitly set to (shouldn't change) -MemAllValue = None + # create ASM file with instruction + with open(self.AsmFilename+'.asm', 'wb') as fd: + fd.write('\torg\t%07o\n' % addr) + fd.write('\t%s\n' % opcode[1:-1]) + fd.write('\tend\n') -# registers value all explicitly set to (shouldn't change) -RegAllValue = None + # now assemble file + os.system('../iasm/iasm -l %s.lst %s.asm' % (self.AsmFilename, self.AsmFilename)) -# temporary assembler file and listfile prefix -AsmFilename = '_#ASM#_' + # read the listing file to get assembled opcode (second line) + with open(self.AsmFilename+'.lst', 'rb') as fd: + lines = fd.readlines() + line = lines[1] + (opcode, _) = line.split(None, 1) + + return int(opcode, base=8) -def assemble(addr, opcode): - """Assemble a single instruction, return opcode.""" + def setreg(self, name, value): + """Set register to a value. - # 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') + Remember value to check later. + """ - # now assemble file - os.system('../iasm/iasm -l %s.lst %s.asm' % (AsmFilename, AsmFilename)) + self.reg_values[name] = value - # read the listing file to get assembled opcode (second line) - with open(AsmFilename+'.lst', 'rb') as fd: - lines = fd.readlines() - line = lines[1] - (opcode, _) = line.split(None, 1) - - return int(opcode, base=8) - - -def setreg(name, value): - """Set register to a value. - - Remember value to check later. - """ - - global RegValues - - RegValues[name] = value - - if name == 'ac': - MainCPU.AC = value - elif name == 'l': - MainCPU.L = value & 1 - elif name == 'pc': - MainCPU.PC = value - elif name == 'ds': - MainCPU.DS = value - else: - raise Exception('setreg: bad register name: %s' % name) - -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 - - # 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) - - 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): - """Set all of memory to a value. - - Remember value to check later. - """ - - global MemAllValue - - log.debug('allmem: setting memory to %07o' % value) - - MemAllValue = value - - for mem in range(MEMORY_SIZE): - Memory.put(value, mem, False) - -def allreg(value): - """Set all registers to a value.""" - - global RegAllValue - - RegAllValue = value - - MainCPU.AC = value - MainCPU.L = value & 1 - MainCPU.PC = value - MainCPU.DS = value - -def check_all_mem(): - """Check memory for unwanted changes.""" - - result = [] - - for mem in range(MEMORY_SIZE): - value = Memory.fetch(mem, False) - if mem in MemValues: - if value != MemValues[mem]: - result.append('Memory at %07o changed, is %07o, should be %07o' - % (mem, value, MemValues[mem])) + if name == 'ac': + self.cpu.AC = value + elif name == 'l': + self.cpu.L = value & 1 + elif name == 'pc': + self.cpu.PC = value + elif name == 'ds': + self.cpu.DS = value else: - if value != MemAllValue: - result.append('Memory at %07o changed, is %07o, should be %07o' - % (mem, value, MemAllValue)) + raise Exception('setreg: bad register name: %s' % name) -def check_all_regs(): - """Check registers for unwanted changes.""" + def setmem(self, addr, value): + """Set memory location to a value.""" - result = [] - - if 'ac' in RegValues: - if MainCPU.AC != RegValues['ac']: - result.append('AC changed, is %07o, should be %07o' - % (MainCPU.AC, RegValues['ac'])) - else: - if MainCPU.AC != RegAllValue: - result.append('AC changed, is %07o, should be %07o' - % (MainCPU.AC, RegAllValue)) - - if 'l' in RegValues: - if MainCPU.L != RegValues['l']: - result.append('L changed, is %02o, should be %02o' - % (MainCPU.L, RegValues['l'])) - else: - if MainCPU.L != RegAllValue & 1: - result.append('L changed, is %02o, should be %02o' - % (MainCPU.L, RegAllValue & 1)) - - if 'pc' in RegValues: - if MainCPU.PC != RegValues['pc']: - result.append('PC changed, is %07o, should be %07o' - % (MainCPU.PC, RegValues['pc'])) - else: - if MainCPU.PC != RegAllValue: - result.append('PC changed, is %07o, should be %07o' - % (MainCPU.PC, RegAllValue)) - - if 'ds' in RegValues: - if MainCPU.DS != RegValues['ds']: - result.append('DS changed, is %07o, should be %07o' - % (MainCPU.DS, RegValues['ds'])) - else: - if MainCPU.DS != RegAllValue: - result.append('DS changed, is %07o, should be %07o' - % (MainCPU.DS, RegAllValue)) - - return result - -def checkreg(reg, value): - """Check register is as it should be.""" - - global RegValues - - if reg == 'ac': - RegValues[reg] = MainCPU.AC - if MainCPU.AC != value: - return 'AC wrong, is %07o, should be %07o' % (MainCPU.AC, value) - elif reg == 'l': - RegValues[reg] = MainCPU.L - if MainCPU.L != value: - return 'L wrong, is %02o, should be %02o' % (MainCPU.L, value) - elif reg == 'pc': - RegValues[reg] = MainCPU.PC - if MainCPU.PC != value: - return 'PC wrong, is %07o, should be %07o' % (MainCPU.PC, value) - elif reg == 'ds': - RegValues[reg] = MainCPU.DS - if MainCPU.DS != value: - return 'DS wrong, is %07o, should be %07o' % (MainCPU.DS, 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 checkrun(state, var2): - """Check CPU run state is as desired.""" - - if str(MainCPU.running).lower() != state: - return 'CPU run state is %s, should be %s' % (str(MainCPU.running), str(state)) - -def setd(state, var2): - """Set display state.""" - - global DisplayState - - if state == 'on': - DisplayState = True - elif state == 'off': - DisplayState = False - else: - raise Exception('setd: bad state: %s' % str(state)) - -def checkd(state, var2): - """Check display state is as expected.""" - - global DisplayState - - if state == 'on' and DisplayState is not True: - return 'DCPU run state is %s, should be True' % str(DisplayState) - if state == 'off' and DisplayState is True: - return 'DCPU run state is %s, should be False' % str(DisplayState) - - -def debug_operation(op, var1, var2): - """Write operation to log file.""" - - if var1: - if var2: - log.debug('Operation: %s %s %s' % (op, var1, var2)) + if isinstance(value, basestring): + log.debug('setmem: addr=%s, value=%s' % (oct(addr), value)) else: - log.debug('Operation: %s %s' % (op, var1)) - else: - log.debug('Operation: %s' % op) + log.debug('setmem: addr=%s, value=%s' % (oct(addr), oct(value))) -def execute(test): - """Execute test string in 'test'.""" + # check if we must assemble var2 + if isinstance(value, basestring) and value[0] == '[': + # assemble an instruction + value = self.assemble(addr, value) + log.debug('setmem: assembled opcode=%07o' % value) - global RegValues, MemValues - global RegAllValue, MemAllValue - global DisplayState + self.mem_values[addr] = value + log.debug('setmem: After, MemValues=%s' % str(self.mem_values)) - # set globals - RegValues = {} - MemValues = {} - RegAllValue = {} - MemAllValue = {} + self.memory.put(value, addr, False) + log.debug('setmem: After, Memory at %07o is %07o' % (addr, self.memory.fetch(addr, False))) - result = [] + def allmem(self, value): + """Set all of memory to a value. - MainCPU.init() - MainCPU.running = True - DisplayState = False - Memory.init() + Remember value to check later. + """ - # clear memory and registers to 0 first - allmem(0) - allreg(0) + log.debug('allmem: setting memory to %07o' % value) - # interpret the test instructions - instructions = test.split(';') - for op in instructions: - fields = op.split(None, 2) - op = fields[0].lower() - try: - var1 = fields[1].lower() - except IndexError: - var1 = None - try: - var2 = fields[2].lower() - except IndexError: - var2 = None + self.mem_all_value = value - debug_operation(op, var1, var2) + for mem in range(MEMORY_SIZE): + self.memory.put(value, mem, False) - # change var strings into numeric values - if var1 and var1[0] in '0123456789': - if var1[0] == '0': - var1 = int(var1, base=8) + def allreg(self, value): + """Set all registers to a value.""" + + self.reg_all_value = value + + self.cpu.AC = value + self.cpu.L = value & 1 + self.cpu.PC = value + self.cpu.DS = value + + def check_all_mem(self): + """Check memory for unwanted changes.""" + + result = [] + + for mem in range(MEMORY_SIZE): + value = self.memory.fetch(mem, False) + if mem in self.mem_values: + if value != self.mem_values[mem]: + result.append('Memory at %07o changed, is %07o, should be %07o' + % (mem, value, self.mem_values[mem])) else: - var1 = int(var1) - var1 &= 0177777 + if value != self.mem_all_value: + result.append('Memory at %07o changed, is %07o, should be %07o' + % (mem, value, self.mem_all_value)) - if var2 and var2[0] in '0123456789': - if var2[0] == '0': - var2 = int(var2, base=8) - else: - var2 = int(var2) - var2 &= 0177777 + def check_all_regs(self): + """Check registers for unwanted changes.""" - 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) - elif op == 'checkrun': - r = checkrun(var1, var2) - elif op == 'setd': - r = setd(var1, var2) - elif op == 'checkd': - r = checkd(var1, var2) + result = [] + + if 'ac' in self.reg_values: + if self.cpu.AC != self.reg_values['ac']: + result.append('AC changed, is %07o, should be %07o' + % (self.cpu.AC, self.reg_values['ac'])) else: - raise Exception("Unrecognized operation '%s' in: %s" % (op, test)) + if self.cpu.AC != self.reg_all_value: + result.append('AC changed, is %07o, should be %07o' + % (self.cpu.AC, self.reg_all_value)) - if r is not None: + if 'l' in self.reg_values: + if self.cpu.L != self.reg_values['l']: + result.append('L changed, is %02o, should be %02o' + % (self.cpu.L, self.reg_values['l'])) + else: + if self.cpu.L != self.reg_all_value & 1: + result.append('L changed, is %02o, should be %02o' + % (self.cpu.L, self.reg_all_value & 1)) + + if 'pc' in self.reg_values: + if self.cpu.PC != self.reg_values['pc']: + result.append('PC changed, is %07o, should be %07o' + % (self.cpu.PC, self.reg_values['pc'])) + else: + if self.cpu.PC != self.reg_all_value: + result.append('PC changed, is %07o, should be %07o' + % (self.cpu.PC, self.reg_all_value)) + + if 'ds' in self.reg_values: + if self.cpu.DS != self.reg_values['ds']: + result.append('DS changed, is %07o, should be %07o' + % (self.cpu.DS, self.reg_values['ds'])) + else: + if self.cpu.DS != self.reg_all_value: + result.append('DS changed, is %07o, should be %07o' + % (self.cpu.DS, self.reg_all_value)) + + return result + + def checkreg(self, reg, value): + """Check register is as it should be.""" + + if reg == 'ac': + self.reg_values[reg] = self.cpu.AC + if self.cpu.AC != value: + return 'AC wrong, is %07o, should be %07o' % (self.cpu.AC, value) + elif reg == 'l': + self.reg_values[reg] = self.cpu.L + if self.cpu.L != value: + return 'L wrong, is %02o, should be %02o' % (self.cpu.L, value) + elif reg == 'pc': + self.reg_values[reg] = self.cpu.PC + if self.cpu.PC != value: + return 'PC wrong, is %07o, should be %07o' % (self.cpu.PC, value) + elif reg == 'ds': + self.reg_values[reg] = self.cpu.DS + if self.cpu.DS != value: + return 'DS wrong, is %07o, should be %07o' % (self.cpu.DS, value) + else: + raise Exception('checkreg: bad register name: %s' % name) + + def checkmem(self, addr, value): + """Check a memory location is as it should be.""" + + self.mem_values[addr] = value + log.debug('checkmem: After, MemValues=%s' % str(self.mem_values)) + + memvalue = self.memory.fetch(addr, False) + if memvalue != value: + return 'Memory wrong at address %07o, is %07o, should be %07o' % (addr, memvalue, value) + + def checkcycles(self, cycles, var2=None): + """Check that opcode cycles used is correct.""" + + if cycles != self.used_cycles: + return 'Opcode used %d cycles, expected %d' % (self.used_cycles, cycles) + + def run(self, addr, var2): + """Execute instruction.""" + + if addr is not None: + # force PC to given address + self.setreg('pc', addr) + + self.used_cycles = self.cpu.execute_one_instruction() + + def checkrun(self, state, var2): + """Check CPU run state is as desired.""" + + if str(self.cpu.running).lower() != state: + return 'CPU run state is %s, should be %s' % (str(self.cpu.running), str(state)) + + def setd(self, state, var2): + """Set display state.""" + + if state == 'on': + self.display_state = True + elif state == 'off': + self.display_state = False + else: + raise Exception('setd: bad state: %s' % str(state)) + + def checkd(self, state, var2): + """Check display state is as expected.""" + + if state == 'on' and self.display_state is not True: + return 'DCPU run state is %s, should be True' % str(self.display_state) + if state == 'off' and self.display_state is True: + return 'DCPU run state is %s, should be False' % str(self.display_state) + + def debug_operation(self, 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(self, test): + """Execute test string in 'test'.""" + + # set globals + self.reg_values = {} + self.mem_values = {} + self.reg_all_value = {} + self.mem_all_value = {} + + result = [] + + self.memory = Memory.Memory() + #self.cpu = MainCPU.MainCPU(memory, display, displaycpu, kbd, ttyin, ttyout, ptp, ptr) + self.cpu = MainCPU.MainCPU(self.memory, None, None, None, None, None, None, None) + self.cpu.running = True + self.display_state = False + + # clear memory and registers to 0 first + self.allmem(0) + self.allreg(0) + + # interpret the test instructions + instructions = test.split(';') + for op in instructions: + fields = op.split(None, 2) + op = fields[0].lower() + try: + var1 = fields[1].lower() + except IndexError: + var1 = None + try: + var2 = fields[2].lower() + except IndexError: + var2 = None + + self.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: + var1 = int(var1) + var1 &= 0177777 + + if var2 and var2[0] in '0123456789': + if var2[0] == '0': + var2 = int(var2, base=8) + else: + var2 = int(var2) + var2 &= 0177777 + + if op == 'setreg': + r = self.setreg(var1, var2) + elif op == 'setmem': + r = self.setmem(var1, var2) + elif op == 'run': + r = self.run(var1, var2) + elif op == 'checkcycles': + r = self.checkcycles(var1, var2) + elif op == 'checkreg': + r = self.checkreg(var1, var2) + elif op == 'checkmem': + r = self.checkmem(var1, var2) + elif op == 'allreg': + r = self.allreg(var1, var2) + elif op == 'allmem': + r = self.allmem(var1, var2) + elif op == 'checkrun': + r = self.checkrun(var1, var2) + elif op == 'setd': + r = self.setd(var1, var2) + elif op == 'checkd': + r = self.checkd(var1, var2) + else: + raise Exception("Unrecognized operation '%s' in: %s" % (op, test)) + + if r is not None: + result.append(r) + + # now check all memory and regs for changes + r = self.check_all_mem() + if r: result.append(r) + r = self.check_all_regs() + if r: + result.extend(r) - # now check all memory and regs for changes - r = check_all_mem() - if r: - result.append(r) + if result: + print(test) + print('\t' + '\n\t'.join(result)) - r = check_all_regs() - if r: - result.extend(r) + self.memdump('core.txt', 0, 0200) - if result: - print(test) - print('\t' + '\n\t'.join(result)) + def memdump(self, filename, start, number): + """Dump memory from 'start' into 'filename', 'number' words dumped.""" - memdump('core.txt', 0, 0200) + 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 ' % self.memory.fetch(a, False) + a += 1 + fd.write('%s\n' % line) -def memdump(filename, start, number): - """Dump memory from 'start' into 'filename', 'number' words dumped.""" + def main(self, filename): + """Execute CPU tests from 'filename'.""" - 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) + log.debug("Running test file '%s'" % filename) -def main(filename): - """Execute CPU tests from 'filename'.""" + # get all tests from file + with open(filename, 'rb') as fd: + lines = fd.readlines() - log.debug("Running test file '%s'" % filename) + # read lines, join continued, get complete tests + tests = [] + test = '' + for line in lines: + line = line[:-1] # strip newline + if not line: + continue # skip blank lines - # get all tests from file - with open(filename, 'rb') as fd: - lines = fd.readlines() + if line[0] == '#': # a comment + continue - # read lines, join continued, get complete tests - tests = [] - test = '' - for line in lines: - line = line[:-1] # strip newline - if not line: - continue # skip blank lines + if line[0] == '\t': # continuation + if test: + test += '; ' + test += line[1:] + else: # beginning of new test + if test: + tests.append(test) + test = line - if line[0] == '#': # a comment - continue - - if line[0] == '\t': # continuation - if test: - test += '; ' - test += line[1:] - else: # beginning of new test - if test: - tests.append(test) - test = line - - # flush last test - if test: - tests.append(test) - - # now do each test - for test in tests: - log.debug('Executing test: %s' % test) - execute(test) + # flush last test + if test: + tests.append(test) + # now do each test + for test in tests: + log.debug('Executing test: %s' % test) + self.execute(test) ################################################################################ @@ -524,4 +488,5 @@ if __name__ == '__main__': sys.exit(10) f.close() - main(filename) + test = TestCPU() + test.main(filename)