SSブログ

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

本日3つめの記事。とうとう1日中これをやっていた。たとえこれが世界一退屈なブログ記事であろうとも、書いてる本人は必死だ。
アセンブラは数十年やってない。ほとんど全部忘れた。それをネット検索で学び直しつつ、ニーモニックひとつひとつの意味を追ってゆく。
ニーモニックの解説はネット上にある。DOSのシステムコールの解説もネット上にある。問題はPC-98のソフトウェア割り込みの詳細だ。大昔、私はそれの分厚い本を持っていた。でも要らなくなったからとっくの昔に捨ててしまった。ネット上にはなかなか見つからない。どうするおじさん。その時空の彼方から我らがウルトラマンが助けに・・・は来なかった。なんと来たのは、私自身だった。昔の私がやって来た。それなりにえらいぞ私。大昔の若者だった私が、タイムマシンに乗って、いまおじさんの私を助けに来てくれたよ。という所までが今回の話。
このへんで書いておくが、ここから下の数十行だか百数十行だかには、ひたすらニーモニックがずらずらと続く。必死で解読した私以外の人には間違いなく退屈だから、このページを下のほうまでずーっとスクロールしちゃって、記事の最後のあたりまで行くといいと思う。



コードセグメントのアドレス0000Hから0016Hまでの命令
プログラム先頭のメインルーチン。最終的にアドレス6FECHへ飛ぶまで。

MOV AX,1D7C
MOV DS,AX
データセグメントは1D7CH
(これはまあ、たまたま1D7CHだったと考えるか。)

XOR AX,AX
MOV ES,AX
MOV AL,ES:[0584]
MOV [384B],AL
セグメント0000Hのアドレス0584Hにある1バイトの値を
データセグメントのアドレス384BHに入れる
(0000:0584はBIOSデータエリアで、OSがブートしたディスク装置の種類。その情報をデータ領域にコピーした。)

LEA SI,[1F6A]
ソースインデックスレジスタにアドレスとして1F6AHを入れる
(SIの値はCALL先で使う。何の値かはまだわからん。)

CALL 6FEC
コードセグメントのアドレス6FECHへ行ってこい




コードセグメントのアドレス6FECHから7024Hまでの命令
メインルーチンから飛んできたサブルーチン。最終的にアドレス7026Hまたは704EHへジャンプしてサブルーチンは続く。
上記CALL 6FECの次行へ戻れるのはいつのことやら。

CMP WORD PTR [13B8],+01
JNZ 7026
データセグメントのアドレス13B8Hからの2バイトが+01でなければ
アドレス7026Hへ飛べ
(1D7C:13B8にはまだ何か入れる命令はない。あらかじめ何かの値が入っている。)
(+01の記述の意味がまだ調べられないでいる。なんで+が付いているんだ。)

MOV AX,1D7C
MOV ES,AX
MOV DI,1EE8
ADD DI,+11
MOV AX,0000
MOV CX,0028
REP STOSW
セグメント1D7CH(データセグメント)のアドレス1EE8H+11から0000Hの書き込みを28H回(40回)繰り返す
(結果として1D7C:1EE8+11からの80バイトは0になる。)
(+11の記述の意味がまだ調べられないでいる。たぶん16進数だろうが。)
(CLDはどこでしているんだ?)

MOV DI,1EE8
MOV CX,0004
REP MOVSW
データセグメントのアドレス1F6AH(SIがここで使われる)から8バイト分、データセグメントのアドレス1EE8Hへコピーする
(SIはインクリメントされて1F72Hになり、DIはインクリメントされて1EF0Hになっている。)
(CLDはどこでしたんだ?)

MOV AX,[13BA]
ADD AL,62
MOV [DI],AL
データセグメントのアドレス13BAHにある1バイトを取り出し、それに62Hを足し、それをデータセグメントのアドレス1EF0Hに入れる

ADD SI,+02
ADD DI,+09
MOV CX,0050
REP MOVSB
SIは先ほどMOVSWで使われてインクリメントされ1F72Hになっているが、さらに2を足す。DIは1EF0Hになっているが、さらに9を足す。SIからDIへ50Hバイト分コピーする
(何度も言うがCLDはどこでしたんだ?)

MOV SI,1EE8
JMP SHORT 704E
ソースインデックスレジスタに値1EE8Hを入れ
アドレス704EHへ飛べ
(SIに入れた1EE8はどうやら何かのファイルを読み込むさいのアドレスで、WORD PTR[SI]にはファイルを読み込むバッファのセグメント、WORD PTR[SI+2]には同じくバッファのオフセット、WORD PTR[SI+4]にはファイル長、[SI+8]からはファイル名が格納されている。)

感想
PUSHもPOPも出てこないから、このプログラムはCで書かれていない。冗長でないのは有難い。でもまだまだ相当かかりそうだ。




コードセグメントのアドレス704EHから7079Hまでの命令
サブルーチンの続き。JMP SHORT 704Eのほうを追いかけることにした。最終的にRETでメインルーチンへ戻る。

PUSH SI
PUSH DS
SIとDSをスタックへ退避したぞ

MOV AX,3D00
MOV DX,SI
ADD DX,+08
INT 21
JB 707E
ハンドルによるファイルのオープン(ASCIIZファイル名はデータセグメントのアドレス(1EE8H+08H)にあり)
もしもエラーならば707EHへ飛べ

MOV BX,AX
PUSH BX
MOV AH,3F
MOV CX,[SI+04]
MOV DX,[SI+02]
MOV DS,[SI]
INT 21
POP BX
JB 707A
オープンしたファイルからセグメントWORD PTR[1EE8H]のオフセットWORD PTR[1EE8H+02H]へ
WORD PTR[1EE8H+04H]のバイト数だけ読み込む
もしもエラーならば707AHへ飛べ

