I am creating a Virtual Machine (VM) with Python, I am currently working on adding to my Assembly Instructions but more specifically adding IF
statements. Currently my CPU gets stuck in a loop and never fully computing the IF
statements (infinitely repeating my debug statement of fetching {instruction}), I need help fixing this problem.
I tried adding a few debug print statements within the IF to figure out where it all went wrong but couldn’t deduce a place or how the problem happened. I checked a few sites like Stackoverflow to see if a similar question had popped up but I found nothing. I also asked ChatGPT to see if it was able to fix the issue, but nothing worked.
computer.py
import pygame
import os
class RAM:
def __init__(self, size):
self.memory = [0] * size
self.variables = {}
os.system('cls' if os.name == 'nt' else 'clear')
print("Running [INSERT_NAME]...")
def write(self, address_or_name, value):
if isinstance(address_or_name, str): # Handle variables
self.variables[address_or_name] = value # No conversion to int here
else: # Handle memory addresses
if 0 <= address_or_name < len(self.memory):
self.memory[address_or_name] = value
def read(self, address_or_name):
if isinstance(address_or_name, str): # Handle variables
value = self.variables.get(address_or_name, 0)
return value
else: # Handle memory addresses
if 0 <= address_or_name < len(self.memory):
return self.memory[address_or_name]
else:
print(f"Error: Attempted to read from an invalid address {address_or_name}")
return 0
def load_program_from_file(self, filename):
try:
with open(filename, 'r') as f:
lines = f.read().splitlines()
address = 0
for line in lines:
line = line.split(';')[0].strip() # Remove comments and trim whitespace
if line:
self.memory[address] = line
address += 1
except FileNotFoundError:
print(f"Error: File {filename} not found")
except Exception as e:
print(f"Error: {e}")
class CPU:
def __init__(self, ram, display_surface):
self.ram = ram
self.registers = {
'A': 0,
'PC': 0,
}
self.running = True
self.display_surface = display_surface
self.in_if_block = False
self.execute_if_block = False
self.skip_until_end = False
def fetch(self):
pc = self.registers['PC']
if 0 <= pc < len(self.ram.memory):
instruction = self.ram.read(pc)
print(f"Fetched instruction: {instruction}") # For debugging
return instruction
else:
print(f"Error: Program Counter {pc} is out of range")
self.running = False
return None
def execute(self, instruction):
instruction = str(instruction).strip()
if self.in_if_block:
if instruction == 'THEN':
return # Just skip 'THEN' line
elif instruction == 'END':
self.in_if_block = False
self.skip_until_end = False
elif self.execute_if_block:
if instruction.startswith('DRAW'):
self.perform_draw()
elif self.skip_until_end:
return # Just skip until END
elif instruction.startswith('IF'):
print("if block detected") # For debugging
self.in_if_block = True
parts = instruction.split()
if len(parts) >= 5: # Adjust to make sure it has enough parts
var_name = parts[2]
operator = parts[3]
value = int(parts[4])
var_value = int(self.ram.read(var_name))
print(f"Condition check: {var_value} {operator} {value}") # Debugging
condition_met = False
if operator == '>':
condition_met = var_value > value
elif operator == '<':
condition_met = var_value < value
elif operator == '>=':
condition_met = var_value >= value
elif operator == '<=':
condition_met = var_value <= value
elif operator == '==':
condition_met = var_value == value
elif operator == '!=':
condition_met = var_value != value
self.execute_if_block = condition_met
self.skip_until_end = not condition_met
elif instruction.startswith('SET'):
var_name = self.ram.read(self.registers['PC'] + 1) # Get the variable name from the next memory address
value = int(self.ram.read(self.registers['PC'] + 2)) # Get the value from the following memory address
self.ram.write(var_name, value) # Write the value to the variable in RAM
self.registers['PC'] += 3 # Increment PC by 3 to move past the SET command and its parameters
elif instruction.startswith('LOAD'):
var_name = instruction.split()[1]
self.registers['A'] = int(self.ram.read(var_name))
elif instruction.startswith('DRAW'):
self.perform_draw()
elif instruction == 'HALT':
print("Halting CPU...")
self.running = False
else:
pass
def perform_draw(self):
x_value = self.ram.read(self.registers['PC'] + 1)
y_value = self.ram.read(self.registers['PC'] + 2)
color = self.ram.read(self.registers['PC'] + 3)
try:
x_value = int(self.ram.read(self.registers['PC'] + 1))
except Exception:
x_value = self.ram.read(self.registers['PC'] + 1)
try:
y_value = int(self.ram.read(self.registers['PC'] + 2))
except Exception:
y_value = self.ram.read(self.registers['PC'] + 2)
if isinstance(x_value, str):
x_value = int(self.ram.read(x_value))
if isinstance(y_value, str):
y_value = int(self.ram.read(y_value))
color = int(color)
if color not in [0, 1]:
print(f"Error: Invalid color index: {color}.")
return
self.draw_pixel(x_value, y_value, color)
self.registers['PC'] += 4 # Move past the DRAW command and its parameters
def draw_pixel(self, x, y, color):
colors = [(0, 0, 0), (255, 255, 255)] # 0 = Black, 1 = White
if 0 <= x < self.display_surface.get_width() and 0 <= y < self.display_surface.get_height():
self.display_surface.set_at((x, y), colors[color])
else:
print(f"Attempted to draw pixel at out of bounds ({x}, {y}).")
def run(self):
while self.running:
instruction = self.fetch()
if instruction:
self.execute(instruction)
pygame.display.update()
program.asm
SET
x_coord
10
SET
y_coord
15
IF
LOAD x_coord > 11
THEN
DRAW
LOAD x_coord
LOAD y_coord
1
END
DRAW
1
1
1
HALT
main.py
import pygame
import sys
import computer
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((16, 16))
color = (0, 0, 0)
screen.fill(color)
pygame.display.set_caption('Computer')
clock = pygame.time.Clock()
# Initialize RAM and CPU
ram = computer.RAM(1024)
cpu = computer.CPU(ram, screen)
# Load program from file
ram.load_program_from_file('program.asm')
# Run the CPU
cpu.run()
# Pygame main loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.flip()
clock.tick(60)
New contributor