職人養成道場


Date: 12/03/03 20:54:16
From: 名無しさん

Re:BASICの使い方、ダイレクトモード(4)


●数値や文字の比較

コンピュータは数値の計算だけではなく、数を区別する事が可能だ。
数を区別するとは、数が等しいか、または大きいか小さいかを区別する事だ。
BASICのダイレクトモードで計算する際に>,<記号を使うと数の判定を行う事が出来る。

? 1000>1
-1
Ok


この式は、> 記号を使い、1000と1という数を比較し、>記号の左側の数が大きいか、小さいかを
コンピュータが判断する。
この結果、-1という数値が表示されると、"左側の数が大きい"と判定されたことになる。

? 1>10
0
Ok



この場合は、1と10を比較して1は10よりも大きいかという判定を行っている。
1は10よりも小さい数なので、結果が0となり、"1は10よりも大きくはない"という
判定結果となった。
0の場合はfalse,-1の場合はtrueと表現することもある。

数値の判断や判定はコンピュータでは大きいか小さいかだけでなく、同じか、同じでないか、
という判定も可能だ。

? 10=10

この式の場合は、変数の代入ではなく、10と10の数が等しいかという判断を行う。
判断には下の式のように変数を使う事も可能だ。

? a=10

この場合は、変数aに10を代入するのではなく、変数aと、10という数を比較し、同じであるか
という判定を行っている。

コンピュータはデジタルで判断を処理するので、同じか、同じでないか、という基準で
数を判定する。
?記号(PRINT命令)に続く=(イコール)記号は、代入ではなく式として扱われる。


判定は数値以外に文字や文字列の判定も行える。

? "abc"="abc"

この例の場合は文字列が等しいかを判定する。


数値や文字の判定を利用すると、BASICのIF命令を使い、条件判定して処理を行わせる事が可能だ。
例えば、以下のような命令を実行すると1イコール1の条件の際にのみ特定の
命令を実行することができる。

IF 1=1 THEN PRINT "true"
true
Ok


この結果、trueという文字列が表示される。IF文は条件判定を行った結果を利用して
特定の処理をコンピュータに行わせるものだ。
この命令では、1=1という判定で両者の数値が等しいため、THEN文以降のPRINT文が実行されている。

もし仮に数値が一致しない場合は、THEN文以降の命令は実行されない。

IF 1=10 THEN PRINT "true"

この命令をダイレクト実行すると、"true"という文字は前回のように表示されない。
1と10という数値が同一ではなく、等しくないからだ。
BASIC言語では、数値や文字の判定を行って特定の処理を行わせる事が可能だ。














Date: 12/03/03 20:43:49
From: 名無しさん

Re:BASICの使い方、ダイレクトモード(3)


●変数の利用

BASICインタプリタのダイレクトモードでは変数を使う事が出来る。
変数とは数値を保存する入れ物のようなもので、名前はx,yなどでも良いしa,bという名前
でも良い。数値を一時的に保管する場所と理解する事が可能だ。

a=123.4

BASICのダイレクトモードでこのように入力すると、123.4という数値がaという変数に保存される。
数学や算数では変数という概念が存在するが、代入式は10=aという書き方をする。
プログラミング言語ではこの式が逆になる。

変数の内容を確認する場合は、単純に変数名を指定し、以下のようにする。

? a
123.4
Ok


変数は式に組み込む事も可能だ。

b=3
? (a*2)-b
243.8
Ok


変数を含む式を計算する際、平方根や三角関数のような数学関数を含める事も可能だ。
ラジアンの単位を変換する際に変数を用いれば式が簡単になる。

rd=3.1415926535898/180

? sin(rd*90)
1
Ok


sin 90°を求める際に1°の際のラジアンを保存した変数に角度に掛けることで、度の単位を
簡単に扱うことができる。

変数は名前に決まりがあり、英文字のa〜zまでの二文字で表す。
変数名は、a,A,A1,AB,などが存在しても良い。逆に1a,2bという文字は変数名として
適切ではない。
変数には限られた名前スペースしか割り当てられないので、より実用的に多くの変数を
扱う方法として配列と呼ばれる変数も存在する。














Date: 12/03/03 20:40:26
From: 名無しさん

Re:BASICの使い方、ダイレクトモード(2)


●ダイレクトモードのつかいかた

BASICインタプリタにはダイレクトモードとプログラムモードが存在する。
ダイレクトモードは、直接BASIC命令を実行し結果を表示させるもので、電卓のように
計算したりBASIC命令を実行することが出来る。

プログラムモードは予めプログラムという命令を作成しておき、コンピュータに計算を
行わせるモードで、BASICプログラムを入力し実行することが可能だ。

現在のWindows開発環境などではプログラムはエディタを備えているものが殆どで、
ダイレクト実行モードは特別なウインドウが用意されていたり、別途インタプリタ環境などが
用意されている場合が多い。
MSXBASICインタプリタはダイレクトモードとプログラムモードを明確に区別しない。

ダイレクト実行を確かめるために簡単な計算を行ってみることにしよう。
コンピュータが起動しBASICインタプリタが動作している状態で、
以下の文字を入力して[Enter]または[Return]キーを押してみよう。

? 123+2

または、

print 123+2

BASIC画面で ? 記号はBASIC言語のPRINT命令と同じ意味だ。PRINT命令は計算したり文字を
表示させる命令だ。

ここでは利便性のために?記号を使う事とする。
?命令を実行すると123+2を計算し結果を表示する。
答えは、

? 123+2
 125
Ok



と表示される。

文字や文字列も表示できる。文字や文字列を表現する場合は""記号で文字列を囲う。

? "hello"
hello
Ok


この例に示すように、"hello"として書かれたhelloという文字がBASICインタプリタに
認識されて画面に表示されている。

BASICのダイレクトモードは間違った命令や文字を入力するとエラーメッセージが表示される。
Syntax error、は構文に問題があるというエラーだ。エラーメッセージはいくつかの種類が
あるのでエラーの原因を区別する際に参考にすると良いだろう。


●数値計算

コンピュータは計算の道具として発達してきたので基本的な数学の式の計算が可能だ。

? 123+2+125
250
Ok



四則演算は数学記号と違い、+、−、×、÷、がそれぞれ+,-,*,/という記号に
割り当てられているので注意しよう。

ダイレクト実行モードではBASICインタプリタは関数電卓として利用する事が可能だ。
BASICには基本的な数学関数が用意されているので、市販されている関数電卓のように
使うことができる。
関数電卓として2の3乗を計算したい場合は、^記号を使う。

? 2^3
8
Ok


2の平方根を求めたい場合はsqr()関数を使う。

? sqr(2)
1.4142..
Ok


BASIC言語では平方根は√記号を用いずにSQR関数という形で表現する。関数は名前と引数を与える事で
さまざまな機能と計算上の表現を行う。


計算式の記号には以下のようなものがある

^ べき乗
* かけざん
/ わりざん
\ 整数の割り算
mod 割り算の余り数を求める
+ たし算
- ひき算

四則演算のほかににカッコ()記号を使い計算順序を指定する事も可能だ。

? (1+2)*(3+4)

数学には記号式の計算と数値の計算が存在するが、コンピュータは主に数値計算を得意
とする。もちろん数式計算も可能だがBASIC言語では直接扱えない。


●三角関数

高等数学では三角関数を学ぶ。BASIC言語でも三角関数を扱う事が可能だ。
BASIC言語に限らず多くのプログラミング言語では三角関数は角度指定の単位はラジアンという
単位で指定する。
角度を360度(degree)で表現する場合は単位変換をおこなう必要がある。

ラジアン(radian)と度(degree)を変換する場合は、1°=0.0174radという関係式を利用し変換する。
ラジアンは、1度=0.0174ラジアンという関係なので、角度*0.0174
という変換式で求める事が出来る。

? 3.14159/180
.017444
Ok

? sin(90*0.01744)
0.999999
Ok



三角関数の90°の時の答えが1とならず0.9999となるのは、数値計算の誤差の為だ。
四捨五入すると1となる。0.99999は極めて1に近い数だ。

この誤差の原因は円周率、πの数値が小数点以下五桁というおおまかな数であるからだ。
正確な数を求めたい場合はより正確な円周率を与えないと、この誤差は縮まらない。
小数点以下13桁を正確に入力して再計算すると、今度は1と表示される。

? 3.1415926535898/180
0.017453292519943

? sin(90*0.017453292519943)
1

日本の小学校で円周率は3と教えられた人もいるかもしれないがそれは間違いで、
世界でも、日本でも円周率を3とは教えてはいない。
円周率は小数が無限に続く数で、この数の正確さが数値計算の誤差となって現われる。
全ての応用分野で円周率πが3では計算上全く実用にならないことを覚えておく必要がある。

