File:  [Isaki's NoNo m68k/m88k emulator] / nono / exp / fmoveto.php
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:36 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, v024, v023, v022, HEAD
nono 1.2.0

;
; nono
; Copyright (C) 2025 nono project
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 1. Redistributions of source code must retain the above copyright
;    notice, this list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright
;    notice, this list of conditions and the following disclaimer in the
;    documentation and/or other materials provided with the distribution.
;
; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
; AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
; SUCH DAMAGE.

; FMOVE.? FPn,<ea> で例外の起きる <ea> を確認する。
; あと不当命令のところで命令を処理してしまわないことを確認する。

; How to compile
;  > has fmoveto.has
;  > hlk fmoveto.o

	.include doscall.mac
	.include iocscall.mac
	.list
	.cpu	68030

PRINT	.macro	msg
	pea.l	msg
	DOS	_PRINT
	addq.l	#4,sp
	.endm

	.text
	.even

<?php
	// レジスタ割り当て
	// d0-d3	work
	// d4	d8(An,IX) の IX で使う。形式だけでいいので値は 0。
	// d7	1=検査対象命令でFライン例外がおきた
	//		2=FPSR の condition byte が変化した
	// a0-a1	work
	// a2	結果(1テストにつき、期待値.b と結果.b)バッファ
	// a3	検査対象命令の次の位置 (Fライン例外から戻るのに使用)
	// a4	元のFライン例外ハンドラ置き場
?>
<?php
	// サイズ名から dstfmt への変換。
	$sizelist = array(
		"B" => 6,
		"W" => 4,
		"L" => 0,
		"S" => 1,
		"D" => 5,
		"X" => 2,
		"P" => 3,
	);
	// EA 種別と、テストに使うモード(6ビット)、表示名。	
	$ealist = array(
		"d"  => array("mode" => 006, "name" => "Dn"),	// d6 を使う
		"a"  => array("mode" => 010, "name" => "An"),
		"m"  => array("mode" => 020, "name" => "(An)"),
		"mi" => array("mode" => 030, "name" => "(An)+"),
		"md" => array("mode" => 040, "name" => "-(An)"),
		"r"  => array("mode" => 050, "name" => "d16(An)"),
		"x"  => array("mode" => 060, "name" => "d8(An,IX)"),
		// ちょっとテストが難しいので保留。
		//"w"  => array("mode" => 071, "name" => "Abs",
		"p"  => array("mode" => 072, "name" => "(PC)"),
		"i"  => array("mode" => 074, "name" => "#imm"),
		"o"  => array("mode" => 075, "name" => "075"),
	);

	// "exp_int", "exp_mem" はそれぞれ .BWLS と .DXP の場合の期待値。
	// 下位 4 ビットが FPSR の Condition Byte で、
	// 4ビット目はトラップの有無、%0 なら通過、%1 ならトラップを表す。
	// Condition Byte には通常絶対成立しない $b を入れておいて
	// これが変化するかしないかを調べる。
	$testlist = array();

	// FMOVE.? FPn,<ea>
	$table = array(
		//			  +----- .B/W/L/S の期待値 (0が通過、1がトラップ)
		// 			  |	 +-- .D/X/P   の期待値 (〃)
		"d"  => array(0, 1),
		"a"  => array(1, 1),
		"m"  => array(0, 0),
		"mi" => array(0, 0),
		"md" => array(0, 0),
		"r"  => array(0, 0),
		"x"  => array(0, 0),
		"w"  => array(0, 0),
		"p"  => array(1, 1),
		"i"  => array(1, 1),
		"o"  => array(1, 1),
	);
	foreach ($table as $eatype => $exparray) {
		list ($exp_int, $exp_mem) = $exparray;
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x0b);
		$em = ($exp_mem == 1 ? 0x1b : 0x0b);
		$test = array(
			"opname"	=> "fmove",
			"is_store"	=> true,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf2006000 | ($eamode << 16),
			"has_dstfmt"=> true,
			"expected"	=> array(
				"B" => $ei,
				"W" => $ei,
				"L" => $ei,
				"S" => $ei,
				"D" => $em,
				"X" => $em,
				"P" => $em,
			),
		);
		$testlist[] = $test;
	}

	// Fgen.? <ea>,FPn (代表として FMOVE を使う)
	$table = array(
		//			  +----- .B/W/L/S の期待値 (0が通過、1がトラップ)
		// 			  |	 +-- .D/X/P   の期待値 (〃)
		"d"  => array(0, 1),
		"a"  => array(1, 1),
		"m"  => array(0, 0),
		"mi" => array(0, 0),
		"md" => array(0, 0),
		"r"  => array(0, 0),
		"x"  => array(0, 0),
		"w"  => array(0, 0),
		"p"  => array(0, 0),
		"i"  => array(0, 0),
		"o"  => array(1, 1),
	);
	foreach ($table as $eatype => $exparray) {
		list ($exp_int, $exp_mem) = $exparray;
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x04);
		$em = ($exp_mem == 1 ? 0x1b : 0x04);
		$test = array(
			"opname"	=> "fgen",
			"is_store"	=> false,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf2004000 | ($eamode << 16),
			"has_dstfmt"=> true,
			"expected"	=> array(
				"B" => $ei,
				"W" => $ei,
				"L" => $ei,
				"S" => $ei,
				"D" => $em,
				"X" => $em,
				"P" => $em,
			),
		);
		$testlist[] = $test;
	}
	// FSCALE.? <ea>,FPn は NetBSD FPE が Fgen とは別パスで実装してあるので
	// 同じ結果になることを確認する。
	foreach ($table as $eatype => $exparray) {
		list ($exp_int, $exp_mem) = $exparray;
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x04);
		$em = ($exp_mem == 1 ? 0x1b : 0x04);
		$test = array(
			"opname"	=> "fscale",
			"is_store"	=> false,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf2004026 | ($eamode << 16),
			"has_dstfmt"=> true,
			"expected"	=> array(
				"B" => $ei,
				"W" => $ei,
				"L" => $ei,
				"S" => $ei,
				"D" => $em,
				"X" => $em,
				"P" => $em,
			),
		);
		$testlist[] = $test;
	}

	// FScc.B <ea>。サイズは .B のみ。
	// An と (PC), #imm に相当するところには FDBcc、FTRAPcc がいるので
	// 検査しない(出来ない)。
	$table = array(
		// 		.B の期待値 (0が通過、1がトラップ)
		"d"  => 0,	
		"m"  => 0,
		"mi" =>	0,
		"md" =>	0,
		"r"  => 0,
		"x"  => 0,
		"w"  => 0,
		"o"  => 1,
	);
	foreach ($table as $eatype => $exp_int) {
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x0b);	// フラグは変化しない
		$test = array(
			"opname"	=> "fscc",
			"is_store"	=> true,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf2400000 | ($eamode << 16),	// FSF
			"expected"	=> array(
				"B" => $ei,
			),
		);
		$testlist[] = $test;
	}

	// FMOVEM.X FPn,<ea>。
	// FMOVEM 自体のテストではないのでレジスタリストは FPn 1つのみ。
	$table = array(
		// 		期待値 (0が通過、1がトラップ)
		"d"  => 1,
		"a"  => 1,
		"m"  => 0,
		"mi" => 1,
		"md" => 0,
		"r"  => 0,
		"x"  => 0,
		"w"  => 0,
		"p"  => 1,
		"i"  => 1,
		"o"  => 1,
	);
	foreach ($table as $eatype => $exp_int) {
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x0b);	// フラグは変化しない
		$cmode = ($eatype == "md") ? 0 : 0x1000;
		$test = array(
			"opname"	=> "fmovem_rm",
			"is_store"	=> true,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf200e001 | ($eamode << 16) | $cmode,
			"expected"	=> array(
				"X" => $ei,
			),
		);
		$testlist[] = $test;
	}

	// FMOVEM.X <ea>,FPn。
	// FMOVEM 自体のテストではないのでレジスタリストは FPn 1つのみ。
	$table = array(
		// 		期待値 (0が通過、1がトラップ)
		"d"  => 1,
		"a"  => 1,
		"m"  => 0,
		"mi" => 0,
		"md" => 1,
		"r"  => 0,
		"x"  => 0,
		"w"  => 0,
		"p"  => 0,
		"i"  => 1,
		"o"  => 1,
	);
	foreach ($table as $eatype => $exp_int) {
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		$ei = ($exp_int == 1 ? 0x1b : 0x0b);	// フラグは変化しない
		$cmode = ($eatype == "md") ? 0 : 0x1000;
		$test = array(
			"opname"	=> "fmovem_mr",
			"is_store"	=> false,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf200c001 | ($eamode << 16) | $cmode,
			"expected"	=> array(
				"X" => $ei,
			),
		);
		$testlist[] = $test;
	}

	$crlist = array(
		"c" => array("mode" => (4 << 10), "name" => "FPCR"),
		"s" => array("mode" => (2 << 10), "name" => "FPSR"),
		"i" => array("mode" => (1 << 10), "name" => "FPIAR"),
		"m" => array("mode" => (3 << 10), "name" => "FPSR/FPIAR"),
	);

	// FMOVEM.L FPcr,<ea>。
	// FPIAR かどうか、リストが複数かどうかで変わる。
	// 横はサイズではなくレジスタリストをとる。
	$table = array(
		//			FPCR FPSR FPIAR multi
		"d"  => array(	0,	0,	0,	1),	// 単一レジスタのみ可能
		"a"  => array(	1,	1,	0,	1),	// FPIAR のみ可能
		"m"  => array(	0,	0,	0,	0),
		"mi" => array(	0,	0,	0,	0),
		"md" => array(	0,	0,	0,	0),
		"r"  => array(	0,	0,	0,	0),
		"x"  => array(	0,	0,	0,	0),
		"w"  => array(	0,	0,	0,	0),
		"p"  => array(	1,	1,	1,	1),
		"i"  => array(	1,	1,	1,	1),
		"o"  => array(	1,	1,	1,	1),
	);
	foreach ($table as $eatype => $exparray) {
		list ($exp_cr, $exp_sr, $exp_ir, $exp_m) = $exparray;
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		// フラグは変化しない
		$ec = ($exp_cr == 1 ? 0x1b : 0x0b);
		$es = ($exp_sr == 1 ? 0x1b : 0x0b);
		$ei = ($exp_ir == 1 ? 0x1b : 0x0b);
		$em = ($exp_m  == 1 ? 0x1b : 0x0b);
		$test = array(
			"opname"	=> "fmovem_cm",
			"is_store"	=> true,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf200a000 | ($eamode << 16),
			"fmovemc"	=> true,
			"expected"	=> array(
				"c" => $ec,
				"s" => $es,
				"i" => $ei,
				"m" => $em,
			),
		);
		$testlist[] = $test;
	}

	// FMOVEM.L <ea>,FPcr。
	// FPIAR かどうか、リストが複数かどうかで変わる。
	$table = array(
		//			FPCR FPSR FPIAR multi
		"d"  => array(	0,	0,	0,	1),	// 単一レジスタのみ可能
		"a"  => array(	1,	1,	0,	1),	// FPIAR のみ可能
		"m"  => array(	0,	0,	0,	0),
		"mi" => array(	0,	0,	0,	0),
		"md" => array(	0,	0,	0,	0),
		"r"  => array(	0,	0,	0,	0),
		"x"  => array(	0,	0,	0,	0),
		"w"  => array(	0,	0,	0,	0),
		"p"  => array(	0,	0,	0,	0),
		"i"  => array(	0,	0,	0,	0),
		"o"  => array(	1,	1,	1,	1),
	);
	foreach ($table as $eatype => $exparray) {
		list ($exp_cr, $exp_sr, $exp_ir, $exp_m) = $exparray;
		if (!isset($ealist[$eatype])) continue;
		$eamode = $ealist[$eatype]['mode'];
		// フラグは FPSR への代入だけ変化することになる
		$ec = ($exp_cr == 1 ? 0x1b : 0x0b);
		$es = ($exp_sr == 1 ? 0x1b : 0x00);	// FPSR
		$ei = ($exp_ir == 1 ? 0x1b : 0x0b);
		$em = ($exp_m  == 1 ? 0x1b : 0x00);	// FPSR/FPIAR
		$test = array(
			"opname"	=> "fmovem_mc",
			"is_store"	=> false,
			"eatype"	=> $eatype,
			"eamode"	=> $eamode,
			"inst"		=> 0xf2008000 | ($eamode << 16),
			"fmovemc"	=> true,
			"expected"	=> array(
				"c" => $ec,
				"s" => $es,
				"i" => $ei,
				"m" => $em,
			),
		);
		$testlist[] = $test;
	}


	// コマンド名を抽出する。
	$cmdlist = array();
	foreach ($testlist as $test) {
		$opname = $test['opname'];
		if (!isset($cmdlist[$opname])) {
			$cmdlist[$opname] = $opname;
		}
	}
