aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_asm/assembler.py
blob: 4cd2cefa62d7499c7f21e0d5ca9387dfe6eb761b (plain)
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
#!/usr/bin/env python3
# Assemble ATK16 assembly to bytecode

import sys
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()
  # initially one nop
  result.extend(nop)

  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 main():
  if len(sys.argv) != 3:
    print("usage: assembler.py <infile> <outfile> # read from file")
    print("       assembler.py - <outfile>        # read from stdin")
    sys.exit(1)

  infile_path = sys.argv[1]
  outfile_path = sys.argv[2]

  src: str = ""
  if (infile_path == "-"):
    for line in sys.stdin:
      src += line
  else:
    with open(infile_path, "r") as f:
      src = f.read()

  result = assemble(src, infile_path)

  with open(outfile_path, "wb") as f:
    f.write(result.program)

  pad_binary(outfile_path, 64 * 1024)

  print(f"Wrote 64 KB to {outfile_path} ({len(result.program)} B without padding)")

  dbg_outfile_path = outfile_path + ".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()