feat: working assembler! second pass implemenation. mnemonic parsing and binary construction and writing
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
// Assembled from Add.asm
|
0000000000000010
|
||||||
// 12 lines processed
|
1110110000010000
|
||||||
|
0000000000000011
|
||||||
|
1110000010010000
|
||||||
|
0000000000000000
|
||||||
|
1110001100001000
|
||||||
@@ -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
|
||||||
|
|||||||
16
project-6-assembler/assembler/provided-asm-files/Max.hack
Normal file
16
project-6-assembler/assembler/provided-asm-files/Max.hack
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
0000000000000000
|
||||||
|
1111110000010000
|
||||||
|
0000000000000001
|
||||||
|
1111010011010000
|
||||||
|
0000000000001010
|
||||||
|
1110001100000001
|
||||||
|
0000000000000001
|
||||||
|
1111110000010000
|
||||||
|
0000000000001100
|
||||||
|
1110101010000111
|
||||||
|
0000000000000000
|
||||||
|
1111110000010000
|
||||||
|
0000000000000010
|
||||||
|
1110001100001000
|
||||||
|
0000000000001110
|
||||||
|
1110101010000111
|
||||||
27483
project-6-assembler/assembler/provided-asm-files/Pong.hack
Normal file
27483
project-6-assembler/assembler/provided-asm-files/Pong.hack
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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"""
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user