はじめに
先日、ツイッターでとある忍者ゲームのソースがGitHubで公開されたことを知り、折角なのでビルドしてみることとしました。
作業方針
プログラムは 6502 のアセンブラで書かれており、オリジナルの開発環境では MS-DOS 上で CP/M エミュレータを介して ACT65 というアブソリュートアセンブラを使用してビルドをされたようですが、ACT65 は持っていないので別の方法を採ります。
CP/M 時代に良く使われたアセンブラに MS 社の MACRO-80 という製品があり、8080 と Z80 の両方のニモニックを受け付けるリロケータブルマクロアセンブラで優れものでした。これの MSX-DOS 用に販売されたものは MSX とは関係ない筈の 6502 のニモニックをなんでか受け付ける機能が製品仕様外に存在したことは御存知でない方以外には広く知られています。今回はこれを使用することとしました。実機を引っ張り出してきて使うのもしんどいので、『MSX MAGAZINE永久保存版3』に収録された公式MSXエミュレーターを使用しました。他、テキストの編集等に Windows 10 上 で Cygwin を使用しています。
作業の実際
先ず GitHub の件のリポジトリを Cygwin の作業フォルダに拾ってきます。
次に ACT65 と MACRO-80 で書式の違う部分があるので、それの修正を行います。
下記のパッチ
HAT.patch
--- HAT.A65
+++ HAT.A65
@@ -4,5 +4,6 @@
;
cpu '6502'
+ aseg
;
;
adrchk equ 0 ;check memory location
@@ -254,7 +255,7 @@ jmpbtn
bit7st ds 1
;
;------------------------------------------------------------
-EZ equ *
+EZ equ $
;--------------------- < $ed ======> $10f >--------------------------------
; debug board reserved !!!!!!
;----------------------------------------------------------------------------
@@ -528,7 +529,7 @@ snmy ds 1
;
;-------------------------------------------------------------
;
- if * > $700
+ if $ > $700
err ;address $700
endif
;
@@ -574,75 +575,76 @@ ALLPLU MACRO
ENDM
;
PTRSET MACRO DATA,PTR
- lda #low %1
- sta %2
- lda #high %1
- sta %2+1
+ lda #low DATA
+ sta PTR
+ lda #high DATA
+ sta PTR+1
ENDM
;
INCPTR MACRO PTR
- inc %1
- bne *+4
- inc %1+1
+ inc PTR
+ bne $+4
+ inc PTR+1
ENDM
;
DECPTR MACRO PTR
sec
- lda %1
+ lda PTR
sbc #1
- sta %1
- bcs *+4
- dec %1+1
+ sta PTR
+ bcs $+4
+ dec PTR+1
ENDM
;
PUSHM MACRO ADDR
- lda %1
+ lda ADDR
pha
ENDM
;
POPM MACRO ADDR
pla
- sta %1
+ sta ADDR
ENDM
;
JTBL MACRO DATA
- dw %1 - 1
+ dw DATA - 1
ENDM
;
INDCAL MACRO JMPVEC,REG
- lda #high ?%K
+ LOCAL K
+ lda #high K
pha
- lda #low ?%K
+ lda #low K
pha
- lda %1+1,%2
+ lda JMPVEC+1,REG
pha
- lda %1+0,%2
+ lda JMPVEC+0,REG
pha
-?%K: rts
+K: rts
ENDM
;
INDJMP MACRO JMPVEC,REG
- lda %1+1,%2
+ lda JMPVEC+1,REG
pha
- lda %1+0,%2
+ lda JMPVEC+0,REG
pha
rts
ENDM
;
;
ANDDB MACRO ANDDAT,NOMAL
- db %1 and $fc,%2
+ db ANDDAT and $fc,NOMAL
ENDM
;
CKIDB MACRO XLOW,XHIGH,YDATA
- db %1 and $f8,%2,%3 and $f8
+ db XLOW and $f8,XHIGH,YDATA and $f8
ENDM
;
SPBDB MACRO XLOW,XHIGH,YDATA
- db %1 * 8,%2,%3 * 8 + $30
+ db XLOW * 8,XHIGH,YDATA * 8 + $30
ENDM
BOSDB MACRO XCHR0,YCHR0
- db %1*8,%2*8+$30
+ db XCHR0*8,YCHR0*8+$30
ENDM
;
;------------ link files --------------------
@@ -682,4 +684,5 @@ BOSDB MACRO XCHR0,YCHR0
endif
;
;
+ END
\ No newline at end of file
--- HATMUS.A65
+++ HATMUS.A65
@@ -955,19 +955,19 @@ FRQTBL: DW $0000 ; REST
;
;
;
-MCTMPO MACRO %1
+MCTMPO MACRO _1
ENDM
;
MCREST MACRO
DB 0
ENDM
;
-MCTONE MACRO %1,%2
- DB (%1-3)*12+%2+1-3
+MCTONE MACRO _1,_2
+ DB (_1-3)*12+_2+1-3
ENDM
;
-MCLENG MACRO %1
- DB %1+$80
+MCLENG MACRO _1
+ DB _1+$80
ENDM
;
MCEND MACRO
@@ -978,8 +978,8 @@ MCDC MACRO
DB $FE
ENDM
;
-MCRPB MACRO %1
- DB $FD,%1
+MCRPB MACRO _1
+ DB $FD,_1
ENDM
;
MCRPE MACRO
@@ -994,7 +994,7 @@ MCTAIE MACRO
DB $FA
ENDM
;
-MCLEN MACRO %1
+MCLEN MACRO _1
ENDM
;
DATATP:
--- HMAIN.A65
+++ HMAIN.A65
@@ -324,6 +324,7 @@ vramon:
lda #VRMOND
jmp creg1s
;
+scrnoff:
scrnof: ;vram off & nmi off
jsr nmioff
vramof:
@@ -528,8 +529,8 @@ extinc:
sta lfeodr
extirt: rts
;
-SCRDB MACRO df,dt,ds,df
- db %4,%3,%2,%1
+SCRDB MACRO df,dt,ds,df2
+ db df2,ds,dt,df
ENDM
;
scrdat:
--- RECHAT.A65
+++ RECHAT.A65
@@ -191,8 +191,9 @@ clplop:
;
;
CHRSFT MACRO
+ LOCAL K
DEC CHRBIT
- BPL ?%K
+ BPL K
PHA
LDA #7
STA CHRBIT
@@ -200,14 +201,15 @@ CHRSFT MACRO
STA CHRSFD
PLA
INY
- BNE ?%K
+ BNE K
INC CHRBP+1
-?%K: ASL CHRSFD
+K: ASL CHRSFD
ENDM
;
MAPSFT MACRO
+ LOCAL K
DEC MAPBIT
- BPL ?%K
+ BPL K
PHA
LDA #7
STA MAPBIT
@@ -215,12 +217,13 @@ MAPSFT MACRO
STA MAPSFD
PLA
INY
-?%K: ASL MAPSFD
+K: ASL MAPSFD
ENDM
;
COLSFT MACRO
+ LOCAL K
DEC COLBIT
- BPL ?%K
+ BPL K
PHA
LDA #7
STA COLBIT
@@ -228,7 +231,7 @@ COLSFT MACRO
STA COLSFD
PLA
INY
-?%K: ASL COLSFD
+K: ASL COLSFD
ENDM
;
;
を当て、
$ patch < HAT.patch
patching file HAT.A65
patching file HATMUS.A65
patching file HMAIN.A65
patching file RECHAT.A65
$
そして下記の
.SUFFIXES:
.SUFFIXES: .A65 .MAC .SCR .MAC
targets: HAT.MAC HATBOU.MAC HATENM.MAC HATMAP.MAC HATMOV.MAC HATMUS.MAC HATTIL.MAC HMAIN.MAC NINPOU.MAC RECHAT.MAC HTBGM.MAC HTBONUS.MAC HTCAT.MAC HTCLEAR.MAC HTCOMP.MAC HTFIRE.MAC HTOVER.MAC HTTOJO.MAC HTWARP.MAC
.A65.MAC:
perl -f act652m80.pl $< > $@
.SCR.MAC:
perl -f act652m80.pl $< > $@
#!/usr/bin/perl
my $eof = 0;
while (<>) {
s/[\r\n]//g;
if (/\x1a/) {
s/\x1a.*//;
$eof = 1;
}
s/\tmsg/\t;msg/i;
s/\tECHO/\tREPT/i;
$_ = reverse $_;
s/(.*;\t*)([^;]*)$/\2/i;
my $comment = reverse $1;
$_ = reverse $_;
s/\$([a-f])_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)/0\1\2\3\4\5\6\7\8H/ig;
s/\$([0-9])_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)_?([0-9a-f]*)/\1\2\3\4\5\6\7\8H/ig;
s/%([01])_?([01]*)_?([01]*)_?([01]*)_?([01]*)_?([01]*)_?([01]*)_?([01]*)/\1\2\3\4\5\6\7\8B/ig;
s/link\t([^.]+)\.(a65|scr)/include\t\1.mac/i;
s/cpu\s+'6502'/.6502/i;
y/[]/()/;
s/=/EQ/;
s/>/GT/;
s/</LT/;
$_ .= $comment;
print $_ . "\r\n";
if ($eof) {
last;
}
}
print "\x1a";
makefile と Perl スクリプトを使用してソースファイルを変換します。
$ make
perl -f act652m80.pl HAT.A65 > HAT.MAC
perl -f act652m80.pl HATBOU.A65 > HATBOU.MAC
perl -f act652m80.pl HATENM.A65 > HATENM.MAC
perl -f act652m80.pl HATMAP.A65 > HATMAP.MAC
perl -f act652m80.pl HATMOV.A65 > HATMOV.MAC
perl -f act652m80.pl HATMUS.A65 > HATMUS.MAC
perl -f act652m80.pl HATTIL.A65 > HATTIL.MAC
perl -f act652m80.pl HMAIN.A65 > HMAIN.MAC
perl -f act652m80.pl NINPOU.A65 > NINPOU.MAC
perl -f act652m80.pl RECHAT.A65 > RECHAT.MAC
perl -f act652m80.pl HTBGM.SCR > HTBGM.MAC
perl -f act652m80.pl HTBONUS.SCR > HTBONUS.MAC
perl -f act652m80.pl HTCAT.SCR > HTCAT.MAC
perl -f act652m80.pl HTCLEAR.SCR > HTCLEAR.MAC
perl -f act652m80.pl HTCOMP.SCR > HTCOMP.MAC
perl -f act652m80.pl HTFIRE.SCR > HTFIRE.MAC
perl -f act652m80.pl HTOVER.SCR > HTOVER.MAC
perl -f act652m80.pl HTTOJO.SCR > HTTOJO.MAC
perl -f act652m80.pl HTWARP.SCR > HTWARP.MAC
$
以上で ACT65 用のソースファイルを MACRO-80 用に変換できました。変換された MACRO-80 用のソースファイル *.MAC を公式MSXエミュレーター上の MSX-DOS2 へファイルコピーし、これらを MACRO-80 でアセンブルすることでリロケータブルバイナリとプリントファイルが得られます。公式MSXエミュレーター の turboR モードを使用して 10分近く掛かります。
プリントファイルは以下のような内容(一部抜粋)となってます。
FFFA 8051 C dw nmivec ; nmi vector
MSX.M-80 2.00 18-Jan-20 PAGE 1-12
FFFC 8000 C dw start ; reset vector
FFFE 80DA C dw irqvec ; irq vector
C ;
C ORG ROMSTR
C ;
8000 78 C start: sei
8001 D8 C cld
C ;
8002 A9 18 C lda #18H
8004 8D 2000 C sta creg0
8007 AD 2002 C lda stareg
800A AD 2002 C strlp0: lda stareg
800D 10 FB C bpl strlp0
800F AD 2002 C strlp1: lda stareg
8012 10 FB C bpl strlp1
C ;
C endif
C ;
8014 A2 FF C gstart: ldx #0ffH
8016 9A C txs
C ;
8017 C tstart:
8017 A2 00 C ldx #0
8019 8A C txa
801A 95 00 C memcl0: sta 0H,x
801C E8 C inx
801D E0 ED C cpx #0edH
801F D0 F9 C bne memcl0
C ;
8021 A9 02 C lda #2
8023 85 01 C sta 1H
8025 A0 00 C ldy #0
8027 A2 06 C ldx #6
C
8029 A9 00 C lda #0
802B 91 00 C memcl1: sta (0H),y
802D C8 C iny
802E D0 FB C bne memcl1
8030 E6 01 C inc 1H
8032 CA C dex
8033 D0 F6 C bne memcl1
C ;
C ; jmp starta
8035 C starta:
8035 20 8152 C jsr init
さて、MACRO-80 はリロケータブルアセンブラなので実行ファイルを得るには通常はリンク作業が必要となります。HEX ファイルを得るべくリンカ LINK-80 を使用して下記コマンドを実行すると、
H>l80 hat,hat/x/e
エラーとなってしまいました。LINK-80 で扱えるファイルサイズを超えたのか、あるいはメモリ配置的な問題かは追いかけておりませんが LINK-80 では HEX ファイルを作成するのは難しい感じです。
仕方がないのでプリントファイル中に出力されたアドレスと内容を HEX ファイルに変換する方法を採ります。元がアブソリュートアセンブラ用のソースであり、プリントファイル中に存在するシンボルはアセンブル時に全て解決している筈なので問題はありません。
プリントファイル HAT.PRN を Cygwin の作業フォルダにコピーし、下記の Perl スクリプトを使用して HEX ファイルを生成します。
#!/usr/bin/perl
my $eof = 0;
while (<>) {
s/[\r\n]//g;
if (/\x1a/) {
s/\x1a.*//;
$eof = 1;
}
if (/^ ([0-9A-F][0-9A-F][0-9A-F][0-9A-F]) ([0-9A-F][ 0-9A-F]+)'? /) {
my $sum = 0;
my $addr = hex($1);
my $data = $2;
$data =~ s/([0-9A-F][0-9A-F])([0-9A-F][0-9A-F])/\2\1/g;
$data =~ s/ //g;
my $num = int(length($data) / 2);
$sum += $num;
$sum += $addr + ($addr >> 8);
printf(":%02X%04X00", $num, $addr);
for (my $i = 0; $i < $num; $i++) {
my $byte = hex(substr($data, 2 * $i, 2)) & 0xff;
printf("%02X", $byte);
$sum += $byte;
}
printf("%02X\r\n", -$sum & 0xff);
}
if ($eof) {
last;
}
}
print ":00000001FF\r\n\x1a";
$ perl prn2hex.pl < HAT.PRN > HAT.HEX
$
生成された HEX ファイルは以下のような内容(一部抜粋)となってます。
:02FFFA00518034
:02FFFC00008083
:02FFFE00DA80A7
:018000007807
:01800100D8A6
:02800200A918BB
:038004008D0020CC
:03800700AD0220A7
:03800A00AD0220A4
:02800D0010FB66
:03800F00AD02209F
:0280120010FB61
:02801400A2FFC9
:018016009ACF
:02801700A200C5
:018019008ADC
:02801A009500CF
:01801C00E87B
:02801D00E0ED94
:02801F00D0F996
:02802100A902B2
:028023008501D5
:02802500A000B9
:02802700A206AF
:02802900A900AC
:02802B009100C2
:01802D00C88A
:02802E00D0FB85
:02803000E60167
:01803200CA83
:02803300D0F685
:0380350020528155
生成された HEX ファイルが書式的に正しいことの検証として、objcopy コマンドを使用してバイナリファイルへ変換してみます。
$ objcopy -I ihex HAT.HEX -O binary HAT.BIN
$ ls -l HAT.BIN
-rw-r--r-- 1 fujita なし 32768 Jan 18 22:20 HAT.BIN
$
問題ないようです。
ちょっと気になった点
今回はビルドをしてみることを主眼としておりプログラムの内容までは深く見ていないのですが、作業中に発生したアセンブルエラーで
and #$1111_0011
という箇所を見つけました。
A レジスタと直値の論理積を取っていますが、直値の値が 16進数 8桁となっており、8ビット CPU である 6502 のプログラムとしてはおかしい感じです。同ファイルの別の箇所では
and #%1111_0011
同じ数字の並びが使われていますが、こっちは 2進数なので問題はなさそうです。
これが不具合かは実際にプログラムの動作を理解しないと判断できません。最初は不具合だったが動きが面白かったのでそのまま修正されなかった可能性もゲームソフトではあり得ることであり、今後の課題といえるでしょう。
おわりに
おわりです。