SSブログ

孤独へ向って突っ走れ(15) [  PC-98x1(補完計画)]

私の足が悪くなってから1週間以上が過ぎた。ほんの少しずつ治り、昨日は爪先を曲げるのも力を加えなければ痛くないまでになった。ところが今朝起きて、爪先を曲げてみたらちょっと痛い。昨日も一昨日も無理をせず安静にしていたのに、どうしたことか。それでも今日は、いい加減にリハビリというか、外を歩いてみなきゃいけないと思う。

さてアセンブラの件だ。アセンブラはCとは比べ物にならないほどのプログラミングパワーを使う。Cで書いたのと同じゲームプログラムをアセンブラで書こうとすると大変な苦労をする。私は、前回の記事(14)で書いた迷宮からは脱した。ところが今は別の試練迷宮にいる。どこまで行ってもまだ先がある。個々の処理は単純なものだが、それがいつ終わるともわからず続く。努力と根性が試される試練だ。そして「もうやめだ。こんなのにこれ以上時間を使っていられない」と思いやめた時、その人からアセンブラは永遠に遠ざかってゆく。

今回の記事はここまでだ。この先にはニーモニックが延々と続くが、解析した私自身でさえ訳がわからなくなるような膨大な量で、しかもまだサブルーチンの途中までだ。私がどんな試練迷宮にいるかをわかってもらうために出すが、この記事を読んで下さるあなたが読むのはここまでで十分だ。



コードセグメントのアドレス98D0Hから990DHまでの命令
メインルーチンから飛んできたサブルーチン。
最終的に条件分岐で990DHへジャンプするまで。サブルーチンは続く。

MOV Word Ptr CS:[9A0D],0000
コードセグメントのオフセット9A0DHから2バイトに0000Hを入れる

MOV CS:[9A0F],AX
コードセグメントのオフセット9A0FHにAXの値を入れる
(AXには、このサブルーチンに飛ぶ前に000AHを入れてある)

MOV AX,0002
INT 33
マウスポインタを非表示にする

MOV AX,CS:[9A0F]
コードセグメントのオフセット9A0FHから2バイト(先ほど000AHを格納した)をAXに入れる

LEA SI,[4A2A]
SIにアドレスとして4A2AHを入れる

SHL AX,1
AX(000AH)を2倍する(AX=0014H)

ADD SI,AX
SIにAXを足す(SI=4A3EH)

MOV AX,[SI]
データセグメントのオフセット4A3EHから2バイトをAXに入れる
(調べたところ、5107H。ただしプログラム開始後にデータの書き換えがあったかどうかまでは調べていない。)

MOV SI,AX
その2バイト(5107H)をSIに入れる

MOV AX,[SI]
CMP AX,0000
JNZ 990D
データセグメントのオフセット5107Hから2バイト(調べたところ0001H)が0000Hでなければ990DHへ飛べ




コードセグメントのアドレス990DHから9938Hまでの命令
サブルーチン98D0Hの続き。
最終的に条件分岐で9948Hへジャンプするまで。サブルーチンは続く。

MOV AX,[SI+02]
MOV [47E0],AX
MOV BX,[SI+04]
MOV [47E2],BX
データセグメントのオフセット[5107H+02]から2バイト(001EH=AX;後で使う)を[47E0H]に入れ、
[5107H+04]から2バイト(00C8H=BX;後で使う)を[47E2H]に入れる

MOV CX,[SI+06]
ADD CX,+02
MOV [47E4],CX
データセグメントのオフセット[5107H+06]から2バイトを取り出し、2を足し(000BH=CX;後で使う)、[47E4H]に入れる

MOV DX,[SI+08]
SHL DX,1
ADD DX,+01
MOV [47E6],DX
データセグメントのオフセット[5107H+08]から2バイトを取り出し、2倍し、1を足し(0005H=DX;後で使う)、[47E6H]に入れる

SHL CX,1
ADD AX,CX
CMP AX,004B
JB 9948
CXを2倍し、AXと足し(=34H)、それが004BHより小さければ9948Hへ飛べ




コードセグメントのアドレス9948Hから9955Hまでの命令
サブルーチン98D0Hの続き。
最終的に条件分岐で9962Hへジャンプするまで。サブルーチンは続く。

MOV AX,DX
SHL AX,03
ADD AX,DX
ADD AX,BX
CMP AX,0188
JB 9962
DXの値を8倍し、元のDX(0005H)を足し、BX(00C8H)を足す。それ(=00F5H)が0188Hより小さければ9962Hへ飛べ

