328 lines
9.8 KiB
Python
328 lines
9.8 KiB
Python
# 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()
|