?>

start:
	bsr	parse_arg
	clr.l	-(sp)
	DOS	_SUPER

	fmovem.x	fp0-fp7,-(sp)
	fmovem.l	fpcr/fpsr/fpiar,-(sp)

	moveq.l	#0,d0
	fmove.s	d0,fp0		; FP0 = +0.0
	fmove.l	d0,fpcr		; Init round prec, round mode.
	move.l	d0,failed	; Init failed count.

<?php
	$opname = "";
	foreach ($testlist as $test) {
		if ($opname != $test['opname']) {
			$opname = $test['opname'];
			out("@@:");
			out("tst.b	is_{$opname}(pc)");
			out("beq	@f");
			out("PRINT	msg_head_{$opname}(pc)");
			$n = count($test['expected']);
			if ($n == 4 || $n == 7) {
				out("PRINT	msg_head_guide{$n}(pc)");
			}
		}
		out("	bsr	test_{$opname}_{$test['eatype']}");
	}
?>
@@:
	tst.l	failed(pc)
	beq	@f
	PRINT	msg_failed(pc)
@@:
	fmovem.l	(sp)+,fpcr/fpsr/fpiar
	fmovem.x	(sp)+,fp0-fp7
	DOS	_EXIT

<?php
	// ここからテスト本体を展開。
	// テスト中では Dn の場合の格納先は d6、アドレスは A0。
	foreach ($testlist as $test) {
		$eatype = $test['eatype'];
		$eaname = $ealist[$eatype]['name'];
		$expected_array = $test['expected'];
		out("test_{$test['opname']}_{$eatype}:");
		out("	lea.l	result(pc),a2");
		foreach ($expected_array as $sizename => $expected) {
			$inst = $test['inst'];
			if (($test['has_dstfmt'] ?? false) != false) {
				$dstfmt = $sizelist[$sizename];
				$inst |= ($dstfmt << 10);
			}
			if (($test['fmovemc'] ?? false) != false) {
				// FMOVEM FPcr の時だけループ変数は $sizename ではなく $crtype。
				$crtype = $sizename;
				$inst |= $crlist[$crtype]['mode'];
				$crname = $crlist[$crtype]['name'];
			}
			switch ($test['opname']) {
			 case "fmove":
				out("	; FMOVE.{$sizename} FPn,{$eaname}");
				break;
			 case "fgen":
				out("	; Fgen.{$sizename} {$eaname},FPn");
				break;
			 case "fscale":
				out("	; FSCALE.{$sizename} {$eaname},FPn");
				break;
			 case "fscc":
				out("	; FScc.B {$eaname}");
				break;
			 case "fmovem_rm":
				out("	; FMOVEM.X FPn,{$eaname}");
				break;
			 case "fmovem_mr":
				out("	; FMOVEM.X {$eaname},FPn");
				break;
			 case "fmovem_cm":
				out("	; FMOVEM.L {$crname},{$eaname}");
				break;
			 case "fmovem_mc":
				out("	; FMOVEM.L {$eaname},{$crname}");
				break;
			}
			$expected = sprintf("$%02x", $expected);
			out("	move.b	#{$expected},(a2)+");
			// ConditionByte の N, Inf, NAN を立てる。Z は下げておく。
			out("	fmovem.l	#$0b000000,fpsr");
			out("	bsr	set_fline");
			out("	moveq.l	#0,d7");
			// 命令を作成。
			$post = "";
			$dir = ($test['is_store'] ? "store" : "load");
			switch ("{$dir}_{$eatype}") {
			 // 書き込み先のポインタだけ設定。
			 case "store_m":
			 case "store_mi":
				out("	lea.l	workarea(pc),a0");
				break;
			 case "store_md":
				out("	lea.l	workarea+12(pc),a0");
				break;
			 case "store_r":
				out("	lea.l	workarea(pc),a0");
				$post = ".dc.w	$0000";
				break;
			 case "store_x":
				out("	moveq.l	#0,d4");
				out("	lea.l	workarea(pc),a0");
				$post = ".dc.w	$4000";
				break;

			 // 読み込み元になるデータもゼロで用意する必要がある。
			 case "load_d":
				out("	moveq.l	#0,d6");
				break;
			 case "load_m":
			 case "load_mi":
				out("	lea.l	workarea+12(pc),a0");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				break;
			 case "load_md":
				out("	lea.l	workarea(pc),a0");
				out("	move.l	d7,(a0)+");
				out("	move.l	d7,(a0)+");
				out("	move.l	d7,(a0)+");
				break;
			 case "load_r":
				out("	lea.l	workarea+12(pc),a0");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				$post = ".dc.w	$0000";
				break;
			 case "load_x":
				out("	moveq.l	#0,d4");
				out("	lea.l	workarea+12(pc),a0");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				out("	move.l	d7,-(a0)");
				$post = ".dc.w	$4000";
				break;
			 case "load_p":
				out("	bra	@f");
				out("	.dc.l	0, 0, 0");
				out("@@:");
				// 命令本体(4) + 直前のLEA(6) + ここ(12)
				$post = ".dc.w	-4-6-12";
				break;
			 case "load_i":
				switch ($sizename) {
				 case "B":
				 case "W":
					$post = ".dc.w	0";
					break;
				 case "L":
				 case "S":
				 case "c":	// FPCR
				 case "s":	// FPSR
				 case "i":	// FPIAR
					$post = ".dc.l	0";
					break;
				 case "D":
				 case "m":	// FPSR/FPIAR
					$post = ".dc.l	0, 0";
					break;
				 case "X":
				 case "P":
					$post = ".dc.l	0, 0, 0";
					break;
				}
				break;

			 default:
				break;
			}
			out("	lea.l	@f,a3");
			out("	.dc.l	$%08x", $inst);
			if (strlen($post) > 0) {
				out("	{$post}");
			}
			out("@@:");
			out("	bsr	restore_fline");
			out("	fmovem.l	fpsr,d0");
			out("	rol.l	#8,d0");
			out("	or.l	d7,d0");
			out("	move.b	d0,(a2)+");
			out("");
		}
		out("	lea.l	msg_test_{$test['eatype']},a0");
		out("	moveq.l	#" . count($expected_array) . "-1,d0");
		out("	bsr	print_result");
		out("	rts");
		out("");
	}
