|
|
1.1 root 1: ;
2: ; nono
1.1.1.2 ! root 3: ; Copyright (C) 2020 nono project
1.1 root 4: ;
1.1.1.2 ! root 5: ; Redistribution and use in source and binary forms, with or without
! 6: ; modification, are permitted provided that the following conditions
! 7: ; are met:
! 8: ; 1. Redistributions of source code must retain the above copyright
! 9: ; notice, this list of conditions and the following disclaimer.
! 10: ; 2. Redistributions in binary form must reproduce the above copyright
! 11: ; notice, this list of conditions and the following disclaimer in the
! 12: ; documentation and/or other materials provided with the distribution.
! 13: ;
! 14: ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 15: ; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 16: ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 17: ; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 18: ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
! 19: ; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 20: ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
! 21: ; AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! 22: ; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 23: ; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 24: ; SUCH DAMAGE.
1.1 root 25:
26: ; How to compile:
27: ; > has fmovem.has
28: ; > hlk fmovem.o si_util.o
29:
30: .xref hexstr_long
31: .include doscall.mac
32: .list
33: .cpu 68030
34:
35: PRINT .macro msg
36: pea.l msg(pc)
37: DOS _PRINT
38: addq.l #4,sp
39: .endm
40:
41: SET_FLINE_TRAP .macro
42: move.l (a5),a4
43: lea.l fline_handler(pc),a1
44: move.l a1,(a5)
45: .endm
46:
47: RESTORE_FLINE_TRAP .macro
48: move.l a4,(a5)
49: .endm
50:
51: .text
52: .even
53: <?php
54: // レジスタ割り当て
55: // d0/d1 work
56: // d7 1=検査対象命令でFライン例外がおきた
57: // その後は ok を表示するかどうかのカウント
58: // a0/a1 work
59: // a3 検査対象命令の次の位置 (Fライン例外から戻るのに使用)
60: // a4 元のFライン例外ハンドラ置き場
61: // a5 Fライン例外ベクタの位置 $002c
62: ?>
63: <?php
64: // regs がターゲットのFPcr。
65: // C..FPCR
66: // S..FPSR
67: // I..FPIAR の3ビット。
68: // 右辺がそのターゲットの時に使える EA。レジスタ相対は省略でいい?
69: // d..Dn
70: // a..An
71: // m..(An)
72: // +..(An)+
73: // -..-(An)
74: // i..#imm
75:
76: // FMOVEM EA,regs
77: $table = array(
78: //regs EA
79: "___" => "dam+-i",
80: "__I" => "dam+-i",
81: "_S_" => "d.m+-i",
82: "_SI" => "..m+-i",
83: "C__" => "d.m+-i",
84: "C_I" => "..m+-i",
85: "CS_" => "..m+-i",
86: "CSI" => "..m+-i",
87: );
88: foreach ($table as $regs => $eastr) {
89: $ealist = preg_split("//", $eastr, -1, PREG_SPLIT_NO_EMPTY);
90: for ($id = 0; $id < 6; $id++) {
91: $ea = $eastr[$id];
92: if (0) printf("regs='{$regs}' id=${id} ea={$ea}\n");
93:
94: if ($ea == ".") {
95: // 許されない組み合わせ。例外が出ることを確認
96: subr_fail("to", $regs, $id);
97: } else {
98: // アクセス可能な場合のテスト
99: subr_success_to($regs, $id);
100: }
101: }
102: }
103:
104: // FMOVEM regs,EA
105: foreach ($table as $regs => $eastr_src) {
106: // EA が dst になる時は #imm は無効
107: $eastr = preg_replace("/i/", ".", $eastr_src);
108:
109: $ealist = preg_split("//", $eastr, -1, PREG_SPLIT_NO_EMPTY);
110: for ($id = 0; $id < 6; $id++) {
111: $ea = $eastr[$id];
112: if (0) printf("regs='{$regs}' id=${id} ea={$ea}\n");
113:
114: if ($ea == ".") {
115: // 許されない組み合わせ。例外が出ることを確認
116: subr_fail("from", $regs, $id);
117: } else {
118: // アクセス可能な場合のテスト
119: subr_success_from($regs, $id);
120: }
121: }
122: }
123: ?>
124: <?php
125: // 1行を出力する。引数は printf のように fmt... で指定可能。
126: // 改行はこちらで付加する。
127: function out()
128: {
129: $args = func_get_args();
130: $fmt = array_shift($args);
131: print vsprintf($fmt, $args);
132: print "\n";
133: }
134:
135: // regs の省略記号を数値形式に
136: function reg2num($str)
137: {
138: $n = 0;
139: if (preg_match("/I/", $str))
140: $n += 1;
141: if (preg_match("/S/", $str))
142: $n += 2;
143: if (preg_match("/C/", $str))
144: $n += 4;
145: return $n;
146: }
147:
148: // regs 数値形式をニーモニックに
149: function regnum2mnemonic($regnum)
150: {
151: if ($regnum == 0)
152: return "nofpc";
153:
154: $list = array();
155: if ($regnum & 4)
156: $list[] = "fpcr";
157: if ($regnum & 2)
158: $list[] = "fpsr";
159: if ($regnum & 1)
160: $list[] = "fpiar";
161: return join($list, "/");
162: }
163:
164: // regs 数値形式から転送レジスタ数に
165: function regnum2count($regnum)
166: {
167: $counts = array(0, 1, 1, 2, 1, 2, 2, 3);
168: return $counts[$regnum];
169: }
170:
171: // regs 数値形式からマスクを返す。
172: // regs は1ビットだけ立っていること。
173: // %100=FPCR, %010=FPSR, %001=FPIAR
174: function regmask($regnum)
175: {
176: $masks = array(
177: /*0*/ 0,
178: /*1*/ 0xffffffff, // FPIAR
179: /*2*/ 0x0ffffff8, // FPSR
180: /*3*/ 0,
181: /*4*/ 0x0000fff0, // FPCR
182: );
183: return $masks[$regnum];
184: }
185:
186: // EA 番号から命令ワードに。n はすべて0
187: function eanum2word($id)
188: {
189: switch ($id) {
190: case 0: // D0
191: return 0 << 3;
192: case 1: // A0
193: return 1 << 3;
194: case 2: // (A0)
195: return 2 << 3;
196: case 3: // (A0)+
197: return 3 << 3;
198: case 4: // -(An)
199: return 4 << 3;
200: case 5: // #imm
201: return 0x3c;
202: }
203: }
204:
205: // EA 番号をラベル文字列に。
206: function eanum2label($id)
207: {
208: $idstr = array(
209: 0 => "dn",
210: 1 => "an",
211: 2 => "anin",
212: 3 => "anpi",
213: 4 => "anpd",
214: 5 => "imm",
215: );
216: return $idstr[$id];
217: }
218:
219: // EA 番号をニーモニックに。
220: function eanum2mnem($id, $n = 0)
221: {
222: switch ($id) {
223: case 0: return "d{$n}";
224: case 1: return "a{$n}";
225: case 2: return "(a{$n})";
226: case 3: return "(a{$n})+";
227: case 4: return "-(a{$n})";
228: }
229: }
230:
231: function addfunc($name)
232: {
233: global $funclist;
234:
235: $funclist[] = $name;
236: out("test_{$name}:");
237: out(" PRINT msg_{$name}");
238: }
239:
240: // 結果表示のサブルーチンコールを出力
241: function out_result($msg, $exp, $act)
242: {
243: out(" pea.l {$msg}(pc)");
244: out(" move.l {$exp},-(sp)");
245: out(" move.l {$act},-(sp)");
246: out(" bsr result");
247: out(" lea.l 12(sp),sp");
248: }
249:
250: // 許されない組み合わせのテスト。
251: // Fライン例外が起きることをチェックする。
252: function subr_fail($dir, $regs, $id)
253: {
254: $regnum = reg2num($regs);
255: $mnem = regnum2mnemonic($regnum);
256: $label = eanum2label($id);
257:
258: $funcname = ($dir == "to")
259: ? "fmovem_{$label}_to_{$regs}"
260: : "fmovem_{$regs}_to_{$label}";
261: addfunc($funcname);
262:
263: $w1 = 0xf200 | eanum2word($id);
264: $w2 = 0x8000 | ($regnum << 10);
265: if ($dir == "from")
266: $w2 |= 0x2000;
267:
268: out(" SET_FLINE_TRAP");
269: out(" moveq.l #0,d7");
270: out(" lea.l @f,a3");
271: // アセンブルできない可能性があるので命令はバイナリを出力。
272: out(" .dc.w $%04x, $%04x", $w1, $w2);
273: out("@@:");
274:
275: out(" RESTORE_FLINE_TRAP");
276: // 例外が起きるはずなので D7 が非ゼロなら成功(次へ)
277: out(" tst.l d7");
278: out(" bne @f");
279: out(" PRINT msg_fail_notrap");
280: out(" rts");
281: out("@@:");
282: out(" PRINT msg_oktrap");
283: out(" rts");
284: out("");
285: }
286:
287: // アクセス可能な場合のテスト。FMOVEM <ea>,<FPcr>
288: function subr_success_to($regs, $id)
289: {
290: $regnum = reg2num($regs);
291: $mnem = regnum2mnemonic($regnum);
292: $label = eanum2label($id);
293:
294: $funcname = "fmovem_{$label}_to_{$regs}";
295: addfunc($funcname);
296:
297: $w1 = 0xf200 | eanum2word($id);
298: $w2 = 0x8000 | ($regnum << 10);
299: $cpureg = eanum2mnem($id);
300: // count は指定されたレジスタ数 (0..3)
301: // txcount は転送するレジスタ数 (1..3)
302: $count = regnum2count($regnum);
303: $txcount = ($count == 0) ? 1 : $count;
304:
305: // FPcr を空にしておく
306: out(" fmove.l #0,fpcr");
307: out(" fmove.l #0,fpsr");
308: out(" fmove.l #0,fpiar");
309: // 準備
310: switch ($id) {
311: case 0: // Dn (singleのみ)
312: out(" move.l #\$55555555,d0");
313: break;
314: case 1: // An (singleのみ)
315: out(" move.l #\$55555555,a0");
316: break;
317: case 2: // (An)
318: case 3: // (An)+
319: case 4: // -(An)
320: out(" move.l #\$55555555,workarea");
321: if ($count >= 2)
322: out(" move.l #\$aaaaaaaa,workarea+4");
323: if ($count == 3)
324: out(" move.l #\$33333333,workarea+8");
325:
326: if ($id == 4) {
327: // 後ろ基準
328: // nofpc でも転送は発生する
329: out(" lea.l workarea+%d(pc),a0", $txcount * 4);
330: } else {
331: // 前から
332: out(" lea.l workarea(pc),a0");
333: }
334: break;
335: case 5: // #imm
336: $cpureg = "#\$55555555";
337: if ($count >= 2)
338: $cpureg .= ",#\$aaaaaaaa";
339: if ($count == 3)
340: $cpureg .= ",#\$33333333";
341: break;
342: }
343:
344: out(" SET_FLINE_TRAP");
345: out(" moveq.l #0,d7");
346: out(" lea.l @f,a3");
347: // 実行
348: if ($mnem == "nofpc") {
349: out(" .dc.w $%04x, $%04x", $w1, $w2);
350: // #imm,nofpc はアセンブル出来ないのでここで即値をおく
351: if ($id == 5/*imm*/ && $count == 0) {
352: out(" .dc.l \$55555555");
353: }
354: } else {
355: out(" fmovem.l {$cpureg},{$mnem}");
356: }
357: out("@@:");
358:
359: out(" RESTORE_FLINE_TRAP");
360: // 例外は起きないはずなので D7 はゼロなら成功(次へ)
361: out(" tst.l d7");
362: out(" beq @f");
363: out(" PRINT msg_fail_trap");
364: out(" rts");
365: out("@@:");
366:
367: // 検証
368: $expected = array(
369: 0x55555555,
370: 0xaaaaaaaa,
371: 0x33333333,
372: );
373: $i = 0;
374:
375: // An の変化量が正しいか。
376: // この後のレジスタの検証で A0 を使うのでその前に行う。
377: // (An) なら workarea から変化しない
378: // (An)+ なら workarea+count*4
379: // -(An) なら workarea に戻ってくる (最初にオフセットつけてるので)
380: switch ($id) {
381: case 2: // (An)
382: case 4: // -(An)
383: out(" lea.l workarea(pc),a1");
384: out(" cmpa.l a1,a0");
385: out(" beq @f");
386: out_result("msg_fail_addr", "a1", "a0");
387: out("@@:");
388: break;
389: case 3: // (An)+
390: out(" lea.l workarea+%d(pc),a1", $txcount * 4);
391: out(" cmpa.l a1,a0");
392: out(" beq @f");
393: out_result("msg_fail_addr", "a1", "a0");
394: out("@@:");
395: break;
396: }
397:
398: // FPCR
399: out(" fmove.l fpcr,d0");
400: if (($regnum & 4)) {
401: out(" move.l #$%08x,d1", $expected[$i] & regmask(4));
402: $i++;
403: } else {
404: out(" moveq.l #0,d1");
405: }
406: out(" cmp.l d1,d0");
407: out(" beq @f");
408: out_result("msg_fail_cr", "d1", "d0");
409: out("@@:");
410:
411: // FPSR
412: out(" fmove.l fpsr,d0");
413: if (($regnum & 2)) {
414: out(" move.l #$%08x,d1", $expected[$i] & regmask(2));
415: $i++;
416: } else {
417: out(" moveq.l #0,d1");
418: }
419: out(" cmp.l d1,d0");
420: out(" beq @f");
421: out_result("msg_fail_sr", "d1", "d0");
422: out("@@:");
423:
424: // FPIAR
425: out(" fmove.l fpiar,a0");
426: // fmovem *,nofpc は FPIAR が受け取ってしまうようだ
427: if (($regnum & 1) || $regnum == 0) {
428: out(" move.l #$%08x,a1", $expected[$i] & regmask(1));
429: $i++;
430: } else {
431: out(" move.l d7,a1 ; d7=0");
432: }
433: out(" cmpa.l a1,a0");
434: out(" beq @f");
435: out_result("msg_fail_ir", "a1", "a0");
436: out("@@:");
437:
438: out(" tst.l d7");
439: out(" bne @f");
440: out(" PRINT msg_ok");
441: out("@@:");
442: out(" rts");
443: out("");
444: }
445:
446: // アクセス可能な場合のテスト。FMOVEM <FPcr>,<ea>
447: function subr_success_from($regs, $id)
448: {
449: $regnum = reg2num($regs);
450: $mnem = regnum2mnemonic($regnum);
451: $label = eanum2label($id);
452:
453: $funcname = "fmovem_{$regs}_to_{$label}";
454: addfunc($funcname);
455:
456: $w1 = 0xf200 | eanum2word($id);
457: $w2 = 0xa000 | ($regnum << 10);
458: $cpureg = eanum2mnem($id);
459: // count は指定されたレジスタ数 (0..3)
460: // txcount は転送するレジスタ数 (1..3)
461: $count = regnum2count($regnum);
462: $txcount = ($count == 0) ? 1 : $count;
463:
464: // FPcr に初期値をセット
465: // nofpc の時は FPIAR
466: $expected = array(
467: 0x55555555,
468: 0xaaaaaaaa,
469: 0x33333333,
470: );
471: $i = 0;
472: out(" fmove.l #$%08x,fpcr", ($regnum & 4) ? $expected[$i++] : 0);
473: out(" fmove.l #$%08x,fpsr", ($regnum & 2) ? $expected[$i++] : 0);
474: out(" fmove.l #$%08x,fpiar",
475: (($regnum & 1) || $count == 0) ? $expected[$i++] : 0);
476:
477: // 準備
478: switch ($id) {
479: case 2: // (An)
480: case 3: // (An)+
481: out(" lea.l workarea(pc),a0");
482: break;
483: case 4: // -(An)
484: // nofpc でも転送は発生する
485: out(" lea.l workarea+%d(pc),a0", $txcount * 4);
486: break;
487: case 5: // #imm は dst にならない
488: break;
489: }
490:
491: out(" SET_FLINE_TRAP");
492: out(" moveq.l #0,d7");
493: out(" lea.l @f,a3");
494: // 実行
495: if ($mnem == "nofpc")
496: out(" .dc.w $%04x, $%04x", $w1, $w2);
497: else
498: out(" fmovem.l {$mnem},{$cpureg}");
499: out("@@:");
500:
501: out(" RESTORE_FLINE_TRAP");
502: // 例外は起きないはずなので D7 はゼロなら成功(次へ)
503: out(" tst.l d7");
504: out(" beq @f");
505: out(" PRINT msg_fail_trap");
506: out(" rts");
507: out("@@:");
508:
509: // 検証
510:
511: // An の変化量が正しいか
512: // (An) なら workarea から変化しない
513: // (An)+ なら workarea+count*4
514: // -(An) なら workarea に戻ってくる (最初にオフセットつけてるので)
515: switch ($id) {
516: case 2: // (An)
517: case 4: // -(An)
518: out(" lea.l workarea(pc),a1");
519: out(" cmpa.l a1,a0");
520: out(" beq @f");
521: out_result("msg_fail_addr", "a1", "a0");
522: out("@@:");
523: break;
524: case 3: // (An)+
525: out(" lea.l workarea+%d(pc),a1", $txcount * 4);
526: out(" cmpa.l a1,a0");
527: out(" beq @f");
528: out_result("msg_fail_addr", "a1", "a0");
529: out("@@:");
530: break;
531: }
532:
533: $i = 0;
534: switch ($id) {
535: case 0: // Dn
536: for ($r = 4; $r >= 1; $r >>= 1) {
537: // 各 fpn の該当する時か、nofpc なら FPIAR
538: if (($regnum & $r) || ($r == 1 && $count == 0)) {
539: out(" move.l #$%08x,d1", $expected[$i] & regmask($r));
540: $i++;
541: out(" cmp.l d1,d0");
542: out(" beq @f");
543: out_result("msg_fail", "d1", "d0");
544: out("@@:");
545: }
546: }
547: break;
548: case 1: // An
549: for ($r = 4; $r >= 1; $r >>= 1) {
550: // 各 fpn の該当する時か、nofpc なら FPIAR
551: if (($regnum & $r) || ($r == 1 && $count == 0)) {
552: out(" movea.l #$%08x,a1", $expected[$i] & regmask($r));
553: $i++;
554: out(" cmp.l a1,a0");
555: out(" beq @f");
556: out_result("msg_fail", "a1", "a0");
557: out("@@:");
558: }
559: }
560: break;
561: case 2: // (An)
562: case 3: // (An)+
563: case 4: // -(An)
564: for ($r = 4; $r >= 1; $r >>= 1) {
565: // 各 fpn の該当する時か、nofpc なら FPIAR
566: if (($regnum & $r) || ($r == 1 && $count == 0)) {
567: out(" move.l #$%08x,d1", $expected[$i] & regmask($r));
568: out(" cmp.l workarea+%d,d1", $i * 4);
569: $i++;
570: out(" beq @f");
571: out_result("msg_fail", "d1", sprintf("workarea+%d", $i * 4));
572: out("@@:");
573: }
574: }
575: break;
576: }
577:
578: out(" tst.l d7");
579: out(" bne @f");
580: out(" PRINT msg_ok");
581: out("@@:");
582: out(" rts");
583: out("");
584: }
585: ?>
586: <?php
587: // lea.l $0000002c.l,a5 だと has.x に絶対アドレッシングだと
588: // 怒られてしまうため仕方なく
589: ?>
590: start:
591: clr.l -(sp)
592: DOS _SUPER
593: movem.l d7/a3-a5,-(sp)
594: moveq.l #$2c,d0 ; alternate of "lea.l $2c,a5"
595: move.l d0,a5 ;
596: <?php
597: foreach ($funclist as $name) {
598: out(" bsr test_{$name}");
599: }
600: ?>
601: movem.l (sp)+,d7/a3-a5
602: DOS _EXIT
603:
604: ; 結果表示
605: ; スタックには、メッセージ、期待値、実際の値の順で積む。
606: result:
607: link a6,#0
608: movem.l d0/a0,-(sp)
609:
610: move.l 8+8(a6),-(sp)
611: DOS _PRINT
612: addq.l #4,sp
613:
614: move.l 8+4(a6),d0
615: lea.l buf(pc),a0
616: bsr hexstr_long
617: PRINT buf
618: PRINT msg_but
619:
620: move.l 8+0(a6),a0
621: lea.l buf(pc),a0
622: bsr hexstr_long
623: PRINT buf
624: PRINT msg_crlf
625:
626: addq.l #1,d7 ; errcnt
627: movem.l (sp)+,d0/a0
628: unlk a6
629: rts
630:
631: ; Fライン例外ハンドラ
632: ; a3 に戻りアドレスをセットしてあること
633: ; d7 を 1 にして帰る
634: fline_handler:
635: moveq.l #1,d7
636: move.l a3,2(sp)
637: rte
638:
639: msg_oktrap: .dc.b $9,"ok(trap)",$d,$a,0, 0,0
640: msg_fail_notrap: .dc.b $9,"FAIL: trap expected but not occured",$d,$a,0
641: msg_fail_trap: .dc.b $9,"FAIL: unexpected trap occured",$d,$a,0
642:
643: msg_ok: .dc.b $9,"ok",$d,$a,0
644: msg_fail: .dc.b $9,"fail",$d,$a,0
645: msg_fail_cr: .dc.b $9,"FAIL: fpcr expects ",0
646: msg_fail_sr: .dc.b $9,"FAIL: fpsr expects ",0
647: msg_fail_ir: .dc.b $9,"FAIL: fpiar expects ",0
648: msg_fail_addr: .dc.b $9,"FAIL: an expects ",0
649: msg_but: .dc.b " but ",0
650: msg_crlf: .dc.b $d,$a,0
651:
652:
653: <?php
654: foreach ($funclist as $name) {
655: out("msg_{$name}: .dc.b \"{$name}\",0");
656: }
657: ?>
658:
659: .data
660: .even
661: workarea: .ds.b 24
662: buf: .dc.b 20
663:
664: .end start
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.