概要
- いにしえの 8bit CPU NS製 SC/MP-II に関する記事です。
- C言語でCPUエミュレータを書いて、NIBL BASICを動かしてみました。
- ソースコードは公開です。
https://github.com/iruka-git/SCMP2Emulator
SC/MP-II CPUは、どこが変であるかを簡単にご説明
- 8bit CPUなのに、16bitポインタが4つもある。
- ところが P0 P1 P2 P3 (と勝手に命名)のポインタのうち、最初のやつ P0 は実はプログラムカウンタであること。
- これら4つは統一的に扱われている。(PC相対とPTR相対は命令コード内では同じ扱い)
- 相対オフセット値は命令コードのオペランド(8bit)で指定するので、PTR[127]~PTR[-128]までのメモリーが演算対象になったり、ロード、ストア命令が使えたりする
- 最近分かったこと:PC以外のP1~P3も、分岐命令の飛び先に使用できる(+-127)
- それ以外のアドレッシングモードがない。(即値 8bitは存在)
- :
- ところで、相対オフセット値が 0x80 (-128) だけ特別扱いで、その場合のみ PTR相対のオフセットは定数ではなく、Eレジスタ(8bit)で代用されるんだ。(知らんがなそんなこと)
- また、オートインデックスという不思議なアドレッシングモードを持っていて、相対オフセットでメモリーアクセスしたあとで、インデックスレジスタが実効アドレスに変化する、というやつがある。
- これは68000やPDP-11などにもみられる奴だけど、一つ注意があって
- 相対値が負の時はフェッチされるデータはPTR+相対オフセットのメモリーになるんだけど
- 相対値が正の時はフェッチされるデータはPTR+00 のメモリーになる。
- もちろん命令実行後は PTR は オフセット値が加減算される。
- 使い方によってはスタックポインタ的に使えなくはない。
- :
エミュレータは、なんですんなりと動かなかったの?
- 上記のオートインデックスの実装が間違っていたこと
- 相対オフセットが 0x80 のときに相対値をEレジスタにすり替える実装をしていなかったこと
- ななんと、ステータスレジスタの初期値が 0x20 (out B=1) でないと、NIBL BASICは動きません。(知らんがな)
このCPUのだめなところ
- 上記の16bitポインタへのアクセス(LD,ST)が存在しない(!??)
- Aレジスタ(8bit) と、ポインタの上位あるいは下位とをエクスチェンジ(交換)することだけが許されている。(XPAL P1, XPAH P2 など)
- 8bit オフセットを超えるジャンプ命令が存在しない
- それどころか、一般的なCPUに存在するはずの、CALL/RET 命令が存在しない
- (代わりに、ミニコン時代の XPPCという、ポインタとPCを交換する命令だけが存在)
- 遅い。とっても遅い。どれくらい遅いかというと、外部クロック1MHzで仮に動かした場合、典型的な即値8bit を Aレジスタに与える 2byte命令 (LDI #0xNN) の実行時間は72クロック(72マイクロ秒)・・・絶句します
追伸
-
NIBL BASIC は、ロストテクノロジーで書かれたアセンブラなので、解読はおそらく無理です。(SC/MP-IIのアセンブラをすらすら読める人でも)
-
上記以外にも変なところはいっぱいあって、たとえば
-
プログラムカウンタはフェッチ直前に+1される。これにより、リセット直後の0番地は永遠に実行されない。
-
XPPC命令でロングジャンプしたときも、飛び先の命令フェッチ直前に+1される。なので、実際のエントリーアドレス-1をPTRに入れておく必要がある。
-
引き算の代わりにコンプリメンタリーADD(引く数のビットを反転しただけで加算)で代用している(つまり2の補数ではなく1の補数)。なので、キャリーフラグの意味が普通のALUと逆になる。
-
:
で、SC/MP-IIIでは、まともになったの?
- SC/MP-III用のNIBL BASICをエミュレータで動かせないか試行中です。→うごきました
- SC/MP-IIIは EレジスタとAレジスタを連結して16bitレジスタとして使えるようになりました。
- ポインタオフセットが0x80(-128)だったときに、オフセット値をEレジスタの内容に読み替える処理は、無くなりました。
- 6800/6502系のゼロページアドレッシングに近い、FFxxページの256バイトを直接参照するダイレクトアドレッシングが出来るようになりました。
- ポインタ P1 がスタックポインタになり、16bit 絶対番地指定のJSR命令やJMP命令 が追加されました。
- 乗除算用にTレジスタ(16bit)が追加され、16bitの乗算/除算命令がそれぞれ追加されています。
ここが変だよ SC/MP-III CPU
- ついでなので、SC/MP-III エミュレータとか、独自言語を入れてみて、だめだったことを追記します。
まず、独自言語Asmpp3 を公開しています。
SC/MP-III CPU 用のマシン語モニタ:
https://github.com/iruka-git/SCMP3monitor
SC/MP-II用も書きました。独自言語Asmpp2 を公開しています。
SC/MP-II CPU 用のマシン語モニタ:
https://github.com/iruka-git/SCMP2monitor
SC/MP-IIIのどこがだめ?
-
比較命令(cmp) がない。減算で代用。(できるか!A regが毎回壊れる。)
-
大小比較の結果の取得や分岐が大変面倒。条件分岐命令が A regの内容でしか分岐できない。しかも条件分岐命令は BZ、BNZ、BP (Aregが正) の3個のみ。
-
さらに 16bitレジスタ EA の値を大小比較しようとすると、もっと面倒なことに・・・
-
まあ、それ以外は、可もなく不可もなく、16bit同士の乗除算命令がある8bitレガシーCPUは珍しいんですけどね。・・・
おまけ
-
2024年版、NIBLFP_2024 BASIC も、ついでに動かしてみました。
https://github.com/iruka-git/SCMP2_NIBLFP_2024BASIC -
最初、動かなかったんですが、まさか、分岐命令のPC相対オフセットが、PC以外でも使えるなんて・・・ (JZ 12(P3) など) 夢にも思わなかったわ。
参考
SC/MP使いの憂鬱
https://electrelic.com/electrelic/node/1299