メモ:他のアセンブラとの違い
本内容について
機能的な大きな課題がないかどうかを確認する目的で、 自作および公開されているソースファイルを yas80 用でアセンブルできるかどうか試したものです。
ここでは 移植元のアセンブラの仕様を網羅する ことは目標とせず、 該当ソースファイルで使用している機能範囲に限定した内容であるため、 参考レベルの情報として"メモ"と呼んでいます。
z80as との違い
変更点
@@, @nのラベルを適当な名前に置き換えるか、-aオプションの利用を検討する。DB/DW/DSのラベルの:を削除する:演算子を数式に置き換える- ギャップが
0のため、オプション-f 0を指定する
書き換え Python スクリプト例
import re
import sys
from collections import defaultdict
class Labels:
def __init__(self):
self.map = defaultdict(list)
def append(self, name, line_number):
self.map[name].append(line_number)
def __len__(self):
count = 0
for v in self.map.values():
count += len(v)
return count
def __in__(self, name):
return name in self.map
def replace(self, n, name):
name = name.lower()
if name == "@f":
num = self.forward(n, "@@")
return f"L___{num}"
elif name == "@b":
num = self.backward(n, "@@")
return f"L___{num}"
elif name.endswith("f"):
num = self.forward(n, name[:-1])
return f"L_{name[1]}_{num}"
elif name.endswith("b"):
num = self.backward(n, name[:-1])
return f"L_{name[1]}_{num}"
else:
raise ValueError(f"Invalid label reference: {name}:{n}")
def forward(self, n, name):
lst = self.map.get(name, None)
if lst is None:
raise ValueError(f"Label {name}:{n} not found")
for num in lst:
if num >= n:
return num
raise ValueError(f"Label {name}:{n} not found")
def backward(self, n, name):
lst = self.map.get(name, None)
if lst is None:
raise ValueError(f"Label {name}:{n} not found")
for num in reversed(lst):
if num <= n:
return num
raise ValueError(f"Label {name}:{n} not found")
def main(infile, outfile):
try:
lines = read_lines(infile)
except Exception as e:
print(f"Error reading file: {e}")
exit(1)
labels = get_labels(lines)
print(len(labels), "labels")
new_lines = patch(lines, labels)
with open(outfile, "w") as f:
f.writelines(new_lines)
def read_lines(file) -> list[str]:
try:
with open(file, encoding="utf-8") as f:
return f.readlines()
except UnicodeDecodeError:
with open(file, encoding="cp932") as f:
return f.readlines()
def get_labels(lines) -> Labels:
map = Labels()
for n, line in enumerate(lines, start=1):
m = re.match(r"(@[@\d])\s*:", line)
if m is not None:
map.append(m.group(1), n)
return map
def patch(lines, labels) -> list[str]:
new_lines = []
for n, line in enumerate(lines, start=1):
# ラベル定義
new_line = re.sub(
r"(@[@\d])\s*:", lambda m: f"L{m.group(1)}_{n}:".replace("@", "_"), line
)
# ラベル参照
new_line = re.sub(
r"@\d?[fb]",
lambda m: labels.replace(n, m.group(0)),
new_line,
flags=re.IGNORECASE,
)
# DB/DW/DS の前の : を削除
new_line = re.sub(r":(\s*(?:db|dw|ds))", r" \1", new_line, flags=re.IGNORECASE)
# コロン演算子の置き換え
new_line = re.sub(
r"(\d+)\s*:\s*(\d+)", r"(\1 * 256) + \2", new_line, flags=re.IGNORECASE
)
# BININCLUDE -> INCBIN は機能追加したので変更不要
# new_line = re.sub(r"BINCLUDE", "INCBIN", new_line, flags=re.IGNORECASE)
new_lines.append(new_line)
return new_lines
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: patch.py input output")
exit(1)
main(*sys.argv[1:])
AILZ80ASM との違い
変更点
#if/#elif/#else/#endifの#を削除.local単独行に:を追加.localが多い場合は-aオプションの利用を検討するexistsは組み込み関数$definedが使用できないか検討.@H,@Lは組み込み関数$H(), $L()に置き換える- charmap 定義の
@を_に変更 - charmap 適用の
@を_に変更 - charmap 適用方法修正 ':' -> 関数呼出し形式
- charmap は前方参照できないので、必要に応じて定義位置を変更
db/dw/dsのラベルに:がある場合削除include "file", bをincbinに変更function name() => expressionの=>を削除- macro 定義内の
.nameを@nameに変更 REPT ENDMはREPT ENDRに変更REPT m LAST -nはEXITM IF利用へ書き換えるHIGH,LOW演算子を関数呼出し$HIGH(),$LOW()へ置き換える
ORGの配置管理の違い
命令のベースアドレスと実際に格納されるアドレスを別にする場合、
ORG疑似命令を使用し設定することになりますが、
yas80 と AILZ80ASM とではアプローチが異なっています。
| 項目 | yas80 | AILZ80ASM |
|---|---|---|
| 配置アドレス指定 | 自動: 先行セグメントの続きになる 特定のアドレスとする場合、先行セグメント末尾でのアライメント調整が必要 |
手動: ORGの第2引数で指定特定のアドレスとするのは容易な反面、連続して配置する場合は第1引数で計算が必要か? |
| 配置アドレス参照 | $$を使用 |
$$を使用 |
このためスクリプトで簡単に書き換えるといったことはできず、 メモリマップを考慮しながらの修正が必要になります。
公開されているものではLSX-Dodgers
がORGによる配置指定をしているのが分かったので、
PC8801 用を対象に移行を試みたところ、次の内容で同一バイナリが生成できるところまでは確認できました。
- patch.py による書き換え
ORGで第2引数を指定している場合、RELに置き換えるか無効化- いくつかのラベル定義を
name equ $$に置き換え、配置アドレスとする
diff -u '-x=*.bin' '-x=*.bat' '-x=*.bin' patched/88INIT.ASM temp/88INIT.ASM
--- patched/88INIT.ASM
+++ temp/88INIT.ASM
@@ -270,7 +270,7 @@
DB 0
AT_SUBSYS:
- ORG SUBSYS,AT_SUBSYS-OFFSET
+ ORG SUBSYS,REL ; AT_SUBSYS-OFFSET
JP PUTCOM
JP PUTDAT
JP GETDAT
@@ -304,5 +304,5 @@
DAA
JP MSG_A
DS 4
- ORG $$+OFFSET,$$ ;$DEPHASE
-AT_TRAPE:
+ ; ORG $$+OFFSET,$$ ;$DEPHASE
+AT_TRAPE equ $$
Only in temp: 88INIT.ASM.bak
diff -u '-x=*.bin' '-x=*.bat' '-x=*.bin' patched/88SUB.ASM temp/88SUB.ASM
--- patched/88SUB.ASM
+++ temp/88SUB.ASM
@@ -2,8 +2,8 @@
; LSX-Dodgers SUB DISK SYSTEM
; PC-8801mkIISR
-AT_DSS:
- ORG DSS,AT_DSS-OFFSET
+AT_DSS equ $$
+ ORG DSS,REL ; AT_DSS-OFFSET
JP READDISKEX
JP WRITEDISKEX
JP GETPARAMS_N
@@ -425,7 +425,8 @@
IN A,(0FBH) ;FDC μPD765A データレジスタリード
RET
- ORG $$+OFFSET,$$ ;$DEPHASE
+ ; ORG $$+OFFSET,$$ ;$DEPHASE
+ ORG $D880
AT_DSSE:
INITE:
DS BDOS-INITE
diff -u '-x=*.bin' '-x=*.bat' '-x=*.bin' patched/88TABLE.ASM temp/88TABLE.ASM
--- patched/88TABLE.ASM
+++ temp/88TABLE.ASM
@@ -46,5 +46,5 @@
DB 00H,00H,00H,00H,00H,00H,00H,00H ;B
DB 00H,00H,00H,00H,00H,08H,12H,08H ;C
VRAME:
- ORG $$+OFFSET,$$ ;$DEPHASE
-AT_VRAME:
+ ; ORG $$+OFFSET,$$ ;$DEPHASE
+AT_VRAME equ $$
diff -u '-x=*.bin' '-x=*.bat' '-x=*.bin' patched/88VRAM.ASM temp/88VRAM.ASM
--- patched/88VRAM.ASM
+++ temp/88VRAM.ASM
@@ -2,8 +2,8 @@
; LSX-Dodgers VRAM
; PC-8801mkIISR
-AT_VRAM:
- ORG 0F000H,AT_VRAM-OFFSET
+AT_VRAM equ $$
+ ORG 0F000H,REL ; AT_VRAM-OFFSET
ESC1:
LD A,E
CP 'A'
書き換え Python スクリプト例
- スクリプトで処理後手作業で修正する前提のため、変更点の一部には対応してません。
import re
import sys
def main(infile, outfile):
try:
lines = read_lines(infile)
except Exception as e:
print(f"Error reading file: {e}")
exit(1)
new_lines = patch(lines)
with open(outfile, "w", encoding="utf-8") as f:
f.writelines(new_lines)
def read_lines(file) -> list[str]:
try:
with open(file, encoding="utf-8") as f:
return f.readlines()
except UnicodeDecodeError:
with open(file, encoding="cp932") as f:
return f.readlines()
def patch(lines:list[str]) -> list[str]:
new_lines = []
for line in lines:
line = delete_pound(line)
line = exists_to_defined(line)
line = add_colon(line)
line = add_colon2(line)
line = remove_arrow(line)
line = charmap(line)
line = incbin(line)
line = remove_color(line)
line = high_low(line)
new_lines.append(line)
return new_lines
# if/elif/else/endifの # prefix を削除
def delete_pound(line:str) ->str:
return re.sub(r"#(if|elif|else|endif)", r"\1", line, flags=re.IGNORECASE)
# exists name を $defined(name) に置換
def exists_to_defined(line:str) -> str:
return re.sub(r"\sexists\s+(\w+)\s*$", r" $defined(\1)" "\n", line, flags=re.IGNORECASE)
# .local を .local: に置換
def add_colon(line:str)->str:
m = re.match(r"\s*(\.\w+)\s*(?:;|$)", line)
if m is None:
return line
return f"{m.group(1)}:\n"
# .local instruction => .local: instruction
def add_colon2(line:str)->str:
m = re.match(r"\s*(\.\w+)\s+(\w+)\s(.*)", line, flags=re.IGNORECASE)
if m is None:
return line
if m.group(2).upper() in ["DB", "DW", "DS"]:
return line
return f"{m.group(1)}:\t{m.group(2)} {m.group(3)}\n"
# charmap
def charmap(line:str) -> str:
line = re.sub(r"charmap(\s+)@", r"charmap\1_", line, flags=re.IGNORECASE)
return re.sub(r'@(\w+):("[^"]*")', r"_\1(\2)", line)
# incbin
def incbin(line:str) -> str:
return re.sub(r'include\s+("[^"]+")\s*,\s*B\s*$', "INCBIN \\1\n", line, flags=re.IGNORECASE)
# function name() => expression の => を削除
def remove_arrow(line:str) -> str:
return re.sub(r"\s=>\s", " ", line)
# db/dw のラベルの: を削除
def remove_color(line:str) -> str:
return re.sub(r":(\s*)(db|dw|ds)(\s+)", r" \1\2\3", line, flags=re.IGNORECASE)
# high low の関数呼出し化
def high_low(line:str) -> str:
return re.sub(r"(HIGH|LOW)\s+(\w+)", r" $\1(\2) ", line, flags=re.IGNORECASE)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: patch.py input output")
exit(1)
main(*sys.argv[1:])