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