感想
最初のうちはゲームの初期設定ルーチンが多くて懐かしく、INTやOUTなどデータブックと照合すると意味がわかる部分が多くて楽しめたが、ここまで来るとデータセグメントの何だかわからないデータを取り出しては計算したり分岐したりする処理ばかりで、興味がもてない。




コードセグメントのアドレス9962Hから9965Hまでの命令
サブルーチン98D0Hの続き。
最終的にサブルーチンA0D6Hへ行くまで。

PUSH SI
CALL A0D6




コードセグメントのアドレスA0D6HからA101Hまでの命令
サブルーチン98D0Hから飛んできたサブルーチン。
最終的にサブルーチンA32FHへ行くまで。(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H)
ここに出てくる[47E0], [47E2], [47E4], [47E8]にはこのサブルーチンが飛んできたサブルーチン98D0Hでデータを格納した。
この先のコード解析により、
WORD PTR[47E0]は(グラフィックVRAMからバッファへコピーする指定矩形領域の左上のX座標/8)、
WORD PTR[47E2]は指定矩形領域の左上のY座標(ライン数-1)、
WORD PTR[47E4]は(指定矩形領域の横バイト数/2)(横ピクセル数/16)
と思われる。矩形の座標やサイズが内部処理でのBYTE単位やWORD単位になっている。
WORD PTR[47E6]を元にして矩形領域の縦ピクセル数を算出しているが、そこの計算が何をやっているのかわからない。

LEA SI,[47E0]
MOV CX,[SI+04]
MOV AX,[SI+06]
データセグメントのオフセット[47E4]から2バイトをCXに入れ、オフセット[47E6]から2バイトをAXに入れる
(この先のコード解析により、WORD PTR[47E4]は(グラフィックVRAMからバッファへコピーする指定矩形領域の横バイト数/2)と判明。"/2"なのは2バイトずつコピーするから。)

ADD AX,0001
MOV BX,0009
MUL BX
MOV DX,AX
AXに1を足し、9を掛け、その結果の下位2バイトをDXに入れる
(この動作はまだわからないが、後で出てくるサブルーチンA200Hが転送する矩形領域の縦は9ピクセル。)




MOV AX,[SI+02]
MOV BX,0050
PUSH DX
MUL BX
POP DX
データセグメントのオフセット[47E2]から2バイトを取り出し、50H(グラフィックVRAM横バイト数:80)を掛け、その結果の下位2バイトだけがAXに格納される
(おそらくWORD PTR[47E2]は指定矩形領域の左上のY座標。)

ADD AX,[SI]
MOV SI,AX
データセグメントのオフセット47E0Hから2バイトの値をAXに足し、それをSIに入れる
(おそらくWORD PTR[47E0]は(指定矩形領域の左上のX座標/8)。それにAXを足すことで、指定矩形領域の左上のアドレスを得る。)

XOR DI,DI
MOV AX,7F97
MOV DS,AX
DIの値を0にし、データセグメントを7F97Hに変更

