1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
#!/usr/bin/env python3
# Assemble ATK16 assembly to bytecode
import sys
import os
from .asm_ops import *
from .asm_eval import *
from .asm_pass0 import pass_0
from .asm_pass1 import pass_1
from .asm_pass2 import pass_2
from .asm_pass3 import pass_3, DbgLabelTable, DbgSourceTable
from .asm_pass4 import pass_4
from .pad_bin import pad_binary
@dataclass
class AssemblyResult:
program: bytearray
dbg_label_table: DbgLabelTable
dbg_source_table: DbgSourceTable
def assemble(source: str, file_name: str) -> AssemblyResult:
src_lines = source.splitlines()
result0 = pass_0(src_lines, file_name)
result1 = pass_1(result0)
result2 = pass_2(result1)
result3 = pass_3(result2)
result4 = pass_4(result3)
nop = bytearray([0b1000_0000, 0])
result = bytearray()
for line in result4.lines:
for (symbol, symbol_value) in result4.symbols.items():
if line.address == symbol_value:
print(f"{symbol}:")
if len(result) < 2 * line.address + 1:
result.extend((2 * line.address + 1 - len(result)) * nop)
out_line = f"{line.address:>08x} 0x{line.word:>04x} {line.text}"
out_spaces_n = (42 - len(out_line))
out_spaces = out_spaces_n * " " if out_spaces_n > 0 else 4 * " "
print(f"{out_line}{out_spaces}{line.original_text}")
result[2 * line.address + 0] = ((line.word >> 8) & 0xff)
result[2 * line.address + 1] = ((line.word >> 0) & 0xff)
return AssemblyResult(
program=result,
dbg_label_table=result4.dbg_label_table,
dbg_source_table=result4.dbg_source_table,
)
def show_help():
print("usage: assembler.py [-h|--help] [-o outfile] <infile>")
print("")
print("Positional arguments:")
print(" <infile> input file in .atk16 assembler text format (use - for stdin)")
print("")
print("Options:")
print(" -o outfile output file in binary format")
print(" -h|--help show this help")
@dataclass
class CliOptions:
infile: str
outfile: str
def parse_cli_args(args: list[str]) -> CliOptions:
i = 1
infile: str | None = None
outfile: str | None = None
while i < len(args):
if args[i] == "-o":
if i + 1 >= len(args):
print("Missing argument for -o")
show_help()
sys.exit(1)
outfile = args[i + 1]
i += 2
elif args[i] == "-h" or args[i] == "--help":
show_help()
sys.exit(0)
elif not infile:
infile = args[i]
i += 1
else:
raise Exception(f"Unknown argument: {args[i]}")
if not infile or not outfile:
show_help()
sys.exit(1)
return CliOptions(
infile=infile,
outfile=outfile,
)
def main():
options = parse_cli_args(sys.argv)
src: str = ""
if (options.infile == "-"):
for line in sys.stdin:
src += line
else:
with open(options.infile, "r") as f:
src = f.read()
result = assemble(src, options.infile)
out_dirname = os.path.dirname(options.outfile)
os.makedirs(out_dirname, exist_ok=True)
with open(options.outfile, "wb") as f:
f.write(result.program)
pad_binary(options.outfile, 64 * 1024)
print(f"Wrote 64 KB to {options.outfile} ({len(result.program)} B without padding)")
dbg_outfile_path = options.outfile + ".dbg"
with open(dbg_outfile_path, "w") as f:
for i in range(32 * 1024): # 32K words
line = f"0x{i:>04x};"
if i not in result.dbg_source_table:
continue
dbg_source_info = result.dbg_source_table[i]
line += f"{dbg_source_info.src_file}:{dbg_source_info.line_num};{dbg_source_info.expanded_text};{dbg_source_info.original_text};"
if i in result.dbg_label_table:
line += f"{';'.join(result.dbg_label_table[i])}"
f.write(line + "\n")
if __name__ == "__main__":
main()
|