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