?>
<?php
// 1行を出力する。引数は printf のように fmt... で指定可能。
// 改行はこちらで付加する。
function out()
{
	$args = func_get_args();
	$fmt = array_shift($args);
	print vsprintf($fmt, $args);
	print "\n";
}
?>

; 引数を処理する。
; 引数がなければヘルプを表示。
; 引数... があれば列挙されたテストを実行。
; "all" ならすべて実行。
parse_arg:
	movem.l	d0-d2/a0,-(sp)
	tst.b	(a2)
	beq	parse_done
parse_loop:
	adda.l	#1,a2
	move.b	(a2),d0			; '\0' なら終了
	beq	parse_done

	cmpi.b	#' ',d0
	beq	parse_loop
	cmpi.b	#$09,d0
	beq	parse_loop

	; "help"
	lea.l	cmd_help(pc),a0
	bsr match
	beq	print_help

	; "all"
	lea.l	cmd_all(pc),a0
	bsr	match
	bne	@f
<?php
	foreach ($cmdlist as $cmd) {
		out("	st.b	is_{$cmd}");
	}
?>
	; 後は無視していいだろう。
	bra	parse_done
@@:
<?php
	// 個別コマンドのループ
	foreach ($cmdlist as $cmd) {
		out("	; \"{$cmd}\"");
		out("	lea.l	cmd_{$cmd}(pc),a0");
		out("	bsr	match");
		out("	bne	@f");
		out("	st.b	is_{$cmd}");
		out("	bra	parse_loop");
		out("@@:");
	}
