769 lines
26 KiB
Python
769 lines
26 KiB
Python
import pygame, time, os
|
|
|
|
class CPU:
|
|
def __init__(self, memory_size=1024):
|
|
stack_size = 32
|
|
self.memory = [0] * memory_size # Fixed-size memory
|
|
self.stack = [0] * stack_size
|
|
self.SP = len(self.stack) # Stack grows downward
|
|
self.PC = 0x00 # Program Counter
|
|
self.A = 0 # Register A
|
|
self.B = 0 # Register B
|
|
self.C = 0 # Register C
|
|
self.D = 0 # Register D
|
|
self.E = 0 # Register E
|
|
self.F = 0 # Register F
|
|
|
|
self.running = True
|
|
|
|
self.cycles = 0
|
|
|
|
# Initialize pygame screen for 256x256 resolution
|
|
self.V_res = -1
|
|
self.H_res = -1
|
|
self.screen = None
|
|
|
|
self.keydown = False
|
|
self.last_key = None
|
|
|
|
|
|
self.text_buffer = None # Space and white color
|
|
|
|
|
|
|
|
|
|
def state(self):
|
|
# Print Registers in Hex Format
|
|
print("Registers:")
|
|
print(f" A: 0x{self.A:02X}")
|
|
print(f" B: 0x{self.B:02X}")
|
|
print(f" C: 0x{self.C:02X}")
|
|
print(f" D: 0x{self.D:02X}")
|
|
print(f" E: 0x{self.E:02X}")
|
|
print(f" F: 0x{self.F:02X}")
|
|
|
|
# Print Stack (if needed, in a similar hex format)
|
|
print("\nStack:")
|
|
print(self.stack)
|
|
|
|
# Print Total Cycles
|
|
print(f"\nTotal Cycles: {self.cycles}")
|
|
|
|
# Print Memory in Hex, with addresses on the left and values aligned
|
|
print("\nMemory:")
|
|
i = 0
|
|
for address in range(0, len(self.memory), 32): # Iterate by 16 values (one row at a time)
|
|
# Print address
|
|
print(f"0x{address:04X}: ", end="") # Print the memory address in hex (4 digits)
|
|
|
|
# Print 16 values on the same line
|
|
for j in range(32):
|
|
if address + j < len(self.memory): # Avoid out-of-bounds
|
|
print(f"{self.memory[address + j]:02X}", end=" ")
|
|
else:
|
|
print(" ", end=" ") # Empty spaces for remaining uninitialized memory
|
|
print() # Move to the next line
|
|
|
|
|
|
|
|
def load_program(self, program):
|
|
"""Load the machine code program into memory."""
|
|
self.memory[:len(program)] = program
|
|
|
|
|
|
def fetch(self):
|
|
"""Fetch the next instruction."""
|
|
if self.PC >= len(self.memory):
|
|
self.running = False
|
|
return None
|
|
|
|
# Check for 4-byte instructions
|
|
opcode = self.memory[self.PC]
|
|
length = 4 if opcode in (0x08, 0x09) else 3
|
|
instruction = self.memory[self.PC:self.PC + length]
|
|
|
|
if len(instruction) < length:
|
|
instruction += [0] * (length - len(instruction))
|
|
|
|
self.PC += length
|
|
return instruction
|
|
|
|
|
|
|
|
def execute(self, instruction):
|
|
self.cycles+=1
|
|
#print(self.stack, self.SP)
|
|
"""Execute an instruction."""
|
|
if instruction is None:
|
|
return
|
|
|
|
opcode = instruction[0]
|
|
if opcode == 0x00: # Halt
|
|
self.running = False
|
|
elif opcode == 0x01: # LOAD
|
|
reg, value = instruction[1], instruction[2]
|
|
self._load(reg, value)
|
|
elif opcode == 0x02: # MOV
|
|
dest, src = instruction[1], instruction[2]
|
|
self._mov(dest, src)
|
|
elif opcode == 0x03: # ADD
|
|
dest, src = instruction[1], instruction[2]
|
|
self._add(dest, src)
|
|
elif opcode == 0x04: # SUB
|
|
dest, src = instruction[1], instruction[2]
|
|
|
|
self._sub(dest, src)
|
|
|
|
elif opcode == 0x05: # STORE
|
|
reg, addr = instruction[1], instruction[2]
|
|
self._store(reg, addr)
|
|
elif opcode == 0x06: # LOADM
|
|
reg, addr = instruction[1], instruction[2]
|
|
self._loadm(reg, addr)
|
|
elif opcode == 0x08: # BNE (Branch if Not Equal)
|
|
reg, reg2, target = instruction[1], instruction[2], instruction[3]
|
|
self._bne(reg, reg2, target)
|
|
elif opcode == 0x09: # beq (Branch if Equal)
|
|
reg, reg2, target = instruction[1], instruction[2], instruction[3]
|
|
self._beq(reg, reg2, target)
|
|
elif opcode == 0x0A: # int interupt handler
|
|
value, opt = instruction[1], instruction[2]
|
|
self._int(value, opt)
|
|
elif opcode == 0x0B: # push stack
|
|
reg, opt = instruction[1], instruction[2]
|
|
self._push(reg, opt)
|
|
|
|
elif opcode == 0x0C: # pop stack
|
|
reg, opt = instruction[1], instruction[2]
|
|
self._pop(reg, opt)
|
|
elif opcode == 0x0D: # jsr
|
|
value, opt = instruction[1], instruction[2]
|
|
self._jsr(value, opt)
|
|
elif opcode == 0x0E: # ret
|
|
value, opt = instruction[1], instruction[2]
|
|
self._ret(value, opt)
|
|
|
|
elif opcode == 0x0F: # xor
|
|
reg, reg2 = instruction[1], instruction[2]
|
|
self._xor(reg, reg2)
|
|
|
|
elif opcode == 0x10: # and
|
|
reg, reg2 = instruction[1], instruction[2]
|
|
self._and(reg, reg2)
|
|
|
|
elif opcode == 0x11: # jmp
|
|
addr, opt = instruction[1], instruction[2]
|
|
self._jmp(addr, opt)
|
|
|
|
elif opcode == 0x12: # mul
|
|
reg1, reg2 = instruction[1], instruction[2]
|
|
self._mul(reg1, reg2)
|
|
|
|
elif opcode == 0x13: # div
|
|
reg1, reg2 = instruction[1], instruction[2]
|
|
self._div(reg1, reg2)
|
|
|
|
elif opcode == 0x14: # beq (Branch if Equal)
|
|
reg, reg2, target = instruction[1], instruction[2], instruction[3]
|
|
self._blt(reg, reg2, target)
|
|
|
|
elif opcode == 0x15: # beq (Branch if Equal)
|
|
reg, reg2 = instruction[1], instruction[2]
|
|
self._ldb(reg, reg2)
|
|
|
|
elif opcode == 0x16: # beq (Branch if Equal)
|
|
reg, reg2 = instruction[1], instruction[2]
|
|
self._stb(reg, reg2)
|
|
|
|
else:
|
|
raise ValueError(f"Unknown opcode: {opcode}")
|
|
|
|
|
|
|
|
|
|
def _mul(self, reg1, reg2):
|
|
result = self._get_register(reg1) * self._get_register(reg2)
|
|
self._set_register(reg1, result)
|
|
|
|
def _div(self, reg1, reg2):
|
|
a = int(self._get_register(reg1))
|
|
b = int(self._get_register(reg2))
|
|
if b == 0:
|
|
raise ZeroDivisionError("Divided by 0")
|
|
result = a / b
|
|
self._set_register(reg1, result)
|
|
|
|
|
|
def _xor(self, reg, reg2):
|
|
# Retrieve values from the registers, ensuring they're integers
|
|
val1 = int(self._get_register(reg))
|
|
val2 = int(self._get_register(reg2))
|
|
# Perform XOR operation
|
|
result = val1 ^ val2
|
|
|
|
# Ensure the result is also an integer and set it back to the register
|
|
self._set_register(reg, result)
|
|
|
|
def _and(self, reg, reg2):
|
|
result = self._get_register(reg) & self._get_register(reg2)
|
|
self._set_register(reg, result)
|
|
|
|
|
|
def _load(self, reg, value):
|
|
if reg == 0x00:
|
|
self.A = value
|
|
elif reg == 0x01:
|
|
self.B = value
|
|
elif reg == 0x02:
|
|
self.C = value
|
|
elif reg == 0x03:
|
|
self.D = value
|
|
elif reg == 0x04:
|
|
self.E = value
|
|
elif reg == 0x05:
|
|
self.F = value
|
|
else:
|
|
raise ValueError("Invalid register.")
|
|
|
|
def _ldb(self, reg, reg2):
|
|
addr = self._get_register(reg2)
|
|
if addr < 0 or addr >= len(self.memory):
|
|
raise ValueError("Invalid memory address.")
|
|
value = self.memory[addr]
|
|
self._set_register(reg, value)
|
|
|
|
def _stb(self, reg, reg2):
|
|
addr = self._get_register(reg2)
|
|
value = self._get_register(reg)
|
|
|
|
if addr < 0 or addr >= len(self.memory):
|
|
raise ValueError("Invalid memory address.")
|
|
self.memory[addr] = value
|
|
#self._set_register(reg, value)
|
|
|
|
|
|
def _push(self, reg, opt):
|
|
self.SP -= 1
|
|
if self.SP < 0:
|
|
#print(f"STACK OVERFLOW: SP={self.SP}")
|
|
raise OverflowError("Stack overflow")
|
|
val = self._get_register(reg)
|
|
self.stack[self.SP] = val
|
|
#print(f"PUSH: SP={self.SP}, VALUE={val}, STACK={self.stack}")
|
|
|
|
def _pop(self, reg, opt):
|
|
if self.SP >= len(self.stack):
|
|
#print(f"STACK UNDERFLOW: SP={self.SP}")
|
|
raise OverflowError("Stack underflow")
|
|
val = self.stack[self.SP]
|
|
self.stack[self.SP] = 0
|
|
self.SP += 1
|
|
self._set_register(reg, val)
|
|
#print(f"POP: SP={self.SP}, VALUE={val}, STACK={self.stack}")
|
|
|
|
def _jsr(self, value, opt):
|
|
self.SP -= 1
|
|
if self.SP < 0:
|
|
#print(f"STACK OVERFLOW: SP={self.SP}")
|
|
raise OverflowError("Stack overflow")
|
|
print(self.PC, self.PC+3)
|
|
self.stack[self.SP] = self.PC+3
|
|
|
|
self.PC = value
|
|
#print(f"JSR: SP={self.SP}, PC={self.PC}, STACK={self.stack}")
|
|
|
|
def _jmp(self, value, opt):
|
|
self.PC = value
|
|
|
|
|
|
|
|
def _ret(self, value, opt):
|
|
#print(self.stack, self.SP)
|
|
|
|
if self.SP >= len(self.stack):
|
|
#print(f"STACK UNDERFLOW: SP={self.SP}")
|
|
raise OverflowError("Stack underflow")
|
|
address = self.stack[self.SP]
|
|
self.stack[self.SP] = 0
|
|
self.SP += 1
|
|
self.PC = address
|
|
#print(f"RET: SP={self.SP}, PC={self.PC}, STACK={self.stack}")
|
|
|
|
|
|
|
|
|
|
def _display_init(self):
|
|
mode = self._get_register(0x0)
|
|
x = self._get_register(0x1)
|
|
y = self._get_register(0x2)
|
|
|
|
self.V_res = y
|
|
self.H_res = x
|
|
self.mode = mode # Save the display mode
|
|
|
|
print(mode, x, y)
|
|
|
|
pygame.init()
|
|
|
|
if mode == 0: # Bitmap mode
|
|
self.screen = pygame.display.set_mode((x, y))
|
|
self.screen.fill((0, 0, 0)) # Black background
|
|
pygame.display.set_caption("Bitmap Mode")
|
|
elif mode == 1: # Text mode
|
|
self.cell_width = 10 # Width of each text cell (in pixels)
|
|
self.cell_height = 16 # Height of each text cell (in pixels)
|
|
self.max_columns = self.H_res // self.cell_width
|
|
self.max_rows = self.V_res // self.cell_height
|
|
|
|
# Initialize the text buffer
|
|
self.text_buffer = [[(0x20, 0xFFFFFF) for _ in range(self.max_columns)] for _ in range(self.max_rows)]
|
|
|
|
self.font = pygame.font.Font(pygame.font.get_default_font(), self.cell_height)
|
|
self.screen = pygame.display.set_mode((x, y))
|
|
self.screen.fill((0, 0, 0)) # Black background
|
|
pygame.display.set_caption("Text Mode")
|
|
else:
|
|
raise ValueError("Invalid display mode")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _int(self, value, opt):
|
|
|
|
#? Display Interrupts
|
|
if value == 0x70: # Init display
|
|
self._display_init()
|
|
elif value == 0x71: # Set pixel (bitmap mode)
|
|
self._set_pixel()
|
|
elif value == 0x72: # Render character (text mode)
|
|
self._add_text()
|
|
|
|
#? Terminal Interrupts
|
|
elif value == 0x00: # Print character
|
|
self._print_register_char()
|
|
elif value == 0x01: # Print integer
|
|
self._print_register_int()
|
|
|
|
#? System Interrupts
|
|
elif value == 0xFF: # Halt
|
|
self.running = False
|
|
return
|
|
elif value == 0xF6: # Get key down
|
|
keys = pygame.key.get_pressed() # Get the state of all keys
|
|
|
|
key_pressed = False # Flag to check if a key was pressed in this cycle
|
|
current_key = None # Track the currently detected key
|
|
|
|
for keycode in range(len(keys)):
|
|
if keys[keycode]: # If this key is pressed
|
|
current_key = keycode # Set the current key
|
|
|
|
self._set_register(0x0, keycode) # Set register A to the keycode
|
|
|
|
if not self.keydown or (self.last_key != current_key):
|
|
# If it's the first press or a new key is pressed
|
|
self._set_register(0x1, 1) # Set register B to indicate a new key press
|
|
self.keydown = True # Mark that a key is pressed
|
|
self.last_key = current_key # Update the last pressed key
|
|
else:
|
|
# If the same key is still being held
|
|
self._set_register(0x1, 0) # Set register B to indicate no new key
|
|
key_pressed = True
|
|
return
|
|
|
|
if not key_pressed: # If no key is pressed (key release)
|
|
self._set_register(0x0, 0) # Set register A to 0 (no key pressed)
|
|
self._set_register(0x1, 0) # Set register B to 0 (reset key state)
|
|
self.keydown = False # Reset the keydown flag
|
|
self.last_key = None # Clear the last pressed key
|
|
|
|
|
|
|
|
|
|
|
|
elif value == 0xFE: # Error interrupt
|
|
print("Error interrupt")
|
|
print(f"Register A: {self._get_register(0)}")
|
|
print(f"Register B: {self._get_register(1)}")
|
|
print(f"Register C: {self._get_register(2)}")
|
|
self.running = False
|
|
|
|
|
|
|
|
elif value == 0x80: # Read byte from disk
|
|
self._read_byte_from_disk(opt)
|
|
|
|
elif value == 0x81: # write byte from disk
|
|
self._write_byte_from_disk(opt)
|
|
|
|
|
|
|
|
else:
|
|
raise ValueError(f"Unknown interrupt: {value}")
|
|
|
|
def _read_byte_from_disk(self, opt):
|
|
# Register layout:
|
|
# a = drive number (0-9)
|
|
# b = sector number (0-15)
|
|
# c = byte offset within sector (0-254)
|
|
|
|
drive_number = self._get_register(0x0) # Drive number
|
|
sector_number = self._get_register(0x1) # Sector number
|
|
byte_offset = self._get_register(0x2) # Byte offset within sector
|
|
|
|
# Validate input values and trigger error interrupt if invalid
|
|
if not (0 <= drive_number <= 9):
|
|
self._set_register(0x0, 0x81) # Error code 1: Invalid drive number
|
|
self._set_register(0x1, self.PC) # Set current PC to register 0xB
|
|
self._set_register(0x2, 0) # Set register 0xC to 0 as specified
|
|
self._int(0xFE, 0) # Trigger the error interrupt
|
|
return
|
|
|
|
if not (0 <= sector_number <= 15):
|
|
self._set_register(0x0, 0x82) # Error code 2: Invalid sector number
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
if not (0 <= byte_offset <= 254):
|
|
self._set_register(0x0, 0x83) # Error code 3: Invalid byte offset
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
# Construct the disk file path (e.g., "disk0.bin", "disk1.bin")
|
|
disk_file = f"disk{drive_number}.bin"
|
|
|
|
if not os.path.exists(disk_file):
|
|
self._set_register(0x0, 0x84) # Error code 4: Disk file not found
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
# Open the disk file and read the desired byte
|
|
with open(disk_file, 'rb') as f:
|
|
# Calculate the position to read from:
|
|
# (sector_number * 255 bytes per sector) + byte_offset
|
|
position = (sector_number * 255) + byte_offset
|
|
f.seek(position)
|
|
|
|
byte_value = f.read(1) # Read one byte
|
|
if len(byte_value) == 0:
|
|
self._set_register(0x0, 0x85) # Error code 5: Failed to read byte
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
# Store the byte value in a register (0x4 for this example)
|
|
self._set_register(0x4, byte_value[0]) # Store byte in register 0x4
|
|
#print(f"Read byte: {byte_value[0]:#04x} from {disk_file}, sector {sector_number}, byte {byte_offset}")
|
|
|
|
def _write_byte_from_disk(self, opt):
|
|
# Register layout:
|
|
# a = drive number (0-9)
|
|
# b = sector number (0-15)
|
|
# c = byte offset within sector (0-254)
|
|
# d = byte value to write (0-255)
|
|
|
|
drive_number = self._get_register(0x0) # Drive number
|
|
sector_number = self._get_register(0x1) # Sector number
|
|
byte_offset = self._get_register(0x2) # Byte offset within sector
|
|
byte_to_write = self._get_register(0x3) # Byte to write
|
|
|
|
|
|
# Validate input values and trigger error interrupt if invalid
|
|
if not (0 <= drive_number <= 9):
|
|
|
|
self._set_register(0x0, 0x81) # Error code 1: Invalid drive number
|
|
self._set_register(0x1, self.PC) # Set current PC to register 0xB
|
|
self._set_register(0x2, 0) # Set register 0xC to 0 as specified
|
|
self._int(0xFE, 0) # Trigger the error interrupt
|
|
return
|
|
|
|
if not (0 <= sector_number <= 15):
|
|
|
|
self._set_register(0x0, 0x82) # Error code 2: Invalid sector number
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
if not (0 <= byte_offset <= 254):
|
|
self._set_register(0x0, 0x83) # Error code 3: Invalid byte offset
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
# Construct the disk file path (e.g., "disk0.bin", "disk1.bin")
|
|
disk_file = f"drive{drive_number}.bin"
|
|
|
|
if not os.path.exists(disk_file):
|
|
#print(0x84)
|
|
self._set_register(0x0, 0x84) # Error code 4: Disk file not found
|
|
self._set_register(0x1, self.PC)
|
|
self._set_register(0x2, 0)
|
|
self._int(0xFE, 0)
|
|
return
|
|
|
|
# Open the disk file in 'r+b' mode for reading and writing
|
|
with open(disk_file, 'r+b') as f:
|
|
# Calculate the position to write to:
|
|
# (sector_number * 255 bytes per sector) + byte_offset
|
|
position = (sector_number * 255) + byte_offset
|
|
f.seek(position) # Move the file pointer to the desired position
|
|
|
|
# Write the byte to the file
|
|
f.write(bytes([byte_to_write])) # Write a single byte to the file
|
|
#print(f"Written byte: {byte_to_write:#04x} to {disk_file}, sector {sector_number}, byte {byte_offset}")
|
|
|
|
|
|
|
|
def _print_register_char(self):
|
|
|
|
char = self._get_register(0x0)
|
|
print(chr(char), end="")
|
|
def _print_register_int(self):
|
|
|
|
char = self._get_register(0x0)
|
|
print(char, end="")
|
|
|
|
def _mov(self, dest, src):
|
|
value = self._get_register(src)
|
|
self._set_register(dest, value)
|
|
|
|
def _add(self, dest, src):
|
|
result = self._get_register(dest) + self._get_register(src)
|
|
self._set_register(dest, result)
|
|
|
|
def _sub(self, dest, src):
|
|
|
|
result = self._get_register(dest) - self._get_register(src)
|
|
|
|
self._set_register(dest, result)
|
|
|
|
def _store(self, reg, addr):
|
|
if addr < 0 or addr >= len(self.memory):
|
|
raise ValueError("Invalid memory address.")
|
|
self.memory[addr] = self._get_register(reg)
|
|
|
|
|
|
|
|
def _loadm(self, reg, addr):
|
|
if addr < 0 or addr >= len(self.memory):
|
|
raise ValueError("Invalid memory address.")
|
|
value = self.memory[addr]
|
|
self._set_register(reg, value)
|
|
|
|
|
|
def _set_pixel(self):
|
|
if not self.screen or self.mode != 0: # Ensure it's bitmap mode
|
|
return
|
|
|
|
_color = self._get_register(0)
|
|
_x = self._get_register(1)
|
|
_y = self._get_register(2)
|
|
|
|
if 0 <= _x < self.H_res and 0 <= _y < self.V_res:
|
|
r = (_color & 0xFF0000) >> 16
|
|
g = (_color & 0x00FF00) >> 8
|
|
b = (_color & 0x0000FF)
|
|
self.screen.set_at((_x, _y), (r, g, b))
|
|
else:
|
|
raise ValueError("Pixel coordinates out of bounds")
|
|
|
|
|
|
|
|
def _add_text(self):
|
|
if not self.screen or self.mode != 1: # Ensure it's text mode
|
|
return
|
|
|
|
# Retrieve registers
|
|
_char = self._get_register(0) & 0xFF # ASCII character
|
|
_cursor_pos = self._get_register(1) # Current cursor position (in terms of character cells)
|
|
_color = self._get_register(2) # Text color
|
|
|
|
# Ensure cursor position is within bounds
|
|
if _cursor_pos >= self.max_columns * self.max_rows:
|
|
_cursor_pos = (self.max_columns * self.max_rows) - 1
|
|
|
|
# Calculate row and column from the cursor position
|
|
cursor_x = _cursor_pos % self.max_columns
|
|
cursor_y = _cursor_pos // self.max_columns
|
|
|
|
# Update the text buffer
|
|
self.text_buffer[cursor_y][cursor_x] = (_char, _color)
|
|
|
|
# Redraw the entire screen
|
|
self.screen.fill((0, 0, 0)) # Clear the screen
|
|
for row_index, row in enumerate(self.text_buffer):
|
|
for col_index, (char, color) in enumerate(row):
|
|
# Convert to pixel coordinates
|
|
_x = col_index * self.cell_width
|
|
_y = row_index * self.cell_height
|
|
|
|
# Extract color components (RGB)
|
|
r = (color & 0xFF0000) >> 16
|
|
g = (color & 0x00FF00) >> 8
|
|
b = (color & 0x0000FF)
|
|
|
|
# Render the character
|
|
text_surface = self.font.render(chr(char), True, (r, g, b))
|
|
self.screen.blit(text_surface, (_x, _y))
|
|
|
|
# Update the display
|
|
pygame.display.flip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _bne(self, reg, reg2, target):
|
|
"""Branch to target address if register != value."""
|
|
if self._get_register(reg) != self._get_register(reg2):
|
|
if 0 <= target < len(self.memory):
|
|
self.PC = target
|
|
else:
|
|
raise ValueError(f"Invalid branch target address: {target}")
|
|
|
|
def _blt(self, reg, reg2, target):
|
|
"""Branch to target address if register < value."""
|
|
if self._get_register(reg) < self._get_register(reg2):
|
|
if 0 <= target < len(self.memory):
|
|
self.PC = target
|
|
else:
|
|
raise ValueError(f"Invalid branch target address: {target}")
|
|
|
|
|
|
|
|
def _beq(self, reg, reg2, target):
|
|
"""Branch to target address if register == value."""
|
|
if self._get_register(reg) == self._get_register(reg2):
|
|
self.PC = target
|
|
|
|
def _get_register(self, reg):
|
|
if reg == 0x00: # A
|
|
return self.A
|
|
elif reg == 0x01: # B
|
|
return self.B
|
|
elif reg == 0x02: # C
|
|
return self.C
|
|
elif reg == 0x03:
|
|
return self.D
|
|
elif reg == 0x04:
|
|
return self.E
|
|
elif reg == 0x05:
|
|
return self.F
|
|
else:
|
|
raise ValueError(f"Invalid register: {reg}")
|
|
|
|
|
|
def _set_register(self, reg, value):
|
|
if type(value) != int:
|
|
raise TypeError(f"Invalid register type")
|
|
if reg == 0x00:
|
|
self.A = value
|
|
elif reg == 0x01:
|
|
self.B = value
|
|
elif reg == 0x02:
|
|
self.C = value
|
|
elif reg == 0x03:
|
|
self.D = value
|
|
elif reg == 0x04:
|
|
self.E = value
|
|
elif reg == 0x05:
|
|
self.F = value
|
|
else:
|
|
raise ValueError("Invalid register code.")
|
|
|
|
def run(self):
|
|
"""Run the loaded program with error interrupts."""
|
|
line = 0
|
|
timer = 0
|
|
try:
|
|
end = time.time()
|
|
start = time.time()
|
|
while self.running:
|
|
start = time.time()
|
|
|
|
instruction = self.fetch()
|
|
#print(instruction)
|
|
|
|
if self.screen:
|
|
if timer >= 1 / 60:
|
|
pygame.display.update()
|
|
timer = 0
|
|
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
self.running = False
|
|
|
|
self.execute(instruction)
|
|
line += 1
|
|
end = time.time()
|
|
timer += end - start
|
|
|
|
except ValueError as e:
|
|
print(e, self.B, self.C)
|
|
# Trigger an error interrupt with details
|
|
self._set_register(0x0, 0x1) # Error Type
|
|
self._set_register(0x1, self.PC) # Error Address
|
|
self._set_register(0x2, instruction[0]) # Error Type
|
|
|
|
self._int(0xFE, 0)
|
|
except IndexError as e:
|
|
print(e, self.B, self.C)
|
|
# Trigger an error interrupt with details
|
|
self._set_register(0x0, 0x2) # Error Type
|
|
self._set_register(0x1, self.PC) # Error Address
|
|
self._set_register(0x2, instruction[0]) # Error Type
|
|
|
|
self._int(0xFE, 0)
|
|
except OverflowError as e:
|
|
print(e, self.B, self.C)
|
|
# Trigger an error interrupt with details
|
|
self._set_register(0x0, 0x3) # Error Type
|
|
self._set_register(0x1, self.PC) # Error Address
|
|
self._set_register(0x2, instruction[0]) # Error Type
|
|
|
|
self._int(0xFE, 0)
|
|
|
|
except ZeroDivisionError as e:
|
|
print(e, self.B, self.C)
|
|
# Trigger an error interrupt with details
|
|
self._set_register(0x0, 0x4) # Error Type
|
|
self._set_register(0x1, self.PC) # Error Address
|
|
self._set_register(0x2, instruction[0]) # Error Type
|
|
|
|
self._int(0xFE, 0) # call error interrupt
|
|
|
|
#ValueError, IndexError, OverflowError
|
|
|
|
|
|
from program import program
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize CPU, load program, and run
|
|
cpu = CPU()
|
|
cpu.load_program(program)
|
|
cpu.run()
|
|
|
|
|
|
cpu.state() |