POP DS
PUSH DS
MOV AH,3E
INT 21
JB 707A
ファイルをクローズ
もしもエラーならば707AHへ飛べ

POP DS
CLC
POP SI
DSとSIをスタックから戻した
キャリーフラグもクリア

RET
ああ、やっと最初のほうにあったCALL 6FECの次行へ戻れる




コードセグメントのアドレス0017Hから0025Hまでの命令
プログラム先頭から続くメインルーチンに戻ってきた。サブルーチンではない。

CLD
(あっ、いまごろCLDがっ)

MOV WORD PTR [0000],0000
MOV AX,1D7C
MOV DS,AX
データセグメントのオフセット0から2バイトを0に
データセグメントは1D7CH
(いちばん最初と同じだ。)

CALL 4306
コードセグメントのアドレス4306Hへ行ってこい




コードセグメントのアドレス4306Hから・・・

MOV AH,42
MOV CH,80
INT 18







ここでまたINT21か・・・あれ? INT18? DOSシステムコールじゃない!これはPC-98の何らかのハードウェアを制御するために割り込みをかけている。今までのたくさんのネット検索の間に、割り込み番号と用途の簡単な対応を記したサイトは見つかっていた。でも18HはキーボードおよびCRT BIOSに使われるとしか書いていない。AHレジスタに42Hを入れた時の機能がわからないと困る。私はなおもネット検索した。「詳細がネット上にあってびっくりした」という書き込みがあったが、肝心のその詳細へのリンクがすでに切れていた。さらにネット検索し続ければいずれはどこかに見つかるかもしれない。しかし大変なことだ。その時、私は思いついたことがあった。若いころに作ったPC-98の機能を引き出すためのCライブラリ、そこではINT18も使っているに違いない。私はそれを探した。こんなのが見つかった。(ソースコードはpreタグで括ってあるので右端はブログの表示域からはみ出てしまうと思う。たぶん、PCならマウスで選択してコピー&「メモ帳」へ貼り付けで見えるのでは。)
#include <dos.h>

/* 関数プロトタイプ宣言 */
void gsplit( int page, int color, int lines );


void gsplit( int page, int color, int lines )
{
    unsigned char info;
    union REGS inregs, outregs;

    info = ( page & 0x01 ) << 4 | ( color & 0x01 ) << 5 | ( lines + 3 & 0x03 ) << 6;
    inregs.h.ah = 0x42;
    inregs.h.ch = info;
    int86( 0x18, &inregs, &outregs );
}


ほとんど何も覚えてないが、とにかく0x18って書いてある。そしてah=0x42って書いてある。でもこれだけだと機能がわからない。私は自分が大昔に書いたドキュメントを探した。そうしたら、こんなのが残っていた。(ソースコードと同じくpreタグで括ってある。)

3-1.諸状態の設定

void gsplit( int page, int color, int lines )

BIOSを使って次の3項目の設定を同時に行う。
・表示ページの設定
・カラー/モノクロの設定
・400ライン/200ライン(およびVRAM上の表示アドレス)の設定
3項目同時に設定する必要がない場合は、下記のIOポート操作版の処理でそれぞれ別
個に設定することもできる。
・表示ページの設定は本ライブラリ中に別にIOポート操作版を設ける。
・カラー/モノクロの設定はTXTGDC.HにIOポート操作版modeff(GMODE,*)がある。
・400ライン/200ラインの設定はTXTGDC.Hのmodeff(GRPMODE,*)、本ライブラリの glin
  esset()、gsplitset()、gscopeset()を組み合わせることにより実現する。
  -glinesset()では一行のライン数を設定し、gsplitset()、gscopeset()ではGVR
  AMのどこから何ライン分(何行ではない)表示するかを設定する。たとえば、一行
  を1ラインとし、GVRAM先頭から400ライン分表示すれば普通の400ラインモード
  となるが、一行を2ラインとすれば200ライン LOWER モードとなる。一行を同じく2
  ラインとしてGVRAM真中から400ライン表示すれば、画面は200ライン UPPER と
  なる。
  -これでわかる通り、IOポートを直接操作すれば一行3ライン、4ライン、5ライ
  ン等の画面も作れる。この場合、画面は粗くなるが、使用するVRAMは節約できる。
  -いっぽう modeff(GRPMODE,*) は、400/200ラインそのものを設定するのではない。
  これを200ラインに設定すると1ラインおきに描画するらしく、画面に細かい横縞が
  現れる。BIOS版での200ラインモードはこの状態に相当する。しかし専用ディス
  プレイを使う場合には、modeff(GRPMODE,*) の設定は、画面表示の綺麗な400ライン
  用の設定のままが良いだろう。
この関数には以下のマクロが使える:
GPAGE0  GPAGE1  COLOR  MONO  DISPLAY200U  DISPLAY200L  DISPLAY400
マクロのうち COLOR, MONO は TXTGDC.H で、DISPLAY400 は TXTDOS.H で既に使ってい
るので、そのまま利用する。


私は「テニステニス2」のプログラムを見ることで自分を元気にしようと思ってきたが、それだけでなく、これはひょっとすると「大昔の自分との再会」という予想以上の喜ばしい出来事が起きてしまうかもしれない。でも今は、無理をしてPCの前に座り続けたせいでちょっと危ない腰をかばいつつ、もうやめなければいけない。

がんばれおじさん!
負けるなおじさん!
腰痛にも気をつけろ!

コメント(0) 

コメント 0

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