From 8476b6caf41f1e32409931d461714771d9f5c40e Mon Sep 17 00:00:00 2001 From: Ben Grant Date: Thu, 16 May 2024 11:54:49 -0700 Subject: [PATCH] cleanup + more tail instructions --- src/bench.zig | 12 ++--- src/tail.zig | 110 +++++++++++++++++++-------------------- src/tailfuncs.zig | 128 ++++++++++++++++++++++++---------------------- 3 files changed, 126 insertions(+), 124 deletions(-) diff --git a/src/bench.zig b/src/bench.zig index 5e89e6a..41f9892 100644 --- a/src/bench.zig +++ b/src/bench.zig @@ -19,17 +19,17 @@ pub fn main() !void { const rom = try std.fs.cwd().readFileAlloc(allocator, argv[1], 4096 - 512); defer allocator.free(rom); - const instructions = try std.fmt.parseInt(usize, argv[2], 10); + var instructions = try std.fmt.parseInt(usize, argv[2], 10); var cpu = Cpu.init(rom); const before = std.posix.getrusage(std.posix.rusage.SELF); - // for (0..instructions) |_| { - // try cpu.cycle(); - // cpu.dt = 0; - // } - cpu.run(instructions); + while (instructions > 0) { + const to_run = @min(instructions, std.math.maxInt(u16)); + cpu.run(to_run); + instructions -= to_run; + } const after = std.posix.getrusage(std.posix.rusage.SELF); const before_sec = @as(f64, @floatFromInt(before.utime.tv_sec)) + diff --git a/src/tail.zig b/src/tail.zig index a392f0e..d979f9a 100644 --- a/src/tail.zig +++ b/src/tail.zig @@ -1,6 +1,7 @@ const std = @import("std"); const font_data = @import("./font.zig").font_data; +const tailfuncs = @import("./tailfuncs.zig"); pub const Cpu = struct { v: [16]u8 = .{0} ** 16, @@ -10,7 +11,7 @@ pub const Cpu = struct { display: [64 * 32 / 8]u8, mem: [4096]u8, code: [4096]Inst, - instructions: usize = 0, + instructions: u16 = 0, random: std.rand.DefaultPrng, pub fn init(rom: []const u8) Cpu { @@ -33,13 +34,13 @@ pub const Cpu = struct { return cpu; } - pub fn run(self: *Cpu, instructions: usize) void { + pub fn run(self: *Cpu, instructions: u16) void { self.instructions = instructions; - self.code[self.pc].func(self, self.code[self.pc].decoded.to()); + self.code[self.pc].func(self, self.code[self.pc].decoded.toInt()); } }; -const GadgetFunc = *const fn (*Cpu, u32) callconv(.C) void; +const GadgetFunc = *const fn (*Cpu, u32) void; pub const Decoded = union { xy: [2]u4, @@ -47,6 +48,11 @@ pub const Decoded = union { nnn: u12, xyn: [3]u4, + pub const Int = @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = 8 * @sizeOf(Decoded), + } }); + fn decode(opcode: u16, which: enum { xy, xnn, nnn, xyn }) Decoded { switch (which) { .xy => { @@ -69,12 +75,12 @@ pub const Decoded = union { } } - pub inline fn from(x: u32) Decoded { + pub inline fn fromInt(x: Int) Decoded { return @as(*const Decoded, @ptrCast(&x)).*; } - pub inline fn to(self: Decoded) u32 { - return @as(*align(2) const u32, @ptrCast(&self)).*; + pub inline fn toInt(self: Decoded) Int { + return @as(*align(@alignOf(Decoded)) const Int, @ptrCast(&self)).*; } }; @@ -83,89 +89,77 @@ const Inst = struct { decoded: Decoded, }; -fn decode(cpu: *Cpu, decoded: u32) callconv(.C) void { - _ = decoded; - +pub fn decode(cpu: *Cpu, _: Decoded.Int) void { const opcode = std.mem.readInt(u16, cpu.mem[cpu.pc..][0..2], .big); const inst: Inst = switch (@as(u4, @truncate(opcode >> 12))) { 0x0 => switch (opcode & 0xff) { - 0xE0 => .{ .func = &funcs.clear, .decoded = undefined }, - 0xEE => .{ .func = &funcs.ret, .decoded = undefined }, + 0xE0 => .{ .func = &tailfuncs.clear, .decoded = undefined }, + 0xEE => .{ .func = &tailfuncs.ret, .decoded = undefined }, else => .{ .func = &invalid, .decoded = undefined }, }, - 0x1 => .{ .func = &funcs.jump, .decoded = Decoded.decode(opcode, .nnn) }, - 0x2 => .{ .func = &funcs.call, .decoded = Decoded.decode(opcode, .nnn) }, - 0x3 => .{ .func = &funcs.skipIfEqual, .decoded = Decoded.decode(opcode, .xnn) }, - 0x4 => .{ .func = &funcs.skipIfNotEqual, .decoded = Decoded.decode(opcode, .xnn) }, + 0x1 => .{ .func = &tailfuncs.jump, .decoded = Decoded.decode(opcode, .nnn) }, + 0x2 => .{ .func = &tailfuncs.call, .decoded = Decoded.decode(opcode, .nnn) }, + 0x3 => .{ .func = &tailfuncs.skipIfEqual, .decoded = Decoded.decode(opcode, .xnn) }, + 0x4 => .{ .func = &tailfuncs.skipIfNotEqual, .decoded = Decoded.decode(opcode, .xnn) }, 0x5 => switch (opcode & 0xf) { - 0x0 => .{ .func = &funcs.skipIfRegistersEqual, .decoded = Decoded.decode(opcode, .xy) }, + 0x0 => .{ .func = &tailfuncs.skipIfRegistersEqual, .decoded = Decoded.decode(opcode, .xy) }, else => .{ .func = &invalid, .decoded = undefined }, }, - 0x6 => .{ .func = &funcs.setRegister, .decoded = Decoded.decode(opcode, .xnn) }, - 0x7 => .{ .func = &funcs.addImmediate, .decoded = Decoded.decode(opcode, .xnn) }, + 0x6 => .{ .func = &tailfuncs.setRegister, .decoded = Decoded.decode(opcode, .xnn) }, + 0x7 => .{ .func = &tailfuncs.addImmediate, .decoded = Decoded.decode(opcode, .xnn) }, 0x8 => .{ .func = switch (@as(u4, @truncate(opcode))) { - 0x0 => &funcs.setRegisterToRegister, - 0x1 => &funcs.bitwiseOr, - 0x2 => &funcs.bitwiseAnd, - 0x3 => &funcs.bitwiseXor, - 0x4 => &funcs.addRegisters, - 0x5 => &funcs.subRegisters, - 0x6 => &funcs.shiftRight, - 0x7 => &funcs.subRegistersReverse, - 0xE => &funcs.shiftLeft, + 0x0 => &tailfuncs.setRegisterToRegister, + 0x1 => &tailfuncs.bitwiseOr, + 0x2 => &tailfuncs.bitwiseAnd, + 0x3 => &tailfuncs.bitwiseXor, + 0x4 => &tailfuncs.addRegisters, + 0x5 => &tailfuncs.subRegisters, + 0x6 => &tailfuncs.shiftRight, + 0x7 => &tailfuncs.subRegistersReverse, + 0xE => &tailfuncs.shiftLeft, else => &invalid, }, .decoded = Decoded.decode(opcode, .xy), }, 0x9 => switch (opcode & 0xf) { - 0x0 => .{ .func = &funcs.skipIfRegistersNotEqual, .decoded = Decoded.decode(opcode, .xy) }, + 0x0 => .{ .func = &tailfuncs.skipIfRegistersNotEqual, .decoded = Decoded.decode(opcode, .xy) }, else => .{ .func = &invalid, .decoded = undefined }, }, - 0xA => .{ .func = &funcs.setI, .decoded = Decoded.decode(opcode, .nnn) }, - 0xB => .{ .func = &funcs.jumpV0, .decoded = Decoded.decode(opcode, .nnn) }, - 0xC => .{ .func = &funcs.random, .decoded = Decoded.decode(opcode, .xnn) }, - 0xD => .{ .func = &funcs.draw, .decoded = Decoded.decode(opcode, .xyn) }, + 0xA => .{ .func = &tailfuncs.setI, .decoded = Decoded.decode(opcode, .nnn) }, + 0xB => .{ .func = &tailfuncs.jumpV0, .decoded = Decoded.decode(opcode, .nnn) }, + 0xC => .{ .func = &tailfuncs.random, .decoded = Decoded.decode(opcode, .xnn) }, + 0xD => .{ .func = &tailfuncs.draw, .decoded = Decoded.decode(opcode, .xyn) }, 0xE => switch (opcode & 0xff) { - 0x9E => .{ .func = &funcs.skipIfPressed, .decoded = Decoded.decode(opcode, .xnn) }, - 0xA1 => .{ .func = &funcs.skipIfNotPressed, .decoded = Decoded.decode(opcode, .xnn) }, + 0x9E => .{ .func = &tailfuncs.skipIfPressed, .decoded = Decoded.decode(opcode, .xnn) }, + 0xA1 => .{ .func = &tailfuncs.skipIfNotPressed, .decoded = Decoded.decode(opcode, .xnn) }, else => .{ .func = &invalid, .decoded = undefined }, }, 0xF => .{ .func = switch (opcode & 0xff) { - 0x07 => &funcs.readDt, - 0x0A => &funcs.waitForKey, - 0x15 => &funcs.setDt, - 0x18 => &funcs.setSt, - 0x1E => &funcs.incrementI, - 0x29 => &funcs.setIToFont, - 0x33 => &funcs.storeBcd, - 0x55 => &funcs.store, - 0x65 => &funcs.load, - 0x75 => &funcs.storeFlags, - 0x85 => &funcs.loadFlags, + 0x07 => &tailfuncs.readDt, + 0x0A => &tailfuncs.waitForKey, + 0x15 => &tailfuncs.setDt, + 0x18 => &tailfuncs.setSt, + 0x1E => &tailfuncs.incrementI, + 0x29 => &tailfuncs.setIToFont, + 0x33 => &tailfuncs.storeBcd, + 0x55 => &tailfuncs.store, + 0x65 => &tailfuncs.load, + 0x75 => &tailfuncs.storeFlags, + 0x85 => &tailfuncs.loadFlags, else => &invalid, }, .decoded = Decoded.decode(opcode, .xnn), }, }; cpu.code[cpu.pc] = inst; - @call(.always_tail, inst.func, .{ cpu, inst.decoded.to() }); + @call(.always_tail, inst.func, .{ cpu, inst.decoded.toInt() }); } -fn invalid(cpu: *Cpu, decoded: u32) callconv(.C) void { - _ = decoded; // autofix +fn invalid(cpu: *Cpu, _: Decoded.Int) void { std.debug.panic( "invalid instruction: {x:0>4}", .{std.mem.readInt(u16, cpu.mem[cpu.pc..][0..2], .big)}, ); } - -const funcs = @import("./tailfuncs.zig"); - -// export fn inc_stub(cpu: *Cpu, decoded: Decoded, pc: u16) void { -// // const decoded: Decoded = @bitCast(decoded_i); -// decoded.xy[0].*, cpu.v[0xf] = @addWithOverflow(decoded.xy[0].*, decoded.xy[1].*); -// const next = cpu.code[cpu.pc + 2]; -// @call(.always_tail, next.func, .{ cpu, next.decoded, pc + 2 }); -// } diff --git a/src/tailfuncs.zig b/src/tailfuncs.zig index 42afb7e..5e9813b 100644 --- a/src/tailfuncs.zig +++ b/src/tailfuncs.zig @@ -1,7 +1,8 @@ const std = @import("std"); -const Cpu = @import("./tail.zig").Cpu; -const Decoded = @import("./tail.zig").Decoded; +const tail = @import("./tail.zig"); +const Cpu = tail.Cpu; +const Decoded = tail.Decoded; inline fn cont(cpu: *Cpu, comptime inc_by_2: bool) void { if (cpu.instructions == 0) { @@ -13,152 +14,152 @@ inline fn cont(cpu: *Cpu, comptime inc_by_2: bool) void { cpu.pc += 2; } const next = cpu.code[cpu.pc]; - @call(.always_tail, next.func, .{ cpu, next.decoded.to() }); + @call(.always_tail, next.func, .{ cpu, next.decoded.toInt() }); } /// 00E0: clear the screen -pub fn clear(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn clear(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; @memset(&cpu.display, 0); cont(cpu, true); } /// 00EE: return -pub fn ret(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn ret(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; cpu.pc = cpu.stack.popOrNull() orelse @panic("empty stack"); cont(cpu, false); } /// 1NNN: jump to NNN -pub fn jump(cpu: *Cpu, decoded: u32) callconv(.C) void { - cpu.pc = Decoded.from(decoded).nnn; +pub fn jump(cpu: *Cpu, decoded: Decoded.Int) void { + cpu.pc = Decoded.fromInt(decoded).nnn; cont(cpu, false); } /// 2NNN: call address NNN -pub fn call(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn call(cpu: *Cpu, decoded: Decoded.Int) void { cpu.stack.append(cpu.pc + 2) catch @panic("full stack"); - cpu.pc = Decoded.from(decoded).nnn; + cpu.pc = Decoded.fromInt(decoded).nnn; cont(cpu, false); } /// 3XNN: skip next instruction if VX == NN -pub fn skipIfEqual(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const nn = Decoded.from(decoded).xnn; +pub fn skipIfEqual(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const nn = Decoded.fromInt(decoded).xnn; cpu.pc += if (cpu.v[x] == nn) 4 else 2; cont(cpu, false); } /// 4XNN: skip next instruction if VX != NN -pub fn skipIfNotEqual(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const nn = Decoded.from(decoded).xnn; +pub fn skipIfNotEqual(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const nn = Decoded.fromInt(decoded).xnn; cpu.pc += if (cpu.v[x] != nn) 4 else 2; cont(cpu, false); } /// 5XY0: skip next instruction if VX == VY -pub fn skipIfRegistersEqual(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn skipIfRegistersEqual(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("skipIfRegistersEqual at {x:0>3}", .{cpu.pc}); } /// 6XNN: set VX to NN -pub fn setRegister(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const nn = Decoded.from(decoded).xnn; +pub fn setRegister(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const nn = Decoded.fromInt(decoded).xnn; cpu.v[x] = nn; cont(cpu, true); } /// 7XNN: add NN to VX without carry -pub fn addImmediate(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const nn = Decoded.from(decoded).xnn; +pub fn addImmediate(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const nn = Decoded.fromInt(decoded).xnn; cpu.v[x] +%= nn; cont(cpu, true); } /// 8XY0: set VX to VY -pub fn setRegisterToRegister(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const y = Decoded.from(decoded).xy; +pub fn setRegisterToRegister(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x] = cpu.v[y]; cont(cpu, true); } /// 8XY1: set VX to VX | VY -pub fn bitwiseOr(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn bitwiseOr(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("bitwiseOr at {x:0>3}", .{cpu.pc}); } /// 8XY2: set VX to VX & VY -pub fn bitwiseAnd(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const y = Decoded.from(decoded).xy; +pub fn bitwiseAnd(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const y = Decoded.fromInt(decoded).xy; cpu.v[x] &= cpu.v[y]; cont(cpu, true); } /// 8XY3: set VX to VX ^ VY -pub fn bitwiseXor(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn bitwiseXor(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { - const x, const y = Decoded.from(decoded).xy; +pub fn addRegisters(cpu: *Cpu, decoded: Decoded.Int) 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); } /// 8XY5: set VX to VX - VY; set VF to 0 if borrow occurred, 1 otherwise -pub fn subRegisters(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const y = Decoded.from(decoded).xy; +pub fn subRegisters(cpu: *Cpu, decoded: Decoded.Int) 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); } /// 8XY6: set VX to VY >> 1, set VF to the former least significant bit of VY -pub fn shiftRight(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn shiftRight(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { +pub fn subRegistersReverse(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("subRegistersReverse at {x:0>3}", .{cpu.pc}); } /// 8XYE: set VX to VY << 1, set VF to the former most significant bit of VY -pub fn shiftLeft(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const y = Decoded.from(decoded).xy; +pub fn shiftLeft(cpu: *Cpu, decoded: Decoded.Int) 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); } /// 9XY0: skip next instruction if VX != VY -pub fn skipIfRegistersNotEqual(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn skipIfRegistersNotEqual(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("skipIfRegistersNotEqual at {x:0>3}", .{cpu.pc}); } /// ANNN: set I to NNN -pub fn setI(cpu: *Cpu, decoded: u32) callconv(.C) void { - cpu.i = Decoded.from(decoded).nnn; +pub fn setI(cpu: *Cpu, decoded: Decoded.Int) void { + cpu.i = Decoded.fromInt(decoded).nnn; cont(cpu, true); } /// BNNN: jump to NNN + V0 -pub fn jumpV0(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn jumpV0(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("jumpV0 at {x:0>3}", .{cpu.pc}); } /// CXNN: set VX to rand() & NN -pub fn random(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, const nn = Decoded.from(decoded).xnn; +pub fn random(cpu: *Cpu, decoded: Decoded.Int) void { + const x, const nn = Decoded.fromInt(decoded).xnn; const byte = cpu.random.random().int(u8); cpu.v[x] = byte & nn; cont(cpu, true); @@ -166,7 +167,7 @@ pub fn random(cpu: *Cpu, decoded: u32) callconv(.C) void { /// 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: u32) callconv(.C) void { +pub fn draw(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; // std.debug.panic("draw at {x:0>3}, {} to go", .{ cpu.pc, cpu.instructions }); std.log.warn("draw", .{}); @@ -174,84 +175,91 @@ pub fn draw(cpu: *Cpu, decoded: u32) callconv(.C) void { } /// EX9E: skip next instruction if the key in VX is pressed -pub fn skipIfPressed(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn skipIfPressed(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { +pub fn skipIfNotPressed(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { +pub fn readDt(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("readDt at {x:0>3}", .{cpu.pc}); } /// FX0A: wait until any key is pressed, then store the key that was pressed in VX -pub fn waitForKey(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn waitForKey(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { +pub fn setDt(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("setDt at {x:0>3}", .{cpu.pc}); } /// FX18: set the sound timer to the value of VX -pub fn setSt(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn setSt(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { - const x, _ = Decoded.from(decoded).xnn; +pub fn incrementI(cpu: *Cpu, decoded: Decoded.Int) void { + const x, _ = Decoded.fromInt(decoded).xnn; cpu.i +%= cpu.v[x]; cont(cpu, true); } /// FX29: set I to the address of the sprite for the digit in VX -pub fn setIToFont(cpu: *Cpu, decoded: u32) callconv(.C) void { - _ = decoded; - std.debug.panic("setIToFont at {x:0>3}", .{cpu.pc}); +pub fn setIToFont(cpu: *Cpu, decoded: Decoded.Int) void { + const x, _ = Decoded.fromInt(decoded).xnn; + const val: u4 = @truncate(cpu.v[x]); + cpu.i = 5 * @as(u8, val); + cont(cpu, 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: u32) callconv(.C) void { - _ = decoded; - std.debug.panic("storeBcd at {x:0>3}, {} to go", .{ cpu.pc, cpu.instructions }); +pub fn storeBcd(cpu: *Cpu, decoded: Decoded.Int) 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); } /// FX55: store registers [V0, VX] in memory starting at I; set I to I + X + 1 -pub fn store(cpu: *Cpu, decoded: u32) callconv(.C) void { - const x, _ = Decoded.from(decoded).xnn; - @memcpy(cpu.mem[cpu.i .. cpu.i + x + 1], cpu.v[0 .. x + 1]); +pub fn store(cpu: *Cpu, decoded: Decoded.Int) void { + const x, _ = Decoded.fromInt(decoded).xnn; + @memcpy(cpu.mem[cpu.i..][0 .. x + 1], cpu.v[0 .. x + 1]); + @memset(cpu.code[cpu.i..][0 .. x + 1], .{ .func = &tail.decode, .decoded = undefined }); cpu.i = cpu.i + x + 1; cont(cpu, 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: u32) callconv(.C) void { - const x, _ = Decoded.from(decoded).xnn; +pub fn load(cpu: *Cpu, decoded: Decoded.Int) void { + const x, _ = 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); } /// FX75: store registers [V0, VX] in flags. X < 8 -pub fn storeFlags(cpu: *Cpu, decoded: u32) callconv(.C) void { +pub fn storeFlags(cpu: *Cpu, decoded: Decoded.Int) void { _ = 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: u32) callconv(.C) void { +pub fn loadFlags(cpu: *Cpu, decoded: Decoded.Int) void { _ = decoded; std.debug.panic("loadFlags at {x:0>3}", .{cpu.pc}); }