MSXでマシン語BASIC 中級・上級編

前へもどる>mon.html

■マシン語で打ってみよう

マシン語の初心者から中級者向けのページになっています。
MSXのマシン語を始めたい要望もあって作りました(^_^)
これから始める初心者は専門用語がありますので
このページを区切りまで読んで講座にトライしてみてください。
また、講座を読みきった方向けに少し高度な使い方を解説します。
BEEP音を鳴らしたり、VRAMに書き込んで画面を白にしたりして
MSX2テクニカルハンドブック向けの解説になっています。
マシン語は誰でもプログラムできます!体験してみてください。

なお、MSXマガジン永久保存版3HardwareTechnicalDATA資料編も
参考にできます。

 やってみましょうZ80マシン語 初心者でマシン 語を始めたい人向けです

 MSXマシン語適当講座その1 マシン語からBASICの変数を取得、LD、CALL、INC

 MSXマシン語適当講座その2 SUPER-X、実行、アセンブラ、ソースを書く

 MSXマシン語適当講座その3 一時的にレジスタを使う、フラグ、レジスタ、転送

 *MSXでマシン語BASIC 中級編 ハンド アセンブルから開発環境の話題です。

  命令セット一覧:Z80マシン語/MSX-DOS上級編 Z80、モニタアセンブラ、テキストエディタ

・マシン語モニタ基本命令
・マシン語を知る
・MSXBASICでソースを組む場合

・マシン語のユーザーエリアのアドレスに書き込み、実行する
・BASICとマシン語のデータを分ける
・マシン語とBASICのデータの受け渡し
・長いプログラムの場合
・プログラムにしてみる
・大規模なプログラムを作る
・マシン語プログラムをBASIC化
・マップパーツの高速化?
・気楽にマシン語、BIOS、ワークエリアを使ってみる
 -VRAMにアクセスしてみましょう
 -16ビット演算命令を使って加算処理を作る
 -プログラムしやすい環境を作る
 -VRAMのアドレスを移動させながら点を打つ
・MSXにはこんな命令が

■マシン語モニタ基本命令

今度はポケットバンクのマシン語入門のマシン語モニタを使ってみましょう。
読み方は適当ですが、こんな具合です^^;
MSXBASICには内臓されていませんので、別途プログラムが必要です。

B−BASICに戻る。

Basicの頭文字

D−メモリの内容を表示する

Dump(ダンプ)の頭文字

S−メモリの内容を変更する

State(ステート)の頭文字

X−レジスタの内容の表示と変更する

eXchange(エクスチェンジ)の頭文字?

G-マシン語プログラムを実行する

Go(ゴー)の頭文字。

R-VRAMからVRAMへ転送

Read(リード)の頭文字。読み込むという意味?

L-プリンタに出力

LPT:の頭文字。ListPrinTerリストプリンタ?