数学関連の関数はこの他に、SIN,COS,TAN,ATAN,SQR,ABS,LOG,LN,
などがある。この他に必要な関数は組み合わせ計算で求める事も可能だ。

数学関数のBASIC命令について詳しくは資料を参照してほしい。
BASICで利用可能な関数や命令表は以下を参照すること。


BASIC言語命令リファレンス
http://msxjpn.jimdo.com/msx-basic-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89/














Date: 12/03/03 20:34:08
From: 名無しさん

BASICの使い方、ダイレクトモード(1)

●BASICの使い方、ダイレクトモード


BASICはプログラミング言語の一つで、現在でも利用されている処理系の一つだ。
MSXコンピュータの電源を入れるとすぐに起動するのがBASICインタプリタである。

MSXBASICはROMに記録されているため起動は速い。処理系は非常に小さいサイズの言語で、
32KByte程のメモリにBASICの機能を備えている。
プログラムメモリとして利用可能なRAM容量も20KByteという小さなサイズであるが、
プログラミングの基本的な機能を備える。
現在のWindowsで動作する高機能なBASICのように関数やプロシージャといった構造化構文は
サポートしないものの、手続き型プログラミングの基本的な機能は備えている。

BASICインタプリタは命令を逐次解釈しながら処理する。速度は必ずしも速くはないが、
コンパイルやビルドをせず即実行が可能だ。


●BASICの起動


BASIC言語を使いたい場合は単に電源をONにするだけでよい。
コンピュータが起動すると、通常はBASICが動作しはじめると以下のような画面が表示される。


MSX BASIC version 2.0
Copyright 1985 by Microsoft
23414 Bytes free
Disk BASIC version 1.0
0k



この状態でキーボードから文字を入力する事が可能だ。
BASIC言語はプログラミング言語の一つだが、8bitコンピュータで動作するという事もあり
非常に小さな規模のBASICインタプリタである。


●画面モードと設定


画面モードにはテキストとグラフィックの二つがある。
ROMBASICの起動直後はテキスト画面モードという状態となる。
画面モードには、SCREEN 0と、SCREEN 1というものがある。違いは文字フォントや
表示文字数の違いだ。

SCREEN 0

とタイプして[Enter]または[Return]キーを押すと画面モードが変化する。
表示文字数を変える場合は、

WIDTH 80

とする。テキスト画面モードでは画面モードと表示文字数を設定する事が可能だ。

スクリーンモードを次回の起動時から維持したい場合はSET SCREEN命令を実行する。
SCREEN 0:WIDTH 80という状態を設定し、SET SCREENを実行すればよい。
マシンにはSRAMが搭載されておりバッテリーバックアップされている場合、スクリーン
モードがセットされ、次回から起動する際にスクリーンモードが設定される。














Date: 12/01/19 18:06:31
From: hiroaki

Re:r-type mega16 a1fx+FMPAC起動でFMが鳴らない

>A1WXでは内臓FMでFM音源で鳴っています。
>FMPACでメモリを喰われてしまうのかも知れません。

初めて書き込みします。
たしか元々のr-typeはFM音源の存在を確認するルーチンにミスがあって
外付けFM音源しか音が鳴らなかったような記憶があります。
記憶が確かならFM音源スロットの4000H近辺に内臓FM音源ID"****OPLL"(前後逆かも)があるので
そこを"PAC2OPLL"だったかな・・・(FMPACのID)に書き換えたROMスロットを作ればFM音源が鳴ると思います。














Date: 12/01/18 06:29:05
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(11)



● 整数の掛け算とシフト命令


整数の掛け算は基本はシフト演算で行なう。しかし実数の掛け算と異なる所もある。
一番身近な例はC言語のシフト演算だ。

10 << 1;

このC言語の式の結果は、20となる。これは以下の式と等しい。

10 * 2;

整数のシフト演算は、掛け算の代用として使う事が可能だ。
二進法の数値では、ビット列の桁の重みは 2^n に従う。以下のようなものだ。

2^1=2
2^2=4
2^3=8
2^4=16

Windowsの関数電卓には2進、8進、10進、16進数という基数変換機能があるので、
この関係を簡単に確かめる事が出来る。

Windows関数電卓を使い、10進数で、2を入力した時、二進数での状態を確認してほしい。
同様に、10進数の4の時、8の時、16の時の二進数のビットの変化の様子を確認する。

この結果から2進法での乗算(かけ算)はビットの単純な移動であることは明らかだ。
つまり整数の2進法の掛け算を行ないたい場合は、単純なビットのシフト命令で実現が可能
という事だ。

アセンブラのシフト演算命令はビットを右や左に移動させる単純な命令だ。
シフトの処理は幾つかの種類があるが、基本は単純にビットを一つだけ移動させるものだ。
以下の例に示すように整数で1を表す2進数をシフト演算した結果は、2進数の乗算(掛け算)の
結果と等しくなる。

01234567
--------
00000001


上記の数に対して左シフト(SLA)命令を実行すれば、1*(2^1)=2 となり、計算結果は
以下となる。

01234567
--------
00000010

この数は10進数で言えば2だ。
2進数で確認すると結果はビットが1つ移動している。
2進法の掛け算では、2,4,8,16倍という数を1つの命令で実現する事が出来る。
現実のプログラミングでは 2^n 乗を持つ計算というものは比較的多い。



同様にして割り算も実行する事が出来る。割り算の場合は掛け算の時の左シフトを逆にし、
右シフトによって行なう。
100という整数を2分の1とし、割り算を行ないたい場合、下記の100を表現する2進数
ビット列に対して右シフトを一回行えば良い。

01234567
--------
01100100

右シフト命令(SRA)を実行すれば、割り算を行なった事と等価なので100*(1/2)=50という式の
結果、50という数が帰ってくる。

01234567
--------
00110010

このようにどのような数値であっても、2^nの倍数であれば容易に掛け算、割り算が
可能という事だ。


実際のアセンブラの例を以下に示す。この例ではAレジスタに整数の1を置き、
算術左シフト命令(SLA)により掛け算を行なう。式で表せば、A*(2^1)だ。

>M C630

C630 LD A,1
C632 SLA A
C634 RET

>TR C630

算術左シフト命令(SLA)は、指定されたレジスタの内容を一回のみシフト処理を行なう。
計算式上は、A*(2^1) を一回のみ実行するものだ。
A*(2^4)という計算を行ないたい場合は、シフト命令を四回繰返し実行する必要がある。

>M C630

C630 LD A,1
C632 SLA A
C634 SLA A
C636 SLA A
C638 SLA A
C63A RET

この結果Aレジスタの内容は0x10(10進数で16)となる。式で表すと、

1*(2^1)=2
2*(2^1)=4
4*(2^1)=8
8*(2^1)=16

を繰り返し処理したものと等価となり、Aレジスタに16(0x10)が置かれる事となる。
A*(2^4)=16という結果と等しいことがわかる。


割り算の場合も同様だ。割り算の場合は算術右シフト命令(SRL)を用いる。
例として10進数の整数100の2分の1を求める。

>M C640

C640 LD A,064h
C642 SRA A
C644 RET

結果、Aレジスタは0x32(10進数で50)となった。
式で表せば、A/(2^1)=50 と等しい。
SRLとSRA命令はどちらも右シフトだが違いは符号付整数、符号なし整数により使い分ける必要がある。


10進数の整数の掛け算の場合は3倍というような奇数倍の乗算が存在する。
奇数倍の例では単純に二進数の掛け算のみでは実現する事が出来ないので、
シフト命令や他の命令を用いて整数の掛け算を実装する事になる。
つまり 10*3=30 という整数掛け算の場合、左シフト一回と10を加算した値が30という事だ。
アセンブラで現せば下のような命令となるはずだ。

LD A,0Ah
SLA A
ADD A,0Ah


一方で、(2^n)乗倍や、1/2,1/4,1/8等の整数分の1などの場合はシフト命令のみで実現できる。

加減算と繰り返し処理のみでも乗算/除算を行なう事は原理的に可能だが、シフト演算を
用いることで、より速く短いコードを書くことが可能だ。



CPUによっては乗算/除算命令を備える事もある。シフト命令と異なり乗算除算命令は
二つのレジスタの間で整数乗除を行なう。
例えば8bitの長さの場合、HとLの掛け算を行なうと結果はDEなど16bitレジスタに置かれる。
一般に乗算除算命令は整数加減算やシフト演算よりも遅くなる。
掛け算命令はZ800に存在し、HD64180等には8bit乗算命令のみ実装されている。




● BCD計算


