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