|
|
nono 1.7.0
<?php
//
// nono
// Copyright (C) 2014 Tetsuya Isaki
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// gentable.php
// inst*.txt からディスパッチャ部分を生成
//
// switch10 switch-case (10bit) を出力
// switch13 switch-case (13bit) を出力
// mmu8 MMU の switch-case (8bit) を出力
//
if ($argc < 2) {
usage();
}
init_ea();
// 今の所コアも逆アセンブラも 020 以降を区別なく展開する。
$mputype = "[2346]";
switch ($argv[1]) {
case "switch10":
case "switch13":
if (preg_match("/10$/", $argv[1])) {
$nb = 10;
} else {
$nb = 13;
}
$insttable = read_instructions($mputype, "instructions.txt");
$insttable = expandDST($insttable);
$insttable = expandSS($insttable);
$insttable = expandCCC($insttable);
makeop16($insttable);
$op10table = makeop10table();
$op10func = makeop10func($op10table);
$op10hash = makeop10hash($op10func);
output_switch($op10table, $op10func);
output_header($op10hash);
break;
case "mmu8":
$nb = 8;
$insttable = read_instructions($mputype, "instmmu.txt");
makeop16($insttable);
$op10table = makeop10table();
$op10func = makeop10func($op10table);
$op10hash = makeop10hash($op10func);
output_switch($op10table, $op10func);
output_header($op10hash);
break;
default:
usage();
}
exit(0);
?>
<?php
function usage()
{
global $argv;
print "Usage: {$argv[0]} switch10 | switch13\n";
print " input: instruction.txt\n";
print " output: switch.h.new, ops.cpp.new, ops.h.new\n";
print "Usage: {$argv[0]} mmu8\n";
print " input: instmmu.txt\n";
print " output: \n";
exit(1);
}
function init_ea()
{
global $ambits;
// アドレッシングモードの展開パターン
$ambits = array(
"d" => "000rrr",
"a" => "001rrr",
"m" => "010rrr",
"+" => "011rrr",
"-" => "100rrr",
"r" => "101rrr",
"x" => "110rrr",
"w" => "11100r",
"p" => "11101r",
"i" => "111100",
);
}
// instructions.txt を読み込んで $insttable[] 配列にする
function read_instructions($mputype, $filename)
{
$fp = fopen($filename, "r");
if ($fp === false) {
print "fopen failed: {$filename}\n";
exit(1);
}
$insttable = array();
while (($line = fgets($fp))) {
$line = trim(preg_replace("/;.*/", "", $line));
if ($line == "") {
continue;
}
// 1行はこんな感じ
// 00000000SSmmmrrr d.m+-rxw.. 034 ori_S ORI.S #<imm>,<ea>
// 列は1つ以上のタブで区切られている。
// $bits … 1ワード目のビットパターン
// $addr … 対応しているアドレッシングモード
// $mpu … MPU ごとのサポート有無
// $name … 関数名
// $text … コメント
list ($bits, $addr, $mpu, $name, $text) =
preg_split("/\t+/", $line, -1, PREG_SPLIT_NO_EMPTY);
$name = trim($name);
$text = trim($text);
// ターゲット CPU の命令のみ対象にする
if (preg_match("/{$mputype}/", $mpu) == false) {
continue;
}
$newinst = array(
"bits" => $bits,
"addr" => $addr,
"mpu" => $mpu,
"name" => $name,
"text" => $text,
);
// $insttable[] に追加する。
// ただし bits が同じものがあれば text だけ追加。
if (isset($insttable[$bits])) {
$insttable[$bits]["text"] .= ";{$text}";
} else {
$insttable[$bits] = $newinst;
}
}
fclose($fp);
// デバッグ表示
if (0) {
print "read_instruction\n";
foreach ($insttable as $inst) {
printf("%s|%s|%-14s|%s\n",
$inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
}
}
return $insttable;
}
// $insttable のうち MOVE の DST を展開する
function expandDST($insttable)
{
$dst = array(
array("xxx000", "dn", "Dx"),
array("xxx010", "anin", "(Ax)"),
array("xxx011", "anpi", "(Ax)+"),
array("xxx100", "anpd", "-(Ax)"),
array("xxx101", "andi", "d16(Ax)"),
array("xxx110", "anix", "(Ax,IX)"),
array("000111", "absw", "Abs.W"),
array("001111", "absl", "Abs.L"),
);
$inst2table = array();
foreach ($insttable as $inst) {
if (preg_match("/RRRMMM/", $inst["bits"])) {
foreach ($dst as $d) {
$inst2 = $inst;
$inst2["bits"] = preg_replace("/RRRMMM/", $d[0], $inst["bits"]);
$inst2["name"] = preg_replace("/DD/", $d[1], $inst["name"]);
$inst2["text"] = preg_replace("/DD/", $d[2], $inst["text"]);
$inst2table[] = $inst2;
}
} else {
$inst2table[] = $inst;
}
}
// デバッグ表示
if (0) {
print "expandDST\n";
foreach ($inst2table as $inst) {
printf("%s|%s|%-14s|%s\n",
$inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
}
}
return $inst2table;
}
// $insttable の SS と s を展開する
// これらは必ず上位10ビット内にあるので (というか下位6ビット以内に現れる
// やつはテキストにする時点で手動で展開してあるので)、
// サイズ別の関数に展開できる。
function expandSS($insttable)
{
$s3 = array(
array("00", "_b", ".B"),
array("01", "_w", ".W"),
array("10", "_l", ".L"),
);
$s2 = array(
array("0", "_w", ".W"),
array("1", "_l", ".L"),
);
$inst2table = array();
foreach ($insttable as $inst) {
if (preg_match("/SS/", $inst["bits"])) {
foreach ($s3 as $s) {
$inst2 = $inst;
$inst2["bits"] = preg_replace("/SS/", $s[0], $inst["bits"]);
$inst2["name"] = preg_replace("/_S/", $s[1], $inst["name"]);
$inst2["text"] = preg_replace("/\.S/", $s[2], $inst["text"]);
$inst2table[] = $inst2;
}
} else if (preg_match("/s/", $inst["bits"])) {
foreach ($s2 as $s) {
$inst2 = $inst;
$inst2["bits"] = preg_replace("/s/", $s[0], $inst["bits"]);
$inst2["name"] = preg_replace("/_S/", $s[1], $inst["name"]);
$inst2["text"] = preg_replace("/\.S/", $s[2], $inst["text"]);
$inst2table[] = $inst2;
}
} else {
$inst2table[] = $inst;
}
}
// デバッグ表示
if (0) {
print "expandSS\n";
foreach ($inst2table as $inst) {
printf("%s|%s|%-14s|%s\n",
$inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
}
}
return $inst2table;
}
// $insttable の CCC (cpID) を展開する。
// cpID 0 は MMU、1 は FSAVE/FRESTORE なので展開しない。
// cpID 2 は表側で対応してあるのでこちらでは展開しない。
function expandCCC($insttable)
{
$c3 = array(
"011",
"100",
"101",
"110",
"111",
);
$inst2table = array();
foreach ($insttable as $inst) {
if (preg_match("/CCC/", $inst["bits"])) {
foreach ($c3 as $c) {
$inst2 = $inst;
$inst2["bits"] = preg_replace("/CCC/", $c, $inst["bits"]);
$inst2table[] = $inst2;
}
} else {
$inst2table[] = $inst;
}
}
// デバッグ表示
if (0) {
print "expandCCC\n";
foreach ($inst2table as $inst) {
printf("%s|%s|%-14s|%s\n",
$inst["bits"], $inst["addr"], $inst["name"], $inst["text"]);
}
}
return $inst2table;
}
// insttable を16ビットの op16table に展開。
// $optable = array(
// 0 => &$inst0,
// 1 => &$inst1, ...
// )
function makeop16($insttable)
{
global $op16table;
$op16table = array();
foreach ($insttable as $inst) {
makeop16_sub($inst, $inst["bits"]);
}
// デバッグ表示
if (0) {
print "makeop16\n";
for ($i = 0; $i < 65536; $i++) {
$b = str_repeat("0", 15) . decbin($i);
$b = substr($b, -16);
printf("%04x|%s|", $i, $b);
if (isset($op16table[$i])) {
$inst = $op16table[$i];
printf("%s|%-14s|%s\n",
$inst["bits"], $inst["name"], $inst["text"]);
} else {
printf("n/a\n");
}
}
}
}
// inst を1ビットずつ展開する再帰関数
// lv は再帰レベル (デバッグ用)
function makeop16_sub($inst, $bits, $lv = 0)
{
global $op16table;
global $ambits;
// $pos は bits の 0/1 でない最初の文字の位置
$pos = strspn($bits, "01");
if ($pos == 16) {
// 0/1 しか現れていないので確定
// $optable に代入
$num16 = bindec($bits);
$op16table[$num16] = $inst;
} else if (substr($bits, 10) == "mmmrrr") {
// アドレッシングモードによって展開 (いるかな?)
$head = substr($bits, 0, 10);
for ($i = 0; $i < strlen($inst["addr"]); $i++) {
$a = substr($inst["addr"], $i, 1);
if (isset($ambits[$a])) {
makeop16_sub($inst, "{$head}{$ambits[$a]}", $lv + 1);
}
}
} else {
// pos 番目の文字を 0 と 1 に変えて再帰実行
$head = substr($bits, 0, $pos);
$tail = substr($bits, $pos + 1);
makeop16_sub($inst, "{$head}0{$tail}", $lv + 1);
makeop16_sub($inst, "{$head}1{$tail}", $lv + 1);
}
}
// 16ビットテーブルから $nb (10 or 7) ビットテーブルを作成
function makeop10table()
{
global $op16table;
global $nb;
// 1ブロックあたりの命令数 ($nb=10 なら 64)
$ipb = 2 ** (16 - $nb);
$op10table = array();
for ($i = 0; $i < 2 ** $nb; $i++) {
$op10table[$i] = array();
}
for ($i = 0; $i < 65536; $i++) {
if (!isset($op16table[$i])) {
continue;
}
$inst = $op16table[$i];
$j = $i / $ipb;
// op10table[j][] にすでに同じエントリがなければ、追加
$found = false;
foreach ($op10table[$j] as $ji) {
if ($inst["bits"] == $ji["bits"]) {
$found = true;
break;
}
}
if (!$found) {
$op10table[$j][] = $inst;
}
}
// デバッグ用
if (0) {
print "makeop10table\n";
foreach ($op10table as $j => $arr) {
printf("[0x%04x] $%04x|", $j, $j * $ipb);
foreach ($arr as $inst) {
printf(" %s", $inst["name"]);
}
print "\n";
}
}
return $op10table;
}
// op10table から op10func を作成
// op10table は [1024] => array(inst, inst, ...)
// op10func は [1024] => 代表name
function makeop10func($op10table)
{
global $nb;
$op10func = array();
for ($i = 0; $i < 2 ** $nb; $i++) {
$op10func[$i] = "";
}
foreach ($op10table as $i => $arr) {
if (count($arr) == 0) {
$op10func[$i] = "n/a";
continue;
}
// 代表関数はこのブロックの中で最多数なもの。
$funcname = "";
$minbits = (16 - $nb) + 1;
foreach ($arr as $inst) {
// fixed は bits の末尾(16-nb)ビットのうち "0" と "1" の合計数
$chs = count_chars(substr($inst["bits"], $nb), 0);
$fixed = $chs[0x30] + $chs[0x31];
// fixed が小さい = 出力した命令の総数が大きい、なので採用
if ($minbits > $fixed) {
$minbits = $fixed;
$funcname = $inst["name"];
}
}
$op10func[$i] = $funcname;
}
// デバッグ用
if (0) {
print "makeop10func\n";
foreach ($op10func as $i => $name) {
printf("[%4d] %04x {$name}\n", $i, $i * 64);
}
}
return $op10func;
}
// op10hash は [代表name] => 代表name のハッシュ
// 実際に使われてる関数名一覧になる。
function makeop10hash($op10func)
{
$op10hash = array();
foreach ($op10func as $name) {
if ($name != "n/a") {
$op10hash[$name] = $name;
}
}
return $op10hash;
}
// コメントをいい感じにソートしたい。
// $a, $b は "%1111_000000_mmmrrr dam+-rxwpi 034 OPNAME" みたいなやつ。
// これのビットパターン部分でソート。ただし英字は "0"/"1" より前。
// ビットパターンが同じなら出現順を維持(ソースファイルに記載の順)。
function cmp_comments($comm_a, $comm_b)
{
$va = preg_split('/\s+/', $comm_a);
$vb = preg_split('/\s+/', $comm_b);
$a = $va[0];
$b = $vb[0];
// 同じなら a, b の順。
if ($a == $b) {
return -1;
}
// "0"/"1" を英字の後ろにして比較。
$a = str_replace(array("0","1"), array("{","}"), $a);
$b = str_replace(array("0","1"), array("{","}"), $b);
return $a <=> $b;
}
// switch 本体とソースの元を出力
function output_switch($op10table, $op10func)
{
global $nb;
$shift = 16 - $nb;
$ipb = 2 ** (16 - $nb);
$func2 = $op10func;
// 先に不当命令ブロックだけ抜いておく
$not_assigned = array();
for ($i = 0; $i < 2 ** $nb; $i++) {
if (isset($func2[$i]) && $func2[$i] == "n/a") {
$not_assigned[$i] = $i;
unset($func2[$i]);
}
}
$out = "";
$cpp = "";
for ($i = 0; $i < 2 ** $nb; $i++) {
if (!isset($func2[$i])) {
continue;
}
$funcname = $func2[$i];
$cases = array();
$comments = array();
// この関数名をもつブロックに属する人全員のコメントを集める。
// このブロック自身もここで集計するため $j は $i から始める。
for ($j = $i; $j < 2 ** $nb; $j++) {
if (isset($func2[$j]) && $func2[$j] == $funcname) {
foreach ($op10table[$j] as $inst) {
// case
$cases[$j] = $j;
// コメント
// 同一 bits に別の命令が割り当てられているケースは
// ここで2行のコメントに分解
foreach (preg_split("/;/", $inst["text"]) as $text) {
$comm = "{$inst["bits"]} {$inst["addr"]} "
. "{$inst["mpu"]}\t{$text}";
$comments[$comm] = $comm;
}
}
// 集計したら消す
unset($func2[$j]);
}
}
// case
foreach ($cases as $c) {
$out .= sprintf("\t case (0x%04x >> ${shift}):\n", $c * $ipb);
}
// コメント
usort($comments, "cmp_comments");
foreach ($comments as $comm) {
if ($nb == 8) {
// というか MMU というべきか
preg_match("/(...)(.....)(...)(.*)/", $comm, $m);
$comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}_{$m[4]}";
} else {
// 第1ワードというべきか
preg_match("/(....)(......)(.*)/", $comm, $m);
$comm1 = "%{$m[1]}_{$m[2]}_{$m[3]}";
}
$cpp .= "// {$comm1}\n";
// ヘッダに出力するのはビットと名前だけに短縮。
$comm2 = preg_replace('/(\S+)\s+\S+\s+\S+\s+(.*)/', "$1\t$2",
$comm1);
$out .= "\t\t// {$comm2}\n";
}
// 関数
$out .= "\t\tOP_FUNC({$funcname});\n";
$out .= "\t\tbreak;\n";
// ソース
$cpp .= output_func($funcname);
}
// 不当命令のブロックを出力
foreach ($not_assigned as $i) {
$out .= sprintf("\t case (0x%04x >> ${shift}):\n", $i * $ipb);
}
$out .= "\t\tOP_FUNC(illegal);\n";
$out .= "\t\tbreak;\n";
// 不当命令 (ILLEGAL 命令と F ライン命令と不当命令の合流先…)
$cpp .= "// illegal (or not-assigned) instructions\n";
$cpp .= output_func("illegal");
// ファイルに出力
write_file("switch.h.new", $out);
write_file("ops.cpp.new", $cpp);
}
// 関数ソースを出力
function output_func($name)
{
$cpp = "";
$cpp .= "OP_DEF({$name})\n";
$cpp .= "{\n";
$cpp .= "\tOP_FUNC(unimpl);\n";
$cpp .= "}\n";
$cpp .= "\n";
return $cpp;
}
// ヘッダを出力
function output_header($op10hash)
{
$out = "";
foreach ($op10hash as $name) {
$out .= "OP_PROTO({$name});\n";
}
$out .= "OP_PROTO(illegal);\n";
// ファイルに出力
write_file("ops.h.new", $out);
}
// ファイル出力
function write_file($filename, $str)
{
$fp = fopen($filename, "w");
fwrite($fp, $str);
fclose($fp);
print "Output: {$filename}\n";
}
?>
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.