ステップ1
まず、Z80.hを編集してZ80クラスをコンフィグします。
ステップ2
Z80クラスを派生させてメモリの読み書き、I/Oに関するメソッドを記述します。
BUILTIN_MEMORYを1に設定したときは(1)と(2)は無くなります。
CLOCK_EMUが2でなければ(5)は不要です。
(1) メモリを読む
int32_t load(uint16_t adr);
16ビット長のアドレスadrで指定されるメモリの値(8ビット)を0拡張して返します。
バンク切り替えなどは、派生クラスで記述します。
(2) メモリに書く
void store(uint16_t adr, uint8_t data);
16ビット長のアドレスadrで指定されるメモリにdataを書き込みます。
バンク切り替えなどは、派生クラスで記述します。
(3) 入力
int32_t input(uint16_t adr);
16ビット長のアドレスadrで指定されるポートの値(8ビット)を0拡張して返します。
adrの上位8ビットは、命令によりイミディエイト値またはBレジスタの値になります。
(4) 出力
void output(uint16_t adr, uint8_t data);
16ビット長のアドレスadrで指定されるポートにdataを出力します。
adrの上位8ビットは、命令によりイミディエイト値またはBレジスタの値になります。
(5) メモリを読む(M1サイクル;CLOCK_EMUが2のときのみ)
int32_t loadm1(uint16_t adr);
基本的に(1)と同じですが、M1サイクルではこちらが呼ばれます。
(1)〜(5)で、CLOCK_EMUを2に設定したときは、protectedメンバであるwaitにウェイト数を代入します。
例)
int32_t load(uint16_t adr) {
if (adr & 0x8000) wait = 0; // RAM access
else wait = 1; // ROM access
return m[adr];
}
他に、派生クラスからアクセスできるメンバには以下のものがあります。
・ プログラムカウンタ
pc
・ メモリ
m
ビルトインメモリ時、SetMemoryPtr()でセットした値です。
ステップ3
派生クラスを生成して以下のメソッドを呼び出すことにより実行します。
(1) CLOCK_EMUが0のとき
int32_t Execute(int32_t n);
n命令実行し、通常は0を返します。
HALT命令を実行し、割込みが受け付けられなかった場合は負数を返します。この絶対値は実行しなかった命令数です。
(2) CLOCK_EMUが1以上のとき
int32_t Execute(int32_t n);
nクロック実行し、実行し過ぎたクロック数を返します。
HALT命令を実行し、割込みが受け付けられなかった場合は負数を返します。この絶対値は実行しなかったクロック数です。
HALT命令を実行した場合は0を返します。
nに0を指定した場合は、1命令実行してかかったクロック数を返します。
サンプルプログラム
#include "Z80.h"
#include <stdio.h>
class Sample : public Z80 {
public:
int32_t load(uint16_t adr) { return m[adr]; }
void store(uint16_t adr, uint8_t data) { m[adr] = data; }
int32_t input(uint16_t adr) { return 0; }
void output(uint16_t adr, uint8_t data) { printf("output: %x %x\n", adr, data); }
private:
static uint8_t m[0x10000];
};
uint8_t Sample::m[0x10000] = {
0x3c, // LABEL1:INC A
0xd3, 0x00, // OUT (0),A
0x18, 0xfb // JR LABEL1
};
int main() {
Sample s;
s.Execute(100);
return 0;
}