feat: working assembler! second pass implemenation. mnemonic parsing and binary construction and writing

This commit is contained in:
2026-02-24 14:10:09 +01:00
parent b968110397
commit 144179e0b8
8 changed files with 27579 additions and 29 deletions

View File

@@ -3,11 +3,9 @@
// by Nisan and Schocken, MIT Press. // by Nisan and Schocken, MIT Press.
// Computes R0 = 2 + 3 (R0 refers to RAM[0]) // Computes R0 = 2 + 3 (R0 refers to RAM[0])
(lab)
@2 @2
D=A D=A
@3 @3
D=D+A D=D+A
(test)
@0 @0
M=D M=D

View File

@@ -1,2 +1,6 @@
// Assembled from Add.asm 0000000000000010
// 12 lines processed 1110110000010000
0000000000000011
1110000010010000
0000000000000000
1110001100001000

View File

@@ -6,12 +6,10 @@
// Computes R2 = max(R0, R1) (R0,R1,R2 refer to RAM[0],RAM[1],RAM[2]) // Computes R2 = max(R0, R1) (R0,R1,R2 refer to RAM[0],RAM[1],RAM[2])
// Usage: Before executing, put two values in R0 and R1. // Usage: Before executing, put two values in R0 and R1.
// R0 = 10
// R1 = 15
// d = 10
// d = 10 - 15
// D = R0 - R1 // D = R0 - R1
/*
@R0 @R0
D=M D=M
@R1 @R1
@@ -33,3 +31,36 @@
(END) (END)
@END @END
0;JMP 0;JMP
*/
// Max
// Takes two numbers and saves the highest of them
// Get the sum of R0 & R1
// Save it in R2
// Get Value of R0
// Compare it value of R1
// Remember that the conditional can only be compared to zero.
// GT. LS, EQ etc.. All to zero
// Create a label and jump to it
@R0
D=M // Value of R0
@R1
D=D-M // New D(playground) is Old D(Value of R0) Minus Value of R1. Basically R0 - R1
@ITSR0
D;JGT // If D (R0 - R1) is greater than 0, then R0 is biger. Otherwise R1 or equal.
// Else it R1
// The end Statements
(ITSR0) // Jump here if R0
@R0
D=M // Set value of R0 to the playground variable.
(OUTPUT_D) // End up here either way. Depending on value.
@R2
M=D // Set R2 to D either way. Just with or without the ITSR0 iteration
(END) // The end final loop. Necessary to preven stack overflows.
@END
0;JMP // Infinite Jumping to end the program

View File