?>

parse_done:
	; どのコマンドも実行になってなければヘルプを表示。
	moveq.l	#0,d0
	lea.l	is_tests(pc),a0
	moveq.l	#<?php print count($cmdlist); ?>-1,d1
@@:
	or.b	(a0)+,d0
	dbra	d1,@b
	beq	print_help

	movem.l	(sp)+,d0-d2/a0
	rts

; ヘルプを表示して終了。
print_help:
	PRINT	msg_help(pc)
	DOS	_EXIT

; (a2) からの単語が (a0) からのゼロ終端文字列と一致するか。
; 一致すれば d0 = 0 を返して (a2) は単語の最後の1文字を指すようにする。
; 一致しなければ d0 != 0 を返して (a2) は元の位置のまま。
match:
	movem.l	a1,-(sp)
	movea.l	a2,a1		; 戻り値用
match_loop:
	move.b	(a2)+,d0
	cmpi.b	#' ',d0		; d0 が ' ' なら '\0' に
	bne	@f
	moveq.l	#0,d0
	bra	match_main
@@:
	cmpi.b	#$09,d0		; d0 が TAB なら '\0' に
	bne	match_main
	moveq.l	#0,d0
match_main:
	; この状態で比較
	cmp.b	(a0)+,d0
	bne	not_match
	; '\0' 以外で一致したら次の文字へ。
	tst.b	d0
	bne	match_loop
	; '\0' で一致したので完了。
	suba.l	#1,a2
	moveq.l	#0,d0
	movem.l	(sp)+,a1
	rts