CALL A32F
A32FHへ行ってこい
(サブルーチンA32FHは、DS:DI以降のバッファにグラフィックVRAMからオフセットSI, 横(16*CX)ピクセル, 縦DXピクセルの指定矩形領域を格納する。



コードセグメントのアドレスA32FHからA337Hまでの命令
サブルーチンA0D6Hから飛んできたサブルーチン。
最終的にサブルーチンA356Hへ行く(初回)まで。(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA32FH)

MOV AX,A800
MOV ES,AX
PUSH SI
ESにA800H(グラフィックVRAMプレーン0セグメント)を入れ、SIを退避

CALL A356
A356へ行ってこい

感想
処理がグラフィック関係になったので、また興味がもてるようになった。とはいえ、ラスタ演算処理はまだ出てこない。




コードセグメントのアドレスA356HからA375Hまでの命令
サブルーチンA32FHから飛んできたサブルーチン。
最終的にサブルーチンA32FHへ戻るまで。(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA32FH>サブルーチンA356H)
ES=A800H(サブルーチンA32FH;グラフィックVRAMセグメント)
SI(サブルーチンA0D6H;グラフィックVRAMの指定矩形領域左上アドレス)
DS=7F97H(サブルーチンA0D6H;グラフィックデータ格納バッファオフセット)
DI=0(サブルーチンA0D6H;グラフィックデータ格納バッファ先頭アドレス)
CX=1D7C:[47E4](サブルーチンA0D6H;指定矩形領域の横バイト数/2)
DX(サブルーチンA0D6H;指定矩形領域の縦ピクセル数)

PUSH DX
DXの値を退避

A357: ;JMP Shortのラベルはここ
PUSH CX
CXの値を退避

A358: ;LOOPのラベルはここ
MOV AX,ES:[SI]
MOV [DI],AX
ADD SI,+02
ADD DI,+02
LOOP A358
A800:[SI]から7F97:[DI]へ CX回 転送
(これはグラフィックVRAMの指定矩形領域の横1ラインを転送している。矩形の横は(16*CX)ピクセル。)

POP CX
CXの値を元に戻す

MOV AX,0050
SUB AX,CX
SUB AX,CX
ADD SI,AX
DEC DX
JZ A374
JMP Short A357
50H(グラフィックVRAM横バイト数:80)から2*CX(指定矩形領域の横バイト数)を引いた値をSIに足し(=指定矩形領域の次ラインのアドレス)、DXをデクリメント、上のLOOPをDX回(指定矩形領域の縦ピクセル数)繰り返したらA374Hへ行け

A374: ;JZのラベルはここ
POP DX
DXの値を元に戻す

RET
サブルーチンA32FHへ戻れ




コードセグメントのアドレスA338HからA355Hまでの命令
サブルーチンA356H(初回)から戻ってきた後のサブルーチンA32FH。
最終的にサブルーチンA0D6Hへ戻るまで。(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA32FH)

POP SI
SIの値を元に戻す

MOV AX,B000
MOV ES,AX
PUSH SI
CALL A356
POP SI
引き続きグラフィックVRAMプレーン1から指定矩形領域をバッファに取得

MOV AX,B800
MOV ES,AX
PUSH SI
CALL A356
POP SI
グラフィックVRAMプレーン2から指定矩形領域をバッファに取得

MOV AX,E000
MOV ES,AX
CALL A356
グラフィックVRAMプレーン3から指定矩形領域をバッファに取得

RET
サブルーチンA0D6Hへ戻れ




コードセグメントのアドレスA102HからA12CHまでの命令
サブルーチンA0D6Hの続き。サブルーチンA32FHから帰って来たところ。
最終的にサブルーチンA200H(初回)へ行くまで。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H)

MOV AX,1D7C
MOV DS,AX
データセグメントを元に戻す

LEA SI,[47E0]
MOV AX,[SI+02]
MOV BX,0050
MUL BX
データセグメントのオフセット47E2Hから2バイト(指定矩形領域の左上のY座標)を取り出し、50H(グラフィックVRAM横バイト数:80)を掛ける

ADD AX,[SI]
その下位2バイトに[47E0](指定矩形領域の左上のX座標/8)を足す

MOV DI,AX
DIは指定矩形領域の左上のアドレス

MOV CX,[SI+04]
MOV DX,[SI+06]
DEC CX
CXに[47E4](指定矩形領域の横バイト数/2)を入れ、DXに[47E6]を入れる
CXからは1を引く

PUSH SI
PUSH DI
PUSH CX
PUSH DX
SI, DI, CX, DXの値を退避

MOV AX,993A
MOV DS,AX
MOV SI,0000
データセグメントは993AH、SIは0000H

CALL A200
サブルーチンA200Hへ行ってこい




コードセグメントのアドレスA200HからA208Hまでの命令
サブルーチンA0D6Hから飛んできた。
最終的にサブルーチンA2B9Hへ行くまで。
このサブルーチンA200Hは中身がほとんどなく、すぐにサブルーチンA2B9HをCALLしてRETする。サブルーチンA2B9Hのwrapperのようなもの。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA200H)

MOV CX,0001
MOV DX,0009
CALL A2B9
サブルーチンA2B9Hへ行ってこい

サブルーチンA2B9H自体はデータバッファDS:SIからグラフィックVRAM(全4プレーン)の指定場所(DI、セグメントはサブルーチン内で設定)へ、横(CX*2)バイト(=(CX*16)ピクセル)、縦DXピクセルの矩形領域を転送するものだが、上の指定により、ここ(サブルーチンA200H)では横16ピクセル縦9ピクセルの転送となる。サブルーチンA2B9Hの転送は単純な転送でラスタ演算処理はない。




コードセグメントのアドレスA2B9HからA2C1Hまでの命令
サブルーチンA2B9H。サブルーチンA200Hから飛んできた。
最終的にサブルーチンA2E0H(初回)へ行くまで。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA200H>サブルーチンA2B9H)
DS=993AH
SI=0000H(初回)
ES=このサブルーチン内で設定する
DI=指定矩形領域の左上のアドレス(初回)
CX=1
DX=9

MOV AX,A800
MOV ES,AX
PUSH DI
CALL A2E0
ESにA800H(グラフィックVRAMのプレーン0セグメント)を入れて、サブルーチンA2E0Hへ行ってこい




