aboutsummaryrefslogtreecommitdiffstats
path: root/src/assembler.py
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2023-04-10 21:10:16 +0300
committerJan Tuomi <jans.tuomi@gmail.com>2023-04-10 23:55:17 +0300
commit9a6001ad380e83a545188b58145387593b117b48 (patch)
treea991cea5e5163b18c0666de89a7eccd55308b227 /src/assembler.py
parent56fd37c60eb0d58ed4762340ddc85b74347bcd71 (diff)
Refactor into multipass compiler
Diffstat (limited to 'src/assembler.py')
-rwxr-xr-xsrc/assembler.py94
1 files changed, 16 insertions, 78 deletions
diff --git a/src/assembler.py b/src/assembler.py
index a4ddde4..fc7bc45 100755
--- a/src/assembler.py
+++ b/src/assembler.py
@@ -2,10 +2,12 @@
# Assemble ATK16 assembly to bytecode
import sys
-from typing import Callable
-import importlib
from assembler_ops import *
from assembler_eval import *
+from asm_pass1 import pass_1
+from asm_pass2 import pass_2
+from asm_pass3 import pass_3
+from asm_pass4 import pass_4
if len(sys.argv) != 3:
print("usage: assembler.py <infile> <outfile> # read from file")
@@ -47,91 +49,27 @@ def parse(line: str) -> list[str]:
result.append(acc)
return list(filter(lambda x: len(x) > 0, result))
-# 1st pass, gather labels, set options, eval operations
-options = Options()
-address: int = 0
+result1 = pass_1(src_lines)
+result2 = pass_2(result1)
+result3 = pass_3(result2)
+result4 = pass_4(result3)
-for (lineNo, line) in enumerate(src_lines):
- line = line.split(";")[0].strip()
- if line == "": continue
- keyword, *args = line.lower().split()
- match keyword:
- case "@address":
- address = eval(args[0])
- continue
- case "@label":
- labels[args[0]] = address
- # todo bugi: 1st pass ei ota monirivisiä käskyjä huomioon
- continue
- case "@opt":
- opt_name, opt_value = args
- match opt_name:
- case "stack_pointer": options.stack_pointer = opt_value
- case "csr_scratch": options.csr_scratch = opt_value
- case _: raise Exception("Unknown @opt: " + opt_name)
- case "@use":
- module_name, ops = args[0].split(":")
- ops_split = ops.split(",")
- module = importlib.import_module(module_name)
- mod_operations: dict[str, Callable[..., list[int]]] = module.operations
- for op in mod_operations:
- callable = mod_operations[op]
- if ops == "*" or op in ops_split:
- operations[op] = callable
- case _:
- pass
-
- address += 1
-
-# 2nd pass
-
-address: int = 0
nop = bytearray([0b1000_0000, 0])
result = bytearray()
# initially one nop
result.extend(nop)
-for (lineNo, line) in enumerate(src_lines):
- line = line.split(";")[0].strip()
- if line == "": continue
- keyword, *args = parse(line.lower())
-
- meta = Meta(
- address=address,
- options=options
- )
- if keyword in operations:
- words = operations[keyword](meta, *args)
- else:
- match keyword:
- # Directives
- case "@address":
- address = eval_expr(args[0])
- continue
- case "@label" | "@opt" | "@use":
- continue
-
- # Default case: evaluate as is (e.g. data word)
- case _:
- try:
- words = [eval_expr(keyword)]
- except:
- raise Exception(f"Invalid assembly at {infile_path}:{lineNo + 1}\n\n{line}")
-
- for (label, label_addr) in labels.items():
- if address == label_addr:
+for line in result4.lines:
+ for (label, label_addr) in result4.labels.items():
+ if line.address == label_addr:
print(f"{label}:")
- line_printed = False
- for word in words:
- if len(result) < 2 * address + 1:
- result.extend((2 * address + 1 - len(result)) * nop)
+ if len(result) < 2 * line.address + 1:
+ result.extend((2 * line.address + 1 - len(result)) * nop)
- print(f"{address:>08x} 0x{word:>04x} {line if not line_printed else '...'}")
- result[2 * address + 0] = ((word >> 8) & 0xff)
- result[2 * address + 1] = ((word >> 0) & 0xff)
- address += 1
- line_printed = True
+ print(f"{line.address:>08x} 0x{line.word:>04x} {line.text}")
+ result[2 * line.address + 0] = ((line.word >> 8) & 0xff)
+ result[2 * line.address + 1] = ((line.word >> 0) & 0xff)
with open(outfile_path, "wb") as f:
f.write(result)