Z80用浮動小数点演算パッケージ SOROBAN

大貫信昭
 
 パーソナルコンピュータ,「電子計算機」とはいっても,数値演算(特に実数演算)のための特別なハードウェアを持っているわけではありません。8ビットCPUで一度に扱えるデータの大きさは1バイトか2バイトに限られています。整数を扱う場合なら2バイトサイズでも実用になるのですが,実数を表現するためには4バイトから8バイトの大きさのデータを処理しなければなりません。
 こうなると四則演算だけでも結構面倒な処理が必要になってきますし,我々が普段BASICでやっているような処理まで考えると,SINやLOGといった数値関数も必要です。プログラムを作るたびに,いちいちこのような部分までコーデイングしていたのでは大変な労力がかかりますし,データ形式がマチマチになると扱いにくくなるので,このようなものはパッケージ化するのがいちばんです。
 問題はどのような形式でパッケージ化するかということでしょう。データの範囲が限定されているならば,固定小数点という形式で処理を軽くすることも考えられます。しかし,応用範囲を広くしなければパッケージ化の意味がありませんので,ここでは一般的な浮動小数点形式を採用します。さらに本格的なことをやろうとするなら,倍精度くらいの演算精度がほしいところでしょう。
 結局,今回の浮動小数点演算パッケージで使用した浮動小数点のフォーマットはX1やMZと同じ単精度5バイト,倍精度8バイトのいわゆるシャープフォーマットに準拠しています。IEEE準拠なら単精度4バイトですから,若干処理も軽くなりますが,精度的にはどうしても見劣りします。4バイトならデータをレジスタに持つことで大幅な高速化も不可能ではないでしょう。しかし,そうなると倍精度ルーチンとはまったく別の処理となりますので,サイズ的にはずいぶん不利になります。
 現在のS-OSにとってメモリのフリーエリアはなによりも大切ですから,このようなパッケージはできるだけ小さくまとめなければなりません。また,このIEEE形式は世界標準とはいえ,シャープ系のマシンのユーザーにはあまりお馴染みでないでしょうし,シャープフォーマットならデバッグの際にX1turboのBIOSと比べることができるなどのメリットがあったので結局シャープフォーマットに準拠ということになりました。
 
図 浮動小数点のフォーマット
 

◆SOROBANの仕様

 さて,前述のようにこのSOROBANは単精度5バイト,倍精度8バイトをサポートしており, 有効桁数は単精度8桁,倍精度16桁て扱える数値の範囲は2.94×10^-39〜±1.7×10^38となっています。
 このようなパッケージに必要とされる高速,高精度,高機能,コンパクトの4つの条件をすべて満たすのは困難ですが,このパッケージでは精度,機能, コンパクトさともに満足できるレベルだと思います。問題は速度ですが,こちらのほうはまだまだ改善の余地があります(プログラムサイズとの兼ね合いもありますが)。
 このプログラムでは乗除算がボトルネックになっています。X1turboのBIOSと比較すると3倍もの時間がかかってしまいます。そんなに違うことをやっているはずはないのにどうしてこんなに差があるのでしょうか? この部分を高速化すればほかの関数も速くなるので,自信のある方はぜひ手を加えてみてください。
 また,このSOROBANはS-OSシステムに依存していませんので,Z80のRAM上であればそのまま使用することが可能です。CP/Mや各機種のマシン語モニタ上で動作するプログラムから呼び出すこともできます。
 

◆SOROBANの使い方

 SOROBANはリロケータブルバイナリ形式(Oh!MZ1987年11月号参照)で出力されています。そして,実際に使用するときには,配布ディスクに収録されているローダ(LOADER)を使って,SOROBANを使用するアプリケーションの邪魔にならないアドレスにリロケートします。SOROBANはHLレジスタとDEレジスタにデータの先頭アドレスを入れて,処理ルーチンを呼び出すとHLレジスタの指すアドレスから格納されているデータが変更されるという仕様になっています。パッケージの頭にジャンプテーブルがありますので呼び出すときはそこをコールしてください。
 

SOROBANリファレンスマニュアル

■パッケージ仕様
1) 単精度は5バイト,倍精度は8バイト使用する
2) 有効桁数は,単精度8桁,倍精度16桁
3) データ,データ1はHLレジスタの指すアドレスに格納されている浮動小数点形式の数値を示す
4) データ2はDEレジスタの指すアドレスに格納されている浮動小数点形式の数値を示す
5) サブルーチン名の後ろの[ ]の中はパッケージの先頭アドレスからの相対アドレス。たとえば,パッケージがB000Hにあった場合,#ADDのアドレスはB020Hとなる。

#PRCSN [0000]
[機能] 精度
[備考] 内容が5のとき単精度,8のとき倍精度となる(ワークエリア)
#MOVE [0002]
[機能] データ1をデータ2に代入する[HL]==>[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] DE:データ2のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#SWAP [0005]
[機能] データとデータ2を交換する[HL]<=>[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
   DE:データ2のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#CVDBL [0008]
[機能] 単精度のデタを倍精度にする
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存) AF以外のすべてのレジスタ
[備考] バッファは8バイト必要
[ 例 ] 入力:C0 2B 54 A8 BC (1.2345678E+19)
   出力:C0 2B 54 A8 BC 00 00 00 (1.234567800393669E+19)