コードセグメントのアドレスA2E0HからA2F4Hまでの命令
サブルーチンA2B9Hから飛んできた。
最終的にサブルーチンA2B9Hへ戻るまで。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA200H>サブルーチンA2B9H>サブルーチンA2E0H)
DS=993AH
SI=0000H(初回)
ES=呼び出し側のサブルーチン内で設定済み(グラフィックVRAMの指定プレーンのセグメント)
DI=指定矩形領域の左上のアドレス(初回)
CX=1
DX=9

PUSH DX
A2E1: ;JMP Shortのラベルはここ
ループ開始

PUSH CX
REP MOVSW
POP CX
データバッファ(DS:SI)からグラフィックVRAMの指定プレーンの指定場所(ES:DI)へ2バイトを1回(CX=1)転送

MOV AX,0050
SUB AX,CX
SUB AX,CX
ADD DI,AX
50H(グラフィックVRAM横バイト数)から(CX(先ほどの転送回数)*2)を引いたものをDIに足すと、DIは指定矩形領域内の次のラインの先頭を指す

DEC DX
JZ A2F3
9回実行したらループから抜ける

JMP Short A2E1
A2F3: ;JZのラベルはここ
POP DX

RET
サブルーチンA2B9Hへ戻れ




コードセグメントのアドレスA2C2HからA2DFHまでの命令
サブルーチンA2B9Hの続き。サブルーチンA2E0H(初回)から帰って来たところ。
最終的にサブルーチンA200H(初回)へ戻るまで。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA200H>サブルーチンA2B9H)

POP DI
MOV AX,B000
MOV ES,AX
PUSH DI
CALL A2E0
POP DI
ESにB000H(グラフィックVRAMのプレーン1セグメント)を入れて、サブルーチンA2E0Hへ行ってこい

MOV AX,B800
MOV ES,AX
PUSH DI
CALL A2E0
POP DI
ESにB800H(グラフィックVRAMのプレーン2セグメント)を入れて、サブルーチンA2E0Hへ行ってこい

MOV AX,E000
MOV ES,AX
CALL A2E0
ESにE000H(グラフィックVRAMのプレーン3セグメント)を入れて、サブルーチンA2E0Hへ行ってこい

RET
サブルーチンA200Hへ戻れ




コードセグメントのアドレスA209HからA209Hまで、たった1バイトの命令
サブルーチンA200Hの続き。サブルーチンA2B9Hから帰って来たところ。
最終的にサブルーチンA0D6Hへ戻るまで。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H>サブルーチンA200H)

RET
サブルーチンA0D6Hへ戻れ




コードセグメントのアドレスA12DHから141Hまでの命令
サブルーチンA0D6Hの続き。サブルーチンA200H(初回)から帰って来たところ。
複雑なので直下のループ部分だけでひと区切りとする。サブルーチンは続く。
(メインルーチン>サブルーチン98D0H>サブルーチンA0D6H)

サブルーチンA200H(データバッファからグラフィックVRAMへ横16ピクセル縦9ピクセルの矩形領域を転送)はすでに一度実行している。下の処理でさらにループ内で(DX-1)回(DXは退避してあった値を戻して1D7C:[47E6]になっている)繰り返すので、そこまででサブルーチンA200HをDX回繰り返すことになる。毎回のサブルーチンA200Hの直前にSIは初回だけが0000Hでその後(ループ内)は00D8H、DIは初回だけが指定矩形領域の左上のアドレスでその後(ループ内)は02D0H(=720;グラフィックVRAMの9ライン分)を足す。CXとDXは参照しない。

A12D: ;JMP Shortのラベルはここ
(DX-1)回のループ開始

POP DX
POP CX
POP DI
DI, CX, DXの値を元に戻す(SIはそのまま)

DEC DX
JZ A142
DXをデクリメントし、0になったらループから抜ける

ADD DI,02D0
MOV SI,00D8
DIは前回よりも02D0H(=720;グラフィックVRAMの9ライン分)多く、SIは毎回00D8H

PUSH DI
PUSH CX
PUSH DX
DI, CX, DXの値を退避

CALL A200
サブルーチンA200Hへ行ってこい

JMP Short A12D
A12DHへ戻ってループせよ

A142: ;JZのラベルはここ
(DX-1)回のループ終了

ここまでで、データバッファからグラフィックVRAMの指定場所へ横16ピクセル縦1D7C:[47E6]ピクセルの矩形領域を転送したことになる。

感想
たいして複雑な処理をしていないと思うのだが、コードの行数が膨大で、サブルーチンからサブルーチンが呼び出されるので現在どこにいるかがわかりにくく、大変な作業になってきた。

コメント(0) 

コメント 0

コメントの受付は締め切りました