BASICの構造と拡張 - 後半 - 書籍「ADVANCED MZ-700」より引用・ダイジェスト ======================================================================== Hu-BASICのコマンドをふやす -------------------------- Hu-BASICに、以下の4つのコマンドを追加する。 1. マルチスクリーン : コマンド名 : SWS 2. ハードコピー : コマンド名 : HRDC 3. トレーサ : コマンド名 : TRON 行番号 / TROFF 4. ボーレートチェンジ : コマンド名 : BAUD L / BAUD H ( L : 1200ボー / H : 2700ボー) ### マルチスクリーン MZ-700はV-RAMを2画面分持っている。 Hu-BASICで以下を実行すると、2画面が繋がって、 ロールアップ、ロールダウンができる。 CONSOLE[CR] 対してマルチスクリーンは、2画面を独立して使うもので、 片方がスクロールしても他方は影響を受けない。 プログラムの処理内容は、V-RAMの情報を入れ替えるだけ。 * 1枚目の画面は $D000 - $D3FF のV-RAM、$D800 - $DBFF のカラーV-RAMからなる。 * 2枚目の画面は $D400 - $D7FF と $DC00 - $DFFF からなる。 これらを1byteずつ入れ替える。 マルチスクリーン処理をするプログラムは $898E番地から置くが、 $898E は本来、BASICテキストが入り始める番地。 このままではBASICプログラムを入力するとマルチスクリーン部分が壊れる。 そこで、マルチスクリーンのプログラムを入力する前に、 BASICコマンド NEWON でテキスト入力番地を後方へずらす。 以降のユーティリティも同様の処置をする。 例: NEWON &H9000 * BASICから CALL &H898E を実行するたび、 画面が Page1 になったり、 Page2 になる。 * カーソルの位置は変化しない ([CR]で1行下の左端に移動)。 マルチスクリーンは、長いテキストの上のほうと下のほうを 同時に見たい時に有効。例えば…。 LIST 1-100[CR] で 1-100行を画面に出力したあと、 CALL &H898E[CR] で、画面を切り替え、 LIST 1000-1100[CR] で、うしろのほうのリストを見る。前のほうを見たくなったら CALL &H898E[CR} を実行する。 以下がマルチスクリーンを実現するプログラム。 リスト1 マルチスクリーン 898E D3E3 OUT ($E3),A 8990 D5 PUSH DE 8991 E5 PUSH HL 8992 1100D0 LD DE,$D000 8995 2100D4 LD HL,$D400 8998 D9 EXX 8999 1100D8 LD DE,$D800 899C 2100DC LD HL,$DC00 899F D9 EXX 89A0 1A LD A,(DE) 89A1 46 LD B,(HL) 89A2 77 LD (HL),A 89A3 78 LD A,B 89A4 12 LD (DE),A 89A5 13 INC DE 89A6 23 INC HL 89A7 D9 EXX 89A8 1A LD A,(DE) 89A9 46 LD B,(HL) 89AA 77 LD (HL),A 89AB 78 LD A,B 89AC 12 LD (DE),A 89AD 13 INC DE 89AE 23 INC HL 89AF 7A LD A,D 89B0 FEDC CP $DC 89B2 20EB JR NZ $899F 89B4 E1 POP HL 89B5 D1 POP DE 89B6 D3E1 OUT ($E1),A 89B8 C9 RET ### ハードコピー 画面をそのままプリンタに出力するのがハードコピー。 ただし、この場合はプロッタプリンタを仮定しているので、 グラフィックキャラクタなどは無視して 文字 (英大文字、カタカナ) だけをコピーする処理にする。 ハードコピーは、画面の文字を読み込んで、 プリンタに対応するコードを出力する。 画面からの読み込みは V-RAMの内容を読めばよいが、 得られた値はASCIIコードではなくディスプレイコードなので、 そのままプリンタに出力すると違う文字を打ち出してしまう。 コードを変換する必要がある。 ディスプレイコードをASCIIコードに変換するサブルーチンは、 MZ-700モニタ 1Z-009A の DACN ($0BCE番地) に存在するが、 Hu-BASIC動作時、モニタROMが配置されてる領域は D-RAMに切り替わるので、1Z-009A を利用できない。 よって、Hu-BASIC のモニタの中のサブルーチンを使う。 * Hu-BASICの場合、$05C0番地からが、 ディスプレイコードからASCIIコードへの変換サブルーチン。 * ACC にディスプレイコードを入れ、CALL $05C0 を実行すると、 ACC にASCIIコードが入って戻ってくる。 * AF以外のレジスタは変化しない。 * 逆に、ASCIIコードからディスプレイコードへの変換サブルーチンは $05D7番地にある。 プリンタに1文字出力するサブルーチンは、$1821番地にある。 ACC にASCIIコードを入れて、CALL $1821 を実行すると、 プロッタプリンタに1文字出力される。 ところが、このASCIIコード表 (マニュアル P213) で、 空白になっているコードを出力しようとすると、 プロッタは色を変えて16進数を打ち出してしまう。 これではハードコピーにならないので、 そういったコードに対しては、空白 ($20) を出力するように変換する。 Hu-BASICは空白ではなく「.」 (ピリオド) に変換している。 そのルーチンは $17D2番地にあって 「.」 ($2E) に変換しているのは、$17EB の LD A,$2E (3E 2E) 部分。 したがって、$17EC の $2E を $20 に換えて $17D2番地を CALL すれば、 目的は達成できる。 もう一点。 ブレークキーが使えないと、1画面全部プリントし終わるまで何もできない。 プロッタプリンタはスピードが遅いので、 必要な所までコピーしたら、強制的に処理を打ち切りたい。 そこで、1行出力し終わったらブレークキーのチェックもする。 * ブレークキーが押されたかチェックするサブルーチンが $0593番地にある。 * BREAKキーだけなら次のキー入力を待ち、 SHIFT + BREAKキーならZフラグを立てて戻り、 押されなければZフラグをクリアして戻る。 マシン語プログラム中にサブルーチンコールとバンク切り替えがあるときは注意。 画面 (V-RAM) を読む際にバンク切り換えをするが、 そのままサブルーチンコールをすると、スタックポインタが $FFF0 にあるので、 バンク切り換えしたために D-RAM にアクセスできず暴走する。 リスト2 ハードコピー 89B3 E5 PUSH HL 89BA C5 PUSH BC 89BB 213E20 LD HL,$203E 89BE 22EB17 LD ($17EB),HL 89C1 2100D0 LD HL,$D000 89C4 0619 LD B,$19 89C6 0E28 LD C,$28 89C8 D3E3 OUT ($E3),A 89CA 7E LD A,(HL) 89CB D3El OUT ($E1),A 89CD CDC005 CALL $05C0 89D0 CDD217 CALL $17D2 89D3 23 INC HL 89D4 0D DEC C 89D5 79 LD A,C 89D6 B7 OR A 89D7 20EF JR NZ $89C8 89D9 CD1F18 CALL $181F 89DC CD9305 CALL $0593 89DF 2805 JR Z $89E6 89E1 05 DEC B 89E2 78 LD A,B 89E3 B7 OR A 89E4 20E0 JR NZ $89C6 89E6 213E2E LD HL,$2E3E 89E9 22EB17 LD ($17EB),HL 89EC C1 POP BC 89ED E1 POP HL 89EE C9 RET ### トレーサ Hu-BASICのコマンドに TRON がある。 TRON を実行すると、プログラム実行時、 実行中の行番号を画面に出力する。 この状態は TROFF を実行するまで続く。 しかし、行番号だけではデバッグ情報として不十分。 しかも画面一杯に出力されるので プログラムで PRINT した内容が壊れる欠点もある。 ここで紹介するトレーサは、プログラムの各行を実行する直前に、 ユーザーが定義したプログラムを実行するもの。 例えば、以下のプログラムがあるとして…。 10 I=1 20 IF i=3 THEN 50 30 PRINT "AB" :I=I+1 40 GOTO 20 50 END 60 PRINT I 70 TEND TEND は新しいコマンド。トレーササブルーチンの最後に必ずつける。 TRON 60[CR] を実行後、 RUN[CR] とすると、画面には、 0 ← トレーサルーチンの出力 1 ← 〃 1 ← 〃 AB ← メインルーチンの出力 2 2 2 AB 3 3 3 と表示される。 トレーサは、3つのサブルーチンから成り立つ。 #### 1. tron Hu-BASIC の TRON の代わりに呼ばれるルーチン。 TRON 行番号 として呼ばれる。 内部では、TRON は中間コードに変換され、 行番号は 16進数になり、 HLがその行番号のひとつ前を指した状態で、 TRON を処理するサブルーチンのコールが起こる。 TRON 50 ↓ A9 20 12 32 00 ↑ HL * A9 : TRONの中間コード * 20 : ASCIIコードのスペース * 12 32 00 : 行番号 tronルーチンでは DE に16進数を代入し、 HL と DE を入れ換えて $34D6 をコールしている。 $34D6 からのサブルーチンは、 HL に入ってる行番号の行がある絶対番地を HL に代入して戻る。 それをワークエリアにセーブして $2507番地 (オリジナルのTRON) へジャンプする。 オリジナルのTRONは、$3A00 に "AF" を代入するだけ。 そして実行時に $3A00番地が 0 でなければ、 行番号を表示するサブルーチンへ飛ぶ仕組み。 このチェックをやっているのは $1D03番地。次のようになっている。 LD A,($3A00) OR A CALL NZ $250D トレーサは CALL ではなく JP で次のルーチン trace main を呼ぶ。 つまり上の3行目を JP NZ tarce main にすればよい。 #### 2. trace main * まず HLレジスタと $3A03、$3A04、$3A09、$3A0A番地の内容を ワークエリアにセーブする。 * スタックを使わないのは、サブルーチンコールしたときに暴走しないため。 * $3A03 から 2byteには、次に実行すべき行がある絶対番地が入る。 * $3A09 から 2byteには、そのとき実行しようとしている行の行番号が 16進数で入っている。 よって、トレースルーチンの中で行番号を表示したければ、以下にする。 PRINT PEEK(&H8A30)+PEEK(&H8A31)*256 トレースルーチンをトレースするわけにはいかないので $2508 (TROFF) をコールしてから、 $3A03番地に tron で代入したトレースルーチンの絶対番地を代入する。 そして $1CEE番地へジャンプすると、 $3A03 の内容の番地からプログラムを実行してくれる。 #### 3. trace return (TEND) TEND に出会うと、このルーチンを CALLする。 TRON 状態にして、セーブしておいた HL レジスタと、 $3A03、$3A04番地の内容、 $3A09、$3A0A番地の内容を回復して、 tarce main を呼んだ次の行(アセンブラリストで)へジャンプする。 プログラムの実行状況を図にすると以下になる。 図5 トレーサの実行状況 ユーザープログラム │ │ tron main │ └────→ トレースルーチン JPNZ │ ↑ │ │ TROFF trace return │ │ │ └────→ │ │ │ TEND │ │ │ └───→ │ │ │ TRON │ │ └──────────────┘ 前の二つのユーティリティは、 サブルーチンとしてBASIC中からCALL文で呼び出せたが、 このトレーサはそうはいかない。以下が必要になる。 * TEND をコマンドとして登録。 * TRON の実行番地を換える。 * Hu-BASICで Trace flag をチェックしているところを変える。 #### i) TENDの登録 予約語表には、マニュアルにない予約語が多々ある。 その中でも、実行番地が $233D となっているコマンドには処理ルーチンが無い。 そこで、それら予約語の中から適当に選んで書き換えて、 追加コマンドを登録する。 ここでは LSET を選んで TEND にする。 LSET は $2F38 からなので、モニタで以下のように変更。 :2F38=;T ;E ;N C4 対応する実行番地は $3210 から 2byte。 :3210=1A 8A これで TEND は予約語になり、実行番地は $8A1A となった。 #### ii) TRONの実行番地変更 TRONの実行番地は $31AE から 2byteに入っているので、以下で変更。 :31AE=EF 89 #### iii) チェックの変更 先に述べたように、トレースチェックは $1D03 になるので、 これを JP NZ $89FF に換える。 :1D03=C2 FF 89 #### 注意 1. トレーサルーチン中で TRON コマンドを実行してはならない。 2. プログラムを変更したときは TRON コマンドをもう一度実行すること。 3. プログラム実行中に SHIFT + BREAK で実行を中止したときは、 TROFF を実行すること。 トレースをやめるには TROFF コマンドを使う。 - - - 図6 TRONコマンド 89EF 23 INC HL tron 89F0 5E LD E,(HL) 89F1 23 INC HL 89F2 56 LD D,(HL) 89F3 EB EX DE,HL 89F4 CDD634 CALL $34D6 89F7 22368A LD ($8A36),HL 89FA EB EX DE,HL 89FB 23 INC HL 89FC C30725 JP $2507 89FF 22348A LD ($8A34),HL trace main 8A02 2A033A LD HL,($3A03) 8A05 22328A LD ($8A32),HL 8A08 2A093A LD HL,($3A09) 8A0B 22308A LD ($8A30),HL 8A0E CD0825 CALL $2508 8A11 2A368A LD HL,($8A36) 8A14 22033A LD ($3A03),HL 8A17 C3EE1C JP $1CEE 8A1A CD0725 CALL $2507 trace return 8A1D 2A308A LD HL,($8A30) 8A20 22093A LD ($3A09),HL 8A23 2A328A LD HL,($8A32) 8A26 22033A LD ($3A03),HL 8A29 2A348A LD HL,($8A34) 8A2C C3061D JP $1D06 8A2F 00 NOP 8A30 320039 LD ($3900),A ### ボーレートチェンジ Hu-BASICのボーレートは 1200ボーだが、 長いプログラムでは、セーブやロードでとても時間がかかる。 そこで、コマンドでボーレートを 2700ボーに変更する。 また、X1 が 2700ボーなので、X1 のテープも読めるようになる。 ボーレートを変更するには、以下の時間待ち定数を変える。 $E21 : 6F $E2E : 6F $E40 : 36 $E4A : 34 $F78 : 4F 2700ボーにするなら、それぞれを以下に変更する。 $E21 : 27 $E2E : 27 $E40 : 13 $E4A : 12 $F78 : 1D プログラムはリスト3のとおり。 * BAUD の次の1文字が L か H かによって処理を変えている。 * BAUD H で 2700ボーに、BAUD L で 1200ボーになる。 * 引数がないときはスイッチして、1200ボーだったら2700ボーに、 2700ボーだったら1200ボーにする。 以下で、BAUD を予約語として登録する。 モニタで、 :2F3C=;B ;A ;U C4 :3212=38 8A とする。 リスト3 ボーレートチェンジ 8A38 D5 PUSH DE 8A39 7E LD A,(HL) 8A3A 201F JR NZ $8A5B 8A3C 3A210E LD A,($0E21) 8A3F FE6F CP $6F 8A41 2025 JR NZ $8A68 8A43 3E27 LD A,$27 8A45 32210E LD ($0E21),A 8A48 322E0E LD ($0E2E),A 8A4B 3E13 LD A,$13 8A4D 32400E LD ($0E40),A 8A50 3D DEC A 8A51 324A0E LD ($0E4A),A 8A54 3E1D LD A,$1D 8A56 32780F LD ($0F78),A 8A59 D1 POP DE 8A5A C9 RET 8A5B FE3A CP $3A 8A5D 28DD JR Z $8A3C 8A5F 23 INC HL 8A60 FE48 CP $48 8A62 28DF JR Z $8A43 8A64 FE4C CP $4C 8A66 20D4 JR NZ $8A3C 8A68 3E6F LD A,$6F 8A6A 32210E LD ($0E21),A 8A6D 322E0E LD ($0E2E),A 8A70 3E36 LD A,$36 8A72 32400E LD ($0E40),A 8A75 3E34 LD A,$34 8A77 324A0E LD ($0E4A),A 8A7A 3E4F LD A,$4F 8A7C 32780F LD ($0F78),A 8A7F D1 POP DE 8A80 C9 RET ### プログラムによる登録 新しいコマンドを登録するには、以下の作業が必要だが…。 * まずコマンド名を決め、 * その文字数と一致して、 * しかも実行番地が $233D になっているものを選び、 * 予約語表と実行番地を書き換える ただし、実行番地が $233D であっても、 PLOT OFF の OFF のように予約語とあわせて ひとつのコマンドとなっているものは書き換えることはできない。 マルチスクリーンを SWS というコマンドにするには、 :2EEC=;S ;W D3 :31EE=8E 89 ハードコピーを HRDC というコマンドにするには、 :2EE8=;H ;R ;D C3 :31EC=B9 89 それぞれを実行すれば良い。 しかし、こうして全コマンドをその都度登録するのは面倒なので、 プログラムで実現する。リスト4 がそのプログラム。 リスト4 コマンドローダー 8A81 21C18A LD HL,$8AC1 8A84 11E82E LD DE,$2EE8 8A87 010700 LD BC,$0007 8A8A EDB0 LDIR 8A8C 21B989 LD HL,$8989 8A8F 22EC31 LD ($31EC),HL 8A92 218E89 LD HL,$898E 8A95 22EE31 LD ($31EE),HL 8A98 21C88A LD HL,$8AC8 8A9B 11382F LD DE,$2F38 8A9E 010800 LD BC,$0008 8AA1 EDB0 LDIR 8AA3 211A8A LD HL,$8A1A 8AA6 221032 LD ($3210),HL 8AA9 21388A LD HL,$8A38 8AAC 221232 LD ($3212),HL 8AAE 21EF89 LD HL,$89EF 8AB2 22AE31 LD ($31AE),HL 8AB5 3EC2 LD A,$C2 8AB7 32031D LD ($1D03),A 8ABA 21FF89 LD HL,$89FF 8ABD 22041D LD ($1D04),HL 8AC0 C9 RET 8AC1 48 LD C,B 8AC2 52 LD D,D 8AC3 44 LD B,H 8AC4 C35357 JP $5753 8AC7 D354 OUT ($54),A 8AC9 45 LD B,L 8ACA 4E LD C,(HL) 8ACB C44241 CALL NZ $4142 8ACE 55 LD D,L 8ACF C40000 CALL NZ $0000 (※ 原文では $8AC1:48 - $8ACF:C4 の間が線で囲まれて 「データ」と書かれている。 プログラムではなくデータ列が並んでいることを示したいが、 データ列を記述可能なアセンブラを入手できなかった模様。) ユーティリティ4つと、このプログラムを入れて、モニタでセーブする。 *S898E 8ACF 8A81:UTILITY 使い方は、Hu-BASICをロード後、 NEWON &H8AD0 を実行してからモニタに入り、先ほどのテープをロード。 MON *L ロードが終わったら、 *G8A81 を実行。BASICへ戻ると4つの追加コマンドが使用可能になる。 (※ 原文では *G8A31 と書いてあったが、おそらく誤植。*G8A81 だと思われる。) ### コマンド追加済みBASICのテープを作りたい BASICロード後、すぐに追加コマンドを使えるようにしたいなら、 次の方法で新しいBASIC (4つのユーティリティを含む)のテープを作成できる。 ※ 紙面にはそのように書いてあったが、 実際には必要なプログラムが欠落していて、 以降の内容はおそらく実践できない。 1. 電源ON したあと、モニタ 1Z-009A で図7のプログラムを入力。 図7 オープニングメッセージ 0C 0D 20 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 D7 20 20 20 20 20 20 48 55 42 41 53 49 43 20 56 45 52 53 49 4F 4E 20 32 2E 31 41 20 20 0D 20 20 20 20 20 20 20 20 20 20 20 20 20 46 4F 52 20 53 48 41 52 50 20 4D 5A 2D 37 30 30 20 20 20 20 20 20 20 20 20 20 20 20 CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF CF 20 0D 0D 00 ※ 図7とあるが…これは記事の作成ミスだと思われる。 本来は、Hu-BASICを読み込み、しかし実行はしないプログラムの類を $CF00 から打ち込むものと想像するが、 肝心のそのプログラムが紙面のどこにも載っていない。 これでは作業ができない。 しかし、もしかすると、以下のプログラムを改変・利用して解決できるかもしれない。 [MZ のテープを活用しよう - アルゴの記憶](https://web.archive.org/web/20190328121927/http://www.geocities.co.jp/SiliconValley-Sunnyvale/2521/mz700tape.html#1z009a) > MZ-700 用モニタ 1Z-009A についての情報です。 > > ■ テープをバックアップする > > :CF00=CD 27 00 38 03 CD 2A 00 > :CF08=DA FE 00 C3 AD 00 > > 1. 上記を打ち込む。 > 2. JCF00 を実行してPLAYボタンを押し、バックアップ元のテープを読み込む。 > 3. テープを取り出しバックアップ先のテープをセットする。 > 4. J1108 を実行。HIT ANY KEY? と表示されるのでなにかキーを押す。 > 5. RECORDボタンを押すと、バックアップ先のテープに保存します。 > 6. 再度 HIT ANY KEY? と表示されバックアップは終了です。 上記のプログラムをアセンブラで書くと以下になる。 CF00 CD 27 00 CALL $0027 ; テープの情報を読む CF03 38 03 JR C CHK CF05 CD 2A 00 CALL $002A ; テープのデータを読む CHK: CF08 DA FE 00 JP C $00FE ; エラー解析ルーチン CF0B C3 AD 00 JP $00AD ; モニタのホットスタート つまり、テープから情報を読み込み、エラーが無ければデータも読み込んで、 読み込み後は読んだデータを実行せずにモニタのコマンド入力に戻る、 という処理をしている。 2. Hu-BASICのテープをセット。 モニタで *JCF00 を実行後、PLAYボタンを押す。 3. *が画面に出て、テープが止まったら $9B8E番地から、 4つのユーティリティプログラムをリストの番地の順に入力。 リストでは $898E から始まっているので、$1200番地だけずれていることになる。 すべてのデータを $1200番地ずらしてそのまま入れる。 4. $9C9E から プログラム5 を入力。 これは $1200番地から $8A9E byteを $0000 に移すプログラム。 リスト5 :9C9E=AF 11 00 00 CD 33 00 2A :9CA6=71 11 22 0E 12 11 00 00 :9CAE=21 9E 8A B7 ED 52 4D 44 :9CB6=21 00 12 D3 E0 ED B0 C3 :9CBE=00 00 5. $9C06 に 00 を入れる。 6. $368F と $3696 からそれぞれ 2byteを 00 90 に。テキストポインタを設定。 7. 以下を変更。 $9A8B : 00 90 $2DF8 : 00 90 $40EC : 53 57 D3 (マルチスクリーンの登録) $43EE : 8E 89 (マルチスクリーンの登録) $40E8 : 48 52 44 C3 (ハードコピーの登録) $43EC : B9 89 (ハードコピーの登録) $4138 : 54 45 4E C4 (TEND の登録) $4410 : 1A 8A (TEND の登録) $43AE : EF 89 (TRON の実行番地の変更) $2F03 : C2 FF 89 (トレースチェックの変更) $413C : 42 41 55 C4 (BAUD の登録) $4412 : 38 8A 8. 新しいテープをセットして、*S1200 9CBF 9C9E でセーブ。 ファイル名は任意。