いまさらベーシックマスターの開発環境を作ってみる(18) NAKAMOZU Tiny BASICを高速化する
いまさらベーシックマスターの開発環境を作ってみる(17) プロファイラを作ってみる | ず@沖縄で作った簡易プロファイラを使ってNAKAMOZU Tiny BASIC(以下NTB)を高速化してみた。NTBは、下記連載で移植したものだ。
- いまさらベーシックマスターの開発環境を作ってみる(5) NAKAMOZU Tiny BASICを移植してみる(1) | ず@沖縄
- いまさらベーシックマスターの開発環境を作ってみる(6) NAKAMOZU Tiny BASICを移植してみる(2) | ず@沖縄
- いまさらベーシックマスターの開発環境を作ってみる(7) NAKAMOZU Tiny BASICを移植してみる(3) | ず@沖縄
NAKAMOZU Tiny BASICのASCII誌の掲載記事・ソースコードは作者のサイトで公開されている。
1文字読み込みルーチンが多く呼ばれている
インタプリタなので当然のことなのだが、1文字読むルーチン(PKUP)が一番多く呼ばれている。
PKUPは1文字読んで区切り文字かどうかを調べてそれを返すルーチンだ。なので、多く呼ばれるのは仕方ないのだが、多すぎる。どうもダブって読んでいる部分があるようだ。
* ;(PICKUP)
ICPKUP INX
PKUP LDA A 0,X ; スペース読み飛ばす
CMP A #' '
BEQ ICPKUP ; 区切りチェック
TST A ; 区切り $00 Z=1 C=0
BEQ RTN6 ; 区切り ':' Z=1 C=1
CMP A #':' ; 他 Z=0 C=1
TBL SEC
RTN6 RTS
PICKUPの情報を捨ててしまっている
2項演算子の処理を行なっているルーチンの、項を処理した後に演算子があるかどうかを調べている部分で、区切り情報を使っていない。
そのため最後の項のときに処理が無駄になっている。
たとえば、 A=1 のような単純代入の時に何度も演算子チェックをしてしまう (A=1*か? 1/か? 1+か? のように)。
下記は加減算の処理を行うサブルーチンの一部だが、BSR PKUPのあとCMP Aで’+’をチェックしていて、PICKUPの戻り値が使われていない。
これは乗除算、関係演算などの処理でも同様。
EX3 BSR TM1
EX4 LDX XS
BSR PKUP
CMP A #'+'
BNE EX6
BSR TM1 ; 加算
EX5 BSR EX8
BRA EX4
EX6 CMP A #'-'
BSR PKUPの次にBEQを入れて、行末や’:’の時は早めに処理を打ち切るようにした。各レベルの処理が数バイトずつ長くなったが、効果大である。
なるべく割り込みを止めない
6800はポインタとして使えるレジスタが少ないので、SPをインデックスレジスタとして使うテクニックが使われている。もちろん、SPを使っている間に割り込みが来ると暴走してしまうので、SEIとCLIで挟んで割り込みが発生しないようにする。
BASICMASTERでは、割り込みを止めると2つの問題が発生する。
1つ目はタイマーが止まってしまうことだ。CRTの垂直同期に合わせて1/60の割り込みを発生させて、それをソフトウェアでカウントアップすることでタイマー処理を行なっている。割り込みを止めると、タイマーが進まなくなる。
2つ目はBREAKボタンがNMI割り込みであることだ。NMIなのでSEIでは止まらない。ハード的にはBREAKを無視できるようになっているが、そのためにはI/O操作が必要で、専用のモニタルーチンを呼び出す必要がある。せっかくSPを使って高速化してるのに、時間のかかる割り込み禁止・解除のサブルーチンを呼んだのでは意味がない。
ということで、PSHX/PULXルーチンをSPを使わない形に変更してみた。このルーチンはGOSUB/DOで使われていて、割と使用頻度が高い。
このルーチンがSPを使っているのは、6800のIXレジスタは、負のオフセットが使えない為でもある。改良後のルーチンはIXをあらかじめマイナス方向にずらすテクニックを使っている。
下記のようにPSHX/PULX用スタックポインタの使い方を変えた。元はXSPの指す先はPSHX済みのデータがある場所だが、新しい方は次にPSHXする場所になっている。
(UNTIL処理でも、LDX 0,X が LDX2,Xになっている)
若干PSHXが長くなるが、PULXやUNTが短くなっているので問題ないと思う。
UNT LDX XSP ; 不成立なら
LDX 0,X ; IXSTACKの内容を
INX ; 取りだし、そのADRSへ
RTS
(中略)
PSHX STS SPS
JSR NMISEI
TXS
LDX XSP ; IXスタックポインタ
CPX #STACK+3
BEQ ERR10
DEX
DEX
STS 0,X
PX1 STX XSP
TSX
LDS SPS
JMP NMICLI
* ;(IXスタックPUL)
PULX LDX XSP
CPX #IXSTCK
BEQ ERR10
STS SPS
JSR NMISEI
LDS 0,X
INX
INX
BRA PX1
[/code]
[code lang="asm"]
UNT LDX XSP ; 不成立なら
LDX 2,X ; IXSTACKの内容を取りだし、そのADRSへ
RTS
(中略)
PSHX PSHB
STX XSPWK
LDX XSP
CPX #STACK+3-2
BEQ ERR10
LDAB XSPWK+1
STAB 1,X
LDAB XSPWK
STAB 0,X
DEX
DEX
STX XSP
LDX XSPWK
PULB
RTS
* ;(IXスタックPUL)
PULX LDX XSP
CPX #IXSTCK-2
BEQ ERR10
INX
INX
STX XSP
LDX 0,X
RTS
予約語のサーチでもSPが使われているのだが、こちらはかなり面倒くさいので後回し。
どれぐらい速くなったか
ASCIIベンチマークや、円周率計算の速度の結果から10%-15%ほど速度が上がっている。
GOTO/GOSUBの行番号検索や、予約語の検索が遅いんだよなあ。アイディアはあるんだけど、メモリ喰うのでどこまで頑張るか……クロスコンパイラ作った方が早い気もするし。
高速化版はgithubに反映してあります。
続く
ディスカッション
コメント一覧
まだ、コメントがありません