#CVSNG [000B]
[機能] 倍精度のデータを単精度にする
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
[ 例 ] 入力:C0 2B 54 A9 8C EB 1E EC (1.234567890123456E+19)
   出力:C0 2B 54 A9 8D (1.2345679E+19)
#CVSTF [000E]
[機能] ASCII文字列を浮動小数点形式のデータに変換
[入力] HL:データを格納するバッファのアドレス(5or8バイト)
   DE:ASCII文字列の先頭アドレス
[出力] HL:データのアドレス(結果)
   DE:ASCII文字列の終了アドレス+1
[保存] AF,DE以外のすべてのレジスタ
[備考] 関係のないコードが出てきたところでデータ終了とみなす
[ 例 ] 12.3456
   -3
   42.6555E5
   1.2E-12
#CVUTF [0011]
[機能] 16ビット長無符号整数を浮動小数点形式のデータに変換
[入力] HL:データを格納するバッファのアドレス(5or8バイト)
   DE:16ビット長無符号整数
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#CVITF [0014]
[機能] 16ビット長符号付き整数を浮動小数点形式のデータに変換
[入力] HL:データを格納するバッファのアドレス(5or8バイト)
   DE:16ビット長符号付き整数
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#CVTFS [0017]
[機能] 浮動小数点形式のデータをASCII文字列に変換
[入力] HL:データのアドレス
   DE:文字列を格納するバッファのアドレス(18or34バイト)
[出力] DE:ASCII文字列の先頭アドレス(結果)
[保存] AF以外のすべてのレジスタ
[備考] エンドマークとして00Hが付く
[ 例 ] 276.345
   0
   -9.999E-18
#CVFTU [00lA]
[機能] 浮動小数点形式のデータを16ビット長無符号整数に変換
[入力] HL:データのアドレス
[出力] DE:16ビット長無符号整数(結果)
   Z:正なら1,負なら0
[保存] AF,DE以外のすべてのレジスタ
#CVFTI [001D]
[機能] 浮動小数点形式のデータを16ビット長符号付き整数に変換
[入力] HL:データのアドレス
[出力] DE:16ビット長符号付き整数(結果)
[保存] AF, DE以外のすべてのレジスタ
#ADD [0020]
[機能] 加算[HL]=[HL]+[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジス?
#SUB [0023]
[機能] 減算[HL]=[HL]-[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#MUL [0026]
[機能] 乗算[HL]=[HL]*[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#DIV [0029]
[機能] 除算[HL]=[HL]/[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#IDIV [002C]
[機能] 整数除算[HL]=[HL]¥[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#MOD [002F]
[機能] 剰余算[HL]=[HL]MOD[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
#CMP [0032]
[機能] 比較[HL]-[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] [HL]>[DE]の場合A=1 CY=0 Z=0
   [HL]=[DE]の場合A=0 CY=0 Z=1
   [HL]<[DE]の場合A=-1 CY=1 Z=0
[保存] AF以外のすべてのレジスタ
#NEG [0035]
[機能] 符号反転[HL]=-[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#INT [0038]
[機能] データを越えない最大の整数にする[HL]=INT[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#FIX [003B]
[機能] 小数点以下を切り捨てる[HL]=FIX[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#FRAC [003E]
[機能] 小数部をとる [HL]= FRAC[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#CINT [0041]
[機能] 小数第1位を四捨五入して整数にする[HL]=CINT[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#SQR [0044]
[機能] 平方根[HL]=SQR[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#SIN [0047]
[機能] 三角関数SIN[HL]=SIN[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[備考] 引数の値はラジアン単位
[保存] AF以外のすべてのレジスタ
#COS [004A]
[機能] 三角関数COS[HL]=COS[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[備考] 引数の値はラジアン単位
[保存] AF以外のすべてのレジスタ
#TAN [004D]
[機能] 三角関数TAN[HL]=TAN[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[備考] 引数の値はラジアン単位
[保存] AF以外のすべてのレジスタ
#ATN [0050]
[機能] アークタンジェント(逆正接) [HL]=ATN[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
[備考] 引数の値は-π/2〜π/2
#EXP [0053]
[機能] 指数関数[HL]=EXP[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
[備考] 引数の値は88以下
#LOG [0056]
[機能] 自然対数[HL]=LOG[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#POW [0059]
[機能] 累乗[HL]=[HL]^[DE]
[入力] HL:データ1のアドレス
   DE:データ2のアドレス
[出力] HL:データ1のアドレス(結果)
[保存] AF以外のすべてのレジスタ
[備考] データ1が負の場合,データ2は-65535〜65535の整数
#PAI [005C]
[機能] 円周率πのデータ倍にする[HL]=PAI[HL]
[入力] HLデータのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#RAD [005F]
[機能] 度単位からラジアン単位に変換する[HL]=RAD[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#ABS [0062]
[機能] 絶対値[HL]=ABS[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
[保存] AF以外のすべてのレジスタ
#SGN [0065]
[機能] 符号を返す[HL]=SGN[HL]
[入力] HL:データのアドレス
[出力] HL:データのアドレス(結果)
  データ:正の場合1
    0の場合0
    負の場合-1
[保存] AF以外のすべてのレジスタ
 
(C)1989 Nobuaki Ohnuki(original)
(C)1997 Junji Okazaki(edited)
(C)2024 Oh!Ishi,Nibbles Lab.(formatted)