|
|
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: $comm = array(); ! 530: // プレフィックスの長さを調べる ! 531: $plen = 0; ! 532: foreach ($lines as $line) { ! 533: $m = preg_split("/\|/", $line); ! 534: preg_match("/([A-Z_]+:)?(.*)/", $m[0], $b); ! 535: $plen = max($plen, strlen($b[1])); ! 536: } ! 537: foreach ($lines as $line) { ! 538: $m = preg_split("/\|/", $line); ! 539: preg_match("/([A-Z_]+:)?(.*)/", $m[0], $b); ! 540: $cpp .= sprintf("// %{$plen}s%s: %s\n", $b[1], $b[2], $m[1]); ! 541: } ! 542: $cpp .= "OP_DEF({$funcname})\n"; ! 543: $cpp .= "{\n"; ! 544: $cpp .= "\tOP_FUNC(unimpl);\n"; ! 545: $cpp .= "}\n"; ! 546: $cpp .= "\n"; ! 547: ! 548: $funchash[$funcname]["processed"] = true; ! 549: } ! 550: } ! 551: ! 552: if ($pre == "") { ! 553: $pre = "00"; ! 554: } else { ! 555: $pre = strtolower($pre); ! 556: } ! 557: write_file("switch_{$pre}.inc.new", $out); ! 558: write_file("ops_{$pre}.cpp.new", $cpp); ! 559: } ! 560: ! 561: // ヘッダを出力 ! 562: function output_header() ! 563: { ! 564: global $namehash; ! 565: ! 566: $out = ""; ! 567: foreach ($namehash as $name) { ! 568: $out .= "OP_PROTO({$name});\n"; ! 569: } ! 570: ! 571: write_file("ops.h.new", $out); ! 572: } ! 573: ! 574: // 数値 $num を2進数8桁の文字列にして返す ! 575: function decbin8($num) ! 576: { ! 577: $str = str_repeat("0", 7) . decbin($num); ! 578: $str = substr($str, -8); ! 579: return $str; ! 580: } ! 581: ! 582: // ファイル出力 ! 583: function write_file($filename, $str) ! 584: { ! 585: $fp = fopen($filename, "w"); ! 586: fwrite($fp, $str); ! 587: fclose($fp); ! 588: ! 589: print "Output: {$filename}\n"; ! 590: } ! 591: ?>
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.