更新日: 2016 年 8 月 18 日
68060を斬る!!(第8回)
説明
MC68060 の解説記事です。月刊電脳倶楽部 117 号(1998 年 2 月号)から 139 号(1999 年 12 月号)にかけて読み物横町で不定期連載していました。転載にあたって内容の再考証は行っていません。ご了承下さい。
68060_08.txt
──────────────────────────────────── 68060を斬る!! 第8回 鎌田誠 ──────────────────────────────────── 久々の復活です。 今回は 68060 を斬るというよりもむしろ MOVEP を斬る感じ。 ━─────────────────────────────────── MOVEP エミュレーション ──────────────────────────────────── 68060 は 68000 の命令をほとんどすべて解釈することができます。しかし、 たった 1 つだけ、68000 の命令で 68060 に実装されていないものがあります。 それが MOVEP 命令です。MOVEP 命令は 68000〜68040 に実装されていますが、 使用頻度が低いためか、あるいは特殊な挙動をするので 68060 のコアでは実装 が困難だったためか、68060 では削除されてしまいました。その代わり、68060 で MOVEP 命令を含めた未実装命令をエミュレートするためのプログラム 060SP が、Motorola から無償で公開されています。 ● MOVEP 命令とは MOVEP(Move Peripheral(*1) Data)命令は、データレジスタ‐メモリ間の転 送命令です。MOVEP 命令の本来の目的は、8 ビットのデバイスを 68000 の 16 ビットのバスに接続した場合にデバイスのレジスタが 1 バイトおきになってし まうので、それを効率よくアクセスするために設けられた命令です。そのため、 MOVEP 命令はメモリを 1 バイトおきにアクセスするという特徴があります。 (*1) peripheral:「周辺装置の」という意味です。 ● MOVEP 命令の文法と動作 MOVEP 命令には、リード/ライトと、ワード/ロングワードの組み合わせで、4 つのパターンがあります。 (1) MOVEP.W (d16,An),Dn (2) MOVEP.L (d16,An),Dn (3) MOVEP.W Dn,(d16,An) (4) MOVEP.L Dn,(d16,An) なお、MOVEP 命令ではフラグ(コンディションコードレジスタ)は変化しませ ん。 続いて、個々のパターンについて解説します。(*2) (*2) 説明の中の ext(d16) というのは、16 ビットのディスプ レースメントを 32 ビットに符号拡張した値を意味しています。 (1) MOVEP.W (d16,An),Dn An+ext(d16) の 1 バイトを Dn.W の上位バイトに、An+ext(d16)+2 の 1 バイ トを Dn.W の下位バイトに、この順序でそれぞれ転送します。 MOVEP.W (d16,An),Dn の動作 アドレス -1 An+d16 +1 +2 +3 ┬──┬──┬──┬──┬──┬ メモリ │ │ H │ │ L │ │ ┴──┴──┴──┴──┴──┴ | | \ / ↓ ↓ ┌──┬──┬──┬──┐ Dnレジスタ │ │ │ H │ L │ └──┴──┴──┴──┘ 32 16 0 (2) MOVEP.L (d16,An),Dn An+ext(d16) の 1 バイトを Dn.L の最上位バイトに、An+ext(d16)+2 の 1 バ イトを Dn.L の上位ワードの下位バイトに、An+ext(d16)+4 の 1 バイトを Dn.L の下位ワードの上位バイトに、An+ext(d16)+6 の 1 バイトを Dn.L の最下位バ イトに、この順序でそれぞれ転送します。 MOVEP.L (d16,An),Dn の動作 アドレス -1 An+d16 +1 +2 +3 +4 +5 +6 +7 ┬──┬──┬──┬──┬──┬──┬──┬──┬──┬ メモリ │ │HH│ │HL│ │LH│ │LL│ │ ┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ | | | | \ \ / / \ | │ / \ | │ / \ | │ / ↓ ↓ ↓ ↓ ┌──┬──┬──┬──┐ Dnレジスタ │HH│HL│LH│LL│ └──┴──┴──┴──┘ 32 16 0 (3) MOVEP.W Dn,(d16,An) Dn.W の上位バイトを An+ext(d16) の 1 バイトに、Dn.W の下位バイトを An+ext(d16)+2 の 1 バイトに、この順序でそれぞれ転送します。このとき、 An+ext(d16)+1 の 1 バイトは変化しません。 MOVEP.W Dn,(d16,An) の動作 32 16 0 ┌──┬──┬──┬──┐ Dnレジスタ │ │ │ H │ L │ └──┴──┴──┴──┘ | | / \ ↓ ↓ ┬──┬──┬──┬──┬──┬ メモリ │ │ H │ │ L │ │ ┴──┴──┴──┴──┴──┴ アドレス -1 An+d16 +1 +2 +3 ↑ │ ここは変化しない (4) MOVEP.L Dn,(d16,An) Dn.L の最上位バイトを An+ext(d16) の 1 バイトに、Dn.L の上位ワードの下 位バイトを An+ext(d16)+2 の 1 バイトに、Dn.L の下位ワードの上位バイトを An+ext(d16)+4 の 1 バイトに、Dn.L の最下位バイトを An+ext(d16)+6 の 1 バ イトに、この順序でそれぞれ転送します。このとき、An+ext(d16)+1 の 1 バイ ト、An+ext(d16)+3 の 1 バイト、An+ext(d16)+5 の 1 バイトは変化しません。 MOVEP.L Dn,(d16,An) の動作 32 16 0 ┌──┬──┬──┬──┐ Dnレジスタ │HH│HL│LH│LL│ └──┴──┴──┴──┘ | | | | / / \ \ / │ | \ / │ | \ / │ | \ ↓ ↓ ↓ ↓ ┬──┬──┬──┬──┬──┬──┬──┬──┬──┬ メモリ │ │HH│ │HL│ │LH│ │LL│ │ ┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ アドレス -1 An+d16 +1 +2 +3 +4 +5 +6 +7 ↑ ↑ ↑ └─────┼─────┘ ここは変化しない ● MOVEP 命令のフォーマット MOVEP 命令は次のようなフォーマットの 2 ワードの命令です。 MOVEP 命令のフォーマット 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ┌─┬─┬─┬─┬─────┬─┬─┬─┬─┬─┬─┬─────┐ │ 0│ 0│ 0│ 0│ データNo │ 1│Dr│Sz│ 0│ 0│ 1│アドレスNo│ ├─┴─┴─┴─┴─────┴─┴─┴─┴─┴─┴─┴─────┤ │ 16ビットディスプレースメント │ └───────────────────────────────┘ データNo:データレジスタの番号 Dr:転送方向(0=メモリ→レジスタ,1=レジスタ→メモリ) Sz:サイズ(0=ワード,1=ロングワード) アドレスNo:アドレスレジスタの番号 ● MOVEP 命令の使われ方 前述のように、MOVEP 命令の本来の使い方は 8 ビットのデバイスのレジスタ をアクセスすることです。実際、X680x0 の IPL(*3) や BIOS(*4) でも MFP(*5) や SPC(*6) のアクセスに使用されています。ここでは、MOVEP 命令のその他の 使い方を紹介します。 (*3) IPL: Initial Program Loader (*4) BIOS: Basic Input/Output System/Subroutines (*5) MFP: Multi-Function Peripheral (*6) SPC: SCSI Protocol Controller ・256 色モードのグラフィック VRAM にベタ画像を転送する X680x0 のグラフィック VRAM は、モードに関係なく 1 ドット= 1 ワードに なっています。65536 色モードのときは 1 ワード(16 ビット)をすべて使って 1 ドット分の色を表しますが、256 色モードのときは 1 ワードの下位バイト (8 ビット)しか使いません。言い換えると、256 色モードのときは 1 ドット を表す 1 バイトのデータが 1 バイトおきに並んでいることになります。これは MOVEP 命令に適したデータの並び方です。(*7) (*7) 060turbo で 256 色のベタ画像を最も速く表示する方法 は、CRTC(*8) のメモリモードを操作して 2 枚のプレーンに同 時に書き込む方法です。ディスクなどからのデータの供給にか かる時間を無視すると、256×256 ドットで 60fps(*9) くらい 出せます。 (*8) CRTC: CRT Controller (*9) fps: Frames Per Second 256色モードのときのグラフィックVRAM(プレーン0) アドレス $C00000 $C00001 $C00002 $C00003 $C00004 $C00005 … ┌───┬───┬───┬───┬───┬───┬ データ │ │(0, 0)│ │(1, 0)│ │(2, 0)│ └───┴───┴───┴───┴───┴───┴ アドレス $C00400 $C00401 $C00402 $C00403 $C00404 $C00405 … ┌───┬───┬───┬───┬───┬───┬ データ │ │(0, 1)│ │(1, 1)│ │(2, 1)│ └───┴───┴───┴───┴───┴───┴ (a0)〜にある256色のベタ画像を(a1)〜のVRAMに転送する例 ┌──────────────────────────────── │ movem.l (a0)+,d0-d7 ;これで32ドット分 │ movep.l d0,(0+1,a1) │ movep.l d1,(8+1,a1) │ movep.l d2,(16+1,a1) │ movep.l d3,(24+1,a1) │ movep.l d4,(32+1,a1) │ movep.l d5,(40+1,a1) │ movep.l d6,(48+1,a1) │ movep.l d7,(56+1,a1) │ : ・16 ビットの PCM データのエンディアン(*10)を変換する 16 ビットのデータA,B,…がリトルエンディアン(*10)で並んでいるとき、こ れをビッグエンディアン(*10)に変換するには、あらかじめデータの手前を 1 バ イトを空けておいて、各データの上位のバイトを手前に 1 つずつずらすことを 繰り返せば済みます。これは MOVEP 命令に適した作業で、68000 ではローテー ト命令などを使うよりも MOVEP を使ったほうが高速にエンディアンを変換する ことができます。 (*10) エンディアン:数値をメモリに格納するときにメモリを 降順にアクセスするか昇順にアクセスするかを区別するための 言葉です。数値を一番上の位から順にメモリに書き込むとき、 最後に一番下の位を書き込むアドレスが最初に一番上の位を書 き込んだアドレスよりも大きくなる書き方をビッグエンディア ン、小さくなる書き方をリトルエンディアンと呼びます。俗に、 前者はモトローラ並び、後者はインテル並びと呼ばれます。 MOVEP 命令を利用したエンディアンの変換 ├→リトルエンディアンの入力データ ─┬───┬───┬───┬───┬───┬─ 変換前 │ 空き │ AL │ AH │ BL │ BH │ ─┴───┴───┴───┴───┴───┴─ │ │ │ │ └───┬───┘ │ / MOVEPで取り出す │ / │ / │ / │ / │ / │ / │ / MOVEPで書き込む │ ┌───┴───┐ │ ↓ ↓ ─┬───┬───┬───┬───┬───┬─ 変換後 │ AH │ AL │ BH │ BL │ │ ─┴───┴───┴───┴───┴───┴─ ├→ビッグエンディアンの出力データ ↑ ↑ └───┬───┘ │ ここは変化しない MOVEP 命令を利用したエンディアン変換ルーチンの例 ┌──────────────────────────────── │;エンディアンの変換(68000用) │; データのサイズは8の倍数にすること │;<a0.l:出力バッファの先頭(入力データの先頭-1) │;<a1.l:出力バッファの末尾(入力データの末尾-1) │;?d0-d1/a0 │ .text │ .even │convert_endian_68000:: │ move.l a1,d0 │ sub.l a0,d0 ;データの長さ │ beq 3f ;データがない(念のため) │ subq.l #1,d0 │ and.b #$F8,d0 │ move.l d0,d1 │ clr.b d1 │ sub.w d1,d0 │ lsr.l #8,d1 │ eori.b #$F8,d0 │ suba.w d0,a0 │ jmp (2f,pc,d0.w) │1: swap.w d1 │2: │n=0 │ .rept 32 │ movep.l (n+2,a0),d0 ;24 │ movep.l d0,(n,a0) ;24 │ ;1ワードあたり12クロック │n=n+8 │ .endm │ lea.l (256,a0),a0 │ dbra d1,2b │ swap.w d1 │ dbra d1,1b │3: rts ● MOVEP 命令の展開 アセンブラ HAS060.X は、-cmovep[=6] というスイッチを指定することで MOVEP 命令をアセンブル時に他の命令の並びに展開することができます。MOVEP 命令をアセンブル時に展開しておくと、68060 で実行する場合の動作速度が劇的 に高速化されます。ただし、68060 以外で実行する場合はかえって遅くなってし まいます。 次の表は HAS060.X で -cmovep[=6] を指定した場合の MOVEP 命令の展開の基 本形です(HAS060.X のマニュアルより)。実際にはアセンブル時の幾つかの要 因によって展開後のコードが微妙に変化します。詳細は HAS060.X のマニュアル を参照して下さい(*11)。 (*11) HAS060.X の電脳倶楽部に掲載された中での最新版は、 134 号の HAS060.X v3.09+85 です。 MOVEP 命令の展開(基本形) ┏━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━┓ ┃ 展開前 │ 展開後 ┃ ┠───────────────┼───────────────┨ ┃ MOVEP.W (d,An),Dn │ MOVE.W CCR,-(SP) ┃ ┃ │ MOVE.B (d,An),Dn ┃ ┃ │ LSL.W #8,Dn ┃ ┃ │ MOVE.B (d+2,An),Dn ┃ ┃ │ MOVE.W (SP)+,CCR ┃ ┠───────────────┼───────────────┨ ┃ MOVEP.L (d,An),Dn │ MOVE.W CCR,-(SP) ┃ ┃ │ MOVE.B (d,An),Dn ┃ ┃ │ LSL.W #8,Dn ┃ ┃ │ MOVE.B (d+2,An),Dn ┃ ┃ │ SWAP.W Dn ┃ ┃ │ MOVE.B (d+4,An),Dn ┃ ┃ │ LSL.W #8,Dn ┃ ┃ │ MOVE.B (d+6,An),Dn ┃ ┃ │ MOVE.W (SP)+,CCR ┃ ┠───────────────┼───────────────┨ ┃ MOVEP.W Dn,(d,An) │ MOVE.W CCR,-(SP) ┃ ┃ │ ROL.W #8,Dn ┃ ┃ │ MOVE.B Dn,(d,An) ┃ ┃ │ ROL.W #8,Dn ┃ ┃ │ MOVE.B Dn,(d+2,An) ┃ ┃ │ MOVE.W (SP)+,CCR ┃ ┠───────────────┼───────────────┨ ┃ MOVEP.L Dn,(d,An) │ MOVE.W CCR,-(SP) ┃ ┃ │ ROL.L #8,Dn ┃ ┃ │ MOVE.B Dn,(d,An) ┃ ┃ │ ROL.L #8,Dn ┃ ┃ │ MOVE.B Dn,(d+2,An) ┃ ┃ │ ROL.L #8,Dn ┃ ┃ │ MOVE.B Dn,(d+4,An) ┃ ┃ │ ROL.L #8,Dn ┃ ┃ │ MOVE.B Dn,(d+6,An) ┃ ┃ │ MOVE.W (SP)+,CCR ┃ ┗━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┛ ● MOVEP 命令のエミュレーション MOVEP 命令に限らず、68060 の未実装命令はすべて Motorola の 060SP (MC68060 ソフトウェアパッケージ)のソフトウェアエミュレーションルーチン でカバーされています。しかし、060turbo の場合は 68060 を使用しているにも かかわらず 68000 用に作られたプログラムを実行する機会が極端に多いという 特別な事情があるため、MOVEP 命令の使用頻度が高く、高速な MOVEP エミュレ ーションが要求されます。また、IPL のように 060SP を組み込むことができな い段階で MOVEP 命令が使えるようにするには、自前で MOVEP 命令のエミュレー ションを行わなければなりません。 MOVEP 命令はアドレッシングモードが固定されているので、エミュレーション は比較的簡単です。未実装整数命令例外ベクタに MOVEP 命令のオペコードに対 応したエミュレーションルーチンを設定するだけです。命令の解釈よりも指定さ れた番号のレジスタをアクセスするほうが面倒なくらいです。 MOVEP エミュレーションのための未実装整数命令例外ハンドラの処理の流れ (1) 例外スタックフレームからプログラムカウンタを取り出す (2) 例外を発生させた命令の命令コードを取り出す (3) MOVEP 命令でなければもとのベクタへジャンプする (4) アドレスレジスタの内容を取り出す(A7 は USP/SSP のいずれか) (5) ディスプレースメントを取り出してアドレスに加える (6) 復帰アドレスを更新しておく (7) 転送方向(read/write)で分岐する (8) オペレーションサイズ(word/long word)で分岐する (9) 1 バイトずつ転送する (10) MOVEP 命令の直後に復帰する 厳密にエミュレートするなら、「転送中にアクセスエラーが発生した場合に正 確なアクセスエラー例外スタックフレームを生成する」という面倒な作業が必要 になりますが、普通はそこまで考える必要はないでしょう(*12)。MOVEP 命令で バスエラーが出るかどうかで拡張ボードの有無を判断するようなプログラムがあ るとは思えませんから。 (*12) 当然のことですが、Motorola の 060SP はアクセスエラ ーのときの挙動までエミュレートしています。 ● MOVEP エミュレーションの所要時間 68060 のオンチップ FPU(*13) による浮動小数点演算が 68030 用の FPCP(*14) である 68882 よりも速いことが知られていますが、残念ながら MOVEP 命令に関しては、68060 でエミュレーションするよりも 68030 で実行し たほうが速くなっています。 060SP による MOVEP エミュレーションでは、50MHz の 68060 でも 25MHz の 68030 で MOVEP 命令を実行した場合の 10 倍以上の時間がかかってしまいます。 前述の通り、060turbo では MOVEP エミュレーションを高速化してありますが、 それでも 68030 の 3 倍以上、68000 と同じくらいの時間がかかるのです(*15)。 当然、他の命令は 68030 よりも 68060 のほうが圧倒的に速いので、平均すれば 68030 よりも 68060 のほうが速いことには違いありませんが、MOVEP 命令ばか りを大量に使用しているプログラムを 68060 で動かすと 68030 よりも遅くなっ てしまうことがあります。 (*13) FPU: Floating Point Unit (*14) FPCP: Floating Point Co-Processor (*15) 060turbo の MOVEP エミュレーションコードには、まだ 最適化の余地が残されています。 MOVEP 命令の所要時間の比較 ┏━━━━━━┯━━━━┯━━━━━┯━━━━━┯━━━━━┓ ┃ MPU (clock)│ 備考 │クロック数│ 時間(μs)│ 倍率 ┃ ┠──────┼────┼─────┼─────┼─────┨ ┃68000(10MHz)│ │ 16〜24 │ 1.6〜2.4 │ 1.0 ┃ ┃68030(25MHz)│ │ 10〜14 │ 0.4〜0.56│ 4.0〜4.29┃ ┃68030(50MHz)│ │ 10〜14 │ 0.2〜0.28│ 8.0〜8.57┃ ┃68040(25MHz)│040turbo│ 10〜13 │ 0.4〜0.52│ 4.0〜4.62┃ ┃68060(50MHz)│060SP │ 265〜350 │ 5.3〜7.0 │0.30〜0.34┃ ┃68060(50MHz)│060turbo│ 105〜115 │ 2.1〜2.3 │0.76〜1.04┃ ┃68060(50MHz)│HAS060.X│ 5〜9 │0.10〜0.18│16.0〜13.3┃ ┗━━━━━━┷━━━━┷━━━━━┷━━━━━┷━━━━━┛ クロック数と時間は,68000〜68040は理論値,68060は実測値です. ● MOVEP エミュレーションコード 060turbo の ROM の中では MOVEP 命令は使われていませんが、ディスクをア クセスするための BIOS や起動ドライブの IPL あるいは SRAM のコードの中で MOVEP 命令が使われていた場合のために、リセットの直後(IOCS コールが使え るようになるよりも前)から MOVEP 命令をエミュレートできるようにしてあり ます。 次のコードは、060turbo の ROM の中にある MOVEP 命令のエミュレーション ルーチンのソースコードです。MOVEP エミュレーションコードのサンプルとして 引用しました。非常にシンプルでわかりやすいコードになっているので、アセン ブラのわかる人はプログラムの流れを手で辿ってみて下さい。 ┌──────────────────────────────── 〜 1│;未実装整数命令例外ベクタ設定 2│ lea.l uii(pc),a1 ;未実装整数命令例外処理ルーチン(MOVEPのみ対応) 3│ move.l a1,$00F4.w ;未実装整数命令例外ベクタ 〜 4│;---------------------------------------------------------------- 5│;MOVEP以外の未実装整数命令 6│uiiUnknown: 7│ movem.l (sp)+,d0-d1 8│ lea.l (4*(8-2),sp),sp 9│ movea.l (sp)+,a0 10│ lea.l (4*(8-1),sp),sp 11│ bra unknownInterrupt 12│ 13│;MOVEPでユーザモードのとき 14│uiiMovepUser: 15│ move.l usp,a1 ;例外発生時のusp 16│ move.l a1,(4*15,sp) ;例外発生時のsp 17│ bra uiiMovepSuper 18│ 19│;---------------------------------------------------------------- 20│;未実装整数命令例外処理ルーチン(MOVEPのみ対応) 21│; MOVEP.{WL} Dx,(d16,Ay) 22│; MOVEP.{WL} (d16,Ay),Dx 23│uii: 24│ pea.l (8,sp) ;例外発生時のssp 25│ movem.l d0-d7/a0-a6,-(sp) 26│ movea.l (4*16+2,sp),a0 ;例外発生時のpc 27│;<a0.l:例外発生時のpc 28│ move.w (a0)+,d1 ;命令コード 29│;<d1.w:例外発生時の(pc).w 30│;MOVEPか 31│ move.w d1,d0 32│ and.w #$F138,d0 33│ cmp.w #$0108,d0 34│ bne uiiUnknown 35│;---------------------------------------------------------------- 36│;MOVEP命令 37│; MOVEP.{WL} Dx,(d16,Ay) 38│; MOVEP.{WL} (d16,Ay),Dx 39│; ファンクションコードのチェック/変更を一切行っていません 40│; 転送中のアクセスエラーを考慮していません 41│; 次のような異常なメモリ操作は正しくエミュレートできません 42│; MOVEP.W Dx,(d16,SSP) (-$004A<=d16<=-$0001) 43│; MOVEP.L Dx,(d16,SSP) (-$004E<=d16<=-$0001) 44│;<d1.w:例外発生時の(pc).w 45│; 0000ddd1ws001aaa 46│; ddd data register 47│; w read/write(0=read,1=write) 48│; s size(0=word,1=long) 49│; aaa address register 50│;<a0.l:例外発生時のpc+2 51│;<sp.l:スタックに例外発生時のレジスタの内容を保持 52│; 0000 .l d0 53│; 0004 .l d1 54│; 0008 .l d2 55│; 000C .l d3 56│; 0010 .l d4 57│; 0014 .l d5 58│; 0018 .l d6 59│; 001C .l d7 60│; 0020 .l a0 61│; 0024 .l a1 62│; 0028 .l a2 63│; 002C .l a3 64│; 0030 .l a4 65│; 0034 .l a5 66│; 0038 .l a6 67│; 003C .l ssp 68│; 0040 .w sr 69│; 0042 .l pc pcは次の命令のアドレスに更新すること 70│; 0046 .w ベクタオフセット 71│; 0048 sspの位置 72│uiiMovep: 73│ btst.b #5,(4*16,sp) ;例外発生時のsrのS 74│ beq uiiMovepUser ;スーパーバイザモードを優先 75│uiiMovepSuper: 76│;<sp.l:スタックに例外発生時のレジスタの内容を保持 77│; 0000 .l d0 78│; 0004 .l d1 79│; 0008 .l d2 80│; 000C .l d3 81│; 0010 .l d4 82│; 0014 .l d5 83│; 0018 .l d6 84│; 001C .l d7 85│; 0020 .l a0 86│; 0024 .l a1 87│; 0028 .l a2 88│; 002C .l a3 89│; 0030 .l a4 90│; 0034 .l a5 91│; 0038 .l a6 92│; 003C .l uspまたはssp 93│; 0040 .w sr 94│; 0042 .l pc pcは次の命令のアドレスに更新すること 95│; 0046 .w ベクタオフセット 96│; 0048 sspの位置 97│;実効アドレスを求める 98│ moveq.l #7,d0 ;0000000000000111 99│ and.w d1,d0 ;0000000000000aaa 100│ movea.l (4*8,sp,d0.w*4),a1 ;アドレスレジスタの内容 101│ adda.w (a0)+,a1 ;ディスプレースメントを加える 102│;<a0.l:例外発生時のpc+4 103│;<a1.l:実効アドレス 104│;復帰アドレスを更新する 105│ move.l a0,(4*16+2,sp) ;復帰アドレスを更新 106│;データレジスタのアドレスを求める 107│ move.w d1,d0 ;0000ddd1ws001aaa 108│ lsr.w #8,d0 ;000000000000ddd1 109│ lea.l (-2,sp,d0.w*2),a0 ;00000000000ddd10 110│ ;00000000000ddd00 111│ ;データレジスタのアドレス 112│;<a0.l:データレジスタのアドレス 113│;オペレーションモードに従って転送する 114│ add.b d1,d1 115│ bcc uiiMovepRead ;writeを優先 116│;レジスタ→メモリ 117│; MOVEP.{WL} Dx,(d16,Ay) 118│uiiMovepWrite: 119│ bpl uiiMovepWriteWord ;ロングワードを優先 120│;レジスタ→メモリ,ロングワード 121│; MOVEP.L Dx,(d16,Ay) 122│;<a0.l:データレジスタのアドレス 123│;<a1.l:実効アドレス 124│uiiMovepWriteLong: 125│ move.b (a0)+,(a1) 126│ move.b (a0)+,(2,a1) 127│ move.b (a0)+,(4,a1) 128│ move.b (a0),(6,a1) 129│ movem.l (sp)+,d0-d1 130│ lea.l (4*(8-2),sp),sp 131│ movem.l (sp)+,a0-a1 132│ lea.l (4*(8-2),sp),sp 133│ rte 134│ 135│;レジスタ→メモリ,ワード 136│; MOVEP.W Dx,(d16,Ay) 137│;<a0.l:データレジスタのアドレス 138│;<a1.l:実効アドレス 139│uiiMovepWriteWord: 140│ addq.w #2,a0 141│ move.b (a0)+,(a1) 142│ move.b (a0),(2,a1) 143│ movem.l (sp)+,d0-d1 144│ lea.l (4*(8-2),sp),sp 145│ movem.l (sp)+,a0-a1 146│ lea.l (4*(8-2),sp),sp 147│ rte 148│ 149│;メモリ→レジスタ 150│; MOVEP.{WL} (d16,Ay),Dx 151│uiiMovepRead: 152│ bpl uiiMovepReadWord ;ロングワードを優先 153│;メモリ→レジスタ,ロングワード 154│; MOVEP.L (d16,Ay),Dx 155│;<a0.l:データレジスタのアドレス 156│;<a1.l:実効アドレス 157│uiiMovepReadLong: 158│ move.b (a1),(a0)+ 159│ move.b (2,a1),(a0)+ 160│ move.b (4,a1),(a0)+ 161│ move.b (6,a1),(a0) 162│ movem.l (sp)+,d0-d7/a0-a1 163│ lea.l (4*(8-2),sp),sp 164│ rte 165│ 166│;メモリ→レジスタ,ワード 167│; MOVEP.W (d16,Ay),Dx 168│;<a0.l:データレジスタのアドレス 169│;<a1.l:実効アドレス 170│uiiMovepReadWord: 171│ addq.w #2,a0 172│ move.b (a1),(a0)+ 173│ move.b (2,a1),(a0) 174│ movem.l (sp)+,d0-d7/a0-a1 175│ lea.l (4*(8-2),sp),sp 176│ rte 〜 177│;---------------------------------------------------------------- 178│;未定義例外処理 〜 179│unknownInterrupt: 〜 上のソースは私が 060turbo の ROM の準備をしていた 1996 年に作成したも ので、060turbo のα版から製品版までまったく変更していません。copy&paste で持ってきたので引用した範囲は注釈を含めて当時のままなのですが、74 行目 の「スーパーバイザモードを優先」という注釈は明らかに間違っていますね。当 時(060turbo の開発中だった頃)、私はまだ 68060 の分岐命令の所要時間を正 しく把握していなくて「68060 の分岐命令は分岐しないほうが速い」と思い込ん でいたのかも知れません。本当は後方分岐では分岐するほうが速い(*16)ので、 このプログラムではユーザモードのほうが優先されてしまっています。また、破 壊したレジスタを復元する部分のコードも冗長で無駄がありますね。今見るとち ょっと恥ずかしいです。なお、060turbo.sys が組み込まれた後は 060turbo.sys の中にある MOVEP エミュレーションルーチンが使用されるので、実際には ROM の中のエミュレーションコードはほとんど使用されていません。 (*16) 123 号の資料室『68060 の命令の実行時間』を参照。 ━─────────────────────────────────── 目次 ──────────────────────────────────── 第1回(月刊 117 号) <本編> はじめに/パイプラインとは/パイプラインの利点/パイプラインの欠 点/分岐予測/分岐キャッシュを用いた分岐予測/68060 のパイプライ ン/68060 の命令フェッチパイプライン/68060 における分岐予測/ 68060 の命令実行パイプライン <68060 関連の話題> 060turbo における SIMM の効果/Motorola のホームページを覗いてみ よう/エラッタ/マスクセットの見分けかた/HAS060.X/060pcr5.x ━─────────────────────────────────── 第2回(月刊 119 号) <本編> スーパースカラ(superscalar)/ディスパッチテスト(1)/ディス パッチテスト(2)/ディスパッチテスト(3)/ディスパッチテスト (4)/ディスパッチテスト(5)/ディスパッチテスト(6) <68060 関連の話題> XL/Image/060turbo が少し速くなったゾ/mpeg2play.x <68060 の命令のスーパースカラ分類表> スーパースカラ分類の意味/整数命令/特権命令/浮動小数点命令/参 考文献 ━─────────────────────────────────── 第3回(月刊 120 号) <本編> 例外処理/割り込み/例外ハンドラ/例外処理の流れ/ダブルバスフォ ルト/例外スタックフレーム/例外ベクタテーブル <質問箱> データキャッシュが効かない場合のメモリアクセスのクロック数/デー タキャッシュの有効な利用方法/SharpenHimemPatch の副作用? <68060 関連の話題> 060turbo.sys v0.54/060turbo の ROM の不具合 ━─────────────────────────────────── 第4回(月刊 122 号) <本編> 例外スタックフレームの構造/アクセスフォルト ━─────────────────────────────────── 第5回(月刊 124 号) <本編> 分岐予測エラー/アドレスエラー/命令による例外 ━─────────────────────────────────── 第6回(月刊 125 号) <本編> 未実装命令例外 ━─────────────────────────────────── 第7回(月刊 129 号) <本編> MMU ━─────────────────────────────────── 第8回(月刊 139 号) <本編> MOVEP エミュレーション ──────────────────────────────────── (EOF)