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
|
from dataclasses import dataclass
from .asm_ops import *
from .asm_eval import *
from .asm_pass2 import *
@dataclass
class Result3Line:
line_num: int
src_file: str
address: int
parts: list[str]
original_parts: list[str]
@dataclass
class DbgSourceInfo:
src_file: str
line_num: int
expanded_text: str
original_text: str
DbgLabelTable = dict[int, list[str]]
DbgSourceTable = dict[int, DbgSourceInfo]
@dataclass
class Result3:
lines: list[Result3Line]
operations: OpExpansionDict
symbols: dict[str, int]
dbg_label_table: DbgLabelTable
dbg_source_table: DbgSourceTable
def pass_3(result2: Result2) -> Result3:
result_lines: list[Result3Line] = []
symbols: dict[str, int] = {}
address = 0
dbg_label_table: DbgLabelTable = {}
dbg_source_table: DbgSourceTable = {}
def save_dbg_label(addr: int, label: str):
if addr in dbg_label_table:
dbg_label_table[addr].append(label)
else:
dbg_label_table[addr] = [label]
def save_dbg_source(addr: int, src_file: str, line_num: int, expanded_text: str, original_text: str):
dbg_source_table[addr] = DbgSourceInfo(
src_file=src_file,
line_num=line_num,
expanded_text=expanded_text,
original_text=original_text,
)
# On address conflict, if override_mode is True, the last definition wins
# otherwise, an exception is raised
override_mode = False
for line in result2.lines:
keyword, *args = line.parts
match keyword:
case "@address":
address = eval_expr(symbols, args[0])
continue
case "@begin_override":
override_mode = True
continue
case "@end_override":
override_mode = False
continue
case "@label":
label = args[0]
if label in symbols:
raise Exception(f"When defining label {label} as {address:>04x}, symbol {label} already defined as {symbols[label]:>04x}")
symbols[args[0]] = address
save_dbg_label(address, label)
continue
case "@let":
symbols[args[0]] = eval_expr(symbols, args[1])
continue
case _:
dbg_original_text = " ".join(line.original_parts)
dbg_expanded_text = " ".join(line.parts)
res_line = Result3Line(
line_num=line.line_num,
src_file=line.src_file,
parts=line.parts,
address=address,
original_parts=line.original_parts,
)
if override_mode:
for i in range(len(result_lines)):
if result_lines[i].address == address:
result_lines[i] = res_line # overwrite the existing line
break
else:
result_lines.append(res_line)
save_dbg_source(address, line.src_file, line.line_num, dbg_expanded_text, dbg_original_text)
address += 1
result_lines.sort(key=lambda l: l.address)
for result_line in result_lines:
rows_with_same_addr = list(filter(lambda l: l.address == result_line.address, result_lines))
n = len(rows_with_same_addr)
if n > 1:
formatted = format_overlapping_rows(rows_with_same_addr)
raise Exception(f"Overlapping segments: address 0x{result_line.address:>04x} has conflicting definitions:\n{formatted}")
return Result3(
operations=result2.operations,
lines=result_lines,
symbols=symbols,
dbg_label_table=dbg_label_table,
dbg_source_table=dbg_source_table,
)
def format_overlapping_rows(rows: list[Result3Line]) -> str:
longest_val = 0
for row in rows:
joined = " ".join(row.parts)
if len(joined) > longest_val:
longest_val = len(joined)
first_col_width = longest_val + 4
results: list[str] = []
for row in rows:
joined = " ".join(row.parts)
result = joined + (first_col_width - len(joined)) * " " + f" ({row.src_file}:{row.line_num})"
results.append(result)
return "\n".join(results)
|