not_match:
	; 不一致なので a2 を元に戻す。
	movea.l	a1,a2
	moveq.l	#1,d0
	movem.l	(sp)+,a1
	rts

; 結果を1行表示する。
; A0 にテスト名、D0 に(テスト数-1)を入れて呼ぶこと。
;
; A2 からの result バッファは 2バイト1組で、
; +0 バイト目が期待値、bit4 がトラップの有無、bit3-0 が ConditionByte。
; +1 バイト目が結果値、構造は同じ。
;
; 期待値		結果
; (0<<4)|(x)	(0<<4)|(x)	pass:OK
; (0<<4)|(x)	(0<<4)|(y)	flag:FAIL
; (0<<4)|(x)	(1<<4)|(x)	trap:FAIL
; (0<<4)|(x)	(1<<4)|(y)	trap:FAIL
; (1<<4)|(x)	(0<<4)|(x)	pass:FAIL
; (1<<4)|(x)	(0<<4)|(y)	pass:FAIL
; (1<<4)|(x)	(1<<4)|(x)	trap:OK
; (1<<4)|(x)	(1<<4)|(y)	flag:FAIL
print_result:
	movem.l	d0-d4/a2,-(sp)
	move.l	d0,d4
	PRINT	(a0)
	lea.l	result(pc),a2
	moveq.l	#0,d0
	moveq.l	#0,d1
