はじめに
MSX-BASIC のソースコードは MSX(エミュレータを含む)上で打ち込んで実行するのが正しい作法(?)なのかもしれませんが、普段から vim などの便利なテキストエディタに慣れきってしまった軟弱な現代っ子である私にとって、それは中々ハードルが高いです。
今更MSX-BASICを触り始めたのは msx0 関連
という訳で、MSXで扱えるフロッピーディスクイメージ(FAT12形式)を作成できるツールを作ってみました。
これにより、
- PC (vim等) で MSX-BASIC のソースコードを作成
-
dskmgr
でディスクイメージを作成 - MSXエミュレータ上でディスクイメージ読み込み
- MSX-BASIC のソースコードを読み込んで実行
という手順で動くだろうと思ったのですが、実は MSX 上でフロッピーディスクに保存する MSX-BASIC のソースコード(.BASファイル)は、テキスト形式ではなく中間言語形式に変換されたものだったというトラップがあることを(作った後で)知りました。
確かにその方がメモリ消費を押さえつつ、インタプリタの構文解析によるオーバーヘッドも抑えられるメリットがあるので、(当時としては)そういう仕様にならざるを得ないものと類推できます。
という訳で、MSX-BASICのソースファイル(拡張子 .BAS のファイル)については、テキスト形式と中間言語形式を相互変換できるフィルタプログラムを作って、それを咬ませる魔改造をすることにしました。
これでようやく、vimでMSX-BASICのソースコードを書いてエミュレータ上で実行できるようになりました。
フィルタプログラムを作った際、MSX-BASICの実数表現(BCD浮動小数点数)がかなり直感と異なっていて解析するのが少し面倒でした。
という訳で、本書ではMSX-BASICの実数表現(BCD浮動小数点数)についてなるべく分かりやすく解説してみようと思います。
BCD 浮動小数点数
MSX-BASIC(1983)の浮動小数点数は BCD 浮動小数点数という(やや特殊な)方式が採用されています。
IEEE754(1985〜)で規定されているデシマル浮動小数点数の元祖のようなものでしょうか。
デシマル浮動小数点数はC言語ならC23以降の規格で利用できるようです。
一般的な浮動小数点数(バイナリ浮動小数点数)と違って誤差が発生しないため、金融系のシステムでは必須です。Java (BigDecimal) や C# (decimal) などの近代的なプログラム言語では当然使えますし、レガシー言語でも COBOL あたりで使うことができるらしいです。
MSX-BASIC の BCD 浮動小数点数とデシマル浮動小数点数の主な違いは、仮数部の数値表現が BCD (1234 → 0x1234) であることと、指数部の仕様が独特であることぐらいです。つまり、全く別物とも言えるかもしれませんが、基本原理はだいたい同じだと思われます。
(データ構造)
データ構造は次のような形です:
- 1 bit の符号部
- 7 bits の指数部
- 仮数部:
- 24 bits (単精度)
- 56 bits (倍精度)
MSXで倍精度とかなかなかクレイジーですね。(褒め言葉)
しかも、倍精度がデフォルトです。
デフォルトに関しては結構曖昧なようで仕様がよく分からないため一旦削除
(符号部)
MSX-BASICのコード上の浮動小数点数は全て正の値のみとなっています。
負数を表現するには数値の前に 0xF2 (-
) を配置する仕様のようです。
やや無駄感...
(仮数部)
例えば、仮数部が 0x123456
だとすると、指数部を乗じない状態の数値は 0.123456
と仮定されます。
そして、その数値に 10 の n(指数部)乗 を乗算した値が実数となります。
(指数部)
MSX-BASIC の BCD 浮動小数点数の指数部はやや特殊です。
当初、私の直感では実数 123456.0
を表現したい場合、計算式にすると 0.123456 * 10^6
となるので、指数部に 6
を指定した形(0x06, 0x12, 0x34, 0x56
)になるものと想像しました。
しかし、実数 123456.0
を表現したい場合の指数部は 0x46
となっていました。
以下、仮数部 123456
で色々なパターンの実数を求めた時の指数部の内容を示します。
1 LET A=!1234560
2 LET A=!123456
3 LET A=!123.456
4 LET A=!.123456
5 LET A=!.0123456
上記のBASICコードを打ち込んだ時の中間言語形式をダンプして BCD 浮動小数点のバイトコードを調べた結果を下表に示します。
実数 | 計算式(参考) | バイトコード |
---|---|---|
!1234560 |
0.123456 × 107 | 0x47, 0x12, 0x34, 0x56 |
!123456 |
0.123456 × 106 | 0x46, 0x12, 0x34, 0x56 |
!123.456 |
0.123456 × 103 | 0x43, 0x12, 0x34, 0x56 |
!0.123456 |
0.123456 × 100 | 0x40, 0x12, 0x34, 0x56 |
!0.0123456 |
0.123456 × 10-1 | 0x3F, 0x12, 0x34, 0x56 |
つまり、7 bits の指数部は MSB が ON の時が正数、OFFの時が負数となるようです。
また、指数部が 0 の場合、仮数部にどのような値が設定されても実数が 0 になるという謎仕様にもなっているようです。(多分、ゼロの比較を高速化したかった感じかなと想像)