added files from previous
This commit is contained in:
parent
1cb7bdba56
commit
5ea18b5958
347
asm-to-prg.py
347
asm-to-prg.py
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
filename = "try_to_fix_me.asm"
|
filename = "test.asm"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -86,15 +86,24 @@ def convert_to_int(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
|
||||||
|
|
||||||
def preprocess(lines, filename="main.asm"):
|
|
||||||
errors = []
|
errors = []
|
||||||
warnings = []
|
warnings = []
|
||||||
error_flag = False
|
error_flag = False
|
||||||
|
|
||||||
# Memory and stack tracking
|
# Memory and stack tracking
|
||||||
instruction_count = 0
|
instruction_count = 0
|
||||||
memory_limit = 256 # Total memory available
|
memory_limit = 1024 # Total memory available
|
||||||
stack_balance = 0
|
stack_balance = 0
|
||||||
program_length = 0 # To calculate and validate memory access
|
program_length = 0 # To calculate and validate memory access
|
||||||
|
|
||||||
@ -102,12 +111,65 @@ def preprocess(lines, filename="main.asm"):
|
|||||||
valid_registers = {"a", "b", "c", "d", "e", "f"}
|
valid_registers = {"a", "b", "c", "d", "e", "f"}
|
||||||
valid_instructions = {"ldw", "mov", "add", "sub", "str", "ldr", "int",
|
valid_instructions = {"ldw", "mov", "add", "sub", "str", "ldr", "int",
|
||||||
"push", "pop", "jsr", "ret", "xor", "and", "jmp",
|
"push", "pop", "jsr", "ret", "xor", "and", "jmp",
|
||||||
"mul", "div", "bne", "beq", "blt"}
|
"mul", "div", "bne", "beq", "blt", "ldb", "stb"}
|
||||||
label_references = []
|
label_references = []
|
||||||
labels = {}
|
labels = {}
|
||||||
|
|
||||||
# First pass: Parse instructions and calculate program length
|
# Expand include directives
|
||||||
|
expanded_lines = []
|
||||||
for line_number, line in enumerate(lines, start=1):
|
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
|
code = line.split(";")[0].strip() # Strip comments and whitespace
|
||||||
if not code:
|
if not code:
|
||||||
continue
|
continue
|
||||||
@ -116,94 +178,114 @@ def preprocess(lines, filename="main.asm"):
|
|||||||
if code.endswith(":"):
|
if code.endswith(":"):
|
||||||
label_name = code[:-1]
|
label_name = code[:-1]
|
||||||
if label_name in labels:
|
if label_name in labels:
|
||||||
warnings.append((line_number, f"duplicate label '{label_name}'", line))
|
warnings.append((line_number, f"Duplicate label '{label_name}'", line))
|
||||||
labels[label_name] = instruction_count
|
labels[label_name] = instruction_count
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Parse instruction
|
# Parse instruction
|
||||||
parts = re.split(r"\s+", code)
|
parts = code.split()
|
||||||
instruction = parts[0].lower()
|
instruction = parts[0].lower()
|
||||||
if instruction in valid_instructions:
|
if instruction == "db":
|
||||||
if instruction in {"ldw", "mov", "add", "sub", "str", "ldr", "xor", "and", "mul", "div"}:
|
# Handle string definitions
|
||||||
instruction_count += 3 # These are 3-byte instructions
|
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"}:
|
elif instruction in {"bne", "beq", "blt"}:
|
||||||
instruction_count += 4 # Conditional branches are 4-byte instructions
|
instruction_count += 4
|
||||||
elif instruction in {"push", "pop", "int", "jmp", "jsr", "ret"}:
|
elif instruction in {"push", "pop", "int", "jmp", "jsr", "ret"}:
|
||||||
instruction_count += 3 # Fixed size for other instructions
|
instruction_count += 3
|
||||||
else:
|
else:
|
||||||
errors.append((line_number, f"unknown instruction '{instruction}'", line))
|
errors.append((line_number, f"Unknown instruction '{instruction}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
|
|
||||||
program_length = instruction_count # Final length of the program
|
program_length = instruction_count # Final length of the program
|
||||||
|
|
||||||
# Second pass: Validate instructions and operands
|
# Second pass: Validate instructions and operands
|
||||||
for line_number, line in enumerate(lines, start=1):
|
for line_number, line in enumerate(expanded_lines, start=1):
|
||||||
code = line.split(";")[0].strip() # Strip comments and whitespace
|
code = line.split(";")[0].strip()
|
||||||
if not code:
|
if not code:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip labels
|
# Handle labels
|
||||||
if code.endswith(":"):
|
if code.endswith(":"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
parts = re.split(r"\s+", code)
|
parts = code.split()
|
||||||
instruction = parts[0].lower()
|
instruction = parts[0].lower()
|
||||||
operands = parts[1:] if len(parts) > 1 else []
|
operands = parts[1:] if len(parts) > 1 else []
|
||||||
|
|
||||||
# Strip commas from registers and operands
|
# Handle db strings
|
||||||
operands = [op.replace(",", "") for op in operands]
|
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
|
||||||
|
|
||||||
# Validate instruction and operands
|
# 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:
|
if instruction == "ldw" and len(operands) == 2:
|
||||||
reg, value = operands
|
reg, value = operands
|
||||||
if reg not in valid_registers:
|
if reg not in valid_registers:
|
||||||
errors.append((line_number, f"invalid register '{reg}'", line))
|
errors.append((line_number, f"Invalid register '{reg}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
elif instruction == "str" and len(operands) == 2:
|
elif instruction == "str" and len(operands) == 2:
|
||||||
reg, address = operands
|
reg, address = operands
|
||||||
if reg not in valid_registers:
|
if reg not in valid_registers:
|
||||||
errors.append((line_number, f"invalid register '{reg}'", line))
|
errors.append((line_number, f"Invalid register '{reg}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
try:
|
try:
|
||||||
mem_address = int(address, 16)
|
if str(address).startswith("0x"):
|
||||||
|
mem_address = int(address, 16)
|
||||||
|
else:
|
||||||
|
mem_address = int(address)
|
||||||
|
|
||||||
|
|
||||||
if mem_address < program_length:
|
if mem_address < program_length:
|
||||||
errors.append((line_number, f"illegal memory write to program space in '{code}'", line))
|
errors.append((line_number, f"Illegal memory write to program space in '{code}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
if mem_address > memory_limit:
|
if mem_address > memory_limit:
|
||||||
errors.append((line_number, f"illegal memory write out of bounds in '{code}'", line))
|
errors.append((line_number, f"Illegal memory write out of bounds in '{code}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors.append((line_number, f"invalid memory address '{address}'", line))
|
errors.append((line_number, f"Invalid memory address '{address}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
elif instruction in {"add", "sub", "mov", "xor", "and", "mul", "div"} and len(operands) == 2:
|
elif instruction in {"add", "sub", "mov", "xor", "and", "mul", "div", "ldb", "stb"} and len(operands) == 2:
|
||||||
reg1, reg2 = operands
|
reg1, reg2 = operands
|
||||||
if reg1 not in valid_registers or reg2 not in valid_registers:
|
if reg1 not in valid_registers or reg2 not in valid_registers:
|
||||||
errors.append((line_number, f"invalid register(s) in '{code}'", line))
|
errors.append((line_number, f"Invalid register(s) in '{code}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
elif instruction in {"push", "pop"} and len(operands) == 1:
|
elif instruction in {"push", "pop"} and len(operands) == 1:
|
||||||
reg = operands[0]
|
reg = operands[0]
|
||||||
if reg not in valid_registers:
|
if reg not in valid_registers:
|
||||||
errors.append((line_number, f"invalid register '{reg}'", line))
|
errors.append((line_number, f"Invalid register '{reg}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
if instruction == "push":
|
if instruction == "push":
|
||||||
stack_balance += 1
|
stack_balance += 1
|
||||||
if stack_balance > 16: # Example stack limit
|
if stack_balance > 16: # Example stack limit
|
||||||
warnings.append((line_number, "stack overflow detected", line))
|
warnings.append((line_number, "Stack overflow detected", line))
|
||||||
elif instruction == "pop":
|
elif instruction == "pop":
|
||||||
stack_balance -= 1
|
stack_balance -= 1
|
||||||
if stack_balance < 0:
|
if stack_balance < 0:
|
||||||
errors.append((line_number, f"stack underflow detected at '{code}'", line))
|
errors.append((line_number, f"Stack underflow detected at '{code}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
# Validate branch instructions with two registers and one label
|
# Validate branch instructions with two registers and one label
|
||||||
elif instruction in {"bne", "beq", "blt"}:
|
elif instruction in {"bne", "beq", "blt"}:
|
||||||
if len(operands) != 3:
|
if len(operands) != 3:
|
||||||
errors.append((line_number, f"branch instruction '{instruction}' should have 2 registers and 1 label", line))
|
errors.append((line_number, f"Branch instruction '{instruction}' should have 2 registers and 1 label", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
else:
|
else:
|
||||||
reg1, reg2, label = operands
|
reg1, reg2, label = operands
|
||||||
if reg1 not in valid_registers or reg2 not in valid_registers:
|
if reg1 not in valid_registers or reg2 not in valid_registers:
|
||||||
errors.append((line_number, f"invalid register(s) in '{instruction}'", line))
|
errors.append((line_number, f"Invalid register(s) in '{instruction}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
label_references.append((line_number, label, line)) # The third operand should be a label
|
label_references.append((line_number, label, line)) # The third operand should be a label
|
||||||
|
|
||||||
@ -217,12 +299,12 @@ def preprocess(lines, filename="main.asm"):
|
|||||||
# Check undefined labels
|
# Check undefined labels
|
||||||
for line_number, label, line in label_references:
|
for line_number, label, line in label_references:
|
||||||
if label not in labels:
|
if label not in labels:
|
||||||
errors.append((line_number, f"undefined label '{label}'", line))
|
errors.append((line_number, f"Undefined label '{label}'", line))
|
||||||
error_flag = True
|
error_flag = True
|
||||||
|
|
||||||
# Check stack balance at the end
|
# Check stack balance
|
||||||
if stack_balance != 0:
|
if stack_balance != 0:
|
||||||
warnings.append((0, "stack imbalance detected, unbalanced push/pop operations", ""))
|
warnings.append((0, "Stack imbalance detected, unbalanced push/pop operations", ""))
|
||||||
|
|
||||||
# Print errors and warnings
|
# Print errors and warnings
|
||||||
for line_number, message, code_line in errors:
|
for line_number, message, code_line in errors:
|
||||||
@ -241,18 +323,15 @@ def preprocess(lines, filename="main.asm"):
|
|||||||
error_flag = True
|
error_flag = True
|
||||||
print(colored(f"GLOBAL: error: Program too big, size: {program_length}", "red"))
|
print(colored(f"GLOBAL: error: Program too big, size: {program_length}", "red"))
|
||||||
|
|
||||||
|
|
||||||
# Final success message
|
|
||||||
if not error_flag:
|
if not error_flag:
|
||||||
print(colored("Preprocessing complete. No errors detected!", "green"))
|
print(colored(f"{filename}: Done!", "green"))
|
||||||
else:
|
else:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
return expanded_lines
|
||||||
|
|
||||||
|
|
||||||
|
lines = preprocess(lines, filename=filename)
|
||||||
preprocess(lines)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -263,19 +342,39 @@ preprocess(lines)
|
|||||||
|
|
||||||
lineNumber = 0
|
lineNumber = 0
|
||||||
|
|
||||||
|
COMPILE_ERROR = False
|
||||||
|
|
||||||
def _ValueError(message):
|
def _ValueError(message):
|
||||||
|
global COMPILE_ERROR
|
||||||
|
COMPILE_ERROR = True
|
||||||
print("ValueError: %s on line:" % message, lineNumber)
|
print("ValueError: %s on line:" % message, lineNumber)
|
||||||
|
|
||||||
def _IndexError(message):
|
def _IndexError(message):
|
||||||
|
global COMPILE_ERROR
|
||||||
|
COMPILE_ERROR = True
|
||||||
|
|
||||||
print("IndexError: %s on line:" % message, lineNumber)
|
print("IndexError: %s on line:" % message, lineNumber)
|
||||||
|
|
||||||
def _InstructionError(message):
|
def _InstructionError(message):
|
||||||
|
global COMPILE_ERROR
|
||||||
|
COMPILE_ERROR = True
|
||||||
|
|
||||||
print("InstructionError: %s on line:" % message, lineNumber)
|
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:
|
# for line in lines:
|
||||||
# line = line.split(";")[0] # filter out comments
|
# line = line.split(";")[0] # filter out comments
|
||||||
# print(line)
|
# print(line)
|
||||||
@ -287,6 +386,9 @@ current_label = None
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
itterrator = 0
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Remove leading and trailing whitespace
|
# Remove leading and trailing whitespace
|
||||||
stripped_line = line.strip()
|
stripped_line = line.strip()
|
||||||
@ -296,79 +398,166 @@ for line in lines:
|
|||||||
current_label = stripped_line[:-1] # Remove the colon
|
current_label = stripped_line[:-1] # Remove the colon
|
||||||
current_label = current_label.upper()
|
current_label = current_label.upper()
|
||||||
label_to_instructions[current_label] = [] # Initialize empty instruction list
|
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:
|
elif stripped_line:
|
||||||
|
|
||||||
|
|
||||||
# It's an instruction; add it to the current label's list
|
# It's an instruction; add it to the current label's list
|
||||||
if current_label is not None:
|
if current_label is not None:
|
||||||
label_to_instructions[current_label].append(stripped_line)
|
label_to_instructions[current_label].append(stripped_line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
itterrator+=1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# register letter to identifyer
|
# register letter to identifyer
|
||||||
registerDict = {'a':0x0,'b':0x1,'c':0x2,'d':0x3,'e':0x4,'f':0x5}
|
registerDict = {'a':0x0,'b':0x1,'c':0x2,'d':0x3,'e':0x4,'f':0x5}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
current_byte_offset = 0 # Tracks the current byte address
|
current_byte_offset = 0 # Tracks the current byte address
|
||||||
label_addresses = {} # Maps label names to their resolved byte addresses
|
label_addresses = {} # Maps label names to their resolved byte addresses
|
||||||
|
|
||||||
for label in label_to_instructions:
|
for label in label_to_instructions:
|
||||||
label_addresses[label] = current_byte_offset
|
label_addresses[label] = current_byte_offset
|
||||||
for line in label_to_instructions[label]:
|
for line in label_to_instructions[label]:
|
||||||
line = line.strip().split(";")[0] # strip comments
|
line = line.strip().split(";")[0] # Strip comments
|
||||||
line = line.rstrip(" ") # strip spaces at end
|
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
|
||||||
line = line.replace(",", "") # stupid way to remove commas but it works
|
|
||||||
|
|
||||||
line = line.split(" ") # get each part of the instruction
|
|
||||||
|
|
||||||
|
|
||||||
line[0] = line[0].lower() # make instruction lowercase
|
|
||||||
|
|
||||||
if line[0] == '':
|
if line[0] == '':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if line[0] in {"ldw","mov","add","sub","str","ldr","int","push","pop","jsr", "ret", 'xor', 'and', 'jmp', 'mul', 'div'}: # 3 byte instructions
|
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
|
current_byte_offset += 3
|
||||||
|
|
||||||
elif line[0] in {'bne', 'beq', 'blt'}: # 4 byte instructions
|
elif line[0] in {'bne', 'beq', 'blt'}: # 4 byte instructions
|
||||||
current_byte_offset += 4
|
current_byte_offset += 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
current_byte_offset = 0 # Tracks the current byte address
|
current_byte_offset = 0 # Tracks the current byte address
|
||||||
#print(label_addresses)
|
#print(label_addresses)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
outputBytes = []
|
outputBytes = []
|
||||||
for label in label_to_instructions:
|
for label in label_to_instructions:
|
||||||
#print(label)
|
#print(label)
|
||||||
if label_addresses[label] != current_byte_offset:
|
if label_addresses[label] != current_byte_offset and not COMPILE_ERROR:
|
||||||
raise IndexError(f"address mismatch, expected {label_addresses[label]}, got {current_byte_offset}")
|
raise IndexError(f"address mismatch, expected {label_addresses[label]}, got {current_byte_offset}")
|
||||||
# Output the results
|
# Output the results
|
||||||
for line in label_to_instructions[label]:
|
for line in label_to_instructions[label]:
|
||||||
line = line.strip().split(";")[0] # strip comments
|
|
||||||
line = line.rstrip(" ") # strip spaces at end
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
line = line.replace(",", "") # stupid way to remove commas but it works
|
if not line[0]:
|
||||||
|
|
||||||
line = line.split(" ") # get each part of the instruction
|
|
||||||
|
|
||||||
|
|
||||||
line[0] = line[0].lower() # make instruction lowercase
|
|
||||||
|
|
||||||
if line[0] == '':
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#print(line)
|
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
|
#! Code to convert to bytes
|
||||||
bytes = []
|
bytes = []
|
||||||
try:
|
try:
|
||||||
if line[0] == 'ldw': # Load immediate to register
|
|
||||||
|
# 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
|
bytes.append(0x1) # byte for load immediate value
|
||||||
# set register ID:
|
# set register ID:
|
||||||
register = registerDict.get(line[1].lower(),-1)
|
register = registerDict.get(line[1].lower(),-1)
|
||||||
@ -571,7 +760,6 @@ for label in label_to_instructions:
|
|||||||
|
|
||||||
elif line[0] == 'ret': # Load immediate to register
|
elif line[0] == 'ret': # Load immediate to register
|
||||||
bytes.append(0xE)
|
bytes.append(0xE)
|
||||||
# set register ID:
|
|
||||||
bytes.append(0x0) # padding
|
bytes.append(0x0) # padding
|
||||||
bytes.append(0x0) # padding
|
bytes.append(0x0) # padding
|
||||||
|
|
||||||
@ -692,6 +880,7 @@ for label in label_to_instructions:
|
|||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
print(line)
|
||||||
_InstructionError("Unknown Instruction")
|
_InstructionError("Unknown Instruction")
|
||||||
|
|
||||||
|
|
||||||
@ -699,6 +888,7 @@ for label in label_to_instructions:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
_IndexError("Maformed Instruction")
|
_IndexError("Maformed Instruction")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
print(line)
|
||||||
_ValueError("Unknown Error")
|
_ValueError("Unknown Error")
|
||||||
|
|
||||||
current_byte_offset += len(bytes)
|
current_byte_offset += len(bytes)
|
||||||
@ -707,8 +897,17 @@ for label in label_to_instructions:
|
|||||||
lineNumber+=1
|
lineNumber+=1
|
||||||
outputBytes += bytes
|
outputBytes += bytes
|
||||||
|
|
||||||
|
if not COMPILE_ERROR:
|
||||||
|
with open("program.py", "w") as f:
|
||||||
|
bytecode = []
|
||||||
|
for _, y in enumerate(outputBytes):
|
||||||
|
bytecode.append(str(y))
|
||||||
|
|
||||||
print(outputBytes)
|
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 ")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
548
c-to-asm.py
Normal file
548
c-to-asm.py
Normal 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
BIN
drive8.bin
Binary file not shown.
27
main.asm
27
main.asm
@ -1,9 +1,12 @@
|
|||||||
; Initialize text mode
|
; Initialize text mode
|
||||||
|
|
||||||
|
|
||||||
main:
|
main:
|
||||||
ldw a, 1 ; Mode: 1 for text mode
|
ldw a, 0 ; Mode: 1 for text mode
|
||||||
ldw b, 800 ; Horizontal resolution
|
ldw b, 800 ; Horizontal resolution
|
||||||
ldw c, 600 ; Vertical resolution
|
ldw c, 600 ; Vertical resolution
|
||||||
int 0x70 ; Initialize display
|
int 0x70 ; Initialize display
|
||||||
|
jsr test
|
||||||
ldw b, 0 ; Cursor position (character cell index)
|
ldw b, 0 ; Cursor position (character cell index)
|
||||||
ldw c, 0xFFFFFF ; White color
|
ldw c, 0xFFFFFF ; White color
|
||||||
|
|
||||||
@ -25,22 +28,8 @@ main_loop:
|
|||||||
|
|
||||||
; Render the character
|
; Render the character
|
||||||
int 0x72 ; Render the character (using the keycode from register 0x0 at position b)
|
int 0x72 ; Render the character (using the keycode from register 0x0 at position b)
|
||||||
|
|
||||||
; Write letter to disk at index ;push a
|
|
||||||
; Write letter to disk at index ;push b
|
|
||||||
; Write letter to disk at index ;push c
|
|
||||||
; Write letter to disk at index ;push d
|
|
||||||
; Write letter to disk at index ;
|
|
||||||
; Write letter to disk at index ;ldw a, 8 ; disk number
|
|
||||||
; Write letter to disk at index ;ldw b, 0 ; sector number
|
|
||||||
; Write letter to disk at index ;ldr c, 0xEE ; byte offset
|
|
||||||
; Write letter to disk at index ;ldr d, 0xEF ; value to write
|
|
||||||
; Write letter to disk at index ;
|
|
||||||
; Write letter to disk at index ;int 0x81 ; Write
|
|
||||||
; Write letter to disk at index ;
|
|
||||||
; Write letter to disk at index ;pop d
|
|
||||||
; Write letter to disk at index ;pop c
|
|
||||||
; Write letter to disk at index ;pop b
|
|
||||||
; Write letter to disk at index ;pop a
|
|
||||||
; Reset cursor to the top of the screen
|
|
||||||
jmp main_loop ; Jump back to the main loop
|
jmp main_loop ; Jump back to the main loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%include "std.asm"
|
Loading…
Reference in New Issue
Block a user