struct Cpu { memory: Vec, pc: u16, registers: Vec, i: u16, stack: Vec, sp: i8, dt: u8, st: u8, } impl Cpu { fn ramread(&self, index: usize) -> &u8 { &self.memory[index] } fn ramwrite(&mut self, index: usize, value: u8) { self.memory[index] = value; } fn regread(&self, index: usize) -> &u8 { &self.registers[index] } fn regwrite(&mut self, index: usize, value: u8) { self.registers[index] = value; } fn iread(&self) -> &u16 { &self.i } fn iwrite(&mut self, value: u16) { self.i = value; } fn incrementpc(&mut self) { self.pc = self.pc + 2 } fn fetch(&mut self) -> u16 { return ((self.memory[self.pc as usize] as u16) << 8 | self.memory[self.pc as usize + 1 as usize] as u16) } } struct Instruction { name: &'static str, mask: u16, pattern: u16, masks: &'static [u16], shifts: &'static [u8], } const ISET: [Instruction; 34] = [Instruction { name: "CLS", mask: 0xffff, pattern: 0x00e0, masks: &[], shifts: &[] }, Instruction { name: "RET", mask: 0xffff, pattern: 0x00ee, masks: &[], shifts: &[] }, Instruction { name: "JP_ADDR", mask: 0xf000, pattern: 0x1000, masks: &[0x0fff], shifts: &[0] }, Instruction { name: "CALL_ADDR", mask: 0xf000, pattern: 0x2000, masks: &[0x0fff], shifts: &[0] }, Instruction { name: "SE_VX_BYTE", mask: 0xf000, pattern: 0x3000, masks: &[0x0f00, 0x00ff], shifts: &[8, 0] }, Instruction { name: "SNE_VX_BYTE", mask: 0xf000, pattern: 0x4000, masks: &[0x0f00, 0x00ff], shifts: &[8, 0] }, Instruction { name: "SE_VX_VY", mask: 0xf00f, pattern: 0x5000, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "LD_VX_BYTE", mask: 0xf000, pattern: 0x6000, masks: &[0x0f00, 0x00ff], shifts: &[8, 0] }, Instruction { name: "ADD_VX_BYTE", mask: 0xf000, pattern: 0x7000, masks: &[0x0f00, 0x00ff], shifts: &[8, 0] }, Instruction { name: "LD_VX_VY", mask: 0xf00f, pattern: 0x8000, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "OR_VX_VY", mask: 0xf00f, pattern: 0x8001, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "AND_VX_VY", mask: 0xf00f, pattern: 0x8002, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "XOR_VX_VY", mask: 0xf00f, pattern: 0x8003, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "ADD_VX_VY", mask: 0xf00f, pattern: 0x8004, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "SUB_VX_VY", mask: 0xf00f, pattern: 0x8005, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "SHR_VX_VY", mask: 0xf00f, pattern: 0x8006, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "SUBN_VX_VY", mask: 0xf00f, pattern: 0x8007, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "SHL_VX_VY", mask: 0xf00f, pattern: 0x800e, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "SNE_VX_VY", mask: 0xf00f, pattern: 0x9000, masks: &[0x0f00, 0x00f0], shifts: &[8, 4] }, Instruction { name: "LD_I_ADDR", mask: 0xf000, pattern: 0xa000, masks: &[0x0fff], shifts: &[0] }, Instruction { name: "JP_V0_ADDR", mask: 0xf000, pattern: 0xb000, masks: &[0x0fff], shifts: &[0] }, Instruction { name: "RND_VX_BYTE", mask: 0xf000, pattern: 0xc000, masks: &[0x0f00, 0x00ff], shifts: &[8, 0] }, Instruction { name: "DRW_VX_VY_NIBBLE", mask: 0xf000, pattern: 0xd000, masks: &[0x0f00, 0x00f0, 0x000f], shifts: &[8, 4, 0] }, Instruction { name: "SKP_VX", mask: 0xf0ff, pattern: 0xe09e, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "SKNP_VX", mask: 0xf0ff, pattern: 0xe0a1, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_VX_DT", mask: 0xf0ff, pattern: 0xf007, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_VX_K", mask: 0xf0ff, pattern: 0xf00a, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_DT_VX", mask: 0xf0ff, pattern: 0xf015, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_ST_VX", mask: 0xf0ff, pattern: 0xf018, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "ADD_I_VX", mask: 0xf0ff, pattern: 0xf01e, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_F_VX", mask: 0xf0ff, pattern: 0xf029, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_B_VX", mask: 0xf0ff, pattern: 0xf033, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_IB_VX", mask: 0xf0ff, pattern: 0xf055, masks: &[0x0f00], shifts: &[8] }, Instruction { name: "LD_VX_IB", mask: 0xf0ff, pattern: 0xf065, masks: &[0x0f00], shifts: &[8] } ]; fn disassemble(opcode: u16) -> Instruction { let mut instruction; for instr in ISET { if (opcode & instr.mask) == instr.pattern { instruction = instr; return instruction; } } println!("Invalid opcode 0x{}! Bailing out!", format!("{:x}", opcode)); panic!(); } fn decodetest(opcode: u16, expected: &str) { let instruction = disassemble(opcode); if instruction.name != expected { println!("Opcode 0x{} disassembled as instruction {}. Expected {}.", format!("{:x}", opcode), instruction.name, expected); } } fn execute(opcode: u16, instruction: Instruction, mut cpu: Cpu) -> Cpu { let mut args: Vec = vec![]; let mut i = 0; if instruction.masks.len() != 0 { for mask in instruction.masks { let shift = instruction.shifts[i]; args.push((opcode & mask) >> shift); i = i + 1; } } if instruction.name == "LD_VX_BYTE" { cpu.regwrite(args[0] as usize, args[1] as u8); cpu.incrementpc(); } else { println!("Unimplemented instruction {}. Skipping.", instruction.name) } return cpu; } fn main() { let mut cpu = Cpu { memory: vec![0; 4096], pc: 0x200, registers: vec![0; 16], i: 0, stack: vec![0; 16], sp: -1, dt: 0, st: 0 }; println!("CPU initialised!"); torture(); println!("If you see messages above that are about opcodes, there's a bug!"); println!("Executing LD_VX_BYTE with opcode 0x6010 through memory."); cpu.memory[0x200] = 0x60; cpu.memory[0x201] = 0x10; let mut opcode = cpu.fetch(); let mut instruction = disassemble(opcode); cpu = execute(opcode, instruction, cpu); println!("Register 0 is {}!", cpu.registers[0]); } fn torture() { decodetest(0x00e0, "CLS"); decodetest(0x00ee, "RET"); decodetest(0x1123, "JP_ADDR"); decodetest(0x2123, "CALL_ADDR"); decodetest(0x3123, "SE_VX_BYTE"); decodetest(0x4123, "SNE_VX_BYTE"); decodetest(0x5120, "SE_VX_VY"); decodetest(0x6123, "LD_VX_BYTE"); decodetest(0x7123, "ADD_VX_BYTE"); decodetest(0x8120, "LD_VX_VY"); decodetest(0x8121, "OR_VX_VY"); decodetest(0x8122, "AND_VX_VY"); decodetest(0x8123, "XOR_VX_VY"); decodetest(0x8124, "ADD_VX_VY"); decodetest(0x8125, "SUB_VX_VY"); decodetest(0x8126, "SHR_VX_VY"); decodetest(0x8127, "SUBN_VX_VY"); decodetest(0x812e, "SHL_VX_VY"); decodetest(0x9120, "SNE_VX_VY"); decodetest(0xa123, "LD_I_ADDR"); decodetest(0xb123, "JP_V0_ADDR"); decodetest(0xc123, "RND_VX_BYTE"); decodetest(0xd123, "DRW_VX_VY_NIBBLE"); decodetest(0xe19e, "SKP_VX"); decodetest(0xe1a1, "SKNP_VX"); decodetest(0xf107, "LD_VX_DT"); decodetest(0xf10a, "LD_VX_K"); decodetest(0xf115, "LD_DT_VX"); decodetest(0xf118, "LD_ST_VX"); decodetest(0xf11e, "ADD_I_VX"); decodetest(0xf129, "LD_F_VX"); decodetest(0xf133, "LD_B_VX"); decodetest(0xf155, "LD_IB_VX"); decodetest(0xf165, "LD_VX_IB"); }