BCDとはBinary Coded Decimal(2進化10進数)のことだ。BCDは人間が普段
利用する10進数を扱う数表現だ。

通常のCPUレジスタは8bit(1byte)のサイズを持つ。
8個の0と1のビットを組み合わせて数値を表現するものだ。
このサイズで表現可能な数は0-255までの256通りとなる。

通常は255を16進数で表現すると0xFFとなる。BCDの場合はこの二桁の英数字を
それぞれ10進数の1桁、10桁として扱う。
数表現をBCD(2進化10進数)と言う場合は数値表現を通常の16進を使い
10進を表現する。

BCDでは、0x19であれば10進数の19を表現する。
同じように、0x99という時、10進数の99という意味になる。

BCD同士の演算を考えてみよう。0x19+0x02という場合だ。BCD同士の計算の場合、
この例では答えは、19+2=21となる。
16進数で表現されている数値を10進数と置き換えて足し算を行なっただけだ。

アセンブラで具体的なコードを示す。
下記の例はBCDを使わない単純な加算を行なっている。

>M C650

C650 LD A,1
C652 LD H,1
C654 ADD A,H
C655 RET

この結果は、Aレジスタが2となる。

BCD演算を行なう場合、Z80ではDAA命令というBCD補正命令を実行する。
BCD演算補正命令は、通常のアセンブラの加減算命令に対して、演算結果にBCD補正を加え、演算結果をBCD演算結果と等しくする。
コード上は通常の加減算命令にDAA命令を付け加えるのみでBCD演算を実行可能だ。
BCD演算用の特別なアセンブラ命令は必要ない。

C650 LD A,1
C652 LD H,1
C654 ADD A,H
C655 DAA
C656 RET

このようにADD命令の直後にDAA命令を置く事で演算結果がBCD補正される。
BCD補正のよりわかりやすい例を以下に示す。


C650 LD A,063h
C652 LD H,2
C654 ADD A,H
C655 DAA
C656 RET

この例では、Aレジスタに99(0x63)、Hレジスタに2を置き整数加算(ADD)を実行するものだ。
演算を16進で行なった場合、結果は0x65となる。BCD補正なしの時の式を以下に示す。

99(0x63)+2=101(0x65)

この場合は単純な整数の加算で、99+2を実行している。演算結果が101(0x65)となる。
16進数では0x65となっている点に注意してほしい。

DAA命令はBCD補正を行なうので、補正後は値がBCD表現で加算した結果と等しくなる。
この計算結果は結局の所、桁がオーバーフローしBCD表現では0x101という数になる。
DAA命令を用いない場合は0x65である。つまり、BCD演算とは、

0xFF

という16進数のうち、上一桁の0xF0が、10進数の10の位、下一桁の0x0Fが、
10進数の1の位を表す事と等しい。
先ほどの例では、BCD演算は、

0x99+0x2=0x101

という結果となり、99+2=101という計算結果が16進表記によって得られた事になる。
ただこの場合はオーバーフローを起こすので実際は0x01という答えが返ってくる事になる。

BCD表現の際は、16進数はA−Fまでの表現を使わない。以下に10進数と16進数の
BCD表現の対応を示す。
左が通常の10進数、右がBCD表現を行なった16進数だ。

10 = 0x10
20 = 0x20
50 = 0x50
99 = 0x99

100 = 0x100
500 = 0x500
999 = 0x999

1000 = 0x1000

1Byteで表現可能な数は、BCD表現では0−99までとなる。16進数では0xFFが二桁なので
BCD表現では10進数のうち二桁を表現可能という事だ。


引き算も同様で、10-5 を計算する場合、BCDでは単純に16進数を10進数として考えれば済む。

0x10-0x5=0x5

読み取る際は16進数をそのまま10進と読み換えれば良い。


BCDは十進数を簡便に表現する手段としてよく用いられる。BCD計算は二進数で数を
表現しないことから、2進と10進数の変換時の際に誤差を生じにくく、非常に大きな数を
誤差なく計算する際に利用される。
商用システムでは膨大な金額を計算する際に数値誤差を生じさせない為にBCDで計算する例もある。

BCD補正命令の演算結果はi8086とZ80、または64180と実行結果が異なるためCPU判定に利用される事もある。














Date: 12/01/16 21:40:38
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(10)



● CPU上の数表現とエンディアン


数値の1を表す時、8bitレジスタは二進法では右のビットが値の最小となる。
二進法で表すと下記となる。

01234567
--------
00000001

同じ整数の1を表す時、16bitレジスタは8bitを拡張し下位アドレスと上位アドレスを組み
合わせ右側が最小桁となる。

01234567 01234567
-------- --------
00000000 00000001

16biy整数加算を行なう時、16bitではHLレジスタが計算の中心となる。16ビットレジスタは
8bitを二つ組み合わせて16bitレジスタとなるが、桁上がりの際には上位レジスタに繰り上がる。

(H)    (L)
01234567 01234567
-------- --------
00000000 11111111

 +

01234567
--------
00000001

 =

(H)    (L)
01234567 01234567
-------- --------
00000001 00000000


16bitレジスタの加算は8bitと命令が違い多少振る舞いが異なる。
8bitのAレジスタの場合、加算の際の数値はアセンブラの即値によって指定が可能だ。

LD A,0
ADD A,1

16bitレジスタの場合はアセンブラ命令に加算数を即値として与える事が出来ない。
加算する値は即値ではなく一旦レジスタに値をセットする必要がある。

LD BC,1
LD HL,0
ADD HL,BC


16bit(2byte)のサイズの整数をメモリから読み込む場合にはエンディアン(バイトオーダー)に
注意することが必要だ。
メモリに数値を記録し、レジスタに読み取る場合を考えてみよう。

>D C300

ダンプコマンドDと入力し、0xC300,0xC301にそれぞれ1234という数値を入力する。
ダンプモードではカーソルを押すとメモリ表示アドレスがスクロールする。
カーソル位置で数値を入力するとメモリを直接書き換える事が出来る。

C300 12 34 00 00 00 ........
C308 00 00 00 00 00 ........
C310 00 00 00 00 00 ........
C318 00 00 00 00 00 ........

この値のアドレスの関係は以下のとおりだ。
メモリ上のアドレス0xC300と0xC301を比べると0xC301が上位でアドレスが大きい。

0xC300 = 12
0xC301 = 34

この値をCPUのHLレジスタにロードする。

C400 LD HL,(0c300h)
C401 RET

>GO C400

この結果3412という数値がHLレジスタに保存された。

HL = 3412

メモリアドレス上の小さい番地の内容がLレジスタ、大きい番地の内容がHレジスタに
保存される。このような並びの決まりをエンディアン、またはバイトオーダーと呼び、
Z80は一般にリトルエンディアンと呼ばれる規則に従う。

先ほどのメモリ上の数値はHLレジスタに読み込むと数値の並びが逆となる。
Z80はリトルエンディアンだから、16bit,2byteの値を保存する際はアドレスの上位が
下桁となる並びが正しい。

0xC300 = 34
0xC301 = 12

このようにメモリに記録すれば良い事になる。
さらに長い整数を表現したい場合も同様だ。

32bit(4byte)などのlong型整数の計算はCPU命令とレジスタのみでは実現不可能なので
変数をメモリに保存しlong型の加減算などのサブルーチンを作れば良い。



● 変数や数値データの扱い方


アセンブラは整数の扱いをレジスタ中心に考えるが変数をレジスタに割り当てる事は
本来は正しくない。

殆どの場合において一般に変数はレジスタに割り当てるのではなくメモリ上に保存する。
2byteでは整数としてのサイズやレジスタ数が足りないからだ。
8bitCPUでは設計上1byte/2byteのサイズの符号付、符号無し整数は扱えても、2byte以上の
整数は扱えない事になる。
レジスタを中心に変数のサイズを定めているうちはこの制限から脱する事は出来ない。

多くの高級言語が生成するアセンブラも変数はメモリ上に確保される。
レジスタは演算の中心であるが、変数はメモリに保存し、変数の演算は一旦メモリから
読み出し、計算後、書き込む手順を必要とする。これらをロード、モディファイ、ストア
と呼ぶ。

つまり変数を扱う場合の基本的な原則として値はメモリ上に確保し演算する。下記のように
アセンブラを記述する。

LD A,(address)
ADD A,1
LD (address),A

一行目でアドレスからロードし、二行目で処理、三行目で元に戻す(ストアする)。
この規則を拡張すれば、あらゆるサイズの長さの整数をサポートする事が可能だ。

このとき変数の保存先アドレスにインデクスレジスタを利用すれば配列や構造を持つ変数を扱える。
予め変数の保存先を示すアドレスをインデクスレジスタに置いておき、
インデクスレジスタ間接によってメモリのロードストアを行なえば便利だ。