@@ -0,0 +1,16 @@
0000000000000000
1111110000010000
0000000000000001
1111010011010000
0000000000001010
1110001100000001
0000000000000001
1111110000010000
0000000000001100
1110101010000111
0000000000000000
1111110000010000
0000000000000010
1110001100001000
0000000000001110
1110101010000111

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
from pathlib import Path from pathlib import Path
from assembler.code import Code
from assembler.constants.command_types import CommandType from assembler.constants.command_types import CommandType
from assembler.parser import Parser from assembler.parser import Parser
@@ -47,13 +48,16 @@ class Assembler:
Main assembly process - coordinates first and second pass. Main assembly process - coordinates first and second pass.
Public API method that CLI will call. Public API method that CLI will call.
""" """
print("[] - Starting First Pass..") print("[ ] - Starting First Pass..")
self._first_pass() self._first_pass()
print("Symbol Table After First Pass")
print(self.symbol_table.symbols)
print("[x] - First Pass Done..") print("[x] - First Pass Done..")
print("[] - Starting Second Pass..") print("[ ] - Starting Second Pass..")
self._second_pass() self._second_pass()
print("[x] - Second pass done..")
print("[ ] - Writing output file..")
self._write_output()
print("[x] - Output file written..")
self._assembled_success()
def _trim_comments_and_whitespace(self, lines: list[str]) -> list[str]: def _trim_comments_and_whitespace(self, lines: list[str]) -> list[str]:
""" """
@@ -88,7 +92,6 @@ class Assembler:
while self.parser.has_more_commands(): while self.parser.has_more_commands():
self.parser.advance() # Move to next command self.parser.advance() # Move to next command
print(self.parser.command_type())
cmd_type = self.parser.command_type() cmd_type = self.parser.command_type()
if cmd_type == CommandType.L_COMMAND: if cmd_type == CommandType.L_COMMAND:
label = self.parser.symbol() # Extract label name label = self.parser.symbol() # Extract label name
@@ -105,43 +108,59 @@ class Assembler:
Second pass: Translate instructions to binary. Second pass: Translate instructions to binary.
Handles A-commands, C-commands, and resolves symbols. Handles A-commands, C-commands, and resolves symbols.
""" """
print(self.cleaned_lines)
while self.parser.has_more_commands(): while self.parser.has_more_commands():
self.parser.advance() # Move to next command self.parser.advance() # Move to next command
print(self.parser.command_type())
# cmd cmd_type # cmd cmd_type
cmd_type = self.parser.command_type() cmd_type = self.parser.command_type()
if cmd_type == CommandType.L_COMMAND: if cmd_type == CommandType.L_COMMAND:
continue continue
elif cmd_type == CommandType.A_COMMAND: elif cmd_type == CommandType.A_COMMAND:
print("....") binary = self._translate_a_command(self.parser.symbol())
self.binary_instructions.append(binary) # Store resultp
# Do A command things # Do A command things
elif cmd_type == CommandType.C_COMMAND: elif cmd_type == CommandType.C_COMMAND:
print("..") binary = self._translate_c_command()
self.binary_instructions.append(binary) # Store resultp
# Do C command Things # Do C command Things
def _translate_a_command(self, symbol: str) -> str: def _translate_a_command(self, symbol: str) -> str:
""" # Case 1: Check if symbol is a numeric constant (e.g., "2", "100")
Translate A-command to 16-bit binary. if symbol.isdigit():
Handles: numeric constants, predefined symbols, variables. address = int(symbol)
"""
raise NotImplementedError("Not implemented") # Case 2: Check if symbol exists in symbol table (predefined or label)
elif self.symbol_table.contains(symbol):
address = self.symbol_table.get_address(symbol)
# Case 3: It's a new variable - allocate next available address
else:
address = self.next_variable_address
self.symbol_table.add_entry(symbol, address)
self.next_variable_address += 1
# Convert address to 16-bit binary string (format: 0xxxxxxxxxxxxxxx)
return format(address, "016b")
def _translate_c_command(self) -> str: def _translate_c_command(self) -> str:
""" """
Translate C-command to 16-bit binary. Translate C-command to 16-bit binary.
Format: 111 a c1c2c3c4c5c6 d1d2d3 j1j2j3 Format: 111 a c1c2c3c4c5c6 d1d2d3 j1j2j3
""" """
raise NotImplementedError("Not implemented")
comp = Code.comp(self.parser.comp())
dest = Code.dest(self.parser.dest())
jump = Code.jump(self.parser.jump())
binary = f"111{comp}{dest}{jump}"
return binary
def _write_output(self) -> None: def _write_output(self) -> None:
"""Write binary instructions to output file.""" """Write binary instructions to output file."""
with open(self.output_file, "w") as f: with open(self.output_file, "w") as f:
f.write(f"// Assembled from {self.input_file.name}\n") f.write("\n".join(self.binary_instructions))
f.write(f"// {len(self.cleaned_lines)} lines processed\n")
raise NotImplementedError("Not implemented")
def _assembled_success(self) -> None: def _assembled_success(self) -> None:
"""Success Message""" """Success Message"""

View File

@@ -28,7 +28,6 @@ def main():
try: try:
assembler = Assembler(input_path, output_path) assembler = Assembler(input_path, output_path)
assembler.assemble() assembler.assemble()
print(f"Successfully assembled {input_path} -> {output_path}")
except ValueError as e: except ValueError as e:
# Handle validation errors (empty labels, invalid syntax, etc.) # Handle validation errors (empty labels, invalid syntax, etc.)
print(f"Assembly Error: {e}", file=sys.stderr) print(f"Assembly Error: {e}", file=sys.stderr)

View File

@@ -1,4 +1,4 @@
from .code_tables import DEST_TABLE, COMP_TABLE, JUMP_TABLE from .code_tables import COMP_TABLE, DEST_TABLE, JUMP_TABLE
class Code: class Code: