# TO USE KEYPAD TO MANIPULATE DATA: # # A button: run/halt # B button: when halted, toggles address/data entry # Right button: when halted, single-steps # # Address entry: press the two digits for the address. It is entered immediately (there's no "enter" key) # Data entry: likewise. After you press the second digit, it will automatically go to the next address. import time import board import displayio import vectorio import terminalio # for font from adafruit_display_text import label import keypad from digitalio import DigitalInOut, Direction, Pull class TwoDigitHexInput: def __init__(self): self.digits = [0x0, 0x0] self.currentDigit = 0 self.value = 0 def input(self, d): self.digits[self.currentDigit] = d self.value = (self.digits[0] * 16) + self.digits[1] print("INPUT", self.digits) self.currentDigit = 0 if self.currentDigit else 1 def clear(self): self.__init__() print(self.digits) class CPU: def __init__(self): self.running = False self.IP = 254 self.acc = 0 self.flags = { 'C': False, 'Z': False, 'N': False, 'Eq': False } self.instruction = { 'opcode': False, 'operand': False } self.memory = False def load_memory(self, bytes): self.memory = bytes + bytearray(256 - len(bytes)) print(type(self.memory)) print('mem 254', self.memory[254]) # print(self.memory) def start(self): self.running = True def step(self): if self.IP >= 256: self.IP = 0 print("IP:", self.IP) self.instruction['opcode'] = self.memory[self.IP] self.IP = self.IP+1 self.instruction['operand'] = self.memory[self.IP] self.IP = self.IP+1 self.nums2mnems[self.instruction['opcode']](self, self.instruction['operand']) print("instr:", self.instruction['opcode'], self.instruction['operand']) print("mnem:", self.nums2mnems[self.instruction['opcode']]) print("acc:", self.acc) print("running:", self.running) print() # self.print_screen() print("byte 26 (keyboard):", self.memory[26]) print() def hlt(self, operand): self.running = False def nop(self, operand): pass def lda_lit(self, operand): self.acc = operand def lda_mem(self, operand): self.acc = memory[operand] def sta_lit(self, operand): memory[operand] = self.acc def sta_mem(self, operand): memory[memory[operand]] = self.acc def add_lit(self, operand): self.acc = self.acc + operand if self.acc > 255: self.acc = self.acc % 256 self.flags['C'] = True else: self.flags['C'] = False self.flags['Z'] = True if self.acc == 0 else False self.flags['Eq'] = True if self.acc == operand else False self.flags['N'] = True if self.acc > 127 else False def add_mem(self, operand): self.acc = self.acc + self.memory[operand] if self.acc > 255: self.acc = self.acc % 256 self.flags['C'] = True else: self.flags['C'] = False self.flags['Z'] = True if self.acc == 0 else False self.flags['Eq'] = True if self.acc == operand else False self.flags['N'] = True if self.acc > 127 else False def sub_lit(self, operand): self.acc = self.acc - operand if self.acc < 0: self.acc = self.acc % 256 self.flags['C'] = True else: self.flags['C'] = False self.flags['Z'] = True if self.acc == 0 else False self.flags['Eq'] = True if self.acc == operand else False self.flags['N'] = True if self.acc > 127 else False def sub_mem(self, operand): self.acc = self.acc - self.memory[operand] if self.acc > 255: self.acc = self.acc % 256 self.flags['C'] = True else: self.flags['C'] = False self.flags['Z'] = True if self.acc == 0 else False self.flags['Eq'] = True if self.acc == operand else False self.flags['N'] = True if self.acc > 127 else False def jmp_lit(self, operand): self.IP = operand def jmp_mem(self, operand): self.IP = memory[operand] def ske(self, operand): if self.flags['Eq']: self.IP += 2 def skz(self, operand): if self.flags['Z']: self.IP += 2 def skn(self, operand): if self.flags['N']: self.IP += 2 def skc(self, operand): if self.flags['C']: self.IP += 2 def cst(self, operand): self.flags['C'] = True def ccl(self, operand): self.flags['C'] = False nums2mnems = { 0: hlt, 1: nop, 2: lda_lit, 3: sta_lit, 4: add_lit, 5: sub_lit, 6: jmp_lit, 7: ske, 8: skz, 9: skn, 10: skc, 11: cst, 12: ccl, 16: hlt, 17: nop, 18: lda_mem, 19: sta_mem, 20: add_mem, 21: sub_mem, 22: jmp_mem, 23: ske, 24: skz, 25: skn, 26: skc, 27: cst, 28: ccl, } ### MEOWBIT-SPECIFIC STUFF ### # to list board features: print(board.__dir__) btna = DigitalInOut(board.BTNA) btna.direction = Direction.INPUT btna.pull = Pull.UP # down doesn't work btnb = DigitalInOut(board.BTNB) btnb.direction = Direction.INPUT btnb.pull = Pull.UP # down doesn't work btnr = DigitalInOut(board.RIGHT) btnr.direction = Direction.INPUT btnr.pull = Pull.UP # down doesn't work km = keypad.KeyMatrix( row_pins = (board.P0, board.P1, board.P2, board.P3), column_pins = (board.P4, board.P6, board.P8, board.P9) ) # This is global because that way you can update the text by just altering text_area.text displayGroup = displayio.Group() board.DISPLAY.root_group = displayGroup text_area = label.Label(terminalio.FONT, text="") text_area.x = 10 text_area.y = 10 displayGroup.append(text_area) palette = displayio.Palette(1) palette[0] = 0xff00ff class Monitor: def __init__(self, cpu): self.cpu = cpu self.monitorMode = 'addressEntry' # or dataEntry self.monitorAddressInput = TwoDigitHexInput() self.monitorDataInput = TwoDigitHexInput() def handleKeys(self): keypad_event = km.events.get() keyPressed = True if (keypad_event and keypad_event.released) else False key = keypad_event.key_number if keyPressed else False if self.cpu.running: if btna.value == False: print("HALT PRESSED") self.cpu.running = False time.sleep(0.5) # lazy debounce # km.events.clear() # don't track keypresses from during the run if keyPressed: self.cpu.memory[26] = key elif not self.cpu.running: if btna.value == False: self.cpu.running = True print("\nSTARTING") time.sleep(0.5) # lazy debounce if btnb.value == False: self.monitorMode = 'addressEntry' if self.monitorMode != 'addressEntry' else 'dataEntry' print("\nENTERING", self.monitorMode, "MODE") self.monitorDataInput.currentDigit = 0 self.monitorAddressInput.currentDigit = 0 time.sleep(0.5) # lazy debounce if btnr.value == False: print("\nSINGLE STEP FROM MONITOR ADDR") # self.IP = self.monitorAddressInput.value self.cpu.step() time.sleep(0.5) # lazy debounce if keypad_event and keypad_event.released: if self.monitorMode == 'addressEntry': self.monitorAddressInput.input(keypad_event.key_number) self.cpu.IP = self.monitorAddressInput.value print("MA", self.IP) else: self.monitorDataInput.input(keypad_event.key_number) self.cpu.memory[self.IP] = self.monitorDataInput.value print("MD", self.monitorDataInput.value) if self.monitorDataInput.currentDigit == 0: # that was the second keypress, so go to the next addresss self.cpu.IP = (self.cpu.IP + 1) % 256 print("ADVANCING") print("Acc", self.cpu.acc, "IP", self.cpu.IP, "Data", self.cpu.memory[self.cpu.IP], "\n") def printMonitor(self): text = "IP " + str(self.cpu.IP) + "\tDATA " + str(self.cpu.memory[self.cpu.IP]) + "\tACC " + str(self.cpu.acc) + "\nRunning: " + str(self.cpu.running) text_area.text = text def printScreen(self): for i in range(5): for j in range(5): memory_index = (i * 5) + j if self.cpu.memory[memory_index] > 0: print("#", end=" ") circle = vectorio.Circle(pixel_shader=palette, radius=8, x=(10 + (j * 20)), y=(40 + (i * 20))) displayGroup.append(circle) else: print("_", end=" ") print() def run(self): self.cpu.start() t = time.time() while (time.time() - t) < 30: self.handleKeys() if self.cpu.running: self.cpu.step() self.printMonitor() # self.printScreen() time.sleep(0.5) print("timeout") print(self.cpu.memory) cpu = CPU() monitor = Monitor(cpu) prog = '04 FF 04 01 14 01 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01' #prog = '00' program_bytes = bytearray.fromhex(prog.replace(" ", "")) # Add jmp at addr 254: program_with_jump = program_bytes + bytearray(254 - len(program_bytes)) + bytearray.fromhex('0600') cpu.load_memory(program_with_jump) monitor.run()