プログラムしても動かない人は
まず、BASICのプログラムのようなソースを
CMD ASM(”XXC")をして、エラーがなければマシン語コード化されてVRAMに保存されます。
次にRコマンドでメインRAMに転送され、GコマンドかBASICでDEFUSR=&HA000:A=USR(0)で実行できます。
BASICのモニタアセンブラはこんな感じでしょう。

★ダンプの種類

ダンプ表示には00〜0Fhまでと00〜07で区切って08〜0Fhまでと
1アドレスタイプの3つがあります。

例:A000hで10h(イチゼロエイチ)ステップの場合

>DA000

          0 +1  +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F -CHR-
A000:4D 53 58 20 42 41 53 49 20 76 65 72 73 69 6F 6E MSX BASIC version

と何がなんだかよくわかりませんが、
これはメモリのA000h〜A00Fhの内容をモニタしたものです。
このダンプはMSX2マシンのWIDTH80で表示されます。
10hステップはA000、A010、A020というステップ幅です。
CHRはキャラクタコード256種類を表示させています。

・シンプルなタイプ

D000   00 11
D001   20 22
D002   03 33
D003   00 _

1アドレスごとに表示するモニタもある。左からアドレス、書き換え前のコード、後のコードになっている。
_をカーソルにしてみた。入力は2バイト入力してRETURNを繰り返す。
1つ前のアドレスへ修正したい場合

D004    00 33^_

と、^を入れると、

D003    33 _
になる。さらに.の場合は
D003    33 C9.
とする。


・高性能なモニタなら、

A000:4D 53 58 20 42 41 53 49 20 76 65 72 73 69 6F 6E MSX BASIC version

コードとキャラクタコードの表示があります。
入力は続けてコード、またはキャラクタを入力できます。

右のキャラクタのMSXのMは4Dですが、この4DのコードをMSXBASICで調べる方法は

? CHR$(&H4D)
M

となり、Mを調べたい場合は

? HEX$(ASC("M"))
4D

となります。このようにマシン語はアドレスにデータとして書き込みますから、
自分で変数、文字情報、オブジェクトと領域を決めていかなければなりません。
でも難しくはありません。ユーザーエリア内で決めればよいです。
文字データであれば128バイト、変数データであれば12バイトくらいとワークエリアを仮に決めます。
あとは「1つアドレスを読んで、この座標に文字を表示」という繰り返しで表示します。

■マシン語を知る

マシン語はどういうものかをやってみます。
BASICでモニタセンブラがない人もいるでしょう。
このページでは数行でマシン語を体験できるようにしてみます。
これが変換表の一部です。

ニーモニック:CALL 1A00H
マシン語:CD001A
ニーモニック:LD  A,03H
マシン語:3E03
ニーモニック:LD A,B
マシン語:78
ニーモニック:INC A
マシン語:3C
ニーモニック:LD  B,02H
マシン語:0602
ニーモニック:LD  B,A
マシン語:47
ニーモニック:LD  C,0AH
マシン語:0E0A
ニーモニック:LD  HL,003EH
マシン語:213E00
ニーモニック:LD  A,(HL)
マシン語:7E
ニーモニック:INC HL
マシン語:23
ニーモニック:CALL D390H
マシン語:CD90D3
ニーモニック:RET
マシン語:C9
ニーモニック:RET Z
マシン語:C8

似たような命令があるかもしれませんが、
ザイログ系(Z80)ではLD、インテル86系(i8080)ではmovです。
マシンによってコード体系が異なりますので、
よく調べてみてください。

ここではZ80のコードの解説をします。
文字列領域の設定は
LD HL,(DEFBの開始アドレス)
DEFBは文字列の最後に0D0A00を入れると終わりになります。
ということでこの表はMSXBASICでRAMに23と書き込めば
実行するとINC HL
を入力したことになります。

■MSXBASICでソースを組む場合

・その1:モニタアセンブラがない方法

マシン語ショートプログラムに適しています。
まず、コメントとしてマシン語のニーモニックとコードを直接書きます。
こうすることでハンドアセンブラできるようになります。

180 'LD (D000H),A
190 POKE &HD003,&H32

変換表を見ながら初心者はニーモニックとアドレス、変換したマシン語を書きます。
最初は&HD0000として、先にコードを入れます。
次にD003hから書き込む場合&HD003という要領でD003hの
次のアドレスはD004を書き込むのでと通し番号のように16進数で入れます。


・その2:モニタアセンブラがインラインアセンブラ

1 'CALL     000CH 
2 'RET

という行番号ありのインラインアセンブラがあります。
これはコメントではなくて、マシン語として実行できるものです。

たしか、ポケットバンクMSXマシン語入門か
SimpleASMかBASICコンパイラ「べーしっ君」でできたかな。
このタイプはVRAMにマシン語化されて
ORG命令で指定されたアドレスに移動するパターンです。
ポケットバンクのMSXマシン語入門のテープ版ではORG 0D000Hと
ディスク版はORG 0A000Hになっていますので書き換えましょう。
なお、MSX2テクニカルハンドブックのソースはCOMファイル化されますので
MSXBASICとはアドレスが異なります。ORG D000Hと加えてみましょう。


BASICがわからなくても、「マシン語(アセンブラ)?あ〜わかるよ。」という意外な方が多いです。
 マシン語はモニタするだけで全て見ることができます。
ユーザーエリアにデータエリアとワークエリアを自分で決めればいいのです。
 また、MSXBASICのBIOSも備わっているので、文字入力も計算も全てラクにできます。
 マシン語をマスターすれば処理の高速化をすることができます。


Z80マシン語BASIC 上級編

さて、マシン語モニタはわかってきたでしょうか?
これからはMSXBASICでマシン語を書く場合についてです。
アセンブラがない場合のプログラミングから後半は
SUPER-Xを使って本格的にプログラミングする内容です。
少し難しい内容ですからマシン語適当講座を読みきってから
読んでみるとわかりやすいと思います。

■マシン語のユーザーエリアのアドレスに書き込み、実行する

・POKE &HD300,C9 

 D300hにC9を書きます。最初に解説したコードを1バイトずつD300,D301,D302とアドレスを変えて入れます。

・DEF USR=&HD300

 この指定はUSR関数の実行アドレスを指定しています。
実行アドレスを何個かに分けることもできます。(DEFUSR1=,DEFUSR2=など)

・A=USR(0)

 この指定はマシン語プログラムを実行するコマンドです。DEFUSR1=の場合はA=USR1(0)。

 BASICプログラムを作成していたり、RUNコマンドで実行していたり、LISTを表示中もマシン語です。
そこでこのマシン語を利用して、メモリに書き込んだプログラムを実行させてみるコマンドです。
 実行というより正確には処理に回ってもらうが正しいかな。

BASICインタプリタ処理をデファインで示したアドレスからオペレーションを開始します。

■BASICとマシン語のデータを分ける

次は実行できるようにするための設定です。
MSXBASICは通常はMSXBASICのみを使う設定になっています。
MSXBASICプログラムによってはメモリーにかなりの空き領域ができます。
そこで、領域を制限してそれ以降はマシン語のデータを入れるようにします。

*HIMEM(ハイメモリ)のアドレスを出す

 起動時の状態はF380hになっていますが、この状態では
マシン語を書くことができませんので再設定する必要があります。
DISKBASICの場合はHIMEMが、FCFAhら2バイトで入っています。

PRINT HEX$(PEEK(&HFC4B)*&H100+PEEK(&HFC4A))

BASICで書くとこのようになります。これでHIMEMがわかりました。

*メモリの未使用領域を調べます。

? HEX$(FRE(0))
60BD

**この2つを引くとMSXBASICのメモリの上限

? HEX$(PEEK(&HFC4B)*&H100+PEEK(&HFC4A)-FRE(0))
8507
 と表示されます。この上限を超えると
Out of string spaceというエラーがBASICで表示されることになります。
これを500とか30000とかにすると上限が変わって
今度はマシン語領域が狭くなることになります。
文字列領域を増やしすぎると最終的にはOut of memoryというエラーになります。

プログラムをする場合の容量は
8507H以上プログラムをすることを想定して
少し余裕をもってキリ番などのほうがいいかもしれませんね。
8507なら一般的にはA000か、D000かですね。


CLEAR 200,&HD300

BASICの文字列領域の大きさを200バイト、
この指定ではD2FFhまでBASIC、D300hからマシン語領域としています。
CLEAR ,&HD300と第1パラメータを省略することもできます。
文字列領域は初期値が200ですが、BASICプログラムによっては500,1000,3000などの
適当な数でOut of string spaceのエラーを回避できる数字を指定します。

■マシン語とBASICのデータの受け渡し

基本的には共有するアドレスを決めます。
例えばアドレスがD300hから何バイトにするかデータを入れるワークエリアにします。
でも、データにRAMの残骸があったりします。
そこで、MSXBASICの上限(HIMEM)をしっかり覚えておきましょう。
くれぐれもMSXシステムワークエリアに絶対入れてはいけません。
MSXが間違って処理しますからね。
マシンワカル?なら7バイトでD300Hが「マ」で「?」がD306Hとなりますね。
このようにアドレスに何かを入れておけば
マシン語で簡単に処理できます。

A$の内容をマシン語に読めせる場合は
A$の格納形式と番地を知らなければなりませんね。
そういう面倒なことよりは、
このようにユーザーエリアの使っていないところを
ワークエリアとして入れるとよいです。
データが壊れなければ送り値のアドレスだけでもかまいません。
MSX-FANなどにある一部マシン語交じりのBASICプログラムは
一見よくわからないことがありますが、
データエリアをマシン語のワークなどに入れておき、
BASICではPOKEなどで操作する程度のものです。
変数などのエリアを抑えてユーザーがデータを操作したりしています。
一番よいのはマシン語でソースが特に長くなりそうなモノをBASICを
使ったりするとよいです。

 ■長いプログラムの場合

全く使わないのであればマシン語エリアは9000h程度でよいですが、
この場合はマシン語モニタからマシン語で書き
BASICはマシン語の簡易ツールのようなショートなプログラムになります。
これはマシン語専用プログラムをテープからロードする場合などが
一般的ですが
例えば下のサンプル程度ならばよいでしょう。

★サンプルプログラム

100 'i/o
110 CLEAR200,&HD000
120 GOSUB140:PRINTCHR$(12)
130 LOCATE0,0:PRINTHEX$(PEEK(&HD000));" ";:A$= INPUT$(1):PRINTA$;"   ": DEFUSR=&HD001:A= USR(0):GOTO130
140 'IN A,00H
150 POKE &HD001,&HDB
160 'I/O Port No.
170 POKE &HD002,&H98
180 'LD (D000H),A
190 POKE &HD003,&H32
200 POKE &HD004,0
210 POKE &HD005,&HD0
220 'RET
230 POKE &HD006,&HC9

マシン語モニタ、アセンブラがない場合は変換表からコードを調べる感じです。
 これはD000H(&HD000)のアドレスの数字を表示するプログラムで、
サンプルではキーを押すと数字が変わります。
もともとI/Oの直接アクセス確認用のプログラムでPortNo.を書き換えれば
確認ポートが変わります。使い方よりも
どこのアドレスに書き込んで、どこで実行しているかは最低限わかればよいです。

モニタアセンブラによって操作性も変わりますので
わかるまでは、私がプログラムを設定して大まかに講座を進めていこうと思います。

リンク:MSXマシン語適当講座その1
msx-mac1.html

■MSXBASICのプログラムでアドレスにロードしてみる

(1)DATA AFCD0203…<64文字>

 マシン語プログラムを入力する。

(2)
AD=&HD000
FORI=1TO<DATAの行数>
READA$
FORJ=1TO64STEP2
A=VAL(”&H”+MID$(A$,J,2)):POKE AD,A:AD= AD+1
NEXT
NEXT

ADに開始アドレス、FORJ=1TO64の64はDATAの文字数です。
AにAFHが入り、POKEでRAMに書き込み、ADのアドレスをインクリメント(+1)
これをSTEP2だから2,4,6,8と64まで32回繰り返す。

■マシン語プログラムをBASIC化

BASICで使えないのはシステムコールなどDOS関連の命令です。
まず、BASICも使用できるマシン語領域にロードしたものをロードします。
今回はプログラムコンテストがあるのでクリエイタの為に
特別にDATA文作成プログラム (maclist.bas)をダウンロードできます。
このファイルをロードすると4ケタのスタートアドレスを入力して、
エンドアドレスの領域がスペースで変わるので適当なところでRETURN
適当なファイル名で保存。実行するとBASICプログラムが自動作成されます。
ラインが7ということは行数が7。<行数分の-1>のところは数値が6になります。
 既にプログラムがある場合はMERGE命令で保存したファイル名を呼び出す。
混合したものを一旦セーブ。これで完了。

■大規模なプログラムを作る

・BASICのプログラム領域を使う
・MSX−DOSで開発
・メモリディスク領域を使用する
・裏RAMを使う
・VRAMを使う
・ディスクを使う

数キロ程度であればBASICのプログラム領域などを使います。実行するとBASICへ戻れませんが、
最後にRETを書けば戻ります。
そして、次の複数のプログラムをBLOADしたりすることもできます。
そうです!BASICはマシン語の助手としても役に立ちます。

「ファイル操作もしたい!メモリーも使いたい!」というのであればMSX-DOSを使います。
システムコールなどBDOSを使うことでファイル操作が容易になります。
メモリディスク領域を使用する。これはCALLMEMINIを無効化して32Kバイトを使えるようにします。
 裏RAMを使用する。128Kバイトマシン(MSXturboRなど)で有効です。残り64Kバイトが使えます。
最高でVRAM128Kバイトマシンであれば未使用のVRAMから呼び出しながら使うこともできます。
そういうマシン語ツールもあります。

SUPER-X:
http://applekid.at.infoseek.co.jp/super-x.html

とりあえず、ひととおり説明しました。


■マップパーツ描画の高速化?

今度はMSX−FANなどのBASICからのマシン語プログラムのロード方法を説明する。
FOR〜NEXT中にあるREADの後のPOKE、VPOKEの数値を解析してマシン語プログラムエリアを確定させる。
CLEARとDEFUSRでマシン語領域がわかればA=USR(0)などの前に
BSAVEで開始番地と終了番地と実行番地を指定してバイナリを保存するようにプログラムする。
FOR〜NEXT中にあるREADを削除してBLOADをプログラムに埋め込む。
次からはBLOADが成功すればBASICで数分待たされることなく数秒でマシン語を実行できる。
またVRAMもSCREEN1などで文字が太くなった後にBSAVEに,Sオプションを付ければ
次からは数分待たされることはなくなるでしょう

★描画処理をBLOADで高速化

ちょっと待ってください。数秒で画面ができるなら、
画面の作成待ちで数十秒より速いですよね。
BLOADというマシン語は一瞬で画面が変わる。
これがZ80の得意とするブロック転送が使われている。
BLOADをBASIC命令の一つではなくて、プログラムに使えばいい?
マシン語ができないのならBSAVEでシーンごとに画面を保存して、
BLOADで画面を作る?というかVRAMをロードするとうまくいく?
ラムディスクでBLOAD、上へ行ってBLOAD、左へ行ってBLOADと画面は速いですから。
メモリは使いますが、要するに市販ゲームでやっている方法かも。

■気楽にマシン語、BIOS、ワークエリアを使ってみる。

ファンクションキーで遊ぶ
KEY1,"PUSH":KEY2,"POP"
とプログラムする。そして、初期化しましょう。
DEFUSR=62:A=USR(0)

POKE&HFAFC,1
これでローマ字入力モードに入りました。
「Shift」+「かな」で英数モードに戻します。

これでマシン語を手軽に実行できました。

★VRAMにアクセスしてみましょう

VRAMとはテキスト、グラフィックの表示をする部分です。
この部分を書き換えることでグラフィックスを高速に動作させることができます。
これだけではありませんが、ちょっと書きましょう。

一度はVPOKE,VPEEKを使った事がないでしょうか?
例えば32桁の文字幅ならば0〜31までは1行で32からは2行、64からは3行…となります。
これがアドレスになっていて、データが入ることで行(Y)と桁(X)が変わり、文字か色が表示されます。

まずはSCREENで画面を変えたら、VPOKEで書き込む要領でマシン語プログラムを書きます。
最初はSUPER-XからBasicで使っているI/O System(BIOS)を使ってみてください。

・VRAMを読み出す:RDVRM(004AH)
SCREEN0〜3のMSX1(TMS9918)で有効です。

送りのレジスタHL、返りのレジスタAF
変化するレジスタ:AF

・VRAMに書き込む:WRTVRM(004DH)
SCREEN0〜3のMSX1(TMS9918)で有効です。

送りのレジスタHL、AF
変化するレジスタ:AF


*MSX2用に追加されたエントリ

・VRAM読み込みモード:MSETRD(016EH)

・VRAM書き込みモード:MSTWRT(0171H)

・VRAMを読み出す:NRDVRM(0174H)

送りのレジスタHL、返りのレジスタAF
変化するレジスタ:F

・VRAMに書き込む:NWRVRM(0177H)

送りのレジスタHL、送りのレジスタAF
変化するレジスタ:AF

逆アセンブラではラベルを書くことができないのでEQUは不要です。
まず、指定したマシン語アドレスに飛びます。ここからアセンブラで書きます。
LD HL,3010HとLD A,FFHを設定してCALL 004DHと書きますが、
MSX2はCALL 0177Hそして、RETを最後に書きます。
これでBASICのA=USR(0)以後の処理に復帰します。

BASICに戻り、BSAVE"VRAM.BIN"というファイル名でマシン語エリアを保存しておきましょう。
プログラムにCLEAR400,&HD000を書き、BLOAD"VRAM.BIN"を入れます。
DEFUSR=&HD000を書き、SCREENを変えた後にA=USR(0)を入れます。
どういうふうにマシン語プログラムを作るか一例を出します。

★16ビット演算命令を使って加算処理を作る

このプログラムはどのように使うのか?といいますと
マシン語は全てアドレスにコードとして格納しています。
例えば「FULLMSX」をE3EDhから7バイト格納してあるとして
これを読み込んだり、表示したりするには
E3EDhが開始アドレス、下位がED,EE,EF,F0,F1,F2,F3の
E3F3hが終了アドレスになります。
開始アドレスをBCと終了アドレスをDEというレジスタを使って
HLでアドレス間を変化させてHLを走らせます。
16ビット演算命令を使って加算処理を作ります。

FC3Dhならばアドレスの上位(ハイ)がFC、下位(ロー)が3Dとなります。
16ビットレジスタにはBC,DE,HLの3つがあります。
これはBレジスタとCレジスタの2つがセットになったレジスタペアと呼ばれるものです。
もし、レジスタが足りない時はPUSH,POPか、アドレスに書くか、EXで表裏逆転などあります。

HL(エイチエル)、ハイローという16ビットのレジスタを使います。
このレジスタはアドレスの加減算に使います。
BCレジスタとDEレジスタにアドレス値を入れることにしましょう。

3C03Hから457DHまでであればBCに3C03H、DEに457DHを入れます。

LD BC,3C03H
LD DE,457DH

HLレジスタを走らせます。LD HL,BCとしたいのですが、
レジスタペアですから、

LD H,B
LD L,C

となります。これで数値が入りました。+1加算します。

;LOOP
INC HL

CP命令、JP命令の準備に入ります。比較のため
AレジスタにHを入れて上位が一致した場合の分岐に入ります。
LOOPはINC HLのあるアドレスを指定します。
JPのラベルは判りやすいようにアドレスチェック(ADRCHK)の名前(ラベル)にします。

LD A,H
CP D
JP Z,xxxxH(ADRCHK)
JP LOOP

これでLOOPに戻ります。ADRCHKはLD A,Lのあるアドレスを指定します。
そして、下位のチェックです。
上位と下位のチェックを通過したらJP ZからラベルのENDへ飛びます。

:ADRCHK
LD A,L
CP E
JP Z,xxxxH(END)
JP LOOP

ENDはRETのあるアドレスを指定します。
最後にプログラムを終了します。

:END
RET

ソースをリストしてみましょう。
SUPER-XのMコマンドでD000へ飛びます。
A>MD000
左にアドレスが表示されます。このとおりに打ち込みます。
分岐はアドレスがわからない場合は00000Hと仮指定します。
後で正しいアドレスを書きます。
これからアセンブラを書きますが、SUPER-Xでは
LD A,F0HがLD A,0F0Hとなって、アドレス指定は
FC30HならばLD HL,0FC30Hと最初にゼロを加えます。(たぶん)
ラベルと括弧のコメントは説明をわかりやすくするためのもので
打ち込む必要ありません。

D000:LD   BC,3C03H
D003:LD   DE,457DH
D006:LD   H,B
D007:LD   L,C

;LOOP(D008H)
D008:INC   HL
D009:LD   A,H
D00A:CP   D
D00B:JP   Z,0D011H(ADRCHK)
D00E:JP   0D008H(LOOP)

:ADRCHK
D011:LD   A,L
D012:CP   E
D013:JP   Z,xxxxH(END)
D016:JP   LOOP

:END
D019:RET

マシン語らしくなってきました。
ここはBASICですから、QTで戻って、
BSAVE"ADR-TEST.BIN",&HD000,&HD019
これで保存されました。CALL @で
A>GOD000
DE     HL
457D 457D
となりましたね。Aレジスタは計算用ですから、
LD AからCP命令までは値を変える事ができませんが、
比較した後は数は何でもよいです。
これで、終わりです。(エーツ!)
太字の部分からプログラムを加えます。
まず、D008〜D019までをD010に移動させます。
そうすればD008からは何かプログラムが作れますね。
VRAMを読んだり書き込んだりできます。

繰り上がりが生じた場合はFFFFH(64Kバイトの上限)を
超えてもアドレスの計算ができるようにします。
ということはMSX2以降のRAMは128K以上のものもありますから、
計算ができる必要があるかもしれません。

MSXマシン語適当講座その3:
msx-mac3.html

★プログラムしやすい環境を作る

 前回からジャンプ命令を使うことが増えてきました。
これから分岐を多く作るとつながっていないと
無限ループに陥ったり、リセットしなければいけないように
なりがちです。
 プログラムがこれから長くなるのでここでカンタンに
まず、後で追加できるようにアドレスを空けます。
コード00はNOP(ノーオペレーション)で何も動作しません。
これをとりあえずD000H〜D100Hまでを00で埋めます。
SUPER-XでFLコマンドを使ってみます。
A>FLD000,D100
BLOADでプログラムを読み込み
前回のプログラムをD00HからD019Hまでを
とりあえずD010Hに転送させましょう。
A>BTD000,D019,D010
ダンプモードで
D000〜D00Fまでを00にします。これで空になりました。
BEEP音でOkが出るように作っていきましょう。
BEPP音はBIOSにあります。
DEFUSR=&HC0:A=USR(0)で音を確かめてみてください。

ブザーを鳴らす:BEEP (&H00C0H)

今度はマシン語で音を鳴らして終了させます。
A>MD000
D000:CALL 00C0H
       :RET
これでBLOADを実行させても、DEFUSR=&HD000で実行しても
BASICへ戻るようになりました。
これからは前回のような
仮に条件付きのJPで分岐命令を書きたい場合は
JP D000Hと書けばBASICへ戻るようになります。
さらにCALL命令ではCALL D000HでBEEP音が鳴ります。
ここをプログラムが実行したというようなトレースに使えます。
前回の移動に入ります。
D018〜D029までをD020に移動させます。
A>BTD018,D029,D020
そして、D018〜D01Fまでを00にします。
A>FLD018,D01F,00
そうすればD018からは何かプログラムが作れますね。
このままでは暴走モード突入です。
これからが一仕事です。
まず、JP命令のアドレス先を全部書き直します。
念のためにソースをプリントしておきましょう
もう一回見てみましょう!
>D009:LD   A,H
>D00A:CP   D
>D00B:JP   Z,0D011H(ADRCHK)
D033:JP   Z,0D039H
>D00E:JP   0D008H(LOOP)
D036:JP 0D01EH
>:ADRCHK
>D011:LD   A,L
>D012:CP   E
>D013:JP   Z,xxxxH(END)
D03B:JP Z,D041H
>D016:JP   LOOP
D036:JP 0D01EH

なお、LOOPはD01Eになりました。
マシン語はどのようにして動かすかわかったでしょうか?
ここまではいくらかラクに変えることができますが、
実はSUPER-Xはコマンドがあるのです。
BTコマンドでブロック転送をやりましたが、
RTコマンドを使ってみてください。
今回はもしもの時のために書き直しかたを解説しました。

★VRAMのアドレスを移動させながら点を打つ

横一線に点を打ちます。
前々回のプログラムから
BCレジスタを開始アドレス、DEレジスタを終了アドレスとして
VRAMを読んだり書き込んだりできるようにします。
指定部と動作部を作ります。
これから動作部を書きます。
ちょっとプログラムの頭がわかるように00しておきます。
D018:NOP
D019:NOP
D01A:NOP
3バイト空けましょうか、これでJP命令で回避できるようになります。
すでにHLレジスタにアドレスが入っているとします。
FFで白で描きます。
まず、(VRAMの書き込みモード:0171H)にします。
D01B:CALL    0171H
このように直前でレジスタにデータをセットします。
D01E:LD       A,0FFH
D020:CALL    0177H
これでできました。
次に指定部を書き直します。
A>MD010
高速に描く感じを見たいので
0000H〜FFFFHでD01EHでセットしたFFHの白で埋め尽くします。
D010:LD    BC,0000H
D013:LD    DE,0FFFFH
最後にBASICへ入ります。
BSAVE"VRAM.BIN",&HD000,&HD100
そしてプログラミングします。
100 CLEAR 200,&HD000
110 SCREEN5
120 BLOAD"VRAM.BIN"
130 DEFUSR=&HD010:A=USR(0)

LINE命令よりは遅いですが、
VPOKE命令よりははるかに速いです。
今回はプログラムの再配置とVRAMの書き込みを
2回にわたって解説しました。
手順どおりに行えばよいでしょう。
逆アセンブラでプログラムの配置を手動で移動させると
このようにできなくはないです。
実はこれだけではありません。欲を言えば、
このソースではA,HL,BC,DEと
全てのレジスタを使っているところです。
わかりやすくしたつもりですが、ワークエリアに
開始番地と終了番地を退避させたほうがよいかもしれません。
ということもありましたが、
マシン語はBASICの遅い部分をカバーできるので
マシン語は力になってくれますね。

★キーマトリックスでうまいキー操作

キーマトリックスはINKEY$では使うことができないキーを使うことができます。
MSXではありませんが、PC-8801ではINKEY$よりもキーマトリックスを使った操作が多かったです。
2つ同時押しなどに重宝します。
よくわからないが、Aレジスタに数を入れる(送り値)とAレジスタに状態が出る(返り値)
とにかく、D000Hに1を入れて、D000Hの内容をAレジに入れて、CALL 0141Hで
Aレジの内容をD000Hに入れてBASICで読めばラクかなと思ったら
BIOSを使わないでBASICのFBEB〜FBEFHにワークエリアがあるのでした。

10 PRINT CHR$(12)
20 FORI=&HFBE5TO&HFBEF
30 LOCATE0,I-&HFBE5:PRINT HEX$(I);":";BIN$(PEEK(I))
40 NEXT
50 GOTO 20

スキャンすると、ビットが変化します。
2つ以上のキーを同時押しすると行が変化しますから、
それを見てANDで条件付けしてください。

== 例えばCTRL+SPACEをやってみる ==

CTRLを押すと0になります。
FBEB:11111101
これを
? &b11111101
253
と表示します。同様にSPACEも行います。
IF PEEK(&HFBEB)=253 AND PEEK(&HFBED)=254 THEN PRINT"CTRL+SPACE!":END
となります。必ず、I$の条件より前に入れてください。
キーの長押しのようになってしまいがちなので実行後はバッファのクリアを施してください。
ではカンタンに組んでみましょう。

10  I$=INKEY$:IF  I$="" GOTO 10
20  IF PEEK(&HFBEB)=253 AND PEEK(&HFBED)=254 THEN PRINT"CTRL+SPACE!":END
30 GOTO 10

■MSXにはこんな命令が!

VARPTR(<変数名>)

 MSXBASICの変数名またはA$の文字型の格納されている番地を求めます。
<変数名>を#1にするとファイルコントロールブロックの開始番地になります。
通常は使わないがマシン語で変数をやりとりする場合はこの命令を使うといいかもしれない。
 返り値の説明は数値と数式に載っている。この命令はMマガに載っていません。



最後に
マシン語の使い方を数回にわたって解説してきました。
MSXBASICユーザーがマシン語を始めるとテクニカルで難しいように思えますが、
次第に共通点がでてくると思います。また、マシン語という別の選択肢も増えてきます。
続編への直リンクができました!マシン語は1つ1つ細かいので解説が長くなります。
これまで割愛していたところが詳しくなり、かなりMSXがわかるようになります。
まだ、がんばって更新していこうと思います。

MSX2グラフィックプログラミング講座 -HI-
msxi-hi1.html