|
|
nono 1.5.0
<?php
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
if ($argc < 2) {
usage();
}
$insttable = read_instructions("instructions.txt");
$insttable = expand($insttable);
makeop12($insttable);
makeop12hash();
output_cpp();
output_switch();
output_header();
exit(0);
?>
<?php
function usage()
{
global $argv;
print "Usage: ${argv[0]}\n";
exit(1);
}
// instructions.txt を読み込んで $insttable[] 配列にする
function read_instructions($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行はこんな感じ
// 000000_DDDDD_SSSSS_nnnnnn_nnnnnnnnnn ld_d_xrf ld.d(XRF)
// 列は1つ以上のタブで区切られている。
// $bits … ビットパターン
// $name … 関数名
// $text … コメント
$column = preg_split("/\t+/", $line, -1, PREG_SPLIT_NO_EMPTY);
$bits = $column[0];
$name = $column[1];
$text = isset($column[2]) ? $column[2] : "";
$name = trim($name);
$text = trim($text);
// 命令ワード32ビットは 6-5-5 6-5-5 ビットに分解でき、ここでは
// 上位下位ハーフワードの上位6ビットずつを合わせた12ビットを使う。
$bits12 = preg_replace("/(......)_.........._(......)_........../",
"$1$2", $bits);
$bits12 = preg_replace("/[^10n]/", "n", $bits12);
$newinst = array(
"bits" => $bits,
"bits12" => $bits12,
"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|%-10s|%s\n",
$inst["bits"], $inst["bits12"], $inst["name"], $inst["text"]);
}
}
return $insttable;
}
// $insttable を bit12 で展開する。
// これで bits12 には未確定部分がなくなる。
function expand($insttable)
{
do {
$expanded = false;
$inst2table = array();
foreach ($insttable as $inst) {
if (preg_match("/^([01]*)n(.*)/", $inst["bits12"], $m)) {
// $m[1] は先頭から確定部分
// $m[2] が未処理部分
$inst2table[] = array(
"bits" => $inst["bits"],
"bits12" => "{$m[1]}0{$m[2]}",
"name" => $inst["name"],
"text" => $inst["text"],
);
$inst2table[] = array(
"bits" => $inst["bits"],
"bits12" => "{$m[1]}1{$m[2]}",
"name" => $inst["name"],
"text" => $inst["text"],
);
$expanded = true;
} else {
$inst2table[] = $inst;
}
}
// 出来上がったのを差し替える
$insttable = $inst2table;
} while ($expanded);
// デバッグ表示
if (0) {
print "expand\n";
foreach ($insttable as $inst) {
printf("%s| %s|%s\n",
$inst["bits12"], $inst["bits"], $inst["name"]);
}
}
return $insttable;
}
// $insttable を12ビット(4096個)の $op12table に展開。
// $optable = array(
// 0 => &$inst0,
// 1 => &$inst1,
// :
// 命令の存在するところだけ埋められる。
// )
function makeop12($insttable)
{
global $op12table;
$op12table = array();
foreach ($insttable as $inst) {
$num12 = bindec($inst["bits12"]);
$op12table[$num12] = $inst;
}
// デバッグ表示
if (0) {
print "makeop12\n";
for ($i = 0; $i < 4096; $i++) {
$b = str_repeat("0", 11) . decbin($i);
$b = substr($b, -12);
printf("%03x|%s|", $i, $b);
if (isset($op12table[$i])) {
$inst = $op12table[$i];
print "{$inst["bits"]}|{$inst["name"]}\n";
} else {
print "illegal\n";
}
}
}
}
// op12table から op12hash を作成
// op12table は [4096] => array(inst, inst, ...)
// op12hash は [name] => name のハッシュで、実際に使われてる関数名一覧。
function makeop12hash()
{
global $op12table;
global $op12hash;
$op12hash = array();
foreach ($op12table as $inst) {
$name = $inst["name"];
$op12hash[$name] = $name;
}
}
// ソースの雛形を出力
function output_cpp()
{
global $insttable;
global $op12table;
global $op12hash;
$cpp = "";
foreach ($op12hash as $name) {
// name から $inst を引く。うーん。
$inst = array();
foreach ($insttable as $i) {
if ($i["name"] == $name) {
$inst = $i;
break;
}
}
$text = "";
if ($inst["text"] != "") {
$text = "\t{$inst["text"]}";
}
// ソース
$cpp .= <<<__EOM__
// {$inst["bits"]}{$text}
OP_DEF({$name})
{
OP_FUNC(unimpl);
}
__EOM__;
}
// ファイルに出力
write_file("ops.cpp.new", $cpp);
}
// disasm 用の switch-case 文を出力
function output_switch()
{
global $op12table;
// func2 は [1024] => name 形式で不当命令ブロックを抜いたもの
$func2 = array();
foreach ($op12table as $i => $inst) {
$func2[$i] = $inst["name"];
}
$out = "";
for ($i = 0; $i < 4096; $i++) {
if (!isset($func2[$i])) {
continue;
}
$funcname = $func2[$i];
$cases = array();
// この関数名を持つブロックに属する人全員のコメント集める。
// このブロック自身もここで集計するため $j は $i から始める。
for ($j = $i; $j < 4096; $j++) {
if (isset($func2[$j]) && $func2[$j] == $funcname) {
// case
$cases[] = $j;
// 今のところコメントはないのでスルー
// 集計したら消す
unset($func2[$j]);
}
}
// case 部分の出力
// 4個くらいまでなら case 文を羅列、
// それ以上連続してたら範囲で出力。
$cont = true;
for ($k = 1; $k < count($cases); $k++) {
if ($cases[$k] - $cases[$k - 1] != 1) {
$cont = false;
break;
}
}
if ($cont == true && count($cases) > 4) {
$c = array_shift($cases);
$bits12 = str_repeat("0", 11) . decbin($c);
$bits12 = preg_replace("/.*(......)(......)/", "$1_$2", $bits12);
$out .= sprintf("\t case 0x%03x", $c);
$out .= "\t// {$bits12}\n";
$c = array_pop($cases);
$bits12 = str_repeat("0", 11) . decbin($c);
$bits12 = preg_replace("/.*(......)(......)/", "$1_$2", $bits12);
$out .= sprintf("\t ... 0x%03x:", $c);
$out .= "\t// {$bits12}\n";
} else {
foreach ($cases as $c) {
$bits12 = str_repeat("0", 11) . decbin($c);
$bits12 = preg_replace("/.*(......)(......)/", "$1_$2",$bits12);
$out .= sprintf("\t case 0x%03x:", $c);
$out .= "\t// {$bits12}\n";
}
}
// 関数
$out .= "\t\tOP_FUNC({$funcname});\n";
$out .= "\t\tbreak;\n";
}
write_file("switch.inc.new", $out);
}
// ヘッダを出力
function output_header()
{
global $op12hash;
$out = "";
foreach ($op12hash as $name) {
$out .= "OP_PROTO({$name});\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.