ytd_12-bit_computer/emulator/main.py

374 lines
11 KiB
Python

# Kyler Olsen
# Feb 2024
MAX_INT = 0x1000
class Device:
_start: int
_end: int
def __init__(self, start: int, end: int):
self._start = start
self._end = end
def __contains__(self, value: int) -> bool:
return self._start <= value <= self._end
def __getitem__(self, index: int) -> int:
return 0
def __setitem__(self, index: int, value: int):
pass
class Memory:
_rom: list[int]
_devices: list[Device]
_ram: list[int]
def __init__(self, rom: list[int], devices: list[Device]) -> None:
self._rom = [0] * 0x700
self._devices = devices[:]
self._ram = [0] * 0x1000
for i, data in enumerate(rom):
self._rom[i] = data % MAX_INT
def _get_device(self, index: int) -> Device | None:
for device in self._devices:
if index in device:
return device
return None
def __getitem__(self, index: int) -> int:
if 0 <= index <= 0x6FF:
return self._rom[index]
elif 0x700 <= index <= 0x7FF:
device = self._get_device(index)
if device is not None:
return device[index]
else:
return 0
elif 0x800 <= index <= 0xFFF:
return self._ram[index - 0x1000]
else:
raise IndexError
def __setitem__(self, index: int, value: int):
if 0 <= index <= 0x6FF:
pass
elif 0x700 <= index <= 0x7FF:
device = self._get_device(index)
if device is not None:
device[index] = value % MAX_INT
elif 0x800 <= index <= 0xFFF:
self._ram[index - 0x1000] = value % MAX_INT
else:
raise IndexError
class Computer:
_mem: Memory
_running: bool
_halted: bool
_interrupt_flag: list[int]
_pc_last: int
_zero_flag: bool
_interruptable: bool
_pc: int
_sp: int
_pt: int
_d0: int
_d1: int
_d2: int
_d3: int
def __init__(self, mem: Memory):
self._mem = mem
self._running = True
self._halted = False
self._interrupt_flag = []
self._pc_last = 0
self._zero_flag = False
self._interruptable = False
self._pc = 0
self._sp = 0
self._pt = 0
self._d0 = 0
self._d1 = 0
self._d2 = 0
self._d3 = 0
@property
def running(self) -> bool: return self._running
@property
def halted(self) -> bool: return self._halted
@property
def active(self) -> bool: return self.running and self.halted
@property
def zero_flag(self) -> bool: return self._zero_flag
@property
def program_counter(self) -> int: return self._pc
@program_counter.setter
def program_counter(self, value: int):
self._pc_last = self._pc
self._pc = value % MAX_INT
@property
def stack_pointer(self) -> int: return self._sp
@stack_pointer.setter
def stack_pointer(self, value: int): self._sp = value % MAX_INT
@property
def pointer(self) -> int: return self._pt
@pointer.setter
def pointer(self, value: int): self._pt = value % MAX_INT
@property
def data_0(self) -> int: return self._d0
@data_0.setter
def data_0(self, value: int): self._d0 = value % MAX_INT
@property
def data_1(self) -> int: return self._d1
@data_1.setter
def data_1(self, value: int): self._d1 = value % MAX_INT
@property
def data_2(self) -> int: return self._d2
@data_2.setter
def data_2(self, value: int): self._d2 = value % MAX_INT
@property
def data_3(self) -> int: return self._d3
@data_3.setter
def data_3(self, value: int): self._d3 = value % MAX_INT
def get_reg(
self,
index: int,
*,
strict: bool = True,
) -> int:
if strict and not (0 <= index <= 7): raise IndexError
else: index %= 8
return self._get_reg(index)
def _get_reg(self, index: int) -> int:
if index == 1: return self._pc
elif index == 2: return self._sp
elif index == 3: return self._pt
elif index == 4: return self._d0
elif index == 5: return self._d1
elif index == 6: return self._d2
elif index == 7: return self._d3
else: return 0
def set_reg(
self,
index: int,
value: int,
*,
strict: bool = True,
):
if strict and not (0 <= index <= 7): raise IndexError
else: index %= 8
value %= MAX_INT
self._set_reg(index, value)
def _set_reg(self, index: int, value: int):
if index == 1: self._pc = value
elif index == 2: self._sp = value
elif index == 3: self._pt = value
elif index == 4: self._d0 = value
elif index == 5: self._d1 = value
elif index == 6: self._d2 = value
elif index == 7: self._d3 = value
def interrupt(self, index: int):
self._interrupt_flag.append(index % MAX_INT)
def step(self):
instruction = self._mem[self.program_counter]
if instruction == 0: self.NOP()
elif instruction == 1: self.HLT()
elif instruction == 2: self.INT()
elif instruction == 3: self.BNZ()
elif instruction == 4: self.BLK()
elif instruction == 5: self.ENB()
elif instruction & 0xFF8 == 0x10: self.GLA(instruction & 0x7)
elif instruction & 0xFF8 == 0x18: self.GET(instruction & 0x7)
elif instruction & 0xFF8 == 0x20: self.LOD(instruction & 0x7)
elif instruction & 0xFF8 == 0x28: self.STR(instruction & 0x7)
elif instruction & 0xFF8 == 0x30: self.PSH(instruction & 0x7)
elif instruction & 0xFF8 == 0x38: self.POP(instruction & 0x7)
elif instruction & 0xF80 == 0x80: self.LDI(instruction & 0x7F)
elif instruction & 0xFC0 == 0x100:
self.LSH(instruction & 0x38, instruction & 0x7)
elif instruction & 0xFC0 == 0x140:
self.RSH(instruction & 0x38, instruction & 0x7)
elif instruction & 0xFC0 == 0x180:
self.INC(instruction & 0x38, instruction & 0x7)
elif instruction & 0xFC0 == 0x1C0:
self.DEC(instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0x200:
self.AND(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0x400:
self.OR(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0x600:
self.NAD(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0x800:
self.SUB(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0xA00:
self.XOR(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0xC00:
self.NOR(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
elif instruction & 0xE0 == 0xE00:
self.ADD(instruction & 0x1C, instruction & 0x38, instruction & 0x7)
else: raise LookupError()
# === Operations ===
def NOP(self):
self.program_counter += 1
def HLT(self):
self._halted = True
self.program_counter += 1
def INT(self):
self.interrupt(self.pointer)
self.program_counter += 1
def BNZ(self):
if self.zero_flag:
self.program_counter = self.pointer
self.program_counter += 1
def BLK(self):
self._interruptable = False
self.program_counter += 1
def ENB(self):
self._interruptable = True
self.program_counter += 1
def GLA(self, REG: int):
self.set_reg(REG, self._pc_last)
self.program_counter += 1
def GET(self, REG: int):
if self._interrupt_flag: self.set_reg(REG, self._interrupt_flag.pop())
else: self.set_reg(REG, 0)
self.program_counter += 1
def LOD(self, REG: int):
self.set_reg(REG, self._mem[self.pointer])
self.program_counter += 1
def STR(self, REG: int):
self._mem[self.pointer] = self.get_reg(REG)
self.program_counter += 1
def PSH(self, REG: int):
self.set_reg(REG, self._mem[self.stack_pointer])
self.program_counter += 1
def POP(self, REG: int):
self._mem[self.stack_pointer] = self.get_reg(REG)
self.program_counter += 1
def LDI(self, Immediate: int):
self.pointer = Immediate % 0x3F
self.program_counter += 1
def LSH(self, REG_D: int, REG_A: int):
result = self.get_reg(REG_A) << 1
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def RSH(self, REG_D: int, REG_A: int):
result = self.get_reg(REG_A) >> 1
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def INC(self, REG_D: int, REG_A: int):
result = self.get_reg(REG_A) + 1
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def DEC(self, REG_D: int, REG_A: int):
result = self.get_reg(REG_A) - 1
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def AND(self, REG_D: int, REG_A: int, REG_B: int):
result = self.get_reg(REG_A) & self.get_reg(REG_B)
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def OR(self, REG_D: int, REG_A: int, REG_B: int):
result = self.get_reg(REG_A) | self.get_reg(REG_B)
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def NAD(self, REG_D: int, REG_A: int, REG_B: int):
result = (MAX_INT - 1) ^ (self.get_reg(REG_A) & self.get_reg(REG_B))
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def SUB(self, REG_D: int, REG_A: int, REG_B: int):
result = self.get_reg(REG_A) + ((MAX_INT - 1) ^ self.get_reg(REG_B)) + 1
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def XOR(self, REG_D: int, REG_A: int, REG_B: int):
result = self.get_reg(REG_A) ^ self.get_reg(REG_B)
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def NOR(self, REG_D: int, REG_A: int, REG_B: int):
result = (MAX_INT - 1) ^ (self.get_reg(REG_A) | self.get_reg(REG_B))
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1
def ADD(self, REG_D: int, REG_A: int, REG_B: int):
result = self.get_reg(REG_A) + self.get_reg(REG_B)
result %= MAX_INT
self._zero_flag = result == 0
self.set_reg(REG_D, result)
self.program_counter += 1