|
|
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: 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:
252: // insttable を16ビットの op16table に展開。
253: // $optable = array(
254: // 0 => &$inst0,
255: // 1 => &$inst1, ...
256: // )
257: function makeop16($insttable)
258: {
259: global $op16table;
260:
261: $op16table = array();
262:
263: foreach ($insttable as $inst) {
264: makeop16_sub($inst, $inst["bits"]);
265: }
266:
267: // デバッグ表示
268: if (0) {
269: print "makeop16\n";
270: for ($i = 0; $i < 65536; $i++) {
271: $b = str_repeat("0", 15) . decbin($i);
272: $b = substr($b, -16);
273: printf("%04x|%s|", $i, $b);
274:
275: if (isset($op16table[$i])) {
276: $inst = $op16table[$i];
277: printf("%s|%-14s|%s\n",
278: $inst["bits"], $inst["name"], $inst["text"]);
279: } else {
280: printf("illegal\n");
281: }
282: }
283: }
284: }
285:
286: // inst を1ビットずつ展開する再帰関数
287: // lv は再帰レベル (デバッグ用)
288: function makeop16_sub($inst, $bits, $lv = 0)
289: {
290: global $op16table;
291: global $ambits;
292:
293: // $pos は bits の 0/1 でない最初の文字の位置
294: $pos = strspn($bits, "01");
295: if ($pos == 16) {
296: // 0/1 しか現れていないので確定
297: // $optable に代入
298: $num16 = bindec($bits);
299: $op16table[$num16] = $inst;
300: } else if (substr($bits, 10) == "mmmrrr") {
301: // アドレッシングモードによって展開 (いるかな?)
302: $head = substr($bits, 0, 10);
303: for ($i = 0; $i < strlen($inst["addr"]); $i++) {
304: $a = substr($inst["addr"], $i, 1);
305: if (isset($ambits[$a])) {
306: makeop16_sub($inst, "{$head}{$ambits[$a]}", $lv + 1);
307: }
308: }
309: } else {
310: // pos 番目の文字を 0 と 1 に変えて再帰実行
311: $head = substr($bits, 0, $pos);
312: $tail = substr($bits, $pos + 1);
313: makeop16_sub($inst, "{$head}0{$tail}", $lv + 1);
314: makeop16_sub($inst, "{$head}1{$tail}", $lv + 1);
315: }
316: }
317:
318: // 16ビットテーブルから $nb (10 or 7) ビットテーブルを作成
319: function makeop10table()
320: {
321: global $op16table;
322: global $nb;
323:
324: // 1ブロックあたりの命令数 ($nb=10 なら 64)
325: $ipb = 2 ** (16 - $nb);
326:
327: $op10table = array();
328: for ($i = 0; $i < 2 ** $nb; $i++) {
329: $op10table[$i] = array();
330: }
331:
332: for ($i = 0; $i < 65536; $i++) {
333: if (!isset($op16table[$i])) {
334: continue;
335: }
336: $inst = $op16table[$i];
337: $j = $i / $ipb;
338:
339: // op10table[j][] にすでに同じエントリがなければ、追加
340: $found = false;
341: foreach ($op10table[$j] as $ji) {
342: if ($inst["bits"] == $ji["bits"]) {
343: $found = true;
344: break;
345: }
346: }
347: if (!$found) {
348: $op10table[$j][] = $inst;
349: }
350: }
351:
352: // デバッグ用
353: if (0) {
354: print "makeop10table\n";
355: foreach ($op10table as $j => $arr) {
356: printf("0x%04x|", $j, $j * $ipb);
357: foreach ($arr as $inst) {
358: printf(" %s", $inst["name"]);
359: }
360: print "\n";
361: }
362: }
363:
364: return $op10table;
365: }
366:
367: // op10table から op10func を作成
368: // op10table は [1024] => array(inst, inst, ...)
369: // op10func は [1024] => 代表name
370: function makeop10func($op10table)
371: {
372: global $nb;
373:
374: $op10func = array();
375: for ($i = 0; $i < 2 ** $nb; $i++) {
376: $op10func[$i] = "";
377: }
378:
379: foreach ($op10table as $i => $arr) {
380: if (count($arr) == 0) {
381: $op10func[$i] = "illegal";
382: continue;
383: }
384:
385: // 代表関数はこのブロックの中で最多数なもの。
386: $funcname = "";
387: $minbits = (16 - $nb) + 1;
388: foreach ($arr as $inst) {
389: // fixed は bits の末尾(16-nb)ビットのうち "0" と "1" の合計数
390: $chs = count_chars(substr($inst["bits"], $nb), 0);
391: $fixed = $chs[0x30] + $chs[0x31];
392: // fixed が小さい = 出力した命令の総数が大きい、なので採用
393: if ($minbits > $fixed) {
394: $minbits = $fixed;
395: $funcname = $inst["name"];
396: }
397: }
398:
399: $op10func[$i] = $funcname;
400: }
401:
402: // デバッグ用
403: if (0) {
404: print "makeop10func\n";
405: foreach ($op10func as $i => $name) {
406: printf("[%4d] %04x {$name}\n", $i, $i * 64);
407: }
408: }
409:
410: return $op10func;
411: }
412:
413: // op10hash は [代表name] => 代表name のハッシュ
414: // 実際に使われてる関数名一覧になる。
415: function makeop10hash($op10func)
416: {
417: $op10hash = array();
418: foreach ($op10func as $name) {
419: $op10hash[$name] = $name;
420: }
421:
422: return $op10hash;
423: }
424:
425: // switch 本体とソースの元を出力
426: function output_switch($op10table, $op10func)
427: {
428: global $nb;
429:
430: $shift = 16 - $nb;
431: $ipb = 2 ** (16 - $nb);
432:
433: $func2 = $op10func;
434:
435: // 先に不当命令ブロックだけ抜いておく
436: $illegals = array();
437: for ($i = 0; $i < 2 ** $nb; $i++) {
438: if (isset($func2[$i]) && $func2[$i] == "illegal") {
439: $illegals[$i] = $i;
440: unset($func2[$i]);
441: }
442: }
443:
444: $out = "";
445: $cpp = "";
446: for ($i = 0; $i < 2 ** $nb; $i++) {
447: if (!isset($func2[$i])) {
448: continue;
449: }
450: $funcname = $func2[$i];
451:
452: $cases = array();
453: $comments = array();
454:
455: // この関数名をもつブロックに属する人全員のコメントを集める。
456: // このブロック自身もここで集計するため $j は $i から始める。
457: for ($j = $i; $j < 2 ** $nb; $j++) {
458: if (isset($func2[$j]) && $func2[$j] == $funcname) {
459: foreach ($op10table[$j] as $inst) {
460: // case
461: $cases[$j] = $j;
462: // コメント
463: // 同一 bits に別の命令が割り当てられているケースは
464: // ここで2行のコメントに分解
1.1.1.6 ! root 465: foreach (preg_split("/;/", $inst["text"]) as $text) {
1.1 root 466: $comm = "{$inst["bits"]}\t{$text}";
467: $comments[$comm] = $comm;
468: }
469: }
470: // 集計したら消す
471: unset($func2[$j]);
472: }
473: }
474: // case
475: foreach ($cases as $c) {
476: $out .= sprintf("\t case (0x%04x >> ${shift}):\n", $c * $ipb);
477: }
478: // コメント
479: foreach ($comments as $comm) {
480: if ($nb == 8) {
481: // というか MMU というべきか
482: preg_match("/(...)(.....)(...)(.*)/", $comm, $m);
483: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}_{$m[4]}";
484: } else {
485: // 第1ワードというべきか
486: preg_match("/(....)(......)(.*)/", $comm, $m);
487: $comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}";
488: }
489: $out .= "\t\t// {$comm1}\n";
490: $cpp .= "// {$comm1}\n";
491: }
492: // 関数
493: $out .= "\t\tOP_FUNC({$funcname});\n";
494: $out .= "\t\tbreak;\n";
495:
496: // ソース
497: $cpp .= output_func($funcname);
498: }
499:
500: // 不当命令ブロックを出力
501: foreach ($illegals as $i) {
502: $out .= sprintf("\t case (0x%04x >> ${shift}):\n", $i * $ipb);
503: }
504: $out .= "\t\tOP_FUNC(illegal);\n";
505: $out .= "\t\tbreak;\n";
506:
507: // 不当命令 (ILLEGAL 命令ではないので最終的にどうするかはある)
508: $cpp .= "// illegal instructions\n";
509: $cpp .= output_func("illegal");
510:
511: // ファイルに出力
512: write_file("switch.h.new", $out);
513: write_file("ops.cpp.new", $cpp);
514: }
515:
516: // 関数ソースを出力
517: function output_func($name)
518: {
519: $cpp = "";
520: $cpp .= "OP_DEF({$name})\n";
521: $cpp .= "{\n";
522: $cpp .= "\tOP_FUNC(unimpl);\n";
523: $cpp .= "}\n";
524: $cpp .= "\n";
525:
526: return $cpp;
527: }
528:
529: // ヘッダを出力
530: function output_header($op10hash)
531: {
532: $out = "";
533: foreach ($op10hash as $name) {
534: $out .= "OP_PROTO({$name});\n";
535: }
536:
537: // ファイルに出力
538: write_file("ops.h.new", $out);
539: }
540:
541: // ファイル出力
542: function write_file($filename, $str)
543: {
544: $fp = fopen($filename, "w");
545: fwrite($fp, $str);
546: fclose($fp);
547:
548: print "Output: {$filename}\n";
549: }
1.1.1.5 root 550: ?>
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.