LD IX,0c00h

LD A,(IX+0)
ADD A,1
LD (IX+0),A

LD A,(IX+1)
ADD A,1
LD (IX+1),A


桁の繰り上がりなどを考慮したアセンブラであれば、インデクスを換えれば2byteや4byteの長さ
を持つ変数も容易に扱える事がわかるだろう。
インデクスレジスタ経由のメモリ操作は、より複雑な構造体などのデータ構造を扱う場合にも
有効だろう。
レジスタは基本的に演算のみに利用しレジスタの長さが変数型を定める重要な要素ではない
ことは明らかだ。



● 条件分岐とフラグについて


無条件ジャンプはアセンブラでJP命令だ。

C630 JP 0C630h

上の命令である。機能はBASICのGOTO文と等しいものだ。
この命令に対し条件ジャンプは、特定の条件によって分岐先が変わるジャンプ命令だ。
条件分岐はBASICのIF文に等しい。


アセンブラの条件分岐命令は、フラグレジスタの状態によりジャンプ先が二者択一に分岐する。
条件ジャンプは下記のように書く。

C630 JP NZ,0C630h

条件付と無条件ジャンプの違いはニーモニック上でフラグ状態を示す、
Z,NZなどの修飾が追加される所だ。
Zの場合ゼロの時、NZのときはゼロ以外を意味する。

条件ジャンプはいわゆるIF文に相当する。分岐命令はアセンブラではフラグを参照する。
フラグは演算命令などの結果により変化する。

分岐命令はフラグに依存し、そのフラグの状態は他の命令でコンディションは常に変化する
可能性を持っている。
例えば下記に示すアセンブラではAレジスタをDEC命令で減算した際に結果がゼロの時
フラグレジスタが立つ(ONとなる)。
フラグレジスタが立つと、アセンブラの繰り返しループを抜ける。

C630 LD A,02h
C632 DEC A
C633 JP NZ,0C632h
C636 RET

演算の中心はAレジスタだが、フラグのコンディションはAレジスタの状態から影響を受ける
訳ではない。

条件分岐の際、フラグは直前の命令の結果が反映される。DEC A命令はAレジスタを-1減算する。
この演算命令はレジスタを減算する際に同時にフラグも変化させる。
Aレジスタの状態がフラグに反映されるのではなく、命令実行の結果、値とともにフラグが
変化する。

もし演算の結果Aレジスタがゼロとなればフラグレジスタのゼロビットが1となる。
DEC命令結果がゼロとなったことを意味する。
演算命令を実行しない場合フラグは変化しない。
単にゼロをAレジスタにロードしただけではフラグは変化しない。
この動作の仕組みは電子回路や論理回路と呼ばれるデジタル回路の機械的な動作そのものだ。

まとめると、

・アセンブラ条件分岐命令は演算結果の値や命令実行ではなくフラグを参照して分岐する。
・分岐命令を使う際は、分岐命令以前の命令の実行結果がフラグに反映されている。
・フラグはAレジスタの内容状態により変化するのではない。
・分岐命令はAレジスタの内容によって分岐するのではない。
・フラグは演算命令などの結果によって変化する。
・フラグは加減算アセンブラ命令以外でも変化する。
・16bit加減算命令ではフラグが変化しない命令がある。

以上の点に注意したい。














Date: 12/01/16 21:30:22
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(9)



● 2Byteループカウンタ


16bit(2byte)のループカウンタを扱いたい場合は簡単なようで簡単ではない。
なぜならZ80は8bitCPUなので、そもそもハードウエア内部は8bitで設計されており、
8bitを拡張して16bit処理が実装されている為だ。ALUも基本8bitである。
8bitレジスタに対するアセンブラ命令で可能な振る舞いが、16bitペアのレジスタ命令で
可能であるとは必ずしも言えない。これがマシン語の癖だ。

ここで具体例を示そう。

LD HL,0Fh
DEC HL

上のアセンブラ命令は可能だ。しかし8bitレジスタのDEC A命令とは振る舞いが異なる。
16bitレジスタペアのDEC命令はフラグレジスタに結果が反映されないからだ。
対策としては下記のようになる。

LD HL,0fh
LD BC,1h
SBC HL,BC

2byteDEC命令を使わずにSBC減算命令で代替することが可能だ。以下にBIOSでBEEP音を発生させる
HLレジスタ版を示す。


>M C600

C600 LD HL,0Fh
C603 LD BC,1

C606 PUSH HL
C607 PUSH BC
C608 CALL 0C0h
C60B POP BC
C60D POP HL

C60D SBC HL,BC
C60F JP NZ,0C606h
C612 RET

実行はGOコマンドで行なう。

>GO C600


この他にメモリー上にループカウンタを置く例として、通常のメモリーとスタック経由の例を示す。
トレース実行して確認してみてほしい。

以下はメモリを使ってループカウンタを構成する。
レジスタは一時的な演算のみ利用しカウンタ変数はRAM上に保存する。


;HLを初期化
LD HL,0

;メモリ上の変数を10に初期化
LD HL,10
LD (0C670h),HL

;減算カウント数を設定
LD BC,1

LOOP:

;メモリ上の変数をロード、カウント-1し、メモリにストア
LD HL,(0C670h)
SBC HL,BC
LD (0c670h),HL

;減算結果のフラグを見て条件分岐
JP NZ,LOOP:

RET


以下はスタックを利用して変数をスタック上に置いたループカウンタの例。
変数のロードストアをPUSH/POPで代用。
コンパイラによってはローカル変数をスタックに置くものがある。
スタックを利用して変数を用いる方法も存在する訳だ。


;ゼロ初期化
LD HL,0

;カウンタ変数を10にセットしスタックへ置く
LD HL,10
PUSH HL

;減算数を1にセット
LD BC,1

LOOP:

;スタック上の変数を減算
POP HL
SBC HL,BC
PUSH HL

;繰り返し判定
JP NZ,LOOP:

RET


16bitレジスタではHLレジスタが演算の中心となる。
DEC命令は8bitレジスタに対してはフラグが変化し条件分岐に使えたが、
16bitレジスタ用のDEC命令はフラグが変化しないので条件分岐には使えない。
SBC減算命令はレジスタHLとBCを使う。BIOSを呼ぶ前に二つの16bitレジスタを保存して
おく必要がある。


Z80は8bitレジスタを組み合わせて16bitレジスタとして利用可能だが、内部回路的には8bit
なので、16bit演算は完全でなく拡張的と認識した方が良い。
これは一部の命令、例えば8bitのINC/DEC命令はフラグに反映されるが16bitのINC/DECは
フラグに反映されない等の要因だ。

CPU内部のALUが8bit幅なので16bitの単純な加減算は別のアルゴリズムで実装されている
といえるが、この特長は命令セットの癖となりやすい。

一般論としてプログラマの立場からは8bit/16bit区別なく命令語が設計されているほうが
使いやすい。
しかしハードウエア設計者の立場からは回路が複雑となり実装や性能に問題を生じることがある。
プログラマ的に便利なCPU命令セットを採用しても、過去には設計に失敗したり速度が遅く、
期待した以上の性能を出せず消えていったCPUは数多い。

アセンブラではCPU命令を直接扱うために頻繁に冗長なコードが現われる。
時には多くのアセンブラ命令で単純な処理を冗長に記述する必要がある。
それらは現在の最新のCPU設計でも生じる問題だ。
高級言語とは違いアセンブラは冗長なコードになりやすい。その意味と癖は覚えておく必要がある。
それらの資料は文書化されていない場合もあるので注意が必要だ。




● フラグレジスタ


フラグレジスタは8bitレジスタで個々のビットに意味を持たせタレジスタで、
それらビット列は、S,Z,H,P,N,Cなどがある。
それぞれの動作は下記の通りだ。

01234567
--------
SZ*H*PNC

S,マイナス符号時
Z,ゼロ時
H,ハーフキャリー時
P,パリティ・オーバーフロー時
N,二進化十進(BCD)補正時
C,キャリー時
*,無し

フラグの状態はモニタコマンドのレジスタ表示で確認する事が出来る。
下記コマンドを実行する。

>RG

レジスタ表示コマンドRGを実行して表示される下記の部分がフラグを示す。

SZ*H*VNC
--------
00000000


フラグは演算命令などが実行された際に状態が更新される。
加減算以外にシフト、比較、ビット操作などでも変化する。

アセンブラの条件分岐命令は、無条件分岐命令に対して特定のフラグビットを示す修飾子が
追加されている。

