6800の未定義命令(1) 歴史と概要

2024/12/12BASICMASTER, 昔のパソコン

現代のCPUは、定義されていない命令(利用されていないオペコード)を実行すると、未定義命令エラーが検出されてOS trapが発生する。

けれども、往年のコンピューターでは、定義済みの命令と同じ動作をしたり、似たようで違う動作をしたり、はたまた暴走したり、色々なことが起きた。

それらの命令の中でも有用なものは広く使われて、後継CPUでも互換動作をするようになったものもある(Z80の未定義命令が有名)。

ここでは、モトローラMC6800(日立HD46800,富士通MB8861)の未定義命令について見ていきたい。


未定義命令探しが流行った

発端はByte誌1977年12月号である。PP.46-47に掲載された “Undocumented M6800 Instructions” に6つの命令が掲載された。

この中で紹介された命令 "HCF (Halt and Catch Fire)” が目を惹いたのか、記事は人気になった。

Byte Dec. 1977 P.46より引用

BYTE誌1978年1月号P.84-90にもMC6800の命令セットの記事があるが、載っている未定義命令は1977年12月号と同じである。

Web検索してみたが、6800の未定義命令に関する日本語以外の記事は、BYTE誌のこの記事を引用しているものばかりであった(探し方が悪いのだろうか)。

日本の雑誌記事では

過去に6800の未定義命令についての記事を読んだ覚えがあるので、日本の雑誌を探してみた。多数の記事が見つかった。

エンサイクロペディア・アスキーの volume 2にHD46800/MB8861の未定義命令についての記事が大量に載っている。I/O誌にもMB8861の未定義命令の記事がある。

日本では日立・富士通・松下などの錚々(そうそう)たる企業がMC6800の互換CPUを作っていた。そして、それらのCPUを使ったトレーニングキットやパソコンが販売されていた。1970年代の日本では6502よりも6800の方が普及していたと思う。

ASCII誌では、1978年3月号のTBN「マイコン私情へのついしん!」にて くだんのBYTE誌記事の紹介とMB8861N(コスモターミナルD)による追試が行われており、BYTE誌で紹介された命令以外にも4命令が発見されている。

これが火付け役となって半年ほどHD46800/MB8861での未定義命令探しの投稿記事が掲載されている。

調査対象がMC6800でないのは、国産のトレーニングキットである日立H68/TR(HD46800)や富士通L-Kit8(MB8861)が主に使われていたからだろう。

最初の国産パソコンであるベーシックマスター(初代)は、これらの記事が投稿された後の1978年9月発売であり、しかも、このCPUもHD46800だ。
(本家モトローラのMEK6800も売られていたが、どれぐらいシェアがあったのだろうか)

ASCII誌の未定義命令探しで数多くの命令が発見されたが、それほど利用されずに、歴史に埋もれていった。


私はASCII誌やI/O誌、Webの記事を元にMC6800/HD46800/MB8861の未定義命令を一覧にしてみた。ニーモニックはASCII誌の案と6809など後継CPUの命令を参考にして付けてある。

動作確認はVisual 6800 in JavaScriptで行ったが、漏れがあるかもしれない。




ASCII誌掲載記事で誤っていると思われる点を書いておこう。

$CD

PSH PCになっているのは、BSRの誤りと思われる。

記事では未定義命令の後ろに$00を置いてテストしたと書かれていて、その場合はBSRはPCをスタックに積んだあと、直後の命令に分岐するのでPCをPUSHしたかのように見える。

後続バイトを変化させると、相対分岐するのでBSRで良いと思われる。

$CC-$FC

フラグ変化するので、何かが起こっている。$FCはext address先の2バイト目でフラグが変化しているようにも見えるが、詳細不明。

フラグが変化するので、ASCII記事にあるような、単純なjump命令ではない。


使える命令はあるのか?

NBA (A=A and B)は記事「6800のアセンブラプログラム最適化(1)(ベーシックマスター開発 その25)」にも書いたように、関係演算の結果(0か1)をANDしたいときに使える。

TSRはAccの再下位ビットが0か1かの判定に使える。BITA #1でもできるが、1バイト短いのとCarryが立つのが良い。0/1/それ以外の分岐にも使える(BNE/BCxで分岐)。

STX # は、IXを保存するサブルーチンとして使える(エンサイクロペディア・アスキー volume 2 P.355)。2バイト目にCE(LDX#)を入れておけば、読み出しも同じサブルーチンで行える。

STS #も同じことができるが、LDS #の後にRTSをすると、STS #の呼び出し元に戻ってしまう。setjmp/longjmpだと思えば使えないことはない?

TNGはAccが0以外ならCが立つのが利用できそう。

いくつかの命令は後継CPUでも使える

Fuzix-Compiler-Kit の6800エミュレーターは、6800/6801/6802/6803/6808/6303の命令を実行できる。この中で6303のundocumentedsとして以下の命令が挙げられている。

いくつか共通する命令があり、有効に使えるかもしれない。


 *      02              if C is set A = 255, else A = 0 (no flag effect)
 *      03              A = 0xFF
 *      12              A = A - B - carry
 *      13              A = A - B - 1
 *      14              B = A - !C
 *      15/1D           A = B - 1
 *      18 or 1A        A += B but different C/H behaviour
 *      1E              B = A
 *      1F              A = B, set carry
 *      41              TSTA variant
 *      42/52/62/72     negate with carry (SBC from 0) on byte
 *                      42 A 52 B
 *      45              LSR A
 *      4B              DEC A
 *      51              TSTB variant
 *      55              LSR B
 *      5B              DEC B
 *      61              TST x variant
 *      62              negate x
 *      65              LSR x
 *      6B              DEC x
 *      71              TST M
 *      72              NEG M
 *      75              LSR m
 *      7B              DEC m
 *
 *      The NAMCO processors (60A1, 63A1) also add
 *      12/13           addx 1,sp


なぜこのような命令が実行されるのか

$83 (AccA = AccA – M – 1)のように、通常の加減算と1だけ値が違う演算がある。これはSN74181のようなALUを知っていると理解しやすい。

ALU(ARITHMETIC LOGIC UNITS)はCPU内部で各種演算を行うユニットで、外部から与える信号線によって演算の種類を変えられる。74181の場合はC信号を与えるかどうかで、加減算の結果に0/-1/+1が付く。通常はCには命令に応じて0や1やCarry flagの値を与えるのだけど、未定義命令の場合は命令の意図に合わないCが設定されたのだと思われる。



TNG/TSRなどフラグだけが変更される命令は、TSTやABITのような演算の結果を反映せずフラグだけが変化する命令操作が、LSR命令でも実行されたためだと思われる。

Visual 6800 in JavaScriptには、トランジスタレベルでの結線があり、ここから回路図を復元できればさらに多くのことがわかるだろう。しかし、これは私の手に余る。

参考資料