--- nono/exp/fmovemn.php 2026/04/29 17:05:28 1.1.1.3 +++ nono/exp/fmovemn.php 2026/04/29 17:05:36 1.1.1.4 @@ -29,309 +29,202 @@ .xref hexstr_long .include doscall.mac + .include iocscall.mac .list .cpu 68030 PRINT .macro msg - pea.l msg(pc) + pea.l msg DOS _PRINT addq.l #4,sp .endm -SET_FLINE_TRAP .macro - move.l (a5),a4 - lea.l fline_handler(pc),a1 - move.l a1,(a5) - .endm - -RESTORE_FLINE_TRAP .macro - move.l a4,(a5) - .endm - .text .even -start: - bra start2 ".......... !!m+!rxwp!", - 1/*toFP, ctrl*/ => "..m+.rxwp. !!m+!rxwp!", - 2/*fromFP, pred*/ => "....-..... !!m!-rxw!!", - 3/*fromFP, ctrl*/ => "..m..rxw.. !!m!-rxw!!", + // dir ,mode1 easy hard + 0 /* toFP, pred */ => ".......... !!m+!rxwp!", + 2 /* toFP, ctrl */ => "..m+.rxwp. !!m+!rxwp!", + 4 /* fromFP, pred */ => "....-..... !!m!-rxw!!", + 6 /* fromFP, ctrl */ => "..m..rxw.. !!m!-rxw!!", ); + $testlist = array(); + $testnum = 0; foreach ($srctable as $mode21 => $eastr) { - list($easy_str, $hard_str) = preg_split('/\s+/', $eastr); + list ($easy_str, $hard_str) = preg_split('/\s+/', $eastr); $easy_ea = preg_split("//", $easy_str, -1, PREG_SPLIT_NO_EMPTY); $hard_ea = preg_split("//", $hard_str, -1, PREG_SPLIT_NO_EMPTY); + // mode0 ビットは dynamic or static。 for ($mode0 = 0; $mode0 <= 1; $mode0++) { - $mode = $mode21 * 2 + $mode0; - // …のはずだが rxwpi の検査は - // 追加の命令ワード部分を生バイナリで用意しないといけないため - // ちょっと保留。 - for ($id = 0; $id < 5; $id++) { - $ee = $easy_ea[$id]; - $eh = $hard_ea[$id]; - $is_easy = ($ee != '.'); // easy でも検査するか? - if ($eh == "!") { - // 許されない組み合わせの命令。 - subr_fail($mode, $id, $is_easy); - } else { - if ($mode < 4) { - subr_to($mode, $id, $is_easy); - } else { - subr_from($mode, $id, $is_easy); - } + $mode = $mode21 + $mode0; + for ($i = 0; $i < 10; $i++) { + $eatype = substr("dam+-rxwpi", $i, 1); + $ee = $easy_ea[$i]; + $eh = $hard_ea[$i]; + + // 難しいので保留。 + if ($i >= 7) { + continue; } + + // 命令語。 + $inst = 0xf200c060; + $inst |= eatype2word($eatype) << 16; + $inst |= $mode << 11; + + $test = array( + "num" => $testnum++, + "mode" => $mode, + "mem_to_fp" => (($mode & 4) == 0), + "is_pred" => (($mode & 2) == 0), + "dynamic" => (($mode & 1) != 0), + + "eatype" => $eatype, + "is_easy" => ($ee != '.'), + "is_trap" => ($eh == '!'), + "inst" => $inst, + ); + $testlist[] = $test; } } } ?> -,d6 ; Init D6 for dynamic + moveq.l #0,d4 ; Init D4 for d8(An,IX) + fmove.l d4,fpcr ; Init FPCR, especially round mode and round prec. -// $mode 番号からターゲットレジスタに。 -// Ctrl/PostInc と PreDec とで転送する順番が違う。 -// static と dynamic はなんとなく別のマスクを用意しておく。 -function mode2fplist($mode) -{ - $fplist = array( - 0/*pred, static */ => array(6, 5), - 1/*pred, dynamic */ => array(5, 4), - 2/*ctrl, static */ => array(1, 2), - 3/*ctrl, dynamic */ => array(2, 3), - ); - return $fplist[$mode & 3]; -} + ; rough argument check + cmpi.b #4,(a2)+ ; strlen(arg)<4 ? + bcs easymode + cmpi.l #$68617264,(a2) ; "hard" + bne easymode -// EA 番号から命令ワードに。n はすべて0 -function eanum2word($id) -{ - switch ($id) { - case 0: // d: D0 - return 000; - case 1: // a: A0 - return 010; - case 2: // m: (A0) - return 020; - case 3: // +: (A0)+ - return 030; - case 4: // -: -(A0) - return 040; - case 5: // r: d16(An) - return 050; - case 6: // x: d8(An,IX) - return 060; - case 7: // w: Abs - return 070; - case 8: // p: (PC,IX) - return 072; - case 9: // i: #imm - return 074; +hardmode: + moveq.l #-1,d5 + PRINT msg_hard(pc) + "dn", - 1 => "an", - 2 => "anin", - 3 => "anpi", - 4 => "anpd", - 5 => "anid", - 6 => "anix", - 7 => "abs", - 8 => "pcix", - 9 => "imm", - ); - return $idstr[$id]; -} +?> + bsr test_nofpn + bsr test_illdyn + bra done -// EA 番号をニーモニックに。 -function eanum2mnem($id, $n = 0) -{ - switch ($id) { - case 0: return "d{$n}"; - case 1: return "a{$n}"; - case 2: return "(a{$n})"; - case 3: return "(a{$n})+"; - case 4: return "-(a{$n})"; - case 5: return "d16(a{$n})"; - case 6: return "d8(a{$n},ix)"; - case 7: return "abs"; - case 8: return "(pc,ix)"; - case 9: return "#imm"; +easymode: + moveq.l #0,d5 + PRINT msg_easy(pc) + - $text = "fmovem.l "; - switch ($mode) { - case 0: - case 2: - $text .= "{$cpureg},fplist"; - break; - case 1: - case 3: - $text .= "{$cpureg},d6"; - break; - case 4: - case 6: - $text .= "fplist,{$cpureg}"; - break; - case 5: - case 7: - $text .= "d6,{$cpureg}"; - break; - case "nofpn": - $text .= "nofpn,{$cpureg}"; - break; - case "illegal": - // ニーモニックでは伝わらない… - $text .= "{$cpureg},d6_illegal"; - break; - } - - $funclist[$name] = $text; - if ($is_easy) { - $easylist[$name] = true; - } - out("test_{$name}:"); - out(" PRINT msg_{$name}"); -} +done: + movem.l (sp)+,d4-d7/a2-a4 + DOS _EXIT -// 結果表示のサブルーチンコールを出力 -function out_result($msg, $exp, $act) -{ - out(" pea.l {$msg}(pc)"); - out(" move.l {$exp},-(sp)"); - out(" move.l {$act},-(sp)"); - out(" bsr result"); - out(" lea.l 12(sp),sp"); -} + + - - lea.l initdata(pc),a0 -) + $pre = "lea.l {$label}(pc),a0"; + $post = ".dc.w $4000"; + break; + case 'w': // Abs、ここでは Abs.L のみ + $post = ".dc.l {$label}"; + break; + case 'p': // d8(PC,IX) + // これは難しいので保留。 + break; + default: + break; + } + } + if (strlen($pre) > 0) { + out(" {$pre}"); + } + out(" bsr set_fline"); + out(" moveq.l #0,d7"); + out(" lea.l @f,a3"); + out(" .dc.l $%08x", $test["inst"]); + if (strlen($post) > 0) { + out(" {$post}"); + } + out("@@:"); + out(" bsr restore_fline"); +} + +// ターゲットレジスタを返す。 +// 制御モードとプリデクリメントとでビットの割り当てが違う。 +// スタティックと動的ではあえて違うビットを割り当てている。 +function get_fplist($test) +{ + $fplist = array( + 0 /*pred, static */ => array(6, 5), + 1 /*pred, dynamic */ => array(5, 4), + 2 /*ctrl, static */ => array(1, 2), + 3 /*ctrl, dynamic */ => array(2, 3), + ); + $mode = $test["mode"]; + return $fplist[$mode & 3]; +} + +// EA 種別文字から命令ワードに。n はすべて0 +function eatype2word($eatype) +{ + switch ($eatype) { + case 'd': return 000; // D0 + case 'a': return 010; // A0 + case 'm': return 020; // (A0) + case '+': return 030; // (A0)+ + case '-': return 040; // -(A0) + case 'r': return 050; // d16(An) + case 'x': return 060; // d8(An,IX) + case 'w': return 071; // Abs.L + case 'p': return 072; // (PC,IX) + case 'i': return 074; // #imm + } + err("unknown eatype '{$eatype}'"); +} + +// EA 種別文字からニーモニックに。 +function eatype2mnem($eatype, $n = 0) +{ + switch ($eatype) { + case 'd': return "d{$n}"; + case 'a': return "a{$n}"; + case 'm': return "(a{$n})"; + case '+': return "(a{$n})+"; + case '-': return "-(a{$n})"; + case 'r': return "d16(a{$n})"; + case 'x': return "d8(a{$n},ix)"; + case 'w': return "abs"; + case 'p': return "(pc,ix)"; + case 'i': return "#imm"; } + return ""; +} ?> - SET_FLINE_TRAP + +; 空リストは正しく0個転送という扱いになる。 +; たぶん dynamic でも同様だろうけどもうテストしない。 +test_nofpn: + PRINT msg_test_nofpn(pc) + bsr init_from + ; 実行 + lea.l srcdata(pc),a0 + bsr set_fline moveq.l #0,d7 - lea.l workarea(pc),a0 - move.l d7,(a0) - move.l d7,4(a0) - move.l d7,8(a0) lea.l @f,a3 - .dc.w $f210, $f000 ; fmovem nofpn,(a0) + .dc.l $f218d000 ; fmovem.x (a0)+,nofpn @@: - RESTORE_FLINE_TRAP + bsr restore_fline + ; 例外は起きないはずなので D7 がゼロなら成功(次へ) tst.l d7 beq @f - PRINT msg_fail_trap + PRINT msg_fail_trap(pc) rts @@: - pea.l msg_fail_1st(pc) - pea.l zerodata(pc) - pea.l (a0) - bsr check_ext + ; An を照合する。(An)+ だけど変わらないはず。 + lea.l srcdata(pc),a1 + cmp.l a1,a0 + beq @f + pea.l msg_fail_addr(pc) + move.l a1,-(sp) + move.l a0,-(sp) + bsr result lea.l 12(sp),sp +@@: + lea.l workarea(pc),a2 + tst.l d7 bne @f - PRINT msg_ok + PRINT msg_ok(pc) @@: rts - - lea.l initdata(pc),a0 - - SET_FLINE_TRAP +; dynamic レジスタ指定の 0rrr_0000 の %0 のところに何か書かれてても無視。 +test_illdyn: + PRINT msg_test_illdyn(pc) + bsr init_from + ; 実行 lea.l srcdata(pc),a0 moveq.l #$80,d6 moveq.l #0,d7 lea.l @f,a3 - .dc.w $f210, $d86f ; fmovem (a0),d6 + .dc.l $f210d86f ; fmovem.x (a0),d6 @@: - RESTORE_FLINE_TRAP - + bsr restore_fline + ; 例外は起きないはずなので D7 がゼロなら成功(次へ) tst.l d7 beq @f - PRINT msg_fail_trap + PRINT msg_fail_trap(pc) rts @@: - lea.l workarea(pc),a0 - fmovem.x fp0,(a0) - pea.l msg_fail_fp0(pc) - pea.l srcdata(pc) - pea.l (a0) - bsr check_ext + ; An を照合する。 + lea.l srcdata(pc),a1 + cmp.l a1,a0 + beq @f + pea.l msg_fail_addr(pc) + move.l a1,-(sp) + move.l a0,-(sp) + bsr result lea.l 12(sp),sp - - tst.l d7 - bne @f - PRINT msg_ok @@: - rts - + lea.l workarea(pc),a2 -start2: - clr.l -(sp) - DOS _SUPER - movem.l d6-d7/a2-a5,-(sp) - moveq.l #$2c,d0 ; alternate of "lea.l $2c,a5" - move.l d0,a5 ; - moveq.l #0,d0 - fmove.l d0,fpcr ; Init FPCR, especially round mode and round prec. - ; rough argument check - ;moveq.l #0,d0 ; d0 is already cleared. - cmpi.b #4,(a2)+ ; strlen(arg)<4 ? - bcs @f - cmpi.l #$68617264,(a2) ; "hard" - seq.b d0 + tst.l d7 + bne @f + PRINT msg_ok(pc) @@: - tst.b d0 - bne hardmode -easymode: - PRINT msg_easy - $dummy) { - out(" bsr test_{$name}"); - } -?> - bra done + rts -hardmode: - PRINT msg_hard +; test_from 用の初期化。 +; FP0..7 を初期値で埋めるのと、書き込み先バッファを用意。 +; a0/a1 は破壊する。 +init_from: + lea.l initdata(pc),a0 $text) { - out(" bsr test_{$name}"); + for ($i = 0; $i < 8; $i++) { + out(" fmove.x (a0)+,fp{$i}"); } ?> -done: - movem.l (sp)+,d6-d7/a2-a5 - DOS _EXIT + ; 書き込み先を分かりやすいデータで初期化。 + lea.l initdata_from(pc),a0 + lea.l workarea(pc),a1 + move.l (a0)+,(a1)+ + move.l (a0)+,(a1)+ + move.l (a0),(a1)+ + subq.l #8,a0 + move.l (a0)+,(a1)+ + move.l (a0)+,(a1)+ + move.l (a0),(a1)+ + rts ; 結果表示 ; スタックには、メッセージ、期待値、実際の値の順で積む。 @@ -598,14 +589,14 @@ result: move.l 8+4(a6),d0 lea.l buf(pc),a0 bsr hexstr_long - PRINT buf - PRINT msg_but + PRINT buf(pc) + PRINT msg_but(pc) move.l 8+0(a6),d0 lea.l buf(pc),a0 bsr hexstr_long - PRINT buf - PRINT msg_crlf + PRINT buf(pc) + PRINT msg_crlf(pc) addq.l #1,d7 ; errcnt movem.l (sp)+,d0/a0 @@ -631,6 +622,12 @@ check_ext: bra check_ext_done @@: ; not equal + ; テスト名を表示した行に続けてこれを表示すると折り返してしまうので、 + ; これが1件目のエラーなら先に一つ改行する。 + tst.l d7 + bne @f + PRINT msg_crlf(pc) +@@: move.l 8+8(a6),-(sp) DOS _PRINT addq.l #4,sp @@ -645,8 +642,8 @@ check_ext: move.b #'_',(a0)+ move.l (a1),d0 bsr hexstr_long - PRINT buf - PRINT msg_but + PRINT buf(pc) + PRINT msg_but(pc) lea.l buf(pc),a0 move.l 8+0(a6),a2 @@ -658,8 +655,8 @@ check_ext: move.b #'_',(a0)+ move.l (a2),d0 bsr hexstr_long - PRINT buf - PRINT msg_crlf + PRINT buf(pc) + PRINT msg_crlf(pc) addq.l #1,d7 ; errcnt check_ext_done: @@ -667,6 +664,28 @@ check_ext_done: unlk a6 rts +; Fライン例外をテスト用のものに差し替える。 +; 変更前のアドレスを a4 に格納する。 +; a1 は破壊する。 +set_fline: + movem.l d0-d1,-(sp) + moveq.l #$b,d1 + lea.l fline_handler(pc),a1 + IOCS _B_INTVCS + movea.l d0,a4 + movem.l (sp)+,d0-d1 + rts + +; Fライン例外を元に戻す。 +; a1 は破壊する。 +restore_fline: + movem.l d0-d1,-(sp) + moveq.l #$b,d1 + movea.l a4,a1 + IOCS _B_INTVCS + movem.l (sp)+,d0-d1 + rts + ; Fライン例外ハンドラ ; a3 に戻りアドレスをセットしてあること ; d7 を 1 にして帰る @@ -677,28 +696,30 @@ fline_handler: ; FPn を埋める初期値 initdata: - .dc.l $0, $0, $8 - .dc.l $0, $0, $1 - .dc.l $0, $0, $2 - .dc.l $0, $0, $3 - .dc.l $0, $0, $4 - .dc.l $0, $0, $5 - .dc.l $0, $0, $6 - .dc.l $0, $0, $7 + .dc.l $00010000, $80888888, $88888888 + .dc.l $00010000, $80111111, $11111111 + .dc.l $00010000, $80222222, $22222222 + .dc.l $00010000, $80333333, $33333333 + .dc.l $00010000, $80444444, $44444444 + .dc.l $00010000, $80555555, $55555555 + .dc.l $00010000, $80666666, $66666666 + .dc.l $00010000, $80777777, $77777777 ; ターゲットの FPn に転送するデータ srcdata: - .dc.l $3fff0000, $88112233, $44556677 - .dc.l $dfff0000, $8899aabb, $ccddeeff + .dc.l $3fff0000, $80aaaaaa, $aaaaaaaa + .dc.l $3fff0000, $80bbbbbb, $bbbbbbbb -zerodata: - .dc.l $0, $0, $0 +; 書き込み先の初期化用データ +initdata_from: + .dc.l $90919293, $94959697, $98999a9b msg_hard: .dc.b "hard mode",$d,$a,0 msg_easy: .dc.b "easy mode",$d,$a,0 msg_oktrap: .dc.b $9,"ok(trap)",$d,$a,0, 0,0 msg_fail_notrap: .dc.b $9,"FAIL: trap expected but not occured",$d,$a,0 msg_fail_trap: .dc.b $9,"FAIL: unexpected trap occured",$d,$a,0 +msg_skip_dst: .dc.b $9,"SKIP(dstmem)",0 msg_ok: .dc.b $9,"ok",$d,$a,0 msg_fail_fp0: .dc.b $9,"FAIL: fp0 expects ",0 @@ -716,10 +737,32 @@ msg_but: .dc.b " but ",0 msg_crlf: .dc.b $d,$a,0 $text) { - out("msg_{$name}: .dc.b \"{$text}\",0"); + foreach ($testlist as $test) { + $name = sprintf("%02d:", $test["num"]); + if ($test["is_pred"]) { + $name .= "pred;"; + } else { + $name .= "ctrl;"; + } + $name .= "fmovem.x "; + $m = eatype2mnem($test["eatype"]); + $f = get_fplist($test); + sort($f); + if ($test["dynamic"]) { + $r = "d6"; + } else { + $r = "fp{$f[0]}-fp{$f[1]}"; + } + if ($test["mem_to_fp"]) { + $name .= "{$m},{$r}"; + } else { + $name .= "{$r},{$m}"; + } + out("msg_test_{$test["num"]}: .dc.b \"{$name}\",0"); } ?> +msg_test_nofpn: .dc.b "fmovem.x (a0)+,nofpn",0 +msg_test_illdyn:.dc.b "fmovem.x (a0),d6_bit",0 .data .even