分岐の際には値の比較が中心となるが、分岐条件を判断する場合はゼロまたはゼロ以外、
桁溢れの有無、などによって分岐を行なえる。
フラグは演算の際にも利用される。

フラグレジスタを初期化することも可能だ。ゼロクリアする場合はフラグに値をセットすればいい。

>RG F,0

以上の命令をモニタコマンドで実行すればフラグはゼロにりセットされる。




● 整数の加算とフラグレジスタの変化


フラグは演算の際にも利用される。8bitレジスタの加算の際に桁が溢れた場合は、
桁あふれフラグ(C,キャリーフラグ)がONとなる。加算の際に桁が溢れたら上位桁に繰り上がる
処理を行えば良い。

フラグには以下のような意味のあるビットが並んでいる。

01234567
--------
SZ*H*PNC

S,マイナス符号時
Z,ゼロ時
H,ハーフキャリー時
P,パリティ・オーバーフロー時
N,二進化十進(BCD)補正時
C,キャリー時
*,無し


単純な8bitレジスタの加算を考えてみよう。

LD A,0
ADD A,1

Aレジスタのゼロに対し、1を加え加算を行なったものだ。
この場合結果はAレジスタは1となる。フラグは全く変化しない。
モニタを使い確認する場合は以下のコマンドを実行すればよい。

>RG F,0
>M C620

C620 LD A,0
C622 ADD A,1

>TR C620

実行前に必ずフラグをゼロクリアしておく。
演算結果とフラグの状態はレジスタを確認すれば良い。

次の例ではフラグが変化する。

LD A,0FFh
ADD A,1

式で表せば、255+1=256 となる。
整数加算の結果Aレジスタの内容はゼロとなる。このときフラグレジスタは、ゼロ、
キャリー(桁繰り上がり)、ハーフキャリー(符号付整数の符号部分の繰り上がり)
という3つのフラグビットがONとなる。

なぜAレジスタの内容がゼロとなるかというと、8bitサイズであるからだ。つまり
8bitで表現可能な数値の範囲を超えてしまったという事だ。

演算結果がゼロとなっていて、8bit(1byte)で表現可能な数は0-255(0xFF)だから、桁が
繰り上がる為にフラグはキャリービットがONとなる。
ハーフキャリーは2の補数表現の際のマイナスプラスの符号が変化した際(繰り上がった際)
ONとなる。この例では0xFFから0x0へ遷移した為キャリーとハーフキャリービットがONとなった。

次の例では符号付のフラグが変化する。

LD A,07Fh
ADD A,1

この場合はマイナス符号、ハーフキャリー、パリティ・オーバーフローフラグがONとなる。
なぜなら符号付き整数(signed byte)の場合、2の補数表現を使い-128から127の範囲で
数値表現するからだ。
符号付整数は、0-127迄が正の数、128-255までが負の数として扱われる。だから、
下記の整数加算を行なった場合、符号フラグのみが変化する。

LD A,080h
ADD A,1

この場合は符号無し整数として扱う場合、単純な整数が1だけ増加する。符号は無いので
桁の繰り上がり、繰り下がりだけ注意する。
符号付整数の場合は、2の補数のマイナス表現-1から-128までの部分に該当するので、
この範囲の数値を加算する場合CPUのフラグレジスタの符号ビットは必ずONとなる。

数値を扱う場合、数値データそのものに型を備えているのではなく、数値データを符号付整数
と見るか、符号無し整数と見なすのかはプログラム、ユーザーに一任される。
CPUのALUそのものは符号付と無し整数データを区別せずフラグを立てる。
だからフラグレジスタのビットの状態を見て、符号付整数と符号無し整数の処理をプログラムが
適切に行なう必要がある。














Date: 12/01/16 21:19:30
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(8)



● BIOSを使ったVRAM書込み

BIOSルーチンのNWRVRM(0177h)を使いVRAMへデータを書込みしよう。
このコードはHLレジスタをカウントアップしながらランダムな値をAレジスタにセットし、
データをBIOSルーチンを経由してVDPのVRAMに書込むものだ。


>M C5A0

C5A0 LD HL,0
C5A3 LD BC,0100h

C5A6 LD A,R
C5A8 CALL 0177h
C5AB INC HL

C5AC PUSH HL
C5AD SBC HL,BC
C5AF POP HL

C5B0 JP NZ,0C5A6h
C5B3 RET

>GO C5A0

モニタ上から実行するとテキスト画面にランダムな文字列が表示される。
コメントを追加したアセンブラコードを以下に示す。


;初期化
LD HL,0
LD BC,0100h

LOOP:

;Aレジスタにランダムなデータをセット
LD A,R

;BIOSによるVRAM書込み
CALL 0177h

;アドレスカウント増加
INC HL

;判定
PUSH HL
SBC HL,BC
POP HL
JP NZ,LOOP:(c5a6)
RET



BIOSのNWRVRMはHLレジスタへVRAMアドレスを、Aレジスタへ書込みデータを与える。
Rレジスタはリフレッシュレジスタだが、今回は乱数生成用として用いている。
HLレジスタはカウントアップ、判定はSBC命令を使い引き算でゼロ判定を行なう。
簡単なコードだがBIOSを使っている為に動作は遅い。
VDP関連はBIOSを用いず直接I/Oを操作するほうが良い。

このコードをBASICから実行する際は下記だ。スクリーンモードを変更すればテキスト画面
だけではなくビットマップグラフィック画面にカラーの点を描画する事が出来る。

10 SCREEN 3
20 DEFUSR=&hC5A0
30 A=USR(0)
40 K$=INPUT$(1)
50 SCREEN 0






● 高速なVRAM描画

次の例では前回のコードを改良し、BIOSを経由せず直接I/Oを操作しVRAM書込みを行なう。
この方法はBIOSを用いないので高速に描画が可能だ。

>M C5C0

C5C0 PUSH HL
C5C1 PUSH BC
C5C2 LD HL,0100h
C5C5 LD BC,1

C5C8 LD A,0
C5CA OUT (099h),A
C5CC LD A,08Eh
C5CE OUT (099h),A

C5D0 LD A,0
C5D2 OUT (099h),A
C5D4 LD A,040h
C5D6 OUT (099h),A

C5D8 LD A,R
C5DA OUT (098h),A

C5DC SBC HL,BC
C5DE JP NZ,0C5D8h
C5E1 POP BC
C5E2 POP HL
C5E3 RET

>GO C5C0

このコードを実行するとBIOSを使用した時と同様、テキスト画面に文字がランダムに
表示される。

コメント付きのアセンブラコードを以下に示す。


;レジスタの退避
PUSH HL
PUSH BC

;カウンタセット
LD HL,0100h
LD BC,1

;VDPレジスタ#14を設定(アドレス設定 A14-A16)
LD A,0 ;VALUE
OUT (099h),A
LD A,08Eh ;VDP REG(10001110)
OUT (099h),A

;アドレス設定(A0-A13のうちのVRAMアドレス下位 A0-A7)
LD A,0
OUT (099h),A
;アドレス設定(VRAMアドレス上位 A8-A13)
LD A,040h
OUT (099h),A

LOOP:

;ランダムデータをVRAMへ書込み
LD A,R
OUT (098h),A

;判定
SBC HL,BC
JP NZ,LOOP:(0C5D8h)

POP BC
POP HL
RET



繰り返し回数はHLレジスタ設定値となる。この定数を増やせば描画ライン数は増える。
BIOSと比較し、より高速に画面描画が行なわれる。

VDPへの書込みはI/Oポートの0x98,0x99番地を経由する。
VRAMアドレスはA0-A16の17bit分にて128KByte区画を指定する。その方法は多少複雑である。
基本はアドレスを指定しデータを書き込む。ここではVDPアドレスオートインクリメントを
利用している為一回のアドレス指定で済んでいる。

詳細はVDPアクセス資料を参照する事。
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan?page=2%BE%CF+MSX%2DVIDEO%A4%CE%A5%A2%A5%AF%A5%BB%A5%B9

BASICから実行する際のプログラムを以下に示す。

10 SCREEN 8
20 DEFUSR=&hC5C0
30 A=USR(0)
40 K$=INPUT$(1)
50 SCREEN 0


I/Oとの通信はZ80アセンブラのIN/OUT命令を使う。I/O制御の基本はIN/OUT命令を繰返し利用し、
デバイスのレジスタを設定する。操作はI/Oデバイスに大きく依存する。
この方法は他のCPUでも同様で、I/Oを扱う場合には必ずI/Oデバイス固有の操作が必要となる。
これらはI2Cバス接続のフラッシュメモリであってもシリアル通信チップであっても、
USBメモリ接続基盤であっても同様で、デバイス固有の操作が伴う。














Date: 12/01/16 21:12:54
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(7)



