思い立ったので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が交互になっているのでよさそう。