|
|
1.1 root 1: <?php
2: //
3: // nono
4: // Copyright (C) 2022 nono project
5: // Licensed under nono-license.txt
6: //
7:
8: $inst0table = read_instructions("instructions.txt");
9: $funchash = make_funchash($inst0table);
10:
11: $insttable = expand_rrr($inst0table, "rrr", "r");
12: $insttable = expand_rrr($insttable, "sss", "s");
13: $insttable = expand_fff($insttable);
14: $insttable = expand_ww($insttable);
15: $insttable = expand_zz($insttable);
16: $insttable = expand_vvv($insttable);
17: $insttable = expand_bbb($insttable);
18:
19: $insttable = make($insttable);
20:
21: output_switch($insttable);
22: output_header();
23:
24: exit(0);
25: ?>
26: <?php
27: function usage()
28: {
29: global $argv;
30:
31: print "Usage: ${argv[0]}\n";
32: exit(1);
33: }
34:
35: // instructions.txt を読み込んで $insttable*[] 配列にする
36: function read_instructions($filename)
37: {
38: $fp = fopen($filename, "r");
39: if ($fp === false) {
40: print "fopen failed: {$filename}\n";
41: exit(1);
42: }
43:
44: $insttable = array();
45:
46: while (($line = fgets($fp))) {
47: $line = trim(preg_replace("/;.*/", "", $line));
48: if ($line == "") {
49: continue;
50: }
51:
52: // 1行はこんな感じ
53: // 00rrr100 01 inc_r INC r
54: // CB:00000rrr 01 rlc RLC r
55: // 列は1つ以上のタブで区切られている。
56: // $bits … ビットパターン
57: // $cpus … CPU 区別
58: // $name … 関数名
59: // $text … ニーモニック
60:
61: $column = preg_split("/\t+/", $line, -1, PREG_SPLIT_NO_EMPTY);
62: $bits = $column[0];
63: // 1バイト目なら ":" をつけて書式を揃えておく
64: if (!preg_match("/:/", $bits)) {
65: $bits = ":{$bits}";
66: }
67: $cpus = $column[1];
68: $name = $column[2];
69: $text = isset($column[3]) ? $column[3] : strtoupper($name);
70:
71: $newinst = array(
72: "bits" => trim($bits),
73: "cpus" => trim($cpus),
74: "name" => trim($name),
75: "text" => trim($text),
76: );
77:
78: // $insttable[] に追加。
79: $insttable[$bits] = $newinst;
80: }
81: fclose($fp);
82:
83: if (0) {
84: print "read_instruction:\n";
85: foreach ($insttable as $bits => $inst) {
86: printf("%s|%s|%-10s|%s\n",
87: $inst["bits"], $inst["cpus"], $inst["name"], $inst["text"]);
88: }
89: print "\n";
90: }
91:
92: return $insttable;
93: }
94:
95: // 展開前の $insttable から $funchash[] を作成する。
96: // $funchash は関数名をキーにしたハッシュで
97: // $funchash = array(
98: // "add_hl_ww" => array("00ww1001|ADD HL,ww", "DD:00ww1001|ADD IX,ww"),
99: // :
100: // ); のように、関数名とそれに紐づくニーモニックを集めたもの。
101: function make_funchash($insttable)
102: {
103: $funchash = array();
104: foreach ($insttable as $bits => $inst) {
105: // $bits は1ワード目なら : から始まっているので取り除く
106: $bit2 = preg_replace("/^:/", "", $bits);
107: $name = $inst["name"];
108: $text = $inst["text"];
109: switch ($inst["cpus"]) {
110: case "01R":
111: break;
112: case "-1-": // HD64*180 専用命令
113: $text .= "\t(HD64*180 only)";
114: break;
115: case "--R": // R800 専用命令
116: $text .= "\t(R800 only)";
117: break;
118: case "0-R": // Z80/R800 命令 (HD64*180 未実装命令)
119: $text .= "\t(Z80/R800)";
120: break;
121: default:
122: printf("unknown cpu field: {$inst["cpus"]}\n");
123: exit(1);
124: }
125:
126: if (!isset($funchash[$name])) {
127: $funchash[$name] = array();
128: }
129: $funchash[$name][] = "{$bit2}|{$text}";
130: }
131:
132: if (0) {
133: print "make_funchash:\n";
134: foreach ($funchash as $name => $lines) {
135: printf("%-10s", $name);
136: foreach ($lines as $str) {
137: print " \"{$str}\"";
138: }
139: print "\n";
140: }
141: print "\n";
142: }
143:
144: return $funchash;
145: }
146:
147: // $insttable の rrr を展開する。
148: // regbits は "rrr" か "sss" で、bits 内を置換する文字列。
149: // regtext は "r" か "s" で、text 内を置換する文字列。
150: function expand_rrr($insttable, $regbits, $regtext)
151: {
152: $rrr = array(
153: "000" => "B",
154: "001" => "C",
155: "010" => "D",
156: "011" => "E",
157: "100" => "H",
158: "101" => "L",
159: // "110" (HL) は出力しない
160: "111" => "A",
161: );
162: $inst2table = array();
163: foreach ($insttable as $bits => $inst) {
164: if (preg_match("/{$regbits}/", $bits)) {
165: foreach ($rrr as $rbits => $rname) {
166: $inst2 = $inst;
167: $newbits = preg_replace("/{$regbits}/", $rbits, $bits);
168: $inst2["bits"] = $newbits;
169: $inst2["text"] =
170: preg_replace("/{$regtext}/", $rname, $inst["text"]);
171: $inst2table[$newbits] = $inst2;
172: }
173: } else {
174: // 上書きする際は後者優先
175: $inst2table[$bits] = $inst;
176: }
177: }
178:
179: // デバッグ表示
180: if (0) {
181: print "expand_rrr({$regbits})\n";
182: foreach ($inst2table as $inst) {
183: printf("%s|%-10s|%s\n",
184: $inst["bits"], $inst["name"], $inst["text"]);
185: }
186: }
187:
188: return $inst2table;
189: }
190:
191: // $insttable の fff を展開する。
192: function expand_fff($insttable)
193: {
194: $fff = array(
195: "000" => "NZ",
196: "001" => "NC",
197: "010" => "PO",
198: "011" => "P",
199: "100" => "Z",
200: "101" => "C",
201: "110" => "PE",
202: "111" => "M",
203: );
204:
205: $inst2table = array();
206: foreach ($insttable as $bits => $inst) {
207: if (preg_match("/fff/", $bits)) {
208: foreach ($fff as $fbits => $fname) {
209: $inst2 = $inst;
210: $newbits = preg_replace("/fff/", $fbits, $bits);
211: $inst2["bits"] = $newbits;
212: $inst2["text"] = preg_replace("/f/", $fname, $inst["text"]);
213: $inst2table[$newbits] = $inst2;
214: }
215: } else {
216: // 上書きする際は後者優先
217: $inst2table[$bits] = $inst;
218: }
219: }
220:
221: // デバッグ表示
222: if (0) {
223: print "expand_fff\n";
224: foreach ($inst2table as $inst) {
225: printf("%s|%-10s|%s\n",
226: $inst["bits"], $inst["name"], $inst["text"]);
227: }
228: }
229:
230: return $inst2table;
231: }
232:
233: // $insttable の ww を展開する。
234: function expand_ww($insttable)
235: {
236: $ww = array(
237: "00" => "BC",
238: "01" => "DE",
239: "10" => "HL",
240: "11" => "SP",
241: );
242:
243: $inst2table = array();
244: foreach ($insttable as $bits => $inst) {
245: if (preg_match("/ww/", $bits)) {
246: foreach ($ww as $wbits => $wname) {
247: $inst2 = $inst;
248: $newbits = preg_replace("/ww/", $wbits, $bits);
249: $inst2["bits"] = $newbits;
250: $inst2["text"] = preg_replace("/ww/", $wname, $inst["text"]);
251: $inst2table[$newbits] = $inst2;
252: }
253: } else {
254: // 上書きする際は後者優先
255: $inst2table[$bits] = $inst;
256: }
257: }
258:
259: // デバッグ表示
260: if (0) {
261: print "expand_ww\n";
262: foreach ($inst2table as $inst) {
263: printf("%s|%-10s|%s\n",
264: $inst["bits"], $inst["name"], $inst["text"]);
265: }
266: }
267:
268: return $inst2table;
269: }
270:
271: // $insttable の zz を展開する。
272: function expand_zz($insttable)
273: {
274: $zz = array(
275: "00" => "BC",
276: "01" => "DE",
277: "10" => "HL",
278: "11" => "AF",
279: );
280:
281: $inst2table = array();
282: foreach ($insttable as $bits => $inst) {
283: if (preg_match("/zz/", $bits)) {
284: foreach ($zz as $zbits => $zname) {
285: $inst2 = $inst;
286: $newbits = preg_replace("/zz/", $zbits, $bits);
287: $inst2["bits"] = $newbits;
288: $inst2["text"] = preg_replace("/zz/", $zname, $inst["text"]);
289: $inst2table[$newbits] = $inst2;
290: }
291: } else {
292: // 上書きする際は後者優先
293: $inst2table[$bits] = $inst;
294: }
295: }
296:
297: // デバッグ表示
298: if (0) {
299: print "expand_zz\n";
300: foreach ($inst2table as $inst) {
301: printf("%s|%-10s|%s\n",
302: $inst["bits"], $inst["name"], $inst["text"]);
303: }
304: }
305:
306: return $inst2table;
307: }
308:
309: // $insttable の vvv を展開する。
310: function expand_vvv($insttable)
311: {
312: $vvv = array(
313: "000" => "00H",
314: "001" => "10H",
315: "010" => "20H",
316: "011" => "30H",
317: "100" => "08H",
318: "101" => "18H",
319: "110" => "28H",
320: "111" => "38H",
321: );
322:
323: $inst2table = array();
324: foreach ($insttable as $bits => $inst) {
325: if (preg_match("/vvv/", $bits)) {
326: foreach ($vvv as $vbits => $vname) {
327: $inst2 = $inst;
328: $newbits = preg_replace("/vvv/", $vbits, $bits);
329: $inst2["bits"] = $newbits;
330: $inst2["text"] = preg_replace("/v/", $vname, $inst["text"]);
331: $inst2table[$newbits] = $inst2;
332: }
333: } else {
334: // 上書きする際は後者優先
335: $inst2table[$bits] = $inst;
336: }
337: }
338:
339: // デバッグ表示
340: if (0) {
341: print "expand_vvv\n";
342: foreach ($inst2table as $inst) {
343: printf("%s|%-10s|%s\n",
344: $inst["bits"], $inst["name"], $inst["text"]);
345: }
346: }
347:
348: return $inst2table;
349: }
350:
351: // $insttable の bbb を展開する。
352: function expand_bbb($insttable)
353: {
354: $inst2table = array();
355: foreach ($insttable as $bits => $inst) {
356: if (preg_match("/bbb/", $bits)) {
357: for ($b = 0; $b < 8; $b++) {
358: $inst2 = $inst;
359: $nbits = "00" . base_convert($b, 10, 2);
360: $nbits = substr($nbits, -3);
361: $newbits = preg_replace("/bbb/", $nbits, $bits);
362: $inst2["bits"] = $newbits;
363: $inst2["text"] = preg_replace("/b/", "{$b}", $inst["text"]);
364: $inst2table[$newbits] = $inst2;
365: }
366: } else {
367: // 上書きする際は後者優先
368: $inst2table[$bits] = $inst;
369: }
370: }
371:
372: // デバッグ表示
373: if (0) {
374: print "expand_bbb\n";
375: foreach ($inst2table as $inst) {
376: printf("%s|%-10s|%s\n",
377: $inst["bits"], $inst["name"], $inst["text"]);
378: }
379: }
380:
381: return $inst2table;
382: }
383:
384: // ":00000000" => {
385: // "bits" => ":00000000",
386: // "name" => "nop", ...
387: // }
388: // 形式をプレフィックスを1段目にして
389: // "" => {
390: // "00000000" => {
391: // "bits" => "00000000",
392: // "name" => "nop", ...
393: // },
394: // }
395: // の形式に組み替えたものを返す。
396: //
397: // ついでに namehash[$name] => $name の一覧もグローバル変数で返す。
398: function make($insttable)
399: {
400: global $argv;
401: global $namehash;
402:
403: $inst2table = array();
404:
405: ksort($insttable);
406: foreach ($insttable as $bits => $inst) {
407: preg_match("/^([^:]*):(.*)/", $bits, $m);
408: $pre = $m[1];
409: $bits = $m[2];
410:
411: $inst2 = $inst;
412: $inst2["bits"] = $bits;
413:
414: if (!isset($inst2table[$pre])) {
415: $inst2table[$pre] = array();
416: }
417: $inst2table[$pre][$bits] = $inst2;
418: }
419:
420: if (0) {
421: print "make:\n";
422: foreach ($inst2table as $pre => $table) {
423: foreach ($table as $bits => $inst) {
424: printf("%5s|%s|%-10s|%s\n",
425: $pre, $inst["bits"], $inst["name"], $inst["text"]);
426: }
427: }
428: print "\n";
429: }
430:
431: // 00 テーブルはすべて埋まっていると分かっているので、ここでチェック。
432: // $bits によるハッシュになっているので数だけ数えればいいか?
433: if (count($inst2table[""]) != 256) {
434: printf("{$argv[0]}: Number of entries in table 00 is wrong: %d\n",
435: count($inst2table[""]));
436: exit(1);
437: }
438: // 他のテーブルはすべて埋まってるわけではないので、数えない。
439:
440: foreach ($inst2table as $pre => $table) {
441: foreach ($table as $bits => $inst) {
442: $name = $inst["name"];
443: $namehash[$name] = $name;
444: }
445: }
446: ksort($namehash);
447:
448: return $inst2table;
449: }
450:
451:
452: function output_switch($insttable)
453: {
454: foreach ($insttable as $pre => $table) {
455: output_switch_sub($pre, $table);
456: }
457: }
458:
459: function output_switch_sub($pre, $table)
460: {
461: global $inst0table;
462: global $funchash;
463:
464: // func2 は [256] => name 形式で、存在してるものだけ抜き出したもの。
465: $func2 = array();
466: foreach ($table as $bits => $inst) {
467: $i = base_convert($bits, 2, 10);
468: $i += 0;
469: $func2[$i] = $inst["name"];
470: }
471:
472: $out = "";
473: $cpp = "";
474: for ($i = 0; $i < 256; $i++) {
475: if (!isset($func2[$i])) {
476: continue;
477: }
478: $funcname = $func2[$i];
479:
480: $cases = array();
481: // この関数名を持つブロックに属する人全員のコメント集める。
482: // このブロック自身もここで集計するため $j は $i から始める。
483: for ($j = $i; $j < 256; $j++) {
484: if (isset($func2[$j]) && $func2[$j] == $funcname) {
485: $cases[] = $j;
486:
487: // 集計したら消す
488: unset($func2[$j]);
489: }
490: }
491:
492: // case 部分の出力。
493: // 4個くらいまでなら case 文を羅列、
494: // それ以上連続してたら範囲で出力。
495: $cont = true;
496: for ($k = 1; $k < count($cases); $k++) {
497: if ($cases[$k] - $cases[$k - 1] != 1) {
498: $cont = false;
499: break;
500: }
501: }
502: if ($cont == true && count($cases) > 4) {
503: $c = array_shift($cases);
504: $bits = decbin8($c);
505: $out .= sprintf("\t case 0b{$bits} // 0x%02x\n", $c);
506:
507: $c = array_pop($cases);
508: $bits = decbin8($c);
509: $out .= sprintf("\t ... 0b{$bits}: // 0x%02x\n", $c);
510: } else {
511: foreach ($cases as $c) {
512: $bits = decbin8($c);
513: $out .= sprintf("\t case 0b{$bits}: // 0x%02x\n", $c);
514: }
515: }
516: // 関数
517: $out .= "\t\tOP_FUNC({$funcname});\n";
518: $out .= "\t\tbreak;\n";
519:
520: // ソースの雛形を出力。
521: // DD は基本 00 と同じ処理関数を使うので、関数本体の出力は1回のみ。
522: if (!isset($funchash[$funcname])) {
523: print "$funcname not found\n";
524: exit(1);
525: }
526: if (!isset($funchash[$funcname]["processed"])) {
527: // $funchash からこの関数のコメントを生成
528: $lines = $funchash[$funcname];
529: // プレフィックスの長さを調べる
530: $plen = 0;
531: foreach ($lines as $line) {
532: $m = preg_split("/\|/", $line);
533: preg_match("/([A-Z_]+:)?(.*)/", $m[0], $b);
534: $plen = max($plen, strlen($b[1]));
535: }
536: foreach ($lines as $line) {
537: $m = preg_split("/\|/", $line);
538: preg_match("/([A-Z_]+:)?(.*)/", $m[0], $b);
539: $cpp .= sprintf("// %{$plen}s%s: %s\n", $b[1], $b[2], $m[1]);
540: }
541: $cpp .= "OP_DEF({$funcname})\n";
542: $cpp .= "{\n";
543: $cpp .= "\tOP_FUNC(unimpl);\n";
544: $cpp .= "}\n";
545: $cpp .= "\n";
546:
547: $funchash[$funcname]["processed"] = true;
548: }
549: }
550:
551: if ($pre == "") {
552: $pre = "00";
553: } else {
554: $pre = strtolower($pre);
555: }
556: write_file("switch_{$pre}.inc.new", $out);
557: write_file("ops_{$pre}.cpp.new", $cpp);
558: }
559:
560: // ヘッダを出力
561: function output_header()
562: {
563: global $namehash;
564:
565: $out = "";
566: foreach ($namehash as $name) {
567: $out .= "OP_PROTO({$name});\n";
568: }
569:
570: write_file("ops.h.new", $out);
571: }
572:
573: // 数値 $num を2進数8桁の文字列にして返す
574: function decbin8($num)
575: {
576: $str = str_repeat("0", 7) . decbin($num);
577: $str = substr($str, -8);
578: return $str;
579: }
580:
581: // ファイル出力
582: function write_file($filename, $str)
583: {
584: $fp = fopen($filename, "w");
585: fwrite($fp, $str);
586: fclose($fp);
587:
588: print "Output: {$filename}\n";
589: }
590: ?>
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.