● BIOSを使い文字を表示する。

MSXにはテキストモードとグラフィクスモードが存在する。BASICでSCREEN0,1と呼ばれる
画面モードは、テキストモードと呼ばれTMS9918や9938が持つ機能だ。
このモードは特定のVRAMメモリにASCIIコードを書き込めば文字を表示する事が出来る。
現在使われているキャラクタ表示型液晶モジュール等と全く同様の仕組みだ。

テキストモードでは簡単に英文字やかな文字を表示できる。
アセンブラでBIOSの文字出力(CHPUT,0xA2h)を使って文字を表示させてみよう。

>M C470

C470 LD A,031h
C472 CALL 0A2h
C475 RET

>GO C470


031hはASCII文字コード。CALL 0A2hがBIOSコールだ。
BIOSのCHPUTはDOSのCHPUTシステムコールと似た振る舞いをする。
使い方はAレジスタにASCII文字コードを設定し、BIOSのCHPUT(0xA2)をコールするだけだ。

CHPUTはテキスト画面上で文字を一文字表示する機能を持ちコンソールSTDOUTストリームを
処理する。
改行コードなどに対応しているのでDOS環境のCR+LFにより画面が制御される。
これらはDOSやWindowsの制御コードと同一である。



CHPUTを使って複数の文字列を表示する場合の例を示そう。
";"で始まる行はコメントで、アセンブラ入力の際にモニタへ入力はしない。

>M C480

;"?"を表示
C480 LD A,03Fh
C482 CALL 0A2h

;CR+LFによる改行
C485 LD A,0dh
C487 CALL 0A2h
C48A LD A,0ah
C48C CALL 0A2h

;"@"を表示
C48F LD A,040h
C491 CALL 0A2h
C494 RET

>TR C480

0xD,0xAはそれそれCR,LFだ。CR=0xD,LF=0xAとなっている。コンピュータ上では一行を示す
ラインの終わりに必ずCRとLFを追加する。
このCR,LFを制御コードと呼ぶ。CR,LFは一般的にSJISやASCIIと呼ぶ文字コードで利用される
改行の決まりで、DOSやWindowsなどのOS上で共通する規則だ。






● BIOSを使い長い文字列を表示する。


BIOSのCHPUTルーチンは一文字を画面に表示する機能を持つ。このBIOSルーチンを組み合わせ
複数の文字から成る、文字列を表示させる事が可能だ。


>M C4A0

C4A0 LD HL,0C4FFh
C4A3 INC HL

C4A4 LD A,(HL)
C4A5 CALL 0A2h

C4A8 LD A,(HL)
C4A9 CP 0
C4AB JP NZ,0C4A3h
C4AE RET


このコードをコメント付きで書くと以下のようなものとなる。


;初期値として文字列のアドレス(address-1)を設定。アドレスは0xC500
LD HL,0c4ffh

LOOP:

;文字列アドレスのポインタを+1加算する。
INC HL

;文字列から一文字を取り出す
LD A,(HL)

;CHPUT(BIOS)一文字表示
CALL 0A2h

;判定。NULL終端(ゼロ)を判断
LD A,(HL)
CP 0h
JP NZ,LOOP:

;戻る
RET


上のアセンブラはメモリー上の指定番地からデータを読み込み文字列として表示するものだ。
文字列データはゼロで終わる。いわゆるNULL終端文字列だ。

アセンブラを入力し終えたら文字列を入力しておこう。
文字列はモニタコマンドのAによってテキスト文字入力が可能だ。
モニタから文字列入力する際はアドレスを指定する。

>A C500

C500: The 2Byte Contents Of Index Register IY are loaded to the Stack Pointer SP.

文字が入力できたらアセンブラに間違いが無いことを確認し、実行してみよう。

>GO C4A0

C言語でもアセンブラでも文字列データの最後はNULL(数値のゼロ)で終わる。
このコードはメモリのデータをゼロ判定し、文字列の終わりを判断し表示する。

NULL終端文字列と、改行コードは全く別の話だ。
改行コードは文字列データ内の一行を示す問題だ。文字列データが複数行存在する事も有る。
一方で、NULL終端文字列は連続した文字列データの終わりを意味する。
文字列の終わりは必ず数値のゼロ(0)でなければならない。

このアセンブラコードでは文字列アドレスをHLに設定、アドレスからAレジスタにデータを
読み込みBIOSのCHPUTで文字表示する。
CP 0h命令でAレジスタと数値のゼロを比較し、ゼロであれば文字列の終わりに達したと判定し
処理を終了する。


当然このアセンブラコードもBASIC上から実行が可能だ。
BASIC上から下記の命令を実行すれば文字列を表示すれば良い。

DEFUSR=&hC4A0
A=USR(0)














Date: 12/01/13 02:19:57
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(6)



● BIOSでBEEP音を出す


BASIC環境ではBIOSROMは0x0-0x7FFFに有るのでマシン語から簡単にアクセス可能だ。
そこでBEEP音を発生させるBIOS(00C0h)を使ってみる。

>M C410

c410 CALL 0C0h
c413 RET

このアセンブラを入力し、GO C410 で実行すれば音が鳴る。
通常GOコマンドを実行すると一回だけBEEP音が鳴る。コードを実行すると
合計で二回BEEP音が鳴る。


もし機械語に翻訳されたアセンブラコードを逆アセンブルしたければ、Iコマンドを使う。

>I C400

ダンプコマンドでは16進のバイナリ表示だが、逆アセンブラコマンドであれば
Z80命令としてメモリを逆アセンブルする事が可能だ。




● ジャンプ命令

次にジャンプ命令を使い無限ループを実行してみる。
注意する点はアセンブラニーモニックのアドレス指定表現は3byte(0FFFFh)
先頭にゼロを入れる事だ。

>M C400

c400 NOP
c401 JP 0C400h

>TR C400

このように入力しトレース実行すると、BASICのGOTO文のように特定の番地にジャンプする。

通常はJP命令で無限ループに入るとリセットする他無いが、トレース実行なので
モニタコマンドに戻ってくる。GOコマンドで実行すると本当に無限ループに陥るので
リセットする他ない。



● Z80のループ処理

Z80アセンブラのループ処理の基本はJP命令に分岐先と判定条件を指定するだけだ。
nzの時は非ゼロの時にジャンプする。
下記の例は15回のループで繰り返しを抜けるものだ。

   ld a,0Fh
loop: dec a
   jp nz,loop
   ret

アセンブラをモニタのアセンブラコマンドで入力する。

>M C440

c440 LD A,0Fh
c442 DEC A
c443 JP NZ,0C442h
c446 RET


もし255回のループをしたければ

LD A,0FFH

と書き換えれば良い。


このループ命令を使い、先ほど使用したBEEP音発生BIOSルーチン(0x0C0)にて音を発生
させてみたい。
下記のアセンブラを入力してみてほしい。

>M c440

c440 LD A,0Fh
c442 CALL 0C0h
c444 DEC A
c445 JP NZ,0C442h
c448 RET

このアセンブラを実行すると、音は出るものの無限ループに陥る。リセットが必要となる。
なぜかというとBIOS内部ルーチンでZ80のアキュムレータAレジスタが利用されている
ためにレジスタの内容が破壊されるからだ。

BIOSルーチンもレジスタを使って機械語の処理が行なわれている。それらは高級言語のように
ローカル変数の保護がない。

ここではループカウンタとしてAレジスタを利用した。Aレジスタは汎用のアキュムレータ
なので他でも利用されている可能性が高い。
BIOS内部のルーチンでもレジスタを退避させていない可能性もある。
その場合は自前のコードでレジスタを退避させておく事が必要だ。
一般的にレジスタの退避はスタックを用いる事が多い。


レジスタを退避させてBIOSを呼び出す改良版のコードは以下だ。

>M C440

c440 LD A,0Fh

c442 PUSH AF
c443 CALL 0C0h
c446 POP AF

c447 DEC A
c448 JP NZ,0C442h
c44b RET

ここではCALL命令前後にレジスタ退避のため、PUSH AFを、退避から復帰する
POP AF を追加した。
この改良版では無限ループにならず終了後モニタに戻ってくる。
PUSH/POPのレジスタの退避は16bit長、AFレジスタペアにて必ず行なう。

実行の様子を調べる為にトレース実行をしてみると、BIOS実行後のAFレジスタの内容が
BIOSを呼び出す前と後で異なっているのが確認できる筈だ。

>TR C440

と入力してトレース実行し、BIOSであるCALL 0C0hを呼び出す前には
C443 PUSH AFのラインで

AF=0E0A

であったのに対し、BIOSを実行後、

AF=0B18

となっている点に注目してほしい。これが誤動作した原因だった訳だ。
マシン語やアセンブラを使う時、BIOSを実行する際は、必ずレジスタの退避を考えておく
必要がある訳だ。



● BASICとの連携

ここではさらにマシン語モニタで作成したマシン語コードをBASIC側から呼び
実行する事にしたい。
マシン語モニタSuper-XのメリットはBASICと連携することだと初めに書いた。
モニタから一旦抜ける場合は、QTコマンドを使う。

>QT

以上で一時的にBASICコマンド環境に戻った。この状態でBASICから、

DEF USR=&hC440

と入力し、マシン語ルーチンを登録してみる。
そしてこのコードをBASIC側から実行する。

A=USR(0)

これだけで先ほどマシン語モニタ上でアセンブルしたZ80機械語が実行される。

当然だか、BASIC上でテストしたのち再びマシン語モニタを実行することも可能だ。
モニタに戻る際は、モニタを起動した時のように CALL @ と入力すれば良い。
Super-Xはアセンブラを記述するモニタとして、BASICとモニタ間をシームレスに
移行することが可能だ。

BASICとの連携の際は下記のページが参考となるだろう。

4章 マシン語とのリンク
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan?page=4%BE%CF+%A5%DE%A5%B7%A5%F3%B8%EC%A4%C8%A4%CE%A5%EA%A5%F3%A5%AF

当然このバイナリをディスクなどに保存したいと思うだろう。
その時はBASICのBLOAD/BSAVE命令を使ってC440付近のアドレスを保存すれば良い事になる。
もちろんSuper-Xはディスクへバイナリを保存したりロードしたりする機能も持っている。














Date: 12/01/13 02:09:51
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(5)



● Z80マシン語


Super-Xを用いてマシン語モニタを利用する目的は、Z80機械語、アセンブラのコードを書く事
でもある。
Z80は8bitのCPUである。このコンピュータには幾つかの命令がある。この命令をダイレクトに
メモリーに書き、そのまま実行させることが出来る。

一番の初歩は、何もせず、モニタへ戻ってくるコードを書く事だ。
マシン語として利用可能なメモリアドレス領域は0xC400と判ったから、今度はこの領域に
アセンブラを記述してみる。

まずメモリー領域に何もデータが存在しない事を確認しよう。

>D C400

ESCキーを押すとダンプ表示モードを終了する。
次に、

>M C400

と入力して、以下のアセンブラを入力する。

C400 NOP
C401 NOP
C402 RET

Z80アセンブラを入力後、最後にCTRL−Cを押してアセンブラ入力モードを終了する。
この時点で再度メモリ領域をダンプしてみよう。
ニーモニックから入力したアセンブラが16進コードで保存されている事がわかる筈だ。
アセンブラのニーモニックと16進機械語の対応は下記のようになる。

NOP - 00
RET - C9

NOP(No Operation)命令は何もしない。RET(Return)命令はCALL命令から戻る。
ここではモニタに実行が戻ることを意味する。
共に1Byteの長さを持つ。より詳しくはZ80アセンブラの資料を参考にすると良いだろう。


このコードを実行すると何もせずにモニタに戻ってくる。
実際にコードを実行するコマンドは以下のようなものだ。

>GO C400

実行を終了するとマシン語モニタに帰ってくると同時にレジスタ情報が表示される。


アセンブラは直接入力することが出来るので、下記のように入力しZ80機械語の実行を確認
する事が可能だ。

>M C400

C400 LD A,10h
C402 LD H,20h
C404 RET

このコードの結果はレジスタの状態を見れば確認が可能だ。トレース実行したい場合は、

>TR C400

とコマンドを入力する。リターンキーを押す毎に次の行を実行する。終了する際はESCキー
を押す。
これらのコマンドを組み合わせてZ80アセンブラを翻訳し、コードを実行させることが可能だ。


コンピュータの基本要素は、メモリ、CPU、I/O装置によって組み上げられている。
アセンブラはよりハードウエアに近いプログラミングを意識する。
命令はメモリー、CPU内部の動き、I/O装置への命令、などによって全てをプログラミング
する必要がある。

レジスタはCPU内部の小さなメモリで、このメモリは8bit(1byte)あるいは16bit(2byte)の長さを
一度に扱え、加算や比較を行なう回路が接続されている。
アセンブラではデータはメモリから読み込み、CPU内部のレジスタで全ての処理を行い、結果を
メモリやI/O装置に書込む。

アセンブラプログラムはBASICやC言語などのような高級言語と違い、全ての処理はハードウエア
レベルの命令によってプログラミングを行なう。

現在のレジスタを表示する場合は RG コマンドを使う。
レジスタ一覧にて表示される内容はCPUのレジスタ内容だ

SP,スタックポインタ
PC,プログラムカウンタ
IX,IY、インデクスレジスタ
A,アキュムレータ
F,フラグ
BC,DE,HL、汎用レジスタ

AF',BC',DE',HL'、裏レジスタ


裏レジスタは直接扱う事は無いレジスタで、主に割り込み時などの一時的なレジスタ退避
に利用される。アキュムレータAレジスタは加減算の中心的なレジスタ。
フラグFレジスタはCPUのコンディションを示す読み出しレジスタ。
IX,IYはアドレスポインタの為のインデクスレジスタで配列などと用いるが、MSXの場合は
スロットコールなどでも利用される。

BC,DE,HLは汎用レジスタであるが、HLは16bit長のアキュムレータとして、B,Cレジスタは
Z80のI/Oアクセスやループ処理専用のカウンタとしての役割を持っている。

このほかレジスタ表示コマンドで表示される BP はZ80のレジスタではなくモニタで利用する
デバック用のブレークポインタだ。


RGコマンドはレジスタに値をセットする事も可能だ。Aレジスタに10進の5をセットする
場合は、

>RG A,5

と入力すれば良い。16進の0xffをセットする場合は、

>RG A,0FFh

と入力する。














Date: 12/01/11 00:01:11
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(4)


● マシン語用フリーエリアの確認

マシン語モニタを動作させる際、どのエリアを自由に使えるかを知る必要がある。
BASICプログラムが存在しない場合、あるいはNEW命令を実行直後、
プログラムメモリーには何も存在しない。
従ってBASICインタプリタは0x8000-0x8010程度の数十バイト程度しか
消費していない。

これらを確認するためにメモリーをダンプしてみよう。

>D 8000

メモリーダンプ表示中のゼロと表示されている所は何もデータが無い場所だ。
モニタを起動する際に、DIM A%(10000)としてメモリ領域をゼロクリアしたから
非常に見やすいメモリ表示となったはずだ。

ここで判るとおり、BASICインタプリタが変数やプログラム領域に本当に割り当てている量は、
プログラムや変数の未使用時は、ほんの数バイト程度だという事が判る筈だ。

BASICではRAMは殆ど未使用のため20KByte相当がフリーエリアとして使える事が判るだろう。



BASICインタプリタは、BASICプログラムや変数データをリスト構造で管理している。
プログラムそのものは利用者からはテキストデータとして見えるが、インタプリタが
管理する際はBASICプログラム文字列をリスト構造で連結したデータの塊として管理している。
同様に変数もリスト構造として管理されている。
リスト構造を持つ事で、少ないメモリー領域で有効に活用されるよう工夫されている。

メモリー領域をダンプしながらBASICと行き来しつつ、0x8000付近を見ると、BASICインタプリタが
変数やプログラムをどのように扱っているのか判る。
BASICの変数型やコード表現の資料は下記のアドレスを参照するとより理解が深まるだろう。

3章 BASICの内部構造
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan?page=3%BE%CF+BASIC%A4%CE%C6%E2%C9%F4%B9%BD%C2%A4



● スタックポインタ

次にスタックポインタの位置を知る必要がある。スタックポインタはZ80CPUが管理する
重要なメモリ領域だからである。
スタックの位置は、システム領域のSTKTOP=0xf674で示される。
ダンプコマンドで確認する場合は、

>D F670

で確認する事が出来る。このアドレスはマシンによって異なるが、ワークエリアを確認すると
例えば0xDC87等という値をポイントしている。
この値はZ80スタックポインタのベースポインタのアドレスである。従ってコンピュータの
状態によってスタックの最上位はその都度変化している。

現在のスタックポインタのアドレスを知りたければ、システム領域ではなくCPUのレジスタを
参照するほうが良い。
レジスタ表示はモニタコマンドの RG だ。

>RG

と入力して表示されるレジスタ項目から SP という部分を探して欲しい。この内容が示すアドレス
がスタックポインタの現在の位置である。
殆どの場合、スタックポインタはSTKTOPより下位のアドレスを必ずポイントしている。
(スタックに値を積むとアドレスが小さい方向に増える)
動作状態にもよるが、例えばスタックポインタを意味するレジスタは
SP=0xDB71というアドレスとなっている。

STKTOPより上位のアドレスには文字列用メモリ領域が確保されている。
これは今回はあまり深く考えなくとも良い。

以上のアドレスの内容を踏まえると、BASIC領域からマシン語で扱える領域は、BASICプログラムが
存在しない場合は、0x8000付近から、スタックポインタのあるアドレス近辺である事がわかる。

従って、モニタで利用するマシン語フリーエリアは、0xC400付近を用いても問題は無い。



● Super-Xの電卓機能

簡単な整数電卓機能として、CLコマンドがある。
この単純な計算機能はアドレス計算に必要な、2進、10進、16進の
基数変換に対応する。

>CL 1+2

等と入力すれば答えが2進、10進、16進で同時表示される。
整数同士の演算も可能だ。
10進については2byte整数として符号付と符号無しの表示を行なう。
簡単な2進16進変換であれば、数値の最後に変数クラスを指定すればよい。

>CL 1111B
>CL 1FFH

数値の後に記号、B=2進(bin)、H=16進(hex)を追加すれば良い。














Date: 12/01/10 23:41:27
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(3)


● マシン語領域のフリーエリア

BASICが起動している時、ROMはアドレスの0x0000-0x7fffにマップされている。
このうち、先頭の0x0000-0x1000位までが一般にROMBIOSと呼ばれるマシン語APIルーチンだ。


0x0000-0x1fff, ROMBIOS
0x2000付近, Mathlib(Math Pack)
0x3800-0x7fff, BASICインタプリタなどのルーチン

ROMには上記のようなデータが存在する。この他にMSX2では別スロットにROMBIOSが存在する。
MSX2で追加された機能はそちらにある。


Mathlibはスタック動作をするBCDの64bit長の変数の浮動小数点演算をサポートする。
このライブラリには三角関数や平方根など一般的な数学関数処理が実装されている。
実際の計算では、ワークエリアRAM(0xf380-0xffff)内を利用して計算し、スタック操作によって
実数演算を行なう。
ワークエリアには予めスタックに相当するメモリ領域が予約されている。
利用者はこのスタックエリアに値をロードし、Mathlibの関数をコールすれば実数計算が可能
となる。

0x3800位から始まるROMアドレスにはBASICインタプリタが記録されている。
この部分は基本的なインタプリタ、拡張された命令群、例えばCIRCLEやPLAY命令などが
記録されているほか、幾つかのBASIC命令エントリポイントが含まれている。
整数部分のみに注目すれば16kbyte程度に収まるようなコンパクトさであろう。




● RAMアドレス

メモリーアドレスの0x8000-0xffffまではBASIC動作時にRAMが割り当てられている。
この領域はBASICが使う領域と、システムが使う予約領域が並存して存在する。
メモリーマップの詳細は下記飼料に譲る事にし、ここでは簡単に説明するに留める。

3章 BASICの内部構造
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan?page=3%BE%CF+BASIC%A4%CE%C6%E2%C9%F4%B9%BD%C2%A4


RAMに割り当てられているアドレスのメモリーマップの概要を下記に示す。
RAMの下記アドレスの??はマシン状態によって値が不定である事を意味する。

0x8000-0xde??, BASICプログラム使用
0xdb7? スタックポインタ
0xdb7?-0xde?? BASIC文字列用領域
0xde??-0xf37f, DISKキャッシュバッファ領域(フロッピー接続時)
0xf380-0xffff, システム使用領域(システムワークエリア)


これらのRAMのうち、フロッピードライブが接続されていない場合はDISKBASICバッファ領域は
存在しない。フロッピーが存在する場合はデータバッファ領域として利用される。
(シフトキーを押しながらマシンを起動するとDISKキャッシュ領域は確保されない)

システム予約領域は0xf380からアドレス最後までの0xffff領域となる。
この領域はハードウエア情報の保存、レジスタのバックアップ、ジャンプテーブル、
I/O操作関連、などによって構成されている。詳細はシステムワークエリア資料を参照。

スタックポインタはZ80のスタックが使用するアドレスで、通常BASICプログラムを使う場合
考慮する必要はない。
アセンブラでコードを記述する際にはこの領域を書き換えてはいけない。



BASICが動作している時、RAMの状態は殆どの領域が未使用である。BASICが使うメモリーは
0x8000から始まる。
上限アドレスは0x8000からワークエリア内のHIMEM定数で示されるアドレス迄となるが、
大まかにHIMEMのアドレスは0xf380以下、0xde00以上のアドレスを示すと考えて良い。

このアドレスを知る事は非常に簡単で、BASICからも参照可能である。
システムワークエリアに存在するHIMEMシステム定数は、
0xFC4Aを先頭アドレスとする2byteに記載されている。

10 a=peek(&hFC4a)
20 b=peek(&hFC4b)
30 print hex$(b);hex$(a)

したがってBASICが利用するメモリー上限はこのアドレスを見れば判明する訳だ。

モニタを利用すればより簡単にシステム定数を知る事が出来る。
モニタコマンドを使いダンプコマンドで0xfc40付近を表示させると良い。

>D fc40

FC40 00 00 78 80 F0 08 ........
FC48 00 80 67 DE 00 00 ........

これらのメモリー内容から、FC48と書かれている行の、左から3番目と4番目が0xfc4a,0xfc4b
である。
つまりHIMEM=0xDE67という値が読み取れる。桁の上位と下位が逆である。エンディアンの問題だ。
Z80では16bitアドレスを表示する際、バイト列の下位と上位がこのエンディアンの関係を持っている。

この値から、BASICの利用するフリーエリアの上限は0xDE67である事が判明した。
これは同時にマシン語エリアの上限となる。

古いMSX1用ソフトウエア等がディスクドライブ搭載機で動作しない場合は
マシン語フリーエリア部分として用いられてきたアドレスがディスク装置の
バッファーと衝突する為だろう。














Date: 12/01/10 23:17:48
From: 名無しさん

Re:マシン語モニタ Super-X の使い方(2)


● モニタコマンド

Super-Xには幾つものコマンドが備えられている。
それらのコマンドはDOSのような入力形式で動作する。
例えば、特定のメモリー領域をダンプ表示したい場合は、

>D C900

などと入力すれば良い。最初の英文字がコマンドで、その後に続く文字列は
16進表現のメモリーアドレスを示す番地だ。
世間でモニタコマンドと呼ばれるソフトウエアの操作方法は全てこのような形式で扱う。


一般的に使用するであろう頻度の高いモニタの基本的なコマンドを示す。

D,メモリダンプ
I,逆アセンブル表示
A,アスキーテキストダンプ表示
M,アセンブラ入力
GO,機械語の実行
TR,機械語のトレース実行
RG,レジスタ表示
CL,アドレス16進計算用簡易電卓機能
CK,マシン状態の表示
CU,CPU切り替え
QT,モニタ終了

この他にもたくさんの機能とコマンドを持つ。詳細については英語マニュアルを参照してほしい。




● Super-Xの使用方法。

Super-Xをどのように使うかを簡単に説明する。Super-Xを起動する際は、BASIC環境上から

RUN"SUPER-X.BAS"

を実行しモニタのシステムをロード実行後、

DIM A%(10000)

としてBASICで配列命令を作成し実行後、

_@

または、

call @

をBASIC画面で入力すれば良い。これがモニタを動作させる基本的な手順である。
モニタ自体のプログラムは標準でBASICROM領域のあるページ0,1にロードされる。
つまり16進アドレス0x0-0x7fff付近の裏RAMにロードされる。
この領域はモニタのカーネルが存在するので書き換えてはならない。

モニタ利用時は0x8000-0xffffまでがBASICのRAMとして動作しているので、この領域(32KByte)
が使用可能である。

しかしMSXの起動の際、DOSや他のソフトウエアが起動していたりすると
メモリー上に不必要なデータ、いわゆるゴミデータが残っている。
これらをゼロクリアするとマシン語プログラムやモニタプログラムを動作させる際に見通しが良くなる。
その為にモニタを起動する前に、DIM A%(10000) と入力する。
整数型配列を定義することでBASICインタプリタがフリーエリアをゼロクリアするからだ。

モニタ上では整数配列は使用しないので有っても無くても関係がない。モニタからBASICに戻れば
この配列も消去されるので副作用も無い。
このゼロクリアの初期化はモニタを起動する際に一度だけ実行すれば良い。














Baboo! JAPAN ご意見、ご要望などは まで
Copyright (c) 1995-2014 Baboo! 管理スタッフ
Baboo! BBS トップページ