prloop:
	move.b	(a2)+,d0		; 期待値
	move.b	(a2)+,d1		; 結果
	; 先に failed のカウント
	cmp.b	d1,d0
	beq	@f
	addi.l	#1,failed
@@:
	bfextu	d0{#27:#1},d2	; 期待値のトラップビット
	bfextu	d1{#27:#1},d3	; 結果のトラップビット
	andi.b	#$0f,d0
	andi.b	#$0f,d1
	cmp.b	d1,d0
	sne.b	d0
	neg.b	d0				; d0 は一致なら $0、そうでなければ $1
	lsl.l	#1,d3			; d3 は結果がトラップなら $2、通過なら $0
	or.b	d3,d0
	lsl.l	#2,d2			; d2 は期待値がトラップなら $4、通過なら $0
	or.b	d2,d0
	add.b	d0,d0			; d0 *= 2
	PRINT	msg_result(pc,d0.w*8)
	dbra	d4,prloop
	PRINT	msg_crlf(pc)
	movem.l	(sp)+,d0-d4/a2
	rts

; Fライン例外をテスト用のものに差し替える。
; 変更前のアドレスを a4 に格納する。
set_fline:
	movem.l	d0-d1/a1,-(sp)
	moveq.l	#$b,d1
	lea.l	fline_handler(pc),a1
	IOCS	_B_INTVCS
	movea.l	d0,a4
	movem.l	(sp)+,d0-d1/a1
	rts

; Fライン例外を元に戻す。
restore_fline:
	movem.l	d0-d1/a1,-(sp)
	moveq.l	#$b,d1
	movea.l	a4,a1
	IOCS	_B_INTVCS
	movem.l	(sp)+,d0-d1/a1
	rts

; Fライン例外ハンドラ
; a3 に戻りアドレスをセットしてあること
; d7 を $10 にして帰る
fline_handler:
	moveq.l	#$10,d7
	move.l	a3,2(sp)
	rte

<?php
	foreach ($ealist as $eatype => $arr) {
		$eaname = $arr['name'];
		while (strlen($eaname) < 9) {
			$eaname .= " ";
		}
		out("msg_test_{$eatype}:	.dc.b	\" {$eaname}\",0");
	}
?>

msg_head_fmove:
	.dc.b	"FMOVEtomem ",0
msg_head_fgen:
	.dc.b	"Fgen       ",0
msg_head_fscale:
	.dc.b	"FSCALE     ",0
msg_head_fmovem_cm:
	.dc.b	"FMOVEM_cm  ",0
msg_head_fmovem_mc:
	.dc.b	"FMOVEM_mc  ",0
msg_head_fscc:
	.dc.b	"FScc       .B",$d,$a,0
msg_head_fmovem_rm:
	.dc.b	"FMOVEM_rm  .X",$d,$a,0
msg_head_fmovem_mr:
	.dc.b	"FMOVEM_mr  .X",$d,$a,0

msg_head_guide4:
	.dc.b	"FPCR      "
	.dc.b	"FPSR      "
	.dc.b   "FPIAR     "
	.dc.b	"FPSR/FPIAR",$d,$a,0

msg_head_guide7:
	.dc.b	".B        "
	.dc.b	".W        "
	.dc.b	".L        "
	.dc.b	".S        "
	.dc.b	".D        "
	.dc.b	".X        "
	.dc.b	".P",$d,$a,0

msg_result:
	.dc.b	" pass:OK  ",0,0,0,0,0,0
	.dc.b	" flag:FAIL",0,0,0,0,0,0
	.dc.b	" trap:FAIL",0,0,0,0,0,0
	.dc.b	" trap:FAIL",0,0,0,0,0,0
	.dc.b	" pass:FAIL",0,0,0,0,0,0
	.dc.b	" pass:FAIL",0,0,0,0,0,0
	.dc.b	" trap:OK  ",0,0,0,0,0,0
	.dc.b	" flag:FAIL",0

msg_failed:
	.dc.b	"FAILED"	; ,$d,$a,0
msg_crlf:
	.dc.b	$d,$a,0

msg_help:
	.dc.b	"usage: { 'all' | 'help' | testnames... }",$d,$a
	.dc.b	" testnames:"
<?php
	foreach ($cmdlist as $cmd) {
		out("	.dc.b	\" {$cmd}\"");
	}
?>
	.dc.b	$d,$a,0

cmd_help:
	.dc.b	"help",0
cmd_all:
	.dc.b	"all",0
<?php
	foreach ($cmdlist as $cmd) {
		out("cmd_{$cmd}:");
		out("	.dc.b	\"{$cmd}\",0");
	}
?>

	.bss
	.even
result:
	.ds.w	<?php print count($sizelist)."\n"; ?>
	.align	4
workarea:
	.ds.l	3
failed:
	.ds.l	1

	; テストを実行する場合は !0
is_tests:
<?php
	foreach ($cmdlist as $cmd) {
		out("is_{$cmd}:");
		out("	.ds.b	1");
	}
?>

	.end	start

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.