This commit is contained in:
OusmBlueNinja 2024-12-23 23:17:07 -06:00
parent 77946c4d6f
commit b88b8bffc9
18 changed files with 3056 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

914
asm-to-prg.py Normal file
View File

@ -0,0 +1,914 @@
filename = "test.asm"
#
# Change the filename here to the path of your asm file
# then copy the output to 'main.py' and replace the 'program' variable
# with the list, then run the 'main.py' file with python 3.11+
#
# there are 2 example programs, one that demonstrates bitmap mode
# and one that demonstrates text mode, with typing useing the bios
# interupts.
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
import re
from termcolor import colored
with open(filename,"r") as f:
lines = f.readlines()
def convert_to_int(value):
if isinstance(value, str): # Check if the value is a string
if value.startswith("0x"): # Handle hexadecimal strings
return int(value, 16)
else: # Handle decimal strings
return int(value)
elif isinstance(value, int): # Value is already an integer
return value
else:
raise ValueError(f"Unsupported type for conversion: {type(value)}")
import os
from termcolor import colored
macro_definitions = {}
def preprocess(lines, filename="main.asm", included_files=None):
if included_files is None:
included_files = set() # Tracks included files to prevent recursion
errors = []
warnings = []
error_flag = False
# Memory and stack tracking
instruction_count = 0
memory_limit = 1024 # Total memory available
stack_balance = 0
program_length = 0 # To calculate and validate memory access
# Valid registers and instructions
valid_registers = {"a", "b", "c", "d", "e", "f"}
valid_instructions = {"ldw", "mov", "add", "sub", "str", "ldr", "int",
"push", "pop", "jsr", "ret", "xor", "and", "jmp",
"mul", "div", "bne", "beq", "blt", "ldb", "stb"}
label_references = []
labels = {}
# Expand include directives
expanded_lines = []
for line_number, line in enumerate(lines, start=1):
code = line.strip()
if code.startswith("%include"):
# Handle include directives
parts = code.split(maxsplit=1)
if len(parts) != 2:
errors.append((line_number, "Invalid %include syntax", line))
error_flag = True
continue
include_file = parts[1].strip("\"")
if include_file in included_files:
errors.append((line_number, f"Recursive inclusion detected for file '{include_file}'", line))
error_flag = True
continue
if not os.path.exists(include_file):
errors.append((line_number, f"Included file '{include_file}' not found", line))
error_flag = True
continue
try:
included_files.add(include_file)
with open(include_file, 'r') as f:
included_lines = f.readlines()
expanded_lines.extend(preprocess(included_lines, filename=include_file, included_files=included_files))
except Exception as e:
errors.append((line_number, f"Failed to include file '{include_file}': {str(e)}", line))
error_flag = True
elif code.startswith("%define"):
# Handle macros
parts = code.split(maxsplit=2)
if len(parts) != 3:
errors.append((line_number, "Invalid %define syntax", line))
error_flag = True
continue
macro_name, macro_value = parts[1], parts[2]
if macro_name in macro_definitions:
errors.append((line_number, f"Macro '{macro_name}' redefined", line))
error_flag = True
continue
macro_definitions[macro_name] = macro_value
continue # Skip adding %define line to the output
else:
expanded_lines.append(line)
# First pass: Parse instructions and calculate program length
for line_number, line in enumerate(expanded_lines, start=1):
code = line.split(";")[0].strip() # Strip comments and whitespace
if not code:
continue
# Handle labels
if code.endswith(":"):
label_name = code[:-1]
if label_name in labels:
warnings.append((line_number, f"Duplicate label '{label_name}'", line))
labels[label_name] = instruction_count
continue
# Parse instruction
parts = code.split()
instruction = parts[0].lower()
if instruction == "db":
# Handle string definitions
if len(parts) < 2:
errors.append((line_number, f"Missing operand for '{instruction}'", line))
error_flag = True
continue
string_literal = " ".join(parts[1:]).strip("\"")
instruction_count += len(string_literal) + 1 # Include null terminator
elif instruction in valid_instructions:
if instruction in {"ldw", "mov", "add", "sub", "str", "ldr", "xor", "and", "mul", "div", "ldb", "stb"}:
instruction_count += 3
elif instruction in {"bne", "beq", "blt"}:
instruction_count += 4
elif instruction in {"push", "pop", "int", "jmp", "jsr", "ret"}:
instruction_count += 3
else:
errors.append((line_number, f"Unknown instruction '{instruction}'", line))
error_flag = True
program_length = instruction_count # Final length of the program
# Second pass: Validate instructions and operands
for line_number, line in enumerate(expanded_lines, start=1):
code = line.split(";")[0].strip()
if not code:
continue
# Handle labels
if code.endswith(":"):
continue
parts = code.split()
instruction = parts[0].lower()
operands = parts[1:] if len(parts) > 1 else []
# Handle db strings
if instruction == "db":
string_literal = " ".join(operands).strip("\"")
if not string_literal:
errors.append((line_number, "Empty string literal in 'db'", line))
error_flag = True
continue
# Strip commas from operands
operands = [op.replace(",", "") for op in operands]
# Validate instruction and operands (same as before)
if instruction == "ldw" and len(operands) == 2:
reg, value = operands
if reg not in valid_registers:
errors.append((line_number, f"Invalid register '{reg}'", line))
error_flag = True
elif instruction == "str" and len(operands) == 2:
reg, address = operands
if reg not in valid_registers:
errors.append((line_number, f"Invalid register '{reg}'", line))
error_flag = True
try:
if str(address).startswith("0x"):
mem_address = int(address, 16)
else:
mem_address = int(address)
if mem_address < program_length:
errors.append((line_number, f"Illegal memory write to program space in '{code}'", line))
error_flag = True
if mem_address > memory_limit:
errors.append((line_number, f"Illegal memory write out of bounds in '{code}'", line))
error_flag = True
except ValueError:
errors.append((line_number, f"Invalid memory address '{address}'", line))
error_flag = True
elif instruction in {"add", "sub", "mov", "xor", "and", "mul", "div", "ldb", "stb"} and len(operands) == 2:
reg1, reg2 = operands
if reg1 not in valid_registers or reg2 not in valid_registers:
errors.append((line_number, f"Invalid register(s) in '{code}'", line))
error_flag = True
elif instruction in {"push", "pop"} and len(operands) == 1:
reg = operands[0]
if reg not in valid_registers:
errors.append((line_number, f"Invalid register '{reg}'", line))
error_flag = True
if instruction == "push":
stack_balance += 1
if stack_balance > 16: # Example stack limit
warnings.append((line_number, "Stack overflow detected", line))
elif instruction == "pop":
stack_balance -= 1
if stack_balance < 0:
errors.append((line_number, f"Stack underflow detected at '{code}'", line))
error_flag = True
# Validate branch instructions with two registers and one label
elif instruction in {"bne", "beq", "blt"}:
if len(operands) != 3:
errors.append((line_number, f"Branch instruction '{instruction}' should have 2 registers and 1 label", line))
error_flag = True
else:
reg1, reg2, label = operands
if reg1 not in valid_registers or reg2 not in valid_registers:
errors.append((line_number, f"Invalid register(s) in '{instruction}'", line))
error_flag = True
label_references.append((line_number, label, line)) # The third operand should be a label
elif instruction in {"jmp", "jsr"}:
if len(operands) != 1:
errors.append((line_number, f"'{instruction}' instruction should have 1 operand (label)", line))
error_flag = True
label = operands[0] # The only operand should be a label
label_references.append((line_number, label, line))
# Check undefined labels
for line_number, label, line in label_references:
if label not in labels:
errors.append((line_number, f"Undefined label '{label}'", line))
error_flag = True
# Check stack balance
if stack_balance != 0:
warnings.append((0, "Stack imbalance detected, unbalanced push/pop operations", ""))
# Print errors and warnings
for line_number, message, code_line in errors:
print(colored(f"{filename}:{line_number}: error: {message}", "red"))
print(colored(f" {line_number} | {code_line}", "white"))
print(colored(f" | {'^' * len(code_line)}", "cyan"))
for line_number, message, code_line in warnings:
if line_number == 0:
print(colored(f"{filename}: warning: {message}", "yellow"))
else:
print(colored(f"{filename}:{line_number}: warning: {message}", "yellow"))
print(colored(f" {line_number} | {code_line}", "white"))
if program_length >= memory_limit:
error_flag = True
print(colored(f"GLOBAL: error: Program too big, size: {program_length}", "red"))
if not error_flag:
print(colored(f"{filename}: Done!", "green"))
else:
exit(1)
return expanded_lines
lines = preprocess(lines, filename=filename)
lineNumber = 0
COMPILE_ERROR = False
def _ValueError(message):
global COMPILE_ERROR
COMPILE_ERROR = True
print("ValueError: %s on line:" % message, lineNumber)
def _IndexError(message):
global COMPILE_ERROR
COMPILE_ERROR = True
print("IndexError: %s on line:" % message, lineNumber)
def _InstructionError(message):
global COMPILE_ERROR
COMPILE_ERROR = True
print("InstructionError: %s on line:" % message, lineNumber)
def load_include(filename):
with open(filename, "r") as f:
lines = f.readlines()
return lines
# for line in lines:
# line = line.split(";")[0] # filter out comments
# print(line)
# Dictionary to store labels and associated instructions
label_to_instructions = {}
current_label = None
itterrator = 0
for line in lines:
# Remove leading and trailing whitespace
stripped_line = line.strip()
if stripped_line.endswith(':'):
# It's a label, use it as the new key in the dictionary
current_label = stripped_line[:-1] # Remove the colon
current_label = current_label.upper()
label_to_instructions[current_label] = [] # Initialize empty instruction list
elif stripped_line.startswith('%'):
# Get the command within the current line
command = ""
for char in stripped_line:
if char == "%":
continue
if char == " ": # Space
break
command += char
#print(command)
if command == "define": #! defines are handled in the preprocessor
pass
elif command == "include":
# Get value between quotes
inQuote = False
include_filepath = ""
for char in stripped_line:
if char == '"':
inQuote = not inQuote
elif inQuote:
include_filepath += char
if include_filepath == '':
continue
lines += ["\n"] + load_include(include_filepath)
elif stripped_line:
# It's an instruction; add it to the current label's list
if current_label is not None:
label_to_instructions[current_label].append(stripped_line)
itterrator+=1
# register letter to identifyer
registerDict = {'a':0x0,'b':0x1,'c':0x2,'d':0x3,'e':0x4,'f':0x5}
current_byte_offset = 0 # Tracks the current byte address
label_addresses = {} # Maps label names to their resolved byte addresses
for label in label_to_instructions:
label_addresses[label] = current_byte_offset
for line in label_to_instructions[label]:
line = line.strip().split(";")[0] # Strip comments
line = line.rstrip(" ") # Strip spaces at end
line = line.replace(",", "") # Remove commas
line = line.split(" ") # Get each part of the instruction
line[0] = line[0].lower() # Make instruction lowercase
if line[0] == '':
continue
if line[0] in {"ldw", "mov", "add", "sub", "str", "ldr", "int", "push", "pop", "jsr", "ret", 'xor', 'and', 'jmp', 'mul', 'div', 'ldb', 'stb'}: # 3 byte instructions
current_byte_offset += 3
elif line[0] in {'bne', 'beq', 'blt'}: # 4 byte instructions
current_byte_offset += 4
current_byte_offset = 0 # Tracks the current byte address
#print(label_addresses)
outputBytes = []
for label in label_to_instructions:
#print(label)
if label_addresses[label] != current_byte_offset and not COMPILE_ERROR:
raise IndexError(f"address mismatch, expected {label_addresses[label]}, got {current_byte_offset}")
# Output the results
for line in label_to_instructions[label]:
line = line.strip().split(";")[0] # Strip comments
line = line.rstrip(" ") # Strip trailing spaces
line = line.replace(",", "") # Remove commas
line = line.split(" ") # Split into instruction parts
if not line[0]:
continue
line[0] = line[0].lower() # Normalize instruction to lowercase
# Regular instruction processing (already present in your code)
index = 0
for operator in line:
if operator in macro_definitions:
line[index] = macro_definitions[line[index]]
index += 1
#print(line)
#! Code to convert to bytes
bytes = []
try:
# Handle 'db' directive for defining strings or raw bytes
if line[0] == 'db':
if len(line) < 3:
_InstructionError("Missing data for 'db' directive")
raw_data = " ".join(line[1:]).strip("'\"")
bytes = [ord(char) for char in raw_data] # Convert characters to ASCII values
bytes.append(0) # Null terminator
outputBytes += bytes
current_byte_offset += len(bytes)
continue
# Add 'ldb' instruction handling
elif line[0] == 'ldb': # Load byte to register
bytes.append(0x15) # Assuming 0x15 for 'ldb'
register = registerDict.get(line[1].lower(), -1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(), -1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'stb': # Load byte to register
bytes.append(0x16) # Assuming 0x15 for 'ldb'
register = registerDict.get(line[1].lower(), -1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(), -1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'ldw': # Load immediate to register
bytes.append(0x1) # byte for load immediate value
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
bytes.append(convert_to_int(line[2])) # the actual value as an int
elif line[0] == 'mov': # Load immediate to register
bytes.append(0x2) # byte for load immediate value
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
#bytes.append(0x0)
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
#bytes.append(convert_to_int(line[2])) # the actual value as an int
elif line[0] == 'add': # Load immediate to register
bytes.append(0x3)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'sub': # Load immediate to register
bytes.append(0x4)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'str': # Load immediate to register
bytes.append(0x5)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
bytes.append(convert_to_int(line[2])) # the actual value as an int
elif line[0] == 'ldr': # Load immediate to register
bytes.append(0x6)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
bytes.append(convert_to_int(line[2])) # the actual value as an int
elif line[0] == 'int': # Load immediate to register
bytes.append(0xA)
bytes.append(convert_to_int(line[1])) # the actual value as an int
bytes.append(0x0) #! NEED THIS TO KEEP THE INSTRUCTION AT 3 BYTES
elif line[0] == 'bne': # Load immediate to register
bytes.append(0x8)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
label = line[3].upper()
if label == -1:
_InstructionError("Missing Label")
continue
if label in label_to_instructions:
bytes.append(label_addresses[label])
else:
_InstructionError("Unknown Label")
elif line[0] == 'beq': # Load immediate to register
bytes.append(0x9)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
label = line[3].upper()
if label == -1:
_InstructionError("Missing Label")
continue
if label in label_to_instructions:
bytes.append(label_addresses[label])
else:
_InstructionError("Unknown Label")
elif line[0] == 'push': # Load immediate to register
bytes.append(0xB)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
bytes.append(0x0) # padding
elif line[0] == 'pop': # Load immediate to register
bytes.append(0xC)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
bytes.append(0x0) # padding
elif line[0] == 'jsr': # Load immediate to register
bytes.append(0xD)
# set register ID:
label = line[1].upper()
if label == -1:
_InstructionError("Missing Label")
continue
if label in label_to_instructions:
bytes.append(label_addresses[label])
else:
_InstructionError("Unknown Label")
bytes.append(0x0) # padding
elif line[0] == 'ret': # Load immediate to register
bytes.append(0xE)
bytes.append(0x0) # padding
bytes.append(0x0) # padding
elif line[0] == 'xor': # Load immediate to register
bytes.append(0xF)
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'and': # Load immediate to register
bytes.append(0x10)
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'jmp': # Load immediate to register
bytes.append(0x11)
# set register ID:
label = line[1].upper()
if label == -1:
_InstructionError("Missing Label")
continue
if label in label_to_instructions:
bytes.append(label_addresses[label])
else:
_InstructionError("Unknown Label")
bytes.append(0x0) # padding
elif line[0] == 'mul': # Load immediate to register
bytes.append(0x12)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'div': # Load immediate to register
bytes.append(0x13)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
elif line[0] == 'blt': # Load immediate to register
bytes.append(0x14)
# set register ID:
register = registerDict.get(line[1].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
register = registerDict.get(line[2].lower(),-1)
if register >= 0 and register <= 5:
bytes.append(register)
else:
_ValueError("Invalid Register")
label = line[3].upper()
if label == -1:
_InstructionError("Missing Label")
continue
if label in label_to_instructions:
bytes.append(label_addresses[label])
else:
_InstructionError("Unknown Label")
else:
print(line)
_InstructionError("Unknown Instruction")
except IndexError:
_IndexError("Maformed Instruction")
except ValueError:
print(line)
_ValueError("Unknown Error")
current_byte_offset += len(bytes)
lineNumber+=1
outputBytes += bytes
if not COMPILE_ERROR:
with open("program.py", "w") as f:
bytecode = []
for _, y in enumerate(outputBytes):
bytecode.append(str(y))
prg = "program = [" + ",".join(bytecode) + "]"
f.write(prg)
else:
print(f"Compilation Error")
print(f"This is most likely due to invalid macro, please check your code for typos ")

112
asm/example.asm Normal file
View File

@ -0,0 +1,112 @@
;; Interupt list
; 0x00 -> print register a as char
; 0x01 -> print register a as int
;
; 0xFF -> Halt
; 0xFE -> Error Interupt
; 0xF6 -> put keycode of currently pressed key in A register
; and but a value (bool)(0 or 1) into b register to
; indicate whether it is the first time this key has been
; pressed.
;
;
; 0x70 -> Init graphics,
; (A) mode
; (B) X resolution
; (C) Y Resulution
; 0x71 -> Set Pixel, (bitmap mode)
; (A) Color (0x000000 to 0xFFFFFF)
; (B) x position
; (C) y position
; 0x72 -> Set Char, (Text Mode)
; (A) Character Code
; (B) Cursor Position
; (C) Color (0x000000 to 0xFFFFFF)
;
;
; ! ALLL FILESYSTEM DRIVE NUMBER ARE LOCAL FILES !
; drive( drive number (0 to 9)).bin
; "drive8.bin"
;
; drive number -> ( 0 to 9 )
; sector number -> ( 0 to 15 )
; byte offset -> ( 0 to 254 )
;
; 0x80 -> read byte from disk
; (A) drive number
; (B) sector number
; (C) byte offset
; moves output to A register
; 0x81 -> write byte from disk
; (A) drive number
; (B) sector number
; (C) byte offset
; (D) byte to write
; Instructions
;
; add ; adds 2 registers together acumulating in the first register
; sub ; same as add ; except subtracts
; ldw ; load word, load a immediate to a register
; ldr ; load a byte from a adderss to a register
; ldb ; load a byte from an adress (address stored in a register) to another register
; str ; stores from a register to an address
; jsr ; jump to a label
; push/pop ; stack stuff, takes 1 register
; ret ; return (pops off of stack)
; mov ; move a value from one register to another
; mul ; multiply takes 2 registers
; div ; divide takes 2 registers
; bne/beq ; branch not equal example: b(n)e a, b, Label1
;
;
;
;
;
;
; Campialer option
; %include include anotehr asm file
; %define all accurances of the define will be raplaced
main:
ldw a, 0xFF ; load 255 to A register, Unused
str a, 0xFF ; will cause an error because its writing to program memory
; You will have to make the address bigger than the program
int 0x00 ; interupt to print a register to terminal as a char (debug)
ldw a, 0 ; will be the graphics mode
str a, 0xF0 ; copy A register to memory address
ldw a, 800 ; screen width
str a, 0xF1 ; copy A register to memory address
ldw a, 600 ; screen height
str a, 0xF2 ; copy A register to memory address
jsr init_graphics_mode
init_graphics_mode:
push a
push b
push c
ldr a, 0xF0 ; copy data from memory address to register 'A'
ldr b, 0xF1 ; copy data from memory address to register 'B'
ldr c, 0xF2 ; copy data from memory address to register 'C'
int 0x70 ; all interupts in 0x70 - 0x7F are for graphics,
; only 0x70 to 0x72 are implemented
pop c
pop b
pop a
ret

66
asm/gradiant.asm Normal file
View File

@ -0,0 +1,66 @@
main:
jsr lcd_init ; Initialize the screen
ldw b, 0 ; Initialize register b (x position)
ldw c, 0 ; Initialize register c (y position)
ldw d, 1 ; Increment value
ldw f, 600 ; Screen width/height limit
; Store scaling factors in memory (safe zone past 0xC8)
ldw a, 0x0 ; Scaling factor for green (shift left by 8 bits)
str a, 0xD0 ; Store in memory at address 0xD0
ldw a, 0xFF0000 ; Scaling factor for red (shift left by 16 bits)
str a, 0xC8 ; Store in memory at address 0xC8
ldw a, 0x00FF00 ; Scaling factor for green (shift left by 8 bits)
str a, 0xCC ; Store in memory at address 0xCC
loop:
; Compute red channel
mov a, b ; Copy x position to a
ldr e, 0xC8 ; Load red scaling factor
mul a, e ; Multiply x by red scaling factor
; Compute green channel
mov e, c ; Copy y position to e
ldr a, 0xCC ; Load green scaling factor into a
mul e, a ; Multiply y by green scaling factor (result in e)
; Combine red and green channels
add a, e ; Combine red and green channels (result in a)
; Add blue channel (based on x for simplicity)
add a, b ; Blue intensity based on x position
; Draw the pixel
int 0x71 ; Set pixel color at (b, c)
int 0xF6 ; get key pressed
next:
; Update position
ldw a, 0 ; Reset a
add b, d ; Increment x (b += 1)
bne b, f, loop ; If b < 256, continue loop
ldw b, 0 ; Reset x
add c, d ; Increment y (c += 1)
bne c, f, loop ; If c < 256, continue loop
; Halt
int 0xFF ; Stop program
lcd_init:
push a
push b
push c
ldw a, 0x0 ; mode
ldw b, 256 ; Vertical resolution
ldw c, 256 ; Horizontal resolution
int 0x70 ; Initialize screen
pop c
pop b
pop a
ret

84
asm/try_to_fix_me.asm Normal file
View File

@ -0,0 +1,84 @@
; 0x00 -> print register a as char
; 0x01 -> print register a as int
;
; 0xFF -> Halt
; 0xFE -> Error Interupt
; 0xF6 -> put keycode of currently pressed key in A register
; and but a value (bool)(0 or 1) into b register to
; indicate whether it is the first time this key has been
; pressed.
;
;
; 0x70 -> Init graphics,
; (A) mode
; (B) X resolution
; (C) Y Resulution
; 0x71 -> Set Pixel, (bitmap mode)
; (A) Color (0x000000 to 0xFFFFFF)
; (B) x position
; (C) y position
; 0x72 -> Set Char, (Text Mode)
; (A) Character Code
; (B) Cursor Position
; (C) Color (0x000000 to 0xFFFFFF)
;
;
; ! ALLL FILESYSTEM DRIVE NUMBER ARE LOCAL FILES !
; drive( drive number (0 to 9)).bin
; "drive8.bin"
;
; drive number -> ( 0 to 9 )
; sector number -> ( 0 to 15 )
; byte offset -> ( 0 to 254 )
;
; 0x80 -> read byte from disk
; (A) drive number
; (B) sector number
; (C) byte offset
; moves output to A register
; 0x81 -> write byte from disk
; (A) drive number
; (B) sector number
; (C) byte offset
; (D) byte to write
main:
ldw a, 0xFF ; load 255 to A register, Unused
str a, 0xEF ; will cause an error because its writing to program memory
; You will have to make the address bigger than the program
int 0x00 ; interupt to print a register to terminal as a char (debug)
ldw a, 0 ; will be the graphics mode
str a, 0xF0 ; copy A register to memory address
ldw a, 800 ; screen width
str a, 0xF1 ; copy A register to memory address
ldw a, 600 ; screen height
str a, 0xF2 ; copy A register to memory address
jsr init_graphics_mode
init_graphics_mode:
push a
push b
push c
ldw a, 0xFF ; This is never used
ldr a, 0xF0 ; copy data from memory address to register 'A'
ldr b, 0xF1 ; copy data from memory address to register 'B'
ldr c, 0xF2 ; copy data from memory address to register 'C'
int 0x70 ; all interupts in 0x70 - 0x7F are for graphics,
; only 0x70 to 0x72 are implemented
pop c
pop b
pop a
ret

548
c-to-asm.py Normal file
View File

@ -0,0 +1,548 @@
import re
import os
from termcolor import colored
class Variable:
def __init__(self, name, address, var_type="int"):
self.name = name
self.address = address
self.type = var_type
class Compiler:
def __init__(self):
# 1024 bytes total: 0x000 to 0x3FF
self.data_ptr = 0x400
self.variables = {}
self.struct_definitions = {}
self.in_struct_def = False
self.current_struct_name = None
self.current_struct_fields = []
self.defines = {} # For #define macros
self.typedefs = {} # For typedef
self.label_counter = 0
self.block_stack = [] # For if/while blocks
def new_label(self, prefix):
lbl = f"{prefix}{self.label_counter}"
self.label_counter += 1
return lbl
def preprocess(self, filename):
lines = self._read_file_recursive(filename)
processed_lines = self._apply_defines(lines)
return processed_lines
def _read_file_recursive(self, filename, included_files=None):
if included_files is None:
included_files = set()
if filename in included_files:
# Prevent infinite recursion on includes
return []
included_files.add(filename)
result_lines = []
try:
with open(filename, "r") as f:
for line in f:
line_stripped = line.strip()
# #include "file"
inc_match = re.match(r'#include\s+"([^"]+)"', line_stripped)
if inc_match:
inc_file = inc_match.group(1)
included_content = self._read_file_recursive(inc_file, included_files)
result_lines.extend(included_content)
continue
# #define KEY VALUE
def_match = re.match(r'#define\s+([a-zA-Z_]\w*)\s+(.*)', line_stripped)
if def_match:
key = def_match.group(1)
value = def_match.group(2)
self.defines[key] = value
continue
# typedef oldtype newtype;
tmatch = re.match(r'typedef\s+([a-zA-Z_]\w*)\s+([a-zA-Z_]\w*)\s*;', line_stripped)
if tmatch:
oldt = tmatch.group(1)
newt = tmatch.group(2)
# Resolve oldt if it's also a typedef
oldt = self.apply_typedef(oldt)
self.typedefs[newt] = oldt
continue
result_lines.append(line)
except FileNotFoundError as e:
print(colored(f"{filename}:0: error: {e}", "red"))
return result_lines
def _apply_defines(self, lines):
token_pattern = re.compile(r'([A-Za-z0-9_]+)')
processed = []
for line in lines:
parts = token_pattern.split(line)
# parts: tokens and separators
for i, part in enumerate(parts):
if part in self.defines:
print(f"Replaced {part} with {self.defines[part]}")
part = self.defines[part]
parts[i] = part
new_line = "".join(parts)
processed.append(new_line)
return processed
def apply_typedef(self, t):
if t in self.typedefs:
return self.typedefs[t]
return t
def allocate_bytes(self, count):
start_addr = self.data_ptr - (count - 1)
if start_addr < 0x000:
raise Exception("Out of memory!")
self.data_ptr = start_addr - 1
return start_addr
def allocate_var(self, name, var_type="int"):
var_type = self.apply_typedef(var_type)
if name in self.variables:
return self.variables[name]
if var_type.startswith("struct:"):
sname = var_type.split(":")[1]
fields = self.struct_definitions[sname]
length = len(fields) # each 1 byte
start_addr = self.allocate_bytes(length)
var = Variable(name, start_addr, var_type)
self.variables[name] = var
return var
else:
start_addr = self.allocate_bytes(1)
var = Variable(name, start_addr, var_type)
self.variables[name] = var
return var
def allocate_array(self, name, length, var_type="int"):
var_type = self.apply_typedef(var_type)
arr_start = self.allocate_bytes(length)
var_addr = self.allocate_bytes(1)
var = Variable(name, var_addr, "array")
self.variables[name] = var
return var, arr_start
def store_string(self, string_value):
string_value = string_value.replace('\\n', '\n')
length = len(string_value) + 1
start_addr = self.allocate_bytes(length)
asm = []
current_addr = start_addr
for ch in string_value:
ascii_val = ord(ch)
asm.append(f"ldw a, {ascii_val}")
asm.append(f"str a, 0x{current_addr:X}")
current_addr += 1
asm.append("ldw a, 0")
asm.append(f"str a, 0x{current_addr:X}")
return asm, start_addr
def get_struct_field_offset(self, struct_type, field_name):
sname = struct_type.split(":")[1]
fields = self.struct_definitions[sname]
for i, (fname, ftype) in enumerate(fields):
if fname == field_name:
return i
raise Exception(f"Field {field_name} not found in {struct_type}")
def parse_condition(self, cond_str):
# cond_str like "a == b" or "a != b"
m = re.match(r'([a-zA-Z_]\w*)\s*(==|!=)\s*([a-zA-Z_]\w*)', cond_str.strip())
if not m:
raise Exception("Unsupported condition: " + cond_str)
var1, op, var2 = m.groups()
return var1, op, var2
def compile_condition(self, var1, op, var2):
asm = []
v1 = self.allocate_var(var1)
v2 = self.allocate_var(var2)
asm.append(f"ldr a, 0x{v1.address:X}")
asm.append(f"ldr b, 0x{v2.address:X}")
# a = a - b
asm.append("sub a, b")
return asm, op
def extract_comment(self, line):
comment_index = line.find('//')
if comment_index != -1:
code_part = line[:comment_index]
comment_part = line[comment_index+2:].strip()
return code_part, comment_part
return line, None
def compile_line(self, code_part):
line = code_part.strip()
asm = []
if self.in_struct_def:
if line.startswith("};"):
self.struct_definitions[self.current_struct_name] = self.current_struct_fields
self.in_struct_def = False
self.current_struct_name = None
self.current_struct_fields = []
return asm
mfield = re.match(r'int\s+([a-zA-Z_]\w*)\s*;', line)
if mfield:
fname = mfield.group(1)
ftype = "int"
self.current_struct_fields.append((fname, ftype))
return asm
# struct definition start
msd = re.match(r'struct\s+([a-zA-Z_]\w*)\s*\{', line)
if msd:
self.in_struct_def = True
self.current_struct_name = msd.group(1)
self.current_struct_fields = []
return asm
# struct var declaration
msv = re.match(r'struct\s+([a-zA-Z_]\w*)\s+([a-zA-Z_]\w*)\s*;', line)
if msv:
sname, varname = msv.groups()
var_type = "struct:" + sname
self.allocate_var(varname, var_type)
return asm
# if statement
mif = re.match(r'if\s*\(([^)]+)\)\s*\{', line)
if mif:
cond_str = mif.group(1)
var1, op, var2 = self.parse_condition(cond_str)
end_label = self.new_label("endif")
cond_code, cmp_op = self.compile_condition(var1, op, var2)
asm.extend(cond_code)
# if '==': jump if not zero a != 0
# if '!=': jump if zero a == 0
if cmp_op == '==':
asm.append("bne a, 0, " + end_label)
else:
asm.append("beq a, 0, " + end_label)
self.block_stack.append(('if', end_label))
return asm
# while statement
mwhile = re.match(r'while\s*\(([^)]+)\)\s*\{', line)
if mwhile:
cond_str = mwhile.group(1)
var1, op, var2 = self.parse_condition(cond_str)
start_label = self.new_label("whilestart")
end_label = self.new_label("whileend")
asm.append(start_label + ":")
cond_code, cmp_op = self.compile_condition(var1, op, var2)
asm.extend(cond_code)
if cmp_op == '==':
asm.append("bne a, 0, " + end_label)
else:
asm.append("beq a, 0, " + end_label)
self.block_stack.append(('while', start_label, end_label))
return asm
# end of block
if line == "}":
if not self.block_stack:
return asm
blk = self.block_stack.pop()
if blk[0] == 'if':
end_label = blk[1]
asm.append(end_label + ":")
elif blk[0] == 'while':
start_label = blk[1]
end_label = blk[2]
# jump back to start
asm.append(f"jmp {start_label}")
asm.append(end_label + ":")
return asm
# p.x = number;
m = re.match(r'([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\s*=\s*(\d+)\s*;', line)
if m:
varname, fieldname, value = m.groups()
value = int(value)
v = self.allocate_var(varname)
offset = self.get_struct_field_offset(v.type, fieldname)
asm.append(f"ldr a, 0x{v.address:X}")
if offset != 0:
asm.append(f"ldw b, {offset}")
asm.append("add a, b")
asm.append(f"ldw c, {value}")
asm.append("stb c, a")
return asm
# p.x = var + number;
m = re.match(r'([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\s*\+\s*(\d+)\s*;', line)
if m:
varname, fieldname, srcvar, number = m.groups()
number = int(number)
v = self.allocate_var(varname)
offset = self.get_struct_field_offset(v.type, fieldname)
asm.append(f"ldr a, 0x{v.address:X}")
if offset != 0:
asm.append(f"ldw b, {offset}")
asm.append("add a, b")
v2 = self.allocate_var(srcvar)
asm.append(f"ldr c, 0x{v2.address:X}")
asm.append(f"ldw d, {number}")
asm.append("add c, d")
asm.append("stb c, a")
return asm
# p.x = srcvar;
m = re.match(r'([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\s*;', line)
if m:
varname, fieldname, srcvar = m.groups()
v = self.allocate_var(varname)
offset = self.get_struct_field_offset(v.type, fieldname)
asm.append(f"ldr a, 0x{v.address:X}")
if offset != 0:
asm.append(f"ldw b, {offset}")
asm.append("add a, b")
v2 = self.allocate_var(srcvar)
asm.append(f"ldr c, 0x{v2.address:X}")
asm.append("stb c, a")
return asm
# x = p.x;
m = re.match(r'([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\s*;', line)
if m:
dst, varname, fieldname = m.groups()
v = self.allocate_var(varname)
offset = self.get_struct_field_offset(v.type, fieldname)
vd = self.allocate_var(dst)
asm.append(f"ldr a, 0x{v.address:X}")
if offset != 0:
asm.append(f"ldw b, {offset}")
asm.append("add a, b")
asm.append("ldb c, a")
asm.append(f"str c, 0x{vd.address:X}")
return asm
# print_int(p.x);
m = re.match(r'print_int\(([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\)\s*;', line)
if m:
varname, fieldname = m.groups()
v = self.allocate_var(varname)
offset = self.get_struct_field_offset(v.type, fieldname)
asm.append(f"ldr a, 0x{v.address:X}")
if offset != 0:
asm.append(f"ldw b, {offset}")
asm.append("add a, b")
asm.append("ldb a, a")
asm.append("int 0x01")
return asm
# int arr[10];
m = re.match(r'int\s+([a-zA-Z_]\w*)\[(\d+)\]\s*;', line)
if m:
varname = m.group(1)
length = int(m.group(2))
arr_var, start_addr = self.allocate_array(varname, length)
asm.append(f"ldw a, 0x{start_addr:X}")
asm.append(f"str a, 0x{arr_var.address:X}")
return asm
# int x = number;
m = re.match(r'int\s+([a-zA-Z_]\w*)\s*=\s*(\d+)\s*;', line)
if m:
varname = m.group(1)
value = int(m.group(2))
var = self.allocate_var(varname, "int")
asm.append(f"ldw a, {value}")
asm.append(f"str a, 0x{var.address:X}")
return asm
# int y = x + number;
m = re.match(r'int\s+([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\s*\+\s*(\d+)\s*;', line)
if m:
varname, var2, number = m.groups()
number = int(number)
v1 = self.allocate_var(varname, "int")
v2 = self.allocate_var(var2, "int")
asm.append(f"ldr a, 0x{v2.address:X}")
asm.append(f"ldw b, {number}")
asm.append("add a, b")
asm.append(f"str a, 0x{v1.address:X}")
return asm
# char *msg = "Hello\n";
m = re.match(r'char\s*\*\s*([a-zA-Z_]\w*)\s*=\s*"([^"]*)"\s*;', line)
if m:
varname, string_val = m.groups()
v = self.allocate_var(varname, "char*")
code, start_addr = self.store_string(string_val)
asm.extend(code)
asm.append(f"ldw a, 0x{start_addr:X}")
asm.append(f"str a, 0x{v.address:X}")
return asm
# var = number;
m = re.match(r'([a-zA-Z_]\w*)\s*=\s*(\d+)\s*;', line)
if m:
varname, value = m.groups()
value = int(value)
v = self.allocate_var(varname, "int")
asm.append(f"ldw a, {value}")
asm.append(f"str a, 0x{v.address:X}")
return asm
# var = var2 + number;
m = re.match(r'([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\s*\+\s*(\d+)\s*;', line)
if m:
varname, var2, number = m.groups()
number = int(number)
v1 = self.allocate_var(varname, "int")
v2 = self.allocate_var(var2, "int")
asm.append(f"ldr a, 0x{v2.address:X}")
asm.append(f"ldw b, {number}")
asm.append("add a, b")
asm.append(f"str a, 0x{v1.address:X}")
return asm
# var[index] = number;
m = re.match(r'([a-zA-Z_]\w*)\[(\d+)\]\s*=\s*(\d+)\s*;', line)
if m:
arr, index, value = m.groups()
index = int(index)
value = int(value)
arr_var = self.allocate_var(arr)
asm.append(f"ldr a, 0x{arr_var.address:X}")
asm.append(f"ldw b, {index}")
asm.append("add a, b")
asm.append(f"ldw c, {value}")
asm.append("stb c, a")
return asm
# x = arr[index];
m = re.match(r'([a-zA-Z_]\w*)\s*=\s*([a-zA-Z_]\w*)\[(\d+)\]\s*;', line)
if m:
varname, arr, index = m.groups()
index = int(index)
v = self.allocate_var(varname, "int")
arr_var = self.allocate_var(arr)
asm.append(f"ldr a, 0x{arr_var.address:X}")
asm.append(f"ldw b, {index}")
asm.append("add a, b")
asm.append("ldb d, a")
asm.append(f"str d, 0x{v.address:X}")
return asm
# print_char(var);
m = re.match(r'print_char\(([a-zA-Z_]\w*)\)\s*;', line)
if m:
varname = m.group(1)
v = self.allocate_var(varname)
asm.append(f"ldr a, 0x{v.address:X}")
asm.append("int 0x00")
return asm
# print_char(arr[index]);
m = re.match(r'print_char\(([a-zA-Z_]\w*)\[(\d+)\]\)\s*;', line)
if m:
arr, index = m.groups()
index = int(index)
arr_var = self.allocate_var(arr)
asm.append(f"ldr a, 0x{arr_var.address:X}")
asm.append(f"ldw b, {index}")
asm.append("add a, b")
asm.append("ldb a, a")
asm.append("int 0x00")
return asm
# print_int(var);
m = re.match(r'print_int\(([a-zA-Z_]\w*)\)\s*;', line)
if m:
varname = m.group(1)
v = self.allocate_var(varname)
asm.append(f"ldr a, 0x{v.address:X}")
asm.append("int 0x01")
return asm
# print_string(var);
m = re.match(r'print_string\(([a-zA-Z_]\w*)\)\s*;', line)
if m:
varname = m.group(1)
v = self.allocate_var(varname, "char*")
asm.append("ldw d, 0")
asm.append(f"ldr b, 0x{v.address:X}")
asm.append("ldw c, 1")
asm.append("string_loop:")
asm.append("ldb a, b")
asm.append("beq a, d, string_end")
asm.append("int 0x00")
asm.append("add b, c")
asm.append("jmp string_loop")
asm.append("string_end:")
return asm
# return number;
m = re.match(r'return\s+(\d+)\s*;', line)
if m:
asm.append("int 0xFF")
return asm
# Unrecognized line or empty
return asm
def compile_c(self, c_code):
# First, parse everything to detect structs and typedef done in preprocess
all_lines = c_code.split('\n')
# struct definitions might appear outside main
for cline in all_lines:
self.compile_line(cline)
# Extract lines inside main
lines = []
in_main = False
for cline in all_lines:
cline = cline.rstrip()
if 'int main(' in cline:
in_main = True
continue
if in_main:
if cline.startswith('}'):
in_main = False
break
lines.append(cline)
asm = ["main:"]
for line in lines:
code_part, comment_part = self.extract_comment(line)
instructions = self.compile_line(code_part)
if instructions:
for i, instr in enumerate(instructions):
if i == 0 and comment_part:
asm.append(f" {instr} ; {comment_part}")
else:
asm.append(f" {instr}")
else:
if comment_part:
asm.append(f" ; {comment_part}")
return asm
if __name__ == "__main__":
compiler = Compiler()
preprocessed_lines = compiler.preprocess("main.c")
c_code = "\n".join(preprocessed_lines)
asm_code = compiler.compile_c(c_code)
with open("test.asm", "w") as out:
for line in asm_code:
out.write(line + "\n")

BIN
drive8.bin Normal file

Binary file not shown.

35
main.asm Normal file
View File

@ -0,0 +1,35 @@
; Initialize text mode
main:
ldw a, 0 ; Mode: 1 for text mode
ldw b, 800 ; Horizontal resolution
ldw c, 600 ; Vertical resolution
int 0x70 ; Initialize display
jsr test
ldw b, 0 ; Cursor position (character cell index)
ldw c, 0xFFFFFF ; White color
main_loop:
; Get key down (handle key press/release)
mov f, b ; f <- b
int 0xF6 ; a <- keycode | b <- first press
add b, f, ; b = f + b ; This moves the value in f back to B, and sence b is eyther 1 or 0, it will eyther increment it, or not increment it.
str b, 0xEE
str a, 0xEF
; If a key is not pressed (register 0x1 == 0), continue the loop
ldw d, 0 ; Check if a key is pressed (if register 0x1 == 1)
beq a, d, main_loop ; If A == 0, loop back to main_loop (no key is pressed)
; Render the character
int 0x72 ; Render the character (using the keycode from register 0x0 at position b)
jmp main_loop ; Jump back to the main loop
%include "std.asm"

12
main.c Normal file
View File

@ -0,0 +1,12 @@
int main()
{
char *msg = "Welcome!\n";
print_string(msg);
while (1) // Not implemented
{
}
return 0;
}

769
main.py Normal file
View File

@ -0,0 +1,769 @@
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()

1
program.py Normal file
View File

@ -0,0 +1 @@
program = [1,0,87,5,0,1004,1,0,101,5,0,1005,1,0,108,5,0,1006,1,0,99,5,0,1007,1,0,111,5,0,1008,1,0,109,5,0,1009,1,0,101,5,0,1010,1,0,33,5,0,1011,1,0,10,5,0,1012,1,0,0,5,0,1013,1,0,1004,5,0,1024,1,3,0,6,1,1024,1,2,1,21,0,1,9,0,3,91,10,0,0,3,1,2,17,75,0,10,255,0]

18
std.asm Normal file
View File

@ -0,0 +1,18 @@
; Simplified Standard Library for CPU Emulator
; No sections, strings, or floating-point numbers
; Mode definitions
%define BITMAP_MODE 0
%define TEXT_MODE 1
; Boolean values
%define null 0
%define true 1
%define false 0
test:
ldw b, 0x5
ldb a, b
int 0x1
ret

19
std.h Normal file
View File

@ -0,0 +1,19 @@
#define true 1
#define false 0
#define TEST 256
struct vec2 {
int x;
int y;
};
struct vec3 {
int x;
int y;
int z;
};
int test() {
return 1;
}

34
test.asm Normal file
View File

@ -0,0 +1,34 @@
main:
ldw a, 87
str a, 0x3EC
ldw a, 101
str a, 0x3ED
ldw a, 108
str a, 0x3EE
ldw a, 99
str a, 0x3EF
ldw a, 111
str a, 0x3F0
ldw a, 109
str a, 0x3F1
ldw a, 101
str a, 0x3F2
ldw a, 33
str a, 0x3F3
ldw a, 10
str a, 0x3F4
ldw a, 0
str a, 0x3F5
ldw a, 0x3EC
str a, 0x400
ldw d, 0
ldr b, 0x400
ldw c, 1
string_loop:
ldb a, b
beq a, d, string_end
int 0x00
add b, c
jmp string_loop
string_end:
int 0xFF

309
tests/C-Parcer.py Normal file
View File

@ -0,0 +1,309 @@
# User settings
filename = "main.c"
DEBUG = False
types = ['int', 'void'] # Recognized types
variable_data = {}
CurrentCompilerLine = 1
import re
def CompileError(message):
global CurrentCompilerLine
column = 0
print(f"{filename}:{CurrentCompilerLine}:{column}: Error: {message}")
def DEBUG_MODE_PRINT(*args):
if DEBUG:
print(*args)
# Tokenization regex for splitting code into meaningful segments
tokenizer = re.compile(r'(\w+|\(|\)|,|;|=|\+|-|\*|/|{|})')
# Function to parse C code into chunks
def parse_code_to_chunks(code):
chunks = [] # List to hold all chunks
current_chunk = [] # Temporary list for tokens in the current chunk
tokens = tokenizer.findall(code) # Split the code into tokens
for token in tokens:
if token == ';': # End of a statement
if current_chunk:
#current_chunk.append(token) # Include the semicolon
chunks.append(current_chunk) # Finalize the chunk
current_chunk = [] # Reset for the next chunk
elif token in ['{', '}']: # Start or end of a block
if current_chunk:
chunks.append(current_chunk) # Finalize the current chunk
current_chunk = []
chunks.append([token]) # Braces are standalone chunks
else:
current_chunk.append(token) # Add token to the current chunk
# Add any remaining tokens as a final chunk
if current_chunk:
chunks.append(current_chunk)
return chunks
def classify_assignment(assignment):
# Split the assignment into variable and value
var, value = assignment.split('=', 1)
var = var.strip()
value = value.strip()
# Check if the value is a single number (constant)
if value.isdigit():
return 1 # Single number
# Check if the value is a simple arithmetic expression
elif any(op in value for op in ['+', '-', '*', '/']):
# Check for parentheses and handle them recursively
if '(' in value and ')' in value:
# Expression with parentheses: check for math with variables or constants
return 3 # Math between variables or variables/constants, including parentheses
else:
# Extract operands and operator to handle math without parentheses
operator = next((op for op in ['+', '-', '*', '/'] if op in value), None)
left, right = [p.strip() for p in value.split(operator, 1)]
if left.isdigit() and right.isdigit():
return 2 # Math between two numbers
elif left.isidentifier() or right.isidentifier():
return 3 # Math between variables or variables/constants
# Check if the value is equal to another variable
elif value.isidentifier():
return 4 # Equal to another variable
# Return 0 if the assignment does not match any known category
return 0
import re
def parse_operands(equation: str):
# Remove spaces for easier handling
equation = equation.replace(" ", "")
# Base case: if no parentheses, split by the operator
if '(' not in equation and ')' not in equation:
return parse_simple_expression(equation)
# If parentheses are present, handle recursively
while '(' in equation:
# Find the innermost parentheses expression
innermost = re.search(r'\(([^()]+)\)', equation)
if innermost:
# Recursively parse the expression inside parentheses
inner_expr = innermost.group(1)
result = parse_simple_expression(inner_expr)
# Replace the expression inside parentheses with the parsed result
equation = equation.replace(f'({inner_expr})', result)
return parse_simple_expression(equation)
def parse_simple_expression(equation):
# This function handles simple expressions without parentheses
match = re.match(r'([a-zA-Z0-9_]+|\d+)\s*([+\-*/])\s*([a-zA-Z0-9_]+|\d+)', equation)
if match:
operand1 = match.group(1) # The first operand (could be a variable or constant)
operator = match.group(2) # The operator (+, -, *, /)
operand2 = match.group(3) # The second operand (could be a variable or constant)
# Return operands in a list, this can be extended for more complex parsing
return [operand1, operand2]
else:
raise ValueError(f"Invalid equation format: {equation}")
def create_new_variable(name, address):
variable_data[name] = address
def equasion_to_asm(equasion:list, output_address:int) -> list[list[str]]:
equasion = " ".join(equasion)
#DEBUG_MODE_PRINT(equasion)
output = []
__ = equasion.split("=")
value = __[1].strip()
del __
#DEBUG_MODE_PRINT(value)
if classify_assignment(equasion) == 1:
# constant
# int x = 1;
DEBUG_MODE_PRINT("1 >", value)
output.append([f'ldw a, {value}'])
output.append([f'str a, {output_address}'])
elif classify_assignment(equasion) == 4:
DEBUG_MODE_PRINT("4 >", value)
# equal to variable
# int x = y
address = variable_data[value]
output.append([f'ldr a, {address}']) # move the data in address to register a
output.append([f'str a, {output_address}']) # put register a into free_memory_address
elif classify_assignment(equasion) == 3:
# arithmetic expression: example x = y + 5;
# Evaluate the expression and store the result
DEBUG_MODE_PRINT("3 >", value)
operands = parse_operands(value) # You need to parse operands like 'y + 5'
if operands[0].isidentifier():
output.append([f'ldr a, {variable_data[operands[0]]}']) # load value of 'y' into a
else:
output.append([f'ldw a, {operands[0]}']) # load value of 'y' into a
if operands[1].isidentifier():
output.append([f'ldr b, {variable_data[operands[1]]}']) # load value of 'y' into a
else:
output.append([f'ldw b, {operands[1]}']) # load value of 'y' into a
output.append([f'add a, b']) # perform addition a = y + 5
# Store in variable or in new variable
output.append([f'str a, {output_address}']) # store result into memory address
else:
# Not Implemented
DEBUG_MODE_PRINT("NotImpl: ",equasion)
return output
# Read the C code from the file
with open(filename, "r") as f:
code = f.read()
# Preprocess the code: remove newlines and excess whitespace
code = re.sub(r'\s+', ' ', code.strip())
# Parse the code into chunks
chunks = parse_code_to_chunks(code)
#DEBUG_MODE_PRINT("Chunks:", chunks)
# Extract functions and variables
free_memory_address = 0xFF
output = []
for chunk in chunks:
#DEBUG_MODE_PRINT(chunk)
chunk: list
if chunk[0] in types:
if "=" in chunk:
if chunk[0] in variable_data:
CompileError(f"Redefinition of variable: '{chunk[0]}'")
create_new_variable(chunk[1], free_memory_address)
output += equasion_to_asm(chunk, free_memory_address)
free_memory_address -= 1
elif "=" in chunk:
#DEBUG_MODE_PRINT(chunk)
if chunk[0] in variable_data:
output += equasion_to_asm(chunk, free_memory_address)
else:
CompileError(f"Undefined Variable: '{chunk[0]}'")
else:
# DEBUG_MODE_PRINT(chunk)
for i,_ in enumerate(chunk):
chunk[i] = chunk[i].strip("{}")
if chunk != ['']:
#print(chunk)
if chunk[0] == 'return':
# handle return statements
pass
elif chunk[0].isidentifier():
#print("Function Name: " + str(chunk[0]))
function_name = chunk[0]
arguments = chunk[1:]
#print(arguments)
for itter, argument in enumerate(arguments):
if argument == ',' or argument == '(' or argument == ')':
arguments.pop(itter)
continue
arguments[itter] = argument
#print(arguments)
if function_name == 'syscall':
output += [f'int {int(arguments[0], 16)}']
CurrentCompilerLine += 1
with open("output.asm", "w") as f:
# Write the assembly code to the output file
f.write("main:\n ")
for line in output:
f.write("".join(line))
f.write("\n ")

117
tests/lang-to-asm.py Normal file
View File

@ -0,0 +1,117 @@
def parse_math_to_instructions(math_expression, memory_map):
"""
Converts a simple math expression or variable assignment to assembly instructions.
:param math_expression: The math expression to convert.
:param memory_map: A dictionary tracking variable memory locations.
:return: A list of assembly instructions.
"""
instructions = []
temp_memory_address = 0xFF # Starting memory address for variables
# Check for assignment
if '=' in math_expression:
var_name, expr = map(str.strip, math_expression.split('='))
expr = expr.strip(';') # Remove trailing semicolon
var_name = var_name.strip()
var_name = var_name.strip("int ")
# Generate instructions for the expression
expr_instructions, result_register = parse_expression(expr, memory_map, temp_memory_address)
instructions.extend(expr_instructions)
# Assign the result to the variable
if var_name not in memory_map:
memory_map[var_name] = temp_memory_address
temp_memory_address -= 1
instructions.append(f"str {result_register}, 0x{memory_map[var_name]:X} ; Save variable {var_name} in memory")
else:
# Generate instructions for the expression
expr_instructions, _ = parse_expression(math_expression, memory_map, temp_memory_address)
instructions.extend(expr_instructions)
return instructions
def precedence(op):
"""
Returns the precedence of the given operator.
"""
if op in ('+', '-'):
return 1
if op in ('*', '/'):
return 2
return 0
def apply_operator(instructions, operand_stack, operator):
"""
Applies an operator to the top two operands in the operand stack and generates instructions.
"""
b = operand_stack.pop()
a = operand_stack.pop()
if operator == '+':
instructions.append("add a, b")
elif operator == '-':
instructions.append("sub a, b")
elif operator == '*':
instructions.append("mul a, b")
elif operator == '/':
instructions.append("div a, b")
operand_stack.append('a')
def parse_expression(expr, memory_map, temp_memory_address):
"""
Parses a math expression and generates instructions.
:param expr: The math expression.
:param memory_map: Memory map for variables.
:param temp_memory_address: Memory address to use for new variables.
:return: (list of instructions, result_register)
"""
instructions = []
tokens = expr.replace('(', ' ( ').replace(')', ' ) ').split()
operator_stack = []
operand_stack = []
for token in tokens:
if token.isdigit():
if 'a' not in operand_stack:
instructions.append(f"ldw a, {token}")
operand_stack.append('a')
else:
instructions.append(f"ldw b, {token}")
operand_stack.append('b')
elif token in memory_map:
if 'a' not in operand_stack:
instructions.append(f"ldr a, 0x{memory_map[token]:X}")
operand_stack.append('a')
else:
instructions.append(f"ldr b, 0x{memory_map[token]:X}")
operand_stack.append('b')
elif token in ['+', '-', '*', '/']:
while (operator_stack and precedence(operator_stack[-1]) >= precedence(token)):
apply_operator(instructions, operand_stack, operator_stack.pop())
operator_stack.append(token)
elif token == '(':
operator_stack.append(token)
elif token == ')':
while operator_stack and operator_stack[-1] != '(':
apply_operator(instructions, operand_stack, operator_stack.pop())
operator_stack.pop() # Remove '('
while operator_stack:
apply_operator(instructions, operand_stack, operator_stack.pop())
return instructions, 'a'
# Example Usage
memory_map = {}
expressions = [
"1 + ( 3 + 8 )",
"int variable = 1 + ( 3 + 8 );",
"int variable_dose = variable + ( 4 + 4 );"
]
for expr in expressions:
instructions = parse_math_to_instructions(expr, memory_map)
for instr in instructions:
print(instr)
print('; - - - - - - - - - -')

16
tests/main.c Normal file
View File

@ -0,0 +1,16 @@
int main()
{
int num1 = 5;
int num2 = 3;
int sum = num1 + num2;
sum = num1 + 9;
syscall(0x01);
}
void test()
{
int x = 4;
int y = x;
return x;
}

0
tests/test.py Normal file
View File