e-tipsmemo

ごった煮

TD4 Emulator in Rust

思い立ったのでTD4のエミュレータを書いた。
GitHub - katakanan/td4-emu

命令セット

TD4の命令の定義部分。
オリジナルで定義されている命令以外はNOPということにする。

#[derive(Debug, FromPrimitive, PartialEq)]
pub enum Opecode {
    ADD2A = 0b0000,
    ADD2B = 0b0101,
    MOV2A = 0b0011,
    MOV2B = 0b0111,
    MOVA2B = 0b0001,
    JMP = 0b1111,
    JNC = 0b1110,
    INA = 0b0010,
    INB = 0b0110,
    OUTB = 0b1001,
    OUTIM = 0b1011,
    NOP,
}

ループ部分

    for _ in 0..10 {
        let (opecode, operand) = emu.fetch_decode();
        println!("{:?} {:?}", opecode, operand);
        let next_pc = emu.exec_mut(&opecode, operand);
        emu.reg.pc = next_pc;
        println!("{:?}", emu);
    }

丁寧にfetch, decode, execを分けたが、
巷のエミュレータというのはどうしているのかよく知らない。

実行部

命令名だけ見て実装した。本は斜め読みなので、間違ってるかもしれない。

            Opecode::ADD2A => {
                let tmp = self.reg.a + operand;
                self.reg.flag = (0x10 & tmp) != 0;
                self.reg.a = 0x0F & tmp;
            }
            Opecode::ADD2B => {
                let tmp = self.reg.b + operand;
                self.reg.flag = (0x10 & tmp) != 0;
                self.reg.a = 0x0F & tmp;
            }

4bitの足し算をして、繰上りは5bit目となる。
別の命令処理ではフラグをクリアする。

LED点滅

Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 0, flag: false, pc: 0 }, port: Port { input: 0, output: 0 } }
MOV2B 5
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 5, flag: false, pc: 1 }, port: Port { input: 0, output: 0 } }
OUTB 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 5, flag: false, pc: 2 }, port: Port { input: 0, output: 5 } }
MOV2B 10
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 3 }, port: Port { input: 0, output: 5 } }
OUTB 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 4 }, port: Port { input: 0, output: 10 } }
JMP 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 0 }, port: Port { input: 0, output: 10 } }
MOV2B 5
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 5, flag: false, pc: 1 }, port: Port { input: 0, output: 10 } }
OUTB 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 5, flag: false, pc: 2 }, port: Port { input: 0, output: 5 } }
MOV2B 10
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 3 }, port: Port { input: 0, output: 5 } }
OUTB 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 4 }, port: Port { input: 0, output: 10 } }
JMP 0
Emulator { prg: Mem { mem: [117, 144, 122, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reg: Reg { a: 0, b: 10, flag: false, pc: 0 }, port: Port { input: 0, output: 10 } }

outputが0101と1010が交互になっているのでよさそう。

感想

エミュレータ自体は1日ぐらいで出来る程のシンプルなものだった。
ちゃんと命令をすべてテストしたわけじゃないので、バグがあるかもしれないので都度修正。
ついでに、ちょっと外側から使おうとdefault実装など入れたりした。

ほかのエミュレータにも挑戦したいかもしれない。
Rustで書いた人らはwasmなどにして、ブラウザ上で動かす&UIをつけている人もいる。

あとTD4の部品は買ってあるが実機はまだ作ってなかった。