diff --git a/src/tail.zig b/src/tail.zig index b93dea8..4711b28 100644 --- a/src/tail.zig +++ b/src/tail.zig @@ -38,7 +38,7 @@ pub const Cpu = struct { pub fn run(self: *Cpu, instructions: u16) void { self.instructions = instructions; - self.code[self.pc].func(self, self.code[self.pc].decoded.toInt()); + self.code[self.pc].func(self, self.code[self.pc].decoded.toInt(), self.pc); } pub fn timerTick(self: *Cpu) void { @@ -47,7 +47,7 @@ pub const Cpu = struct { } }; -const GadgetFunc = *const fn (*Cpu, u32) void; +const GadgetFunc = *const fn (*Cpu, u32, u12) void; pub const Decoded = union { xy: [2]u4, @@ -97,8 +97,8 @@ const Inst = struct { decoded: Decoded, }; -pub fn decode(cpu: *Cpu, _: Decoded.Int) void { - const opcode = std.mem.readInt(u16, cpu.mem[cpu.pc..][0..2], .big); +pub fn decode(cpu: *Cpu, _: Decoded.Int, pc: u12) void { + const opcode = std.mem.readInt(u16, cpu.mem[pc..][0..2], .big); const inst: Inst = switch (@as(u4, @truncate(opcode >> 12))) { 0x0 => switch (opcode & 0xff) { 0xE0 => .{ .func = &tailfuncs.clear, .decoded = .{ .none = {} } }, @@ -161,16 +161,16 @@ pub fn decode(cpu: *Cpu, _: Decoded.Int) void { .decoded = Decoded.decode(opcode, .xnn), }, }; - cpu.code[cpu.pc] = inst; - @call(.always_tail, inst.func, .{ cpu, inst.decoded.toInt() }); + cpu.code[pc] = inst; + @call(.always_tail, inst.func, .{ cpu, inst.decoded.toInt(), pc }); } -fn invalid(cpu: *Cpu, _: Decoded.Int) void { +fn invalid(cpu: *Cpu, _: Decoded.Int, pc: u12) void { std.debug.panic( "invalid instruction: {X:0>4} at 0x{X:0>3}", .{ - std.mem.readInt(u16, cpu.mem[cpu.pc..][0..2], .big), - cpu.pc, + std.mem.readInt(u16, cpu.mem[pc..][0..2], .big), + pc, }, ); } diff --git a/src/tailfuncs.zig b/src/tailfuncs.zig index 1305937..ea1927d 100644 --- a/src/tailfuncs.zig +++ b/src/tailfuncs.zig @@ -4,172 +4,182 @@ const tail = @import("./tail.zig"); const Cpu = tail.Cpu; const Decoded = tail.Decoded; -inline fn cont(cpu: *Cpu, comptime inc_by_2: bool) void { +inline fn cont(cpu: *Cpu, pc: u12, comptime inc_by_2: bool) void { if (cpu.instructions == 0) { @setCold(true); + cpu.pc = pc; return; } cpu.instructions -= 1; + var pc_mut = pc; if (inc_by_2) { - cpu.pc += 2; + pc_mut += 2; } - const next = cpu.code[cpu.pc]; - @call(.always_tail, next.func, .{ cpu, next.decoded.toInt() }); + const next = cpu.code[pc_mut]; + @call(.always_tail, next.func, .{ cpu, next.decoded.toInt(), pc_mut }); } /// 00E0: clear the screen -pub fn clear(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn clear(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { _ = Decoded.fromInt(decoded).none; @memset(&cpu.display, 0); - cont(cpu, true); + cont(cpu, pc, true); } /// 00EE: return -pub fn ret(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn ret(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = Decoded.fromInt(decoded).none; - cpu.pc = cpu.stack.popOrNull() orelse @panic("empty stack"); - cont(cpu, false); + const new_pc = cpu.stack.popOrNull() orelse @panic("empty stack"); + cont(cpu, new_pc, false); } /// 1NNN: jump to NNN -pub fn jump(cpu: *Cpu, decoded: Decoded.Int) void { - cpu.pc = Decoded.fromInt(decoded).nnn; - cont(cpu, false); +pub fn jump(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix + const new_pc = Decoded.fromInt(decoded).nnn; + cont(cpu, new_pc, false); } /// 2NNN: call address NNN -pub fn call(cpu: *Cpu, decoded: Decoded.Int) void { - cpu.stack.append(cpu.pc + 2) catch @panic("full stack"); - cpu.pc = Decoded.fromInt(decoded).nnn; - cont(cpu, false); +pub fn call(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + cpu.stack.append(pc + 2) catch @panic("full stack"); + const new_pc = Decoded.fromInt(decoded).nnn; + cont(cpu, new_pc, false); } /// 3XNN: skip next instruction if VX == NN -pub fn skipIfEqual(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfEqual(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const nn = Decoded.fromInt(decoded).xnn; - cpu.pc += if (cpu.v[x] == nn) 4 else 2; - cont(cpu, false); + const new_pc = pc + @as(u12, if (cpu.v[x] == nn) 4 else 2); + cont(cpu, new_pc, false); } /// 4XNN: skip next instruction if VX != NN -pub fn skipIfNotEqual(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfNotEqual(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const nn = Decoded.fromInt(decoded).xnn; - cpu.pc += if (cpu.v[x] != nn) 4 else 2; - cont(cpu, false); + const new_pc = pc + @as(u12, if (cpu.v[x] != nn) 4 else 2); + cont(cpu, new_pc, false); } /// 5XY0: skip next instruction if VX == VY -pub fn skipIfRegistersEqual(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfRegistersEqual(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("skipIfRegistersEqual at {x:0>3}", .{cpu.pc}); } /// 6XNN: set VX to NN -pub fn setRegister(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setRegister(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const nn = Decoded.fromInt(decoded).xnn; cpu.v[x] = nn; - cont(cpu, true); + cont(cpu, pc, true); } /// 7XNN: add NN to VX without carry -pub fn addImmediate(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn addImmediate(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const nn = Decoded.fromInt(decoded).xnn; cpu.v[x] +%= nn; - cont(cpu, true); + cont(cpu, pc, true); } /// 8XY0: set VX to VY -pub fn setRegisterToRegister(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setRegisterToRegister(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x] = cpu.v[y]; - cont(cpu, true); + cont(cpu, pc, true); } /// 8XY1: set VX to VX | VY -pub fn bitwiseOr(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn bitwiseOr(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("bitwiseOr at {x:0>3}", .{cpu.pc}); } /// 8XY2: set VX to VX & VY -pub fn bitwiseAnd(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn bitwiseAnd(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x] &= cpu.v[y]; - cont(cpu, true); + cont(cpu, pc, true); } /// 8XY3: set VX to VX ^ VY -pub fn bitwiseXor(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn bitwiseXor(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("bitwiseXor at {x:0>3}", .{cpu.pc}); } /// 8XY4: set VX to VX + VY; set VF to 1 if carry occurred, 0 otherwise -pub fn addRegisters(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn addRegisters(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x], cpu.v[0xF] = @addWithOverflow(cpu.v[x], cpu.v[y]); - cont(cpu, true); + cont(cpu, pc, true); } /// 8XY5: set VX to VX - VY; set VF to 0 if borrow occurred, 1 otherwise -pub fn subRegisters(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn subRegisters(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x], const overflow = @subWithOverflow(cpu.v[x], cpu.v[y]); cpu.v[0xF] = ~overflow; - cont(cpu, true); + cont(cpu, pc, true); } /// 8XY6: set VX to VY >> 1, set VF to the former least significant bit of VY -pub fn shiftRight(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn shiftRight(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("shiftRight at {x:0>3}", .{cpu.pc}); } /// 8XY7: set VX to VY - VX; set VF to 0 if borrow occurred, 1 otherwise -pub fn subRegistersReverse(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn subRegistersReverse(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x], const overflow = @subWithOverflow(cpu.v[y], cpu.v[x]); cpu.v[0xF] = ~overflow; - cont(cpu, true); + cont(cpu, pc, true); } /// 8XYE: set VX to VY << 1, set VF to the former most significant bit of VY -pub fn shiftLeft(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn shiftLeft(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const y = Decoded.fromInt(decoded).xy; cpu.v[0xF] = (cpu.v[y] >> 7) & 1; cpu.v[x] = cpu.v[y] << 1; - cont(cpu, true); + cont(cpu, pc, true); } /// 9XY0: skip next instruction if VX != VY -pub fn skipIfRegistersNotEqual(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfRegistersNotEqual(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("skipIfRegistersNotEqual at {x:0>3}", .{cpu.pc}); } /// ANNN: set I to NNN -pub fn setI(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setI(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { cpu.i = Decoded.fromInt(decoded).nnn; - cont(cpu, true); + cont(cpu, pc, true); } /// BNNN: jump to NNN + V0 -pub fn jumpV0(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn jumpV0(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("jumpV0 at {x:0>3}", .{cpu.pc}); } /// CXNN: set VX to rand() & NN -pub fn random(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn random(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, const nn = Decoded.fromInt(decoded).xnn; const byte = cpu.random.random().int(u8); cpu.v[x] = byte & nn; - cont(cpu, true); + cont(cpu, pc, true); } /// DXYN: draw an 8xN sprite from memory starting at I at (VX, VY); set VF to 1 if any pixel was /// turned off, 0 otherwise -pub fn draw(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn draw(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x_reg, const y_reg, const n = Decoded.fromInt(decoded).xyn; const x: u16 = cpu.v[x_reg] % 64; const y: u16 = cpu.v[y_reg] % 32; @@ -189,97 +199,103 @@ pub fn draw(cpu: *Cpu, decoded: Decoded.Int) void { } } cpu.v[0xF] = @intFromBool(intersect != 0); - cont(cpu, true); + cont(cpu, pc, true); } /// EX9E: skip next instruction if the key in VX is pressed -pub fn skipIfPressed(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfPressed(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("skipIfPressed at {x:0>3}", .{cpu.pc}); } /// EXA1: skip next instruction if the key in VX is not pressed -pub fn skipIfNotPressed(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn skipIfNotPressed(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("skipIfNotPressed at {x:0>3}", .{cpu.pc}); } /// FX07: store the value of the delay timer in VX -pub fn readDt(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn readDt(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, _ = Decoded.fromInt(decoded).xnn; cpu.v[x] = cpu.dt; - cont(cpu, true); + cont(cpu, pc, true); } /// FX0A: wait until any key is pressed, then store the key that was pressed in VX -pub fn waitForKey(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn waitForKey(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("waitForKey at {x:0>3}", .{cpu.pc}); } /// FX15: set the delay timer to the value of VX -pub fn setDt(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setDt(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, _ = Decoded.fromInt(decoded).xnn; cpu.dt = cpu.v[x]; - cont(cpu, true); + cont(cpu, pc, true); } /// FX18: set the sound timer to the value of VX -pub fn setSt(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setSt(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("setSt at {x:0>3}", .{cpu.pc}); } /// FX1E: increment I by the value of VX -pub fn incrementI(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn incrementI(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, _ = Decoded.fromInt(decoded).xnn; cpu.i +%= cpu.v[x]; - cont(cpu, true); + cont(cpu, pc, true); } /// FX29: set I to the address of the sprite for the digit in VX -pub fn setIToFont(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn setIToFont(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, _ = Decoded.fromInt(decoded).xnn; const val: u4 = @truncate(cpu.v[x]); cpu.i = 5 * @as(u8, val); - cont(cpu, true); + cont(cpu, pc, true); } /// FX33: store the binary-coded decimal version of the value of VX in I, I + 1, and I + 2 -pub fn storeBcd(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn storeBcd(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x, _ = Decoded.fromInt(decoded).xnn; cpu.mem[cpu.i] = cpu.v[x] / 100; cpu.mem[cpu.i +% 1] = (cpu.v[x] / 10) % 10; cpu.mem[cpu.i +% 2] = cpu.v[x] % 10; @memset(cpu.code[cpu.i..][0..2], .{ .func = &tail.decode, .decoded = undefined }); - cont(cpu, true); + cont(cpu, pc, true); } /// FX55: store registers [V0, VX] in memory starting at I; set I to I + X + 1 -pub fn store(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn store(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x: u8, _ = Decoded.fromInt(decoded).xnn; @memcpy(cpu.mem[cpu.i..][0 .. x + 1], cpu.v[0 .. x + 1]); @memset(cpu.code[cpu.i - 1 ..][0 .. x + 2], .{ .func = &tail.decode, .decoded = undefined }); cpu.i = cpu.i + x + 1; - cont(cpu, true); + cont(cpu, pc, true); } /// FX65: load values from memory starting at I into registers [V0, VX]; set I to I + X + 1 -pub fn load(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn load(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { const x: u8, _ = Decoded.fromInt(decoded).xnn; @memcpy(cpu.v[0 .. x + 1], cpu.mem[cpu.i .. cpu.i + x + 1]); cpu.i = cpu.i + x + 1; - cont(cpu, true); + cont(cpu, pc, true); } /// FX75: store registers [V0, VX] in flags. X < 8 -pub fn storeFlags(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn storeFlags(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("storeFlags at {x:0>3}", .{cpu.pc}); } /// FX75: load flags into [V0, VX]. X < 8 -pub fn loadFlags(cpu: *Cpu, decoded: Decoded.Int) void { +pub fn loadFlags(cpu: *Cpu, decoded: Decoded.Int, pc: u12) void { + _ = pc; // autofix _ = decoded; std.debug.panic("loadFlags at {x:0>3}", .{cpu.pc}); }