2019-11-09 23:03 — asano
カテゴリー:
なんか大昔のI/O誌の特集みたいなタイトルですが...
The Macroassembler ASは対応しているCPU多いのですが、先日のMN1613など対応していないものもあります。他にも手持ちのCPUで非対応のものにZ8001, NS32016, HD1-6120, HD642032なんかもあります。
日本独自のMN1613やHD642032(TRON-Chip)はともかく、Z8000(Z8001), NS32000, IM6100(HD1-6120)などは知名度も高いんですけどね。
ASのソースを見ると、codez80.c
とかcode68k.c
のようにcode<CPU名>.c
といったファイルがありCPU別のコードと思われます。これらのファイルサイズを調べると最大のcode68k.c
で160kB程ですが、codescmp.c
, codevector.c
なら7kB程、シンプルなCPUならそう大量のコードを書かなくても対応できるのではないかと思います。
ということでMN1610対応を書いてみることにしました。
code***.c
を参考に推測・試行錯誤した結果です。全体を理解して書いているわけではないので、間違っていたり作者の意図しない使い方をしたりしているかもしれません。ご了承ください。
まずはMN1610系のためのファイルを作成します。ファイル名は何でもよいのですが他に合わせて codemn1610.c
, codemn1610.h
とすることにします。
そのままではコンパイルされないので makedefs.src
に追加しておきます。他のcode***.c
の並びの末尾に追加すればOKです。
それでは codemn1610.c
の中身を書いていきます。
先頭の#include
は適当なcode***.c
を参考にすれば問題ないでしょう。
プロトタイプ宣言を省略するためなのか後ろから前の関数を呼び出す構造になっているので、関数たちは末尾から見ていくほうがわかりやすいと思います。
一番最初(末尾)の関数はこれです。
void codemn1610_init(void)
{
CPUMN1610 = AddCPU("MN1610", SwitchTo_MN1610);
CPUMN1613 = AddCPU("MN1613", SwitchTo_MN1610);
}
AS起動時に呼び出され、処理できるCPU名を登録します。AddCPU()
関数の第1引数はアセンブリソースでCPU MN1610
などと指定されるときの名称、第2引数はCPU
擬似命令で切り替わったときに実行される関数です。戻り値はCPUのIDで、この例のように複数のCPUで同じ関数を登録した場合に後でどちらが選択されているのか判断するために使用できます。
この関数を呼び出すコードはas.c
に追加しなくてはなりません。他のcode***_init();
と同じように追加しておきます。
次は上で登録したSwitchTo_MN1610()
です。
static void SwitchTo_MN1610(void)
{
TurnWords = True;
ConstMode = ConstModeC;
PCSymbol = "*";
HeaderID = 0x6e;
NOPCode = 0x7808; /* MV R0,R0 */
DivideChars = ",";
HasAttrs = False;
ValidSegs = 1 << SegCode;
Grans[SegCode] = 2;
ListGrans[SegCode] = 2;
SegInits[SegCode] = 0;
SegLimits[SegCode] = 0xffff;
MakeCode = MakeCode_MN1610;
IsDef = IsDef_MN1610;
SwitchFrom = SwitchFrom_MN1610;
InitFields();
}
これは前述のようにCPU
擬似命令でMN1610
あるいはMN1613
が選択されたときに実行されます。
TurnWords = True;
これは本来エンディアンの切り替えですが、MN1610系はワードアドレスなのでどちらでもよさそうです。PCSymbol = "*";
式の中で命令のアドレスを参照するときの文字です。NOPCode = 0x7808;
おそらくパディング用と思われますが未確認です。MN1610にはNOP
命令はないので何もしない命令のコードを入れてあります。DivideChars = ",";
オペランドの区切り文字です。HasAttrs = False;
MC68000のMOVE.L
の「L
」のようなものはないのでFalseです。ValidSegs = 1 << SegCode;
セグメント(メモリなどの空間)が一つということでこうしていますが、I/O空間も扱うためには(1 << SegCode) | (1 << SegIO);
とするべきなのかもしれません。Grans[SegCode] = 2;
ワードアドレスなので2、もしバイトアドレスなら1です。ListGrans[SegCode] = 2;
リスティングをワード表示(4桁)にします。SegLimits[SegCode] = 0xffff;
アドレスの上限です。MakeCode = MakeCode_MN1610;
行毎に呼び出される関数を設定します。IsDef = IsDef_MN1610;
詳細不明。とりあえずcodescmp_init();
同様ダミーを設定しています。SwitchFrom = SwitchFrom_MN1610;
CPU
擬似命令で他のCPUが選ばれたときに呼ばれる関数を設定します。InitFields();
命令テーブルを登録します。
続いて他のCPUが選ばれたときの関数です。
static void SwitchFrom_MN1610(void)
{
DeinitFields();
}
これはそのまま名称だけ変えて流用しています。
次は行毎に呼び出される関数です。
static void MakeCode_MN1610(void)
{
CodeLen = 0;
DontPrint = False;
if (Memo("")) return;
if (DecodeIntelPseudo(False)) return;
if (!LookupInstTable(InstTable, OpPart.Str))
WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}
これも流用しています。
DecodeIntelPseudo(False)
を変更すれば擬似命令切り替えられるのかな。既存のMN1610のソースは無いので擬似命令関係の優先度は低いです。新規に書くコードはアセンブラに合わせてしまえば済みますから。
今日の最後は上でダミーと書いたもの。
static Boolean IsDef_MN1610(void)
{
return False;
}
これも流用です。
CPUによってはMemo()
を呼び出しているので、そちらから攻めればわかるのかもしれません。
次回は命令テーブルの登録やオペランドのデコードといった本質のところを書く予定です。
おまけ
現時点でのファイル・パッチを添付します。
AS 1.42bld150 でテスト中のものになります。
まだ0ページ間接インデックスがデコードできないとか、MN1613追加命令がないとか、16進定数の書き方とか、いろいろあるとは思いますが。
コメントを追加