|
|
1.1 root 1: <?php
2: //
3: // nono
4: // Copyright (C) 2014 [email protected]
1.1.1.2 root 5: // Copyright (C) 2020 nono project
6: // Licensed under nono-license.txt
7: //
8:
1.1 root 9: //
10: // gentable.php
11: // inst*.txt からディスパッチャ部分を生成
12: //
13: // switch10 switch-case (10bit) を出力
14: // switch13 switch-case (13bit) を出力
15: // goto10 computed goto (10bit) を出力
16: // mmu8 MMU の switch-case (8bit) を出力
17: //
18:
19: if ($argc < 2) {
20: usage();
21: }
22:
23: init_ea();
1.1.1.3 ! root 24: // 今の所逆アセンブラの分離度が低いので、
! 25: // コアも逆アセンブラも両方とも 68020/030 という扱いにしておく。
! 26: $mputype = "[23]";
1.1 root 27: switch ($argv[1]) {
28: case "switch10":
29: case "switch13":
30: case "goto10":
31: if (preg_match("/10$/", $argv[1])) {
32: $nb = 10;
33: } else {
34: $nb = 13;
35: }
36: $insttable = read_instructions($mputype, "instructions.txt");
37: $insttable = expandDST($insttable);
38: $insttable = expandSS($insttable);
39: $insttable = expandCond($insttable);
40: makeop16($insttable);
41: $op10table = makeop10table();
42: $op10func = makeop10func($op10table);
43: $op10hash = makeop10hash($op10func);
44: if (preg_match("/switch/", $argv[1])) {
45: output_switch($op10table, $op10func);
46: } else {
47: output_goto($op10table, $op10func);
48: }
49: output_header($op10hash);
50: break;
51:
52: case "mmu8":
53: $nb = 8;
54: $insttable = read_instructions($mputype, "instmmu.txt");
55: makeop16($insttable);
56: $op10table = makeop10table();
57: $op10func = makeop10func($op10table);
58: $op10hash = makeop10hash($op10func);
59: output_switch($op10table, $op10func);
60: output_header($op10hash);
61: break;
62: default:
63: usage();
64: }
65: exit(0);
66: ?>
67: <?php
68: function usage()
69: {
70: global $argv;
71:
72: print "Usage: {$argv[0]} switch10 | switch13\n";
73: print " input: instruction.txt\n";
74: print " output: switch.h.new, ops.cpp.new, ops.h.new\n";
75: print "Usage: {$argv[0]} mmu8\n";
76: print " input: instmmu.txt\n";
77: print " output: \n";
78: print "Usage: {$argv[0]} goto10\n";
79: print " input: instruction.txt\n";
80: print " output: goto.h.new, ops.cpp.new, ops.h.new\n";
81: exit(1);
82: }
83:
84: function init_ea()
85: {
86: global $ambits;
87:
88: // アドレッシングモードの展開パターン
89: $ambits = array(
90: "d" => "000rrr",
91: "a" => "001rrr",
92: "m" => "010rrr",
93: "+" => "011rrr",
94: "-" => "100rrr",
95: "r" => "101rrr",
96: "x" => "110rrr",
97: "w" => "11100r",
98: "p" => "11101r",
99: "i" => "111100",
100: );
101: }
102:
103: // instructions.txt を読み込んで $insttable[] 配列にする
104: function read_instructions($mputype, $filename)
105: {
106: $fp = fopen($filename, "r");
107: if ($fp === false) {
108: print "fopen failed: {$filename}\n";
109: exit(1);
110: }
111:
112: $insttable = array();
113:
114: while (($line = fgets($fp))) {
115: $line = trim(preg_replace("/;.*/", "", $line));
116: if ($line == "") {
117: continue;
118: }
119:
120: // 1行はこんな感じ
121: // 00000000SSmmmrrr d.m+-rxw.. 034 ori_S ORI.S #<imm>,<ea>
122: // 列は1つ以上のタブで区切られている。
123: // $bits … 1ワード目のビットパターン
124: // $addr … 対応しているアドレッシングモード
125: // $mpu … MPU ごとのサポート有無
126: // $name … 関数名
127: // $text … コメント
128:
129: list ($bits, $addr, $mpu, $name, $text) =
130: preg_split("/\t+/", $line, -1, PREG_SPLIT_NO_EMPTY);
131: $name = trim($name);
132: $text = trim($text);
133:
134: // ターゲット CPU の命令のみ対象にする
135: if (preg_match("/{$mputype}/", $mpu) == false) {
136: continue;
137: }
138:
139: $newinst = array(
140: "bits" => $bits,
141: "addr" => $addr,
142: "mpu" => $mpu,
143: "name" => $name,
144: "text" => $text,
145: );
146: // $insttable[] に追加する。
147: // ただし bits が同じものがあれば text だけ追加。
148: if (isset($insttable[$bits])) {
149: $insttable[$bits]["text"] .= ";{$text}";
150: } else {
151: $insttable[$bits] = $newinst;
152: }
153: }
154: fclose($fp);
155:
156: // デバッグ表示
157: if (0) {
158: print "read_instruction\n";
159: foreach ($insttable as $inst) {
160: printf("%s|%s|%-14s|%s\n",
161: $inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
162: }
163: }
164:
165: return $insttable;
166: }
167:
168: // $insttable のうち MOVE の DST を展開する
169: function expandDST($insttable)
170: {
171: $dst = array(
172: array("xxx000", "dn", "Dx"),
173: array("xxx010", "anin", "(Ax)"),
174: array("xxx011", "anpi", "(Ax)+"),
175: array("xxx100", "anpd", "-(Ax)"),
176: array("xxx101", "andi", "d16(Ax)"),
177: array("xxx110", "anix", "(Ax,IX)"),
178: array("000111", "absw", "Abs.W"),
179: array("001111", "absl", "Abs.L"),
180: );
181: $inst2table = array();
182: foreach ($insttable as $inst) {
183: if (preg_match("/RRRMMM/", $inst["bits"])) {
184: foreach ($dst as $d) {
185: $inst2 = $inst;
186: $inst2["bits"] = preg_replace("/RRRMMM/", $d[0], $inst["bits"]);
187: $inst2["name"] = preg_replace("/DD/", $d[1], $inst["name"]);
188: $inst2["text"] = preg_replace("/DD/", $d[2], $inst["text"]);
189:
190: $inst2table[] = $inst2;
191: }
192: } else {
193: $inst2table[] = $inst;
194: }
195: }
196:
197: // デバッグ表示
198: if (0) {
199: print "expandDST\n";
200: foreach ($inst2table as $inst) {
201: printf("%s|%s|%-14s|%s\n",
202: $inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
203: }
204: }
205:
206: return $inst2table;
207: }
208:
209: // $insttable の SS と s を展開する
210: // これらは必ず上位10ビット内にあるので (というか下位6ビット以内に現れる
211: // やつはテキストにする時点で手動で展開してあるので)、
212: // サイズ別の関数に展開できる。
213: function expandSS($insttable)
214: {
215: $s3 = array(
216: array("00", "_b", ".B"),
217: array("01", "_w", ".W"),
218: array("10", "_l", ".L"),
219: );
220: $s2 = array(
221: array("0", "_w", ".W"),
222: array("1", "_l", ".L"),
223: );
224:
225: $inst2table = array();
226: foreach ($insttable as $inst) {
227: if (preg_match("/SS/", $inst["bits"])) {
228: foreach ($s3 as $s) {
229: $inst2 = $inst;
230: $inst2["bits"] = preg_replace("/SS/", $s[0], $inst["bits"]);
231: $inst2["name"] = preg_replace("/_S/", $s[1], $inst["name"]);
232: $inst2["text"] = preg_replace("/\.S/", $s[2], $inst["text"]);
233:
234: $inst2table[] = $inst2;
235: }
236: } else if (preg_match("/s/", $inst["bits"])) {
237: foreach ($s2 as $s) {
238: $inst2 = $inst;
239: $inst2["bits"] = preg_replace("/s/", $s[0], $inst["bits"]);
240: $inst2["name"] = preg_replace("/_S/", $s[1], $inst["name"]);
241: $inst2["text"] = preg_replace("/\.S/", $s[2], $inst["text"]);
242:
243: $inst2table[] = $inst2;
244: }
245: } else {
246: $inst2table[] = $inst;
247: }
248: }
249:
250: // デバッグ表示
251: if (0) {
252: print "expandSS\n";
253: foreach ($inst2table as $inst) {
254: printf("%s|%s|%-14s|%s\n",
255: $inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
256: }
257: }
258:
259: return $inst2table;
260: }
261:
262: // $insttable の cccc を展開する。
263: function expandCond($insttable)
264: {
265: $cccc = array(
266: "0000" => "t",
267: "0001" => "f",
268: "0010" => "hi",
269: "0011" => "ls",
270: "0100" => "cc",
271: "0101" => "cs",
272: "0110" => "ne",
273: "0111" => "eq",
274: "1000" => "vc",
275: "1001" => "vs",
276: "1010" => "pl",
277: "1011" => "mi",
278: "1100" => "ge",
279: "1101" => "lt",
280: "1110" => "gt",
281: "1111" => "le",
282: );
283: $inst2table = array();
284: foreach ($insttable as $inst) {
285: if (preg_match("/cccc/", $inst["bits"])) {
286: foreach ($cccc as $cbit => $cname) {
287: $uname = strtoupper($cname);
288: $inst2 = $inst;
289: $inst2["bits"] = preg_replace("/cccc/", $cbit, $inst["bits"]);
290: $inst2["name"] = preg_replace("/cc/", $cname, $inst["name"]);
291: $inst2["text"] = preg_replace("/cc/", $uname, $inst["text"]);
292: $inst2table[] = $inst2;
293: }
294: } else {
295: $inst2table[] = $inst;
296: }
297: }
298:
299: // デバッグ表示
300: if (0) {
301: print "expandCond\n";
302: foreach ($inst2table as $inst) {
303: printf("%s|%s|%-14s|%s\n",
304: $inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
305: }
306: }
307:
308: return $inst2table;
309: }
310:
311: // insttable を16ビットの op16table に展開。
312: // $optable = array(
313: // 0 => &$inst0,
314: // 1 => &$inst1, ...
315: // )
316: function makeop16($insttable)
317: {
318: global $op16table;
319:
320: $op16table = array();
321:
322: foreach ($insttable as $inst) {
323: makeop16_sub($inst, $inst["bits"]);
324: }
325:
326: // デバッグ表示
327: if (0) {
328: print "makeop16\n";
329: for ($i = 0; $i < 65536; $i++) {
330: $b = str_repeat("0", 15) . decbin($i);
331: $b = substr($b, -16);
332: printf("%04x|%s|", $i, $b);
333:
334: if (isset($op16table[$i])) {
335: $inst = $op16table[$i];
336: printf("%s|%-14s|%s\n",
337: $inst["bits"], $inst["name"], $inst["text"]);
338: } else {
339: printf("illegal\n");
340: }
341: }
342: }
343: }
344:
345: // inst を1ビットずつ展開する再帰関数
346: // lv は再帰レベル (デバッグ用)
347: function makeop16_sub($inst, $bits, $lv = 0)
348: {
349: global $op16table;
350: global $ambits;
351:
352: // $pos は bits の 0/1 でない最初の文字の位置
353: $pos = strspn($bits, "01");
354: if ($pos == 16) {
355: // 0/1 しか現れていないので確定
356: // $optable に代入
357: $num16 = bindec($bits);
358: $op16table[$num16] = $inst;
359: } else if (substr($bits, 10) == "mmmrrr") {
360: // アドレッシングモードによって展開 (いるかな?)
361: $head = substr($bits, 0, 10);
362: for ($i = 0; $i < strlen($inst["addr"]); $i++) {
363: $a = substr($inst["addr"], $i, 1);
364: if (isset($ambits[$a])) {
365: makeop16_sub($inst, "{$head}{$ambits[$a]}", $lv + 1);
366: }
367: }
368: } else {
369: // pos 番目の文字を 0 と 1 に変えて再帰実行
370: $head = substr($bits, 0, $pos);
371: $tail = substr($bits, $pos + 1);
372: makeop16_sub($inst, "{$head}0{$tail}", $lv + 1);
373: makeop16_sub($inst, "{$head}1{$tail}", $lv + 1);
374: }
375: }
376:
377: // 16ビットテーブルから $nb (10 or 7) ビットテーブルを作成
378: function makeop10table()
379: {
380: global $op16table;
381: global $nb;
382:
383: // 1ブロックあたりの命令数 ($nb=10 なら 64)
384: $ipb = 2 ** (16 - $nb);
385:
386: $op10table = array();
387: for ($i = 0; $i < 2 ** $nb; $i++) {
388: $op10table[$i] = array();
389: }
390:
391: for ($i = 0; $i < 65536; $i++) {
392: if (!isset($op16table[$i])) {
393: continue;
394: }
395: $inst = $op16table[$i];
396: $j = $i / $ipb;
397:
398: // op10table[j][] にすでに同じエントリがなければ、追加
399: $found = false;
400: foreach ($op10table[$j] as $ji) {
401: if ($inst["bits"] == $ji["bits"]) {
402: $found = true;
403: break;
404: }
405: }
406: if (!$found) {
407: $op10table[$j][] = $inst;
408: }
409: }
410:
411: // デバッグ用
412: if (0) {
413: print "makeop10table\n";
414: foreach ($op10table as $j => $arr) {
415: printf("0x%04x|", $j, $j * $ipb);
416: foreach ($arr as $inst) {
417: printf(" %s", $inst["name"]);
418: }
419: print "\n";
420: }
421: }
422:
423: return $op10table;
424: }
425:
426: // op10table から op10func を作成
427: // op10table は [1024] => array(inst, inst, ...)
428: // op10func は [1024] => 代表name
429: function makeop10func($op10table)
430: {
431: global $nb;
432:
433: $op10func = array();
434: for ($i = 0; $i < 2 ** $nb; $i++) {
435: $op10func[$i] = "";
436: }
437:
438: foreach ($op10table as $i => $arr) {
439: if (count($arr) == 0) {
440: $op10func[$i] = "illegal";
441: continue;
442: }
443:
444: // 代表関数はこのブロックの中で最多数なもの。
445: $funcname = "";
446: $minbits = (16 - $nb) + 1;
447: foreach ($arr as $inst) {
448: // fixed は bits の末尾(16-nb)ビットのうち "0" と "1" の合計数
449: $chs = count_chars(substr($inst["bits"], $nb), 0);
450: $fixed = $chs[0x30] + $chs[0x31];
451: // fixed が小さい = 出力した命令の総数が大きい、なので採用
452: if ($minbits > $fixed) {
453: $minbits = $fixed;
454: $funcname = $inst["name"];
455: }
456: }
457:
458: $op10func[$i] = $funcname;
459: }
460:
461: // デバッグ用
462: if (0) {
463: print "makeop10func\n";
464: foreach ($op10func as $i => $name) {
465: printf("[%4d] %04x {$name}\n", $i, $i * 64);
466: }
467: }
468:
469: return $op10func;
470: }
471:
472: // op10hash は [代表name] => 代表name のハッシュ
473: // 実際に使われてる関数名一覧になる。
474: function makeop10hash($op10func)
475: {
476: $op10hash = array();
477: foreach ($op10func as $name) {
478: $op10hash[$name] = $name;
479: }
480:
481: return $op10hash;
482: }
483:
484: // switch 本体とソースの元を出力
485: function output_switch($op10table, $op10func)
486: {
487: global $nb;
488:
489: $shift = 16 - $nb;
490: $ipb = 2 ** (16 - $nb);
491:
492: $func2 = $op10func;
493:
494: // 先に不当命令ブロックだけ抜いておく
495: $illegals = array();
496: for ($i = 0; $i < 2 ** $nb; $i++) {
497: if (isset($func2[$i]) && $func2[$i] == "illegal") {
498: $illegals[$i] = $i;
499: unset($func2[$i]);
500: }
501: }
502:
503: $out = "";
504: $cpp = "";
505: for ($i = 0; $i < 2 ** $nb; $i++) {
506: if (!isset($func2[$i])) {
507: continue;
508: }
509: $funcname = $func2[$i];
510:
511: $cases = array();
512: $comments = array();
513:
514: // この関数名をもつブロックに属する人全員のコメントを集める。
515: // このブロック自身もここで集計するため $j は $i から始める。
516: for ($j = $i; $j < 2 ** $nb; $j++) {
517: if (isset($func2[$j]) && $func2[$j] == $funcname) {
518: foreach ($op10table[$j] as $inst) {
519: // case
520: $cases[$j] = $j;
521: // コメント
522: // 同一 bits に別の命令が割り当てられているケースは
523: // ここで2行のコメントに分解
524: foreach (split(";", $inst["text"]) as $text) {
525: $comm = "{$inst["bits"]}\t{$text}";
526: $comments[$comm] = $comm;
527: }
528: }
529: // 集計したら消す
530: unset($func2[$j]);
531: }
532: }
533: // case
534: foreach ($cases as $c) {
535: $out .= sprintf("\t case (0x%04x >> ${shift}):\n", $c * $ipb);
536: }
537: // コメント
538: foreach ($comments as $comm) {
539: if ($nb == 8) {
540: // というか MMU というべきか
541: preg_match("/(...)(.....)(...)(.*)/", $comm, $m);
542: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}_{$m[4]}";
543: } else {
544: // 第1ワードというべきか
545: preg_match("/(....)(......)(.*)/", $comm, $m);
546: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}";
547: }
548: $out .= "\t\t// {$comm1}\n";
549: $cpp .= "// {$comm1}\n";
550: }
551: // 関数
552: $out .= "\t\tOP_FUNC({$funcname});\n";
553: $out .= "\t\tbreak;\n";
554:
555: // ソース
556: $cpp .= output_func($funcname);
557: }
558:
559: // 不当命令ブロックを出力
560: foreach ($illegals as $i) {
561: $out .= sprintf("\t case (0x%04x >> ${shift}):\n", $i * $ipb);
562: }
563: $out .= "\t\tOP_FUNC(illegal);\n";
564: $out .= "\t\tbreak;\n";
565:
566: // 不当命令 (ILLEGAL 命令ではないので最終的にどうするかはある)
567: $cpp .= "// illegal instructions\n";
568: $cpp .= output_func("illegal");
569:
570: // ファイルに出力
571: write_file("switch.h.new", $out);
572: write_file("ops.cpp.new", $cpp);
573: }
574:
575: // computed-goto 本体とソースの元を出力
576: function output_goto($op10table, $op10func)
577: {
578: global $nb;
579:
580: $shift = 16 - $nb;
581: $ipb = 2 ** (16 - $nb);
582:
583: // ジャンプテーブルを作成
584: $table = "\t\tstatic const void *jumptable[] = {\n";
585: for ($i = 0; $i < 2 ** $nb; $i++) {
586: $table .= sprintf("\t\t\t/* $%04X */&&label_goto_%s,\n",
587: $i * $ipb, $op10func[$i]);
588: }
589: $table .= "\t\t};\n";
590:
591: $func2 = $op10func;
592:
593: // 先に不当命令ブロックだけ抜いておく
594: $illegals = array();
595: for ($i = 0; $i < 2 ** $nb; $i++) {
596: if (isset($func2[$i]) && $func2[$i] == "illegal") {
597: $illegals[$i] = $i;
598: unset($func2[$i]);
599: }
600: }
601:
602: $out = "";
603: $cpp = "";
604:
605: $out .= "\t\tJUMP;\n";
606:
607: for ($i = 0; $i < 2 ** $nb; $i++) {
608: if (!isset($func2[$i])) {
609: // 不当命令ブロック
610: continue;
611: }
612: $funcname = $func2[$i];
613:
614: $cases = array();
615: $comments = array();
616:
617: // この関数名をもつブロックに属する人全員のコメントを集める。
618: // このブロック自身もここで集計するため $j は $i から始める。
619: for ($j = $i; $j < 2 ** $nb; $j++) {
620: if (isset($func2[$j]) && $func2[$j] == $funcname) {
621: foreach ($op10table[$j] as $inst) {
622: // case
623: $cases[$j] = $j;
624: // コメント
625: // 同一 bits に別の命令が割り当てられているケースは
626: // ここで2行のコメントに分解
627: foreach (split(";", $inst["text"]) as $text) {
628: $comm = "{$inst["bits"]}\t{$text}";
629: $comments[$comm] = $comm;
630: }
631: }
632: // 集計したら消す
633: unset($func2[$j]);
634: }
635: }
636: // ラベル
637: $out .= "\t label_goto_{$funcname}:\n";
638: // コメント
639: foreach ($comments as $comm) {
640: if ($nb == 8) {
641: // というか MMU というべきか
642: preg_match("/(...)(.....)(...)(.*)/", $comm, $m);
643: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}_{$m[4]}";
644: } else {
645: // 第1ワードというべきか
646: preg_match("/(....)(......)(.*)/", $comm, $m);
647: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}";
648: }
649: $out .= "\t\t// {$comm1}\n";
650: $cpp .= "// {$comm1}\n";
651: }
652: // 関数
653: $out .= "\t\tOP_FUNC({$funcname});\n";
654: $out .= "\t\tCHECK_AND_JUMP;\n";
655:
656: // ソース
657: $cpp .= output_func($funcname);
658: }
659:
660: // 不当命令ブロックを出力
661: $out .= "\t label_goto_illegal:\n";
662: $out .= "\t\tOP_FUNC(illegal);\n";
663: $out .= "\t\tCHECK_AND_JUMP;\n";
664:
665: // ファイルに出力
666: write_file("goto.h.new", $table . $out);
667: write_file("ops.cpp.new", $cpp);
668: }
669:
670: // 関数ソースを出力
671: function output_func($name)
672: {
673: $cpp = "";
674: $cpp .= "OP_DEF({$name})\n";
675: $cpp .= "{\n";
676: $cpp .= "\tOP_FUNC(unimpl);\n";
677: $cpp .= "}\n";
678: $cpp .= "\n";
679:
680: return $cpp;
681: }
682:
683: // ヘッダを出力
684: function output_header($op10hash)
685: {
686: $out = "";
687: foreach ($op10hash as $name) {
688: $out .= "OP_PROTO({$name});\n";
689: }
690:
691: // ファイルに出力
692: write_file("ops.h.new", $out);
693: }
694:
695: // ファイル出力
696: function write_file($filename, $str)
697: {
698: $fp = fopen($filename, "w");
699: fwrite($fp, $str);
700: fclose($fp);
701:
702: print "Output: {$filename}\n";
703: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.