Skip to content

Commit cb2d33b

Browse files
committed
add bios disasm
1 parent 0fe5b5a commit cb2d33b

File tree

8 files changed

+5750
-0
lines changed

8 files changed

+5750
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__

scripts/asm/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from jinja2 import nodes
2+
from jinja2.ext import Extension
3+
4+
class AssemblerHighlightExtension(Extension):
5+
tags = {"asm"}
6+
7+
def parse(self, parser):
8+
first = next(parser.stream)
9+
assert first.value == "asm"
10+
lineno = first.lineno
11+
12+
args = [parser.parse_expression()]
13+
14+
body = parser.parse_statements(
15+
["name:endasm"], drop_needle=True)
16+
17+
return nodes.CallBlock(
18+
self.call_method("_highlight", args), [], [], body
19+
).set_lineno(lineno)
20+
21+
def _highlight(self, mode: str, caller):
22+
if mode == "epson":
23+
from asm.epson import render
24+
else:
25+
raise ValueError(mode)
26+
27+
return render(caller())

scripts/asm/common.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import re
2+
import html
3+
from io import TextIOBase
4+
from typing import Self, Sequence
5+
from dataclasses import dataclass, field
6+
7+
SPACE = re.compile(r'(\s+)')
8+
9+
CLASSNAME_TO_COLOR = {
10+
"keyword": "#d73a49",
11+
"variable.language": "#d73a49",
12+
"title.function": "#6f42c1",
13+
"literal": "#005cc5",
14+
"meta": "#005cc5",
15+
"number": "#005cc5",
16+
"variable": "#005cc5",
17+
"string": "#032f62",
18+
"built_in": "#e36209",
19+
"symbol": "#e36209",
20+
"comment": "#6a737d",
21+
}
22+
23+
def _id(id_: str) -> str:
24+
return f"user-content-{id_}" if id_ else ""
25+
26+
def _class(cls: str) -> str:
27+
return "hljs-" + " ".join(
28+
x + "_" * i
29+
for i, x in enumerate(cls.split("."))
30+
)
31+
32+
@dataclass
33+
class AccentedData:
34+
data: str|Self
35+
prefix: str = ""
36+
suffix: str = ""
37+
classname: str = ""
38+
39+
def render(self) -> str:
40+
pfx = ""
41+
if self.classname:
42+
classname = f' class="{_class(self.classname)}"'
43+
if self.classname in CLASSNAME_TO_COLOR:
44+
classname += f' color="{CLASSNAME_TO_COLOR[self.classname]}"'
45+
46+
if self.classname in ("symbol", "variable", "title.function"):
47+
pfx, sfx = f'<a href="#{_id(str(self.data))}"{classname}>', "</a>"
48+
else:
49+
classname = ""
50+
51+
if not pfx:
52+
pfx, sfx = f'<span{classname}>', "</span>"
53+
54+
data = (
55+
self.data.render()
56+
if isinstance(self.data, AccentedData)
57+
else self.data
58+
)
59+
return (
60+
f'{html.escape(self.prefix)}'
61+
f'{pfx}{html.escape(data)}{sfx}'
62+
f'{html.escape(self.suffix)}'
63+
)
64+
65+
def __str__(self) -> str:
66+
return str(self.data)
67+
68+
def __bool__(self) -> bool:
69+
return bool(self.data)
70+
71+
class EmptyRender(AccentedData):
72+
def __init__(self) -> None:
73+
return super().__init__("")
74+
75+
def render(self):
76+
return ""
77+
78+
@dataclass
79+
class Line:
80+
label: AccentedData
81+
comment: AccentedData
82+
83+
def _render(self, *mid: str):
84+
return self.label.render() + "".join(mid) + self.comment.render()
85+
86+
def render(self):
87+
return self._render()
88+
89+
def is_comment(self):
90+
return not self.label and self.comment
91+
92+
@dataclass
93+
class OperatorLine(Line):
94+
mnemonic: AccentedData
95+
args: list[AccentedData]
96+
position: int = -1
97+
cycles: int = -1
98+
99+
def render(self):
100+
return self._render(
101+
self.mnemonic.render(),
102+
*(x.render() for x in self.args),
103+
)
104+
105+
def is_comment(self):
106+
return False
107+
108+
@dataclass
109+
class DirectiveLine(Line):
110+
directive: AccentedData
111+
args: list[AccentedData]
112+
lefthand_arg: AccentedData = field(default_factory=EmptyRender)
113+
position: int = -1
114+
size: int = -1
115+
116+
def render(self):
117+
return self._render(
118+
self.lefthand_arg.render(),
119+
self.directive.render(),
120+
*(x.render() for x in self.args),
121+
)
122+
123+
def is_comment(self):
124+
return False
125+
126+
@dataclass
127+
class MacroLine(Line):
128+
name: AccentedData
129+
symbol: AccentedData
130+
args: list[AccentedData]
131+
132+
def render(self):
133+
return self._render(
134+
self.name.render(),
135+
self.symbol.render(),
136+
*(x.render() for x in self.args),
137+
)
138+
139+
def is_comment(self):
140+
return False
141+
142+
143+
def lines(x) -> Sequence[str]:
144+
if isinstance(x, str):
145+
return x.splitlines()
146+
if isinstance(x, TextIOBase):
147+
return iter(x)
148+
raise TypeError(type(x))
149+
150+
151+
class Renderer:
152+
def __init__(self, use_positions=False) -> None:
153+
self.prefixes: list[str] = []
154+
self.lines: list[Line] = []
155+
self.label_anchors: dict[int,str] = {}
156+
self.prefix_anchors: dict[int,str] = {}
157+
self.use_positions = use_positions
158+
self.cur_line = 0
159+
self.preroll_start = -1
160+
161+
def queue(self, line: Line) -> None:
162+
if not self.use_positions:
163+
self.prefixes.append(str(self.cur_line))
164+
self.prefix_anchors[self.cur_line] = str(self.cur_line)
165+
166+
if line.is_comment():
167+
if self.use_positions:
168+
self.prefixes.append("")
169+
if self.preroll_start < 0:
170+
self.preroll_start = self.cur_line
171+
elif not line:
172+
if self.use_positions:
173+
self.prefixes.append("")
174+
self.preroll_start = -1
175+
else:
176+
if self.use_positions:
177+
if isinstance(line, (OperatorLine, DirectiveLine)) and line.position >= 0:
178+
pos = f"{line.position:06X}"
179+
self.prefix_anchors[self.cur_line] = pos
180+
self.prefixes.append(f"${pos}")
181+
else:
182+
self.prefixes.append("")
183+
184+
if line.label:
185+
self.label_anchors[
186+
self.cur_line
187+
if self.preroll_start < 0
188+
else self.preroll_start
189+
] = str(line.label)
190+
191+
self.lines.append(line)
192+
self.cur_line += 1
193+
194+
def render(self) -> str:
195+
res = '<pre><code class="language-s1c88 hljs">'
196+
longest = max(len(x) for x in self.prefixes)
197+
prefix_class = 'color="#6e7781" class="line-number"'
198+
for i, (p, l) in enumerate(zip(self.prefixes, self.lines)):
199+
if p:
200+
if i in self.prefix_anchors:
201+
name = _id(self.prefix_anchors[i])
202+
res += f'<a name="{name}" href="#{name}" {prefix_class}>'
203+
suffix = "</a>"
204+
else:
205+
res += f"<span {prefix_class}>"
206+
suffix = "</span>"
207+
208+
res += f"{p:>{longest}s}{suffix}"
209+
else:
210+
res += f'<span {prefix_class}>{" " * longest}</span>'
211+
212+
if i in self.label_anchors:
213+
span_id = f' id="{_id(self.label_anchors[i])}"'
214+
else:
215+
span_id = ""
216+
res += f' <span class="line-content"{span_id}>{l.render()}</span>\n'
217+
218+
res += "</code></pre>"
219+
return res

0 commit comments

Comments
 (0)