From eeea8a63277df8d28f04393a159c238e0f5cd791 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 21 Oct 2022 05:12:49 -0300 Subject: [PATCH] chore: mess with debug statements + mask APU I/O reads --- src/apu.zig | 74 +++++++++++++++++++++++++++++++-------------- src/bus/GamePak.zig | 2 +- src/bus/dma.zig | 14 +++++---- src/bus/io.zig | 50 ++++++++++++------------------ src/bus/timer.zig | 18 ++++++----- src/util.zig | 13 ++++++++ 6 files changed, 104 insertions(+), 67 deletions(-) diff --git a/src/apu.zig b/src/apu.zig index 6cdc788..7438e48 100644 --- a/src/apu.zig +++ b/src/apu.zig @@ -9,6 +9,8 @@ const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 }); const AudioDeviceId = SDL.SDL_AudioDeviceID; const intToBytes = @import("util.zig").intToBytes; +const readUndefined = @import("util.zig").readUndefined; +const writeUndefined = @import("util.zig").writeUndefined; const log = std.log.scoped(.APU); pub const host_sample_rate = 1 << 15; @@ -18,25 +20,28 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) T { return switch (T) { u16 => switch (byte) { - 0x60 => apu.ch1.sweep.raw, // SOUND1CNT_L + 0x60 => apu.ch1.getSoundCntL(), 0x62 => apu.ch1.getSoundCntH(), - 0x64 => apu.ch1.freq.raw, // SOUND1CNT_X - + 0x64 => apu.ch1.getSoundCntX(), 0x68 => apu.ch2.getSoundCntL(), - 0x6C => apu.ch2.freq.raw, // SOUND2CNT_H + 0x6C => apu.ch2.getSoundCntH(), - 0x70 => apu.ch3.select.raw, // SOUND3CNT_L - 0x74 => apu.ch3.freq.raw, // SOUND3CNT_X + 0x70 => apu.ch3.select.raw & 0xE0, // SOUND3CNT_L + 0x72 => apu.ch3.getSoundCntH(), + 0x74 => apu.ch3.freq.raw & 0x4000, // SOUND3CNT_X 0x78 => apu.ch4.getSoundCntL(), 0x7C => apu.ch4.getSoundCntH(), - 0x80 => apu.dma_cnt.raw, // SOUNDCNT_L + 0x80 => apu.psg_cnt.raw & 0xFF77, // SOUNDCNT_L + 0x82 => apu.dma_cnt.raw & 0x770F, // SOUNDCNT_H + 0x84 => apu.getSoundCntX(), 0x88 => apu.bias.raw, // SOUNDBIAS - else => @panic("TODO: Unexpected APU u16 read"), + 0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, u8 => switch (byte) { - 0x60 => apu.ch1.sweep.raw, // NR10 + 0x60 => apu.ch1.getSoundCntL(), // NR10 0x63 => apu.ch1.envelope.raw, // NR12 0x69 => apu.ch2.envelope.raw, // NR22 0x73 => apu.ch3.vol.raw, // NR32 @@ -45,9 +50,9 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) T { 0x81 => @truncate(u8, apu.psg_cnt.raw >> 8), // NR51 0x84 => apu.getSoundCntX(), 0x89 => @truncate(u8, apu.bias.raw >> 8), // SOUNDBIAS_H - else => @panic("TODO: Unexpected APU u8 read"), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, - u32 => @panic("TODO: Unexpected APU u32 read"), + u32 => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), else => @compileError("APU: Unsupported read width"), }; } @@ -65,7 +70,7 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void { 0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value), 0xA0 => apu.chA.push(value), // FIFO_A 0xA4 => apu.chB.push(value), // FIFO_B - else => @panic("TODO: Unexpected APU u32 write"), + else => writeUndefined(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, u16 => switch (byte) { 0x60 => apu.ch1.sweep.raw = @truncate(u8, value), // SOUND1CNT_L @@ -88,7 +93,7 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void { 0x88 => apu.bias.raw = value, // SOUNDBIAS // WAVE_RAM 0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value), - else => @panic("TODO: Unexpected APU u16 write"), + else => writeUndefined(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, u8 => switch (byte) { 0x60 => apu.ch1.sweep.raw = value, // NR10 @@ -118,7 +123,7 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void { 0x84 => apu.setSoundCntX(value >> 7 & 1 == 1), // NR52 0x89 => apu.setSoundBiasH(value), 0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value), - else => @panic("TODO: Unexpected APU u8 write"), + else => writeUndefined(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), }, else => @compileError("APU: Unsupported write width"), } @@ -500,9 +505,14 @@ const ToneSweep = struct { return @as(i16, self.sample); } + /// NR10 + pub fn getSoundCntL(self: *const Self) u8 { + return self.sweep.raw & 0x7F; + } + /// NR11, NR12 pub fn getSoundCntH(self: *const Self) u16 { - return @as(u16, self.envelope.raw) << 8 | self.duty.raw; + return @as(u16, self.envelope.raw) << 8 | (self.duty.raw & 0xC0); } /// NR11, NR12 @@ -523,6 +533,11 @@ const ToneSweep = struct { if (!self.isDacEnabled()) self.enabled = false; } + /// NR13, NR14 + pub fn getSoundCntX(self: *const Self) u16 { + return self.freq.raw & 0x4000; + } + /// NR13, NR14 pub fn setSoundCntX(self: *Self, fs: *const FrameSequencer, value: u16) void { self.setNr13(@truncate(u8, value)); @@ -641,7 +656,7 @@ const Tone = struct { /// NR21, NR22 pub fn getSoundCntL(self: *const Self) u16 { - return @as(u16, self.envelope.raw) << 8 | self.duty.raw; + return @as(u16, self.envelope.raw) << 8 | (self.duty.raw & 0xC0); } /// NR21, NR22 @@ -662,6 +677,11 @@ const Tone = struct { if (!self.isDacEnabled()) self.enabled = false; } + /// NR23, NR24 + pub fn getSoundCntH(self: *const Self) u16 { + return self.freq.raw & 0x4000; + } + /// NR23, NR24 pub fn setSoundCntH(self: *Self, fs: *const FrameSequencer, value: u16) void { self.setNr23(@truncate(u8, value)); @@ -759,6 +779,11 @@ const Wave = struct { if (!self.select.enabled.read()) self.enabled = false; } + /// NR31, NR32 + fn getSoundCntH(self: *const Self) u16 { + return @as(u16, self.length & 0xE0) << 8; + } + /// NR31, NR32 pub fn setSoundCntH(self: *Self, value: u16) void { self.setNr31(@truncate(u8, value)); @@ -880,7 +905,7 @@ const Noise = struct { /// NR41, NR42 pub fn getSoundCntL(self: *const Self) u16 { - return @as(u16, self.envelope.raw) << 8 | self.len; + return @as(u16, self.envelope.raw) << 8; } /// NR41, NR42 @@ -903,7 +928,7 @@ const Noise = struct { /// NR43, NR44 pub fn getSoundCntH(self: *const Self) u16 { - return @as(u16, self.poly.raw) << 8 | self.cnt.raw; + return @as(u16, self.poly.raw & 0x40) << 8 | self.cnt.raw; } /// NR43, NR44 @@ -1143,10 +1168,15 @@ const WaveDevice = struct { const base = if (!cnt.bank.read()) @as(u32, 0x10) else 0; // Write to the Opposite Bank in Use const i = base + addr - 0x0400_0090; - switch (T) { - u32, u16, u8 => std.mem.writeIntSliceLittle(T, self.buf[i..][0..@sizeOf(T)], value), - else => @compileError("Ch3 WAVERAM: Unsupported write width"), - } + std.mem.writeIntSliceLittle(T, self.buf[i..][0..@sizeOf(T)], value); + } + + fn read(self: *const Self, comptime T: type, cnt: io.WaveSelect, addr: u32) T { + // TODO: Handle reads when Channel 3 is disabled + const base = if (!cnt.bank.read()) @as(u32, 0x10) else 0; // Read from the Opposite Bank in Use + + const i = base + addr - 0x0400_0090; + return std.mem.readIntSliceLittle(T, self.buf[i..][0..@sizeOf(T)]); } }; diff --git a/src/bus/GamePak.zig b/src/bus/GamePak.zig index 00e380f..b6c65b2 100644 --- a/src/bus/GamePak.zig +++ b/src/bus/GamePak.zig @@ -123,7 +123,7 @@ pub fn write(self: *Self, comptime T: type, word_count: u16, address: u32, value 0x0800_00C8 => log.err("Wrote {} 0x{X:} to I/O Port Control", .{ T, value }), else => {}, }, - u8 => log.warn("Wrote {} 0x{X:} to 0x{X:0>8}, Ignored.", .{ T, value, address }), + u8 => log.debug("Wrote {} 0x{X:} to 0x{X:0>8}, Ignored.", .{ T, value, address }), else => @compileError("GamePak: Unsupported write width"), } } diff --git a/src/bus/dma.zig b/src/bus/dma.zig index 4d328e9..4a7bfd0 100644 --- a/src/bus/dma.zig +++ b/src/bus/dma.zig @@ -4,6 +4,8 @@ const DmaControl = @import("io.zig").DmaControl; const Bus = @import("../Bus.zig"); const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const readUndefined = @import("../util.zig").readUndefined; +const writeUndefined = @import("../util.zig").writeUndefined; pub const DmaTuple = std.meta.Tuple(&[_]type{ DmaController(0), DmaController(1), DmaController(2), DmaController(3) }); const log = std.log.scoped(.DmaTransfer); @@ -20,16 +22,16 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) T { 0xC4 => @as(T, dma.*[1].cnt.raw) << 16, 0xD0 => @as(T, dma.*[1].cnt.raw) << 16, 0xDC => @as(T, dma.*[3].cnt.raw) << 16, - else => @panic("TODO: Unexpected u32 DMA read"), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, u16 => switch (byte) { 0xBA => dma.*[0].cnt.raw, 0xC6 => dma.*[1].cnt.raw, 0xD2 => dma.*[2].cnt.raw, 0xDE => dma.*[3].cnt.raw, - else => @panic("TODO: Unexpected u16 DMA read"), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, - u8 => @panic("TODO: Unexpected u8 DMA read"), + u8 => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), else => @compileError("DMA: Unsupported read width"), }; } @@ -51,7 +53,7 @@ pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void { 0xD4 => dma.*[3].setSad(value), 0xD8 => dma.*[3].setDad(value), 0xDC => dma.*[3].setCnt(value), - else => @panic("TODO: Unexpected u32 DMA write"), + else => writeUndefined(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, u16 => switch (byte) { 0xB0 => dma.*[0].setSad(setU32L(dma.*[0].sad, value)), @@ -81,9 +83,9 @@ pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void { 0xDA => dma.*[3].setDad(setU32H(dma.*[3].dad, value)), 0xDC => dma.*[3].setCntL(value), 0xDE => dma.*[3].setCntH(value), - else => @panic("TODO: Unexpected u16 DMA write"), + else => writeUndefined(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, - u8 => @panic("TODO: Unexpected u8 DMA write"), + u8 => writeUndefined(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), else => @compileError("DMA: Unsupported write width"), } } diff --git a/src/bus/io.zig b/src/bus/io.zig index a4b5cc0..bafc4fb 100644 --- a/src/bus/io.zig +++ b/src/bus/io.zig @@ -11,6 +11,8 @@ const timer = @import("timer.zig"); const dma = @import("dma.zig"); const apu = @import("../apu.zig"); +const readUndefined = @import("../util.zig").readUndefined; +const writeUndefined = @import("../util.zig").writeUndefined; const log = std.log.scoped(.@"I/O"); pub const Io = struct { @@ -56,18 +58,18 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0100...0x0400_010C => timer.read(T, &bus.tim, address), // Serial Communication 1 - 0x0400_0128 => unimplementedRead("Read {} from SIOCNT and SIOMLT_SEND", .{T}), + 0x0400_0128 => readTodo("Read {} from SIOCNT and SIOMLT_SEND", .{T}), // Keypad Input - 0x0400_0130 => unimplementedRead("Read {} from KEYINPUT", .{T}), + 0x0400_0130 => readTodo("Read {} from KEYINPUT", .{T}), // Serial Communication 2 - 0x0400_0150 => unimplementedRead("Read {} from JOY_RECV", .{T}), + 0x0400_0150 => readTodo("Read {} from JOY_RECV", .{T}), // Interrupts 0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw, 0x0400_0208 => @boolToInt(bus.io.ime), - else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }), }, u16 => switch (address) { // Display @@ -78,10 +80,10 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_000A => bus.ppu.bg[1].cnt.raw, 0x0400_000C => bus.ppu.bg[2].cnt.raw, 0x0400_000E => bus.ppu.bg[3].cnt.raw, - 0x0400_004C => unimplementedRead("Read {} from MOSAIC", .{T}), + 0x0400_004C => readTodo("Read {} from MOSAIC", .{T}), // Sound - 0x0400_0060...0x0400_0088 => apu.read(T, &bus.apu, address), + 0x0400_0060...0x0400_009F => apu.read(T, &bus.apu, address), // DMA Transfers 0x0400_00BA...0x0400_00DE => dma.read(T, &bus.dma, address), @@ -90,20 +92,20 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0100...0x0400_010E => timer.read(T, &bus.tim, address), // Serial Communication 1 - 0x0400_0128 => unimplementedRead("Read {} from SIOCNT", .{T}), + 0x0400_0128 => readTodo("Read {} from SIOCNT", .{T}), // Keypad Input 0x0400_0130 => bus.io.keyinput.raw, // Serial Communication 2 - 0x0400_0134 => unimplementedRead("Read {} from RCNT", .{T}), + 0x0400_0134 => readTodo("Read {} from RCNT", .{T}), // Interrupts 0x0400_0200 => bus.io.ie.raw, 0x0400_0202 => bus.io.irq.raw, - 0x0400_0204 => unimplementedRead("Read {} from WAITCNT", .{T}), + 0x0400_0204 => readTodo("Read {} from WAITCNT", .{T}), 0x0400_0208 => @boolToInt(bus.io.ime), - else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }), }, u8 => return switch (address) { // Display @@ -120,18 +122,18 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0060...0x0400_0089 => apu.read(T, &bus.apu, address), // Serial Communication 1 - 0x0400_0128 => unimplementedRead("Read {} from SIOCNT_L", .{T}), + 0x0400_0128 => readTodo("Read {} from SIOCNT_L", .{T}), // Keypad Input - 0x0400_0130 => unimplementedRead("read {} from KEYINPUT_L", .{T}), + 0x0400_0130 => readTodo("read {} from KEYINPUT_L", .{T}), // Serial Communication 2 - 0x0400_0135 => unimplementedRead("Read {} from RCNT_H", .{T}), + 0x0400_0135 => readTodo("Read {} from RCNT_H", .{T}), // Interrupts 0x0400_0200 => @truncate(T, bus.io.ie.raw), 0x0400_0300 => @enumToInt(bus.io.postflg), - else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }), }, else => @compileError("I/O: Unsupported read width"), }; @@ -201,7 +203,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0204 => log.debug("Wrote 0x{X:0>8} to WAITCNT", .{value}), 0x0400_0208 => bus.io.ime = value & 1 == 1, 0x0400_020C...0x0400_021C => {}, // Unused - else => undefinedWrite("Tried to write {} 0x{X:0>8} to 0x{X:0>8}", .{ T, value, address }), + else => writeUndefined(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, address }), }, u16 => switch (address) { // Display @@ -283,7 +285,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0204 => log.debug("Wrote 0x{X:0>4} to WAITCNT", .{value}), 0x0400_0208 => bus.io.ime = value & 1 == 1, 0x0400_0206, 0x0400_020A => {}, // Not Used - else => undefinedWrite("Tried to write {} 0x{X:0>4} to 0x{X:0>8}", .{ T, value, address }), + else => writeUndefined(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, address }), }, u8 => switch (address) { // Display @@ -315,29 +317,17 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0301 => bus.io.haltcnt = if (value >> 7 & 1 == 0) .Halt else std.debug.panic("TODO: Implement STOP", .{}), 0x0400_0410 => log.debug("Wrote 0x{X:0>2} to the common yet undocumented 0x{X:0>8}", .{ value, address }), - else => undefinedWrite("Tried to write {} 0x{X:0>2} to 0x{X:0>8}", .{ T, value, address }), + else => writeUndefined(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, address }), }, else => @compileError("I/O: Unsupported write width"), }; } -fn undefinedRead(comptime format: []const u8, args: anytype) u8 { - log.debug(format, args); - if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); - - return 0; -} - -fn unimplementedRead(comptime format: []const u8, args: anytype) u8 { +fn readTodo(comptime format: []const u8, args: anytype) u8 { log.debug(format, args); return 0; } -fn undefinedWrite(comptime format: []const u8, args: anytype) void { - log.debug(format, args); - if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); -} - /// Read / Write pub const PostFlag = enum(u1) { FirstBoot = 0, diff --git a/src/bus/timer.zig b/src/bus/timer.zig index 28c9528..480ea93 100644 --- a/src/bus/timer.zig +++ b/src/bus/timer.zig @@ -6,6 +6,8 @@ const Scheduler = @import("../scheduler.zig").Scheduler; const Event = @import("../scheduler.zig").Event; const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const readUndefined = @import("../util.zig").readUndefined; +const writeUndefined = @import("../util.zig").writeUndefined; pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) }); const log = std.log.scoped(.Timer); @@ -22,7 +24,7 @@ pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) T { 0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].getCntL(), 0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].getCntL(), 0xC => @as(T, tim.*[3].cnt.raw) << 16 | tim.*[3].getCntL(), - else => @panic("TODO: u32 timer unexpected nybble"), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, u16 => switch (nybble) { 0x0 => tim.*[0].getCntL(), @@ -33,10 +35,10 @@ pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) T { 0xA => tim.*[2].cnt.raw, 0xC => tim.*[3].getCntL(), 0xE => tim.*[3].cnt.raw, - else => @panic("TODO: u16 timer unexpected nybble"), + else => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, - u8 => @panic("TODO: u8 timer unexpected read"), - else => @compileError("TIMX: Unsupported read width"), + u8 => readUndefined(log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), + else => @compileError("TIM: Unsupported read width"), }; } @@ -49,7 +51,7 @@ pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void { 0x4 => tim.*[1].setCnt(value), 0x8 => tim.*[2].setCnt(value), 0xC => tim.*[3].setCnt(value), - else => @panic("TODO: u32 timer unexpected nybble"), + else => writeUndefined(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, u16 => switch (nybble) { 0x0 => tim.*[0].setCntL(value), @@ -60,10 +62,10 @@ pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void { 0xA => tim.*[2].setCntH(value), 0xC => tim.*[3].setCntL(value), 0xE => tim.*[3].setCntH(value), - else => @panic("TODO: u16 timer unexpected nybble"), + else => writeUndefined(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, - u8 => @panic("TODO: u8 timer unexpected write"), - else => @compileError("TIMX: Unsupported write width"), + u8 => writeUndefined(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), + else => @compileError("TIM: Unsupported write width"), }; } diff --git a/src/util.zig b/src/util.zig index 999a668..975b667 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const Log2Int = std.math.Log2Int; // Sign-Extend value of type `T` to type `U` @@ -99,3 +100,15 @@ pub const FilePaths = struct { bios: ?[]const u8, save: ?[]const u8, }; + +pub fn readUndefined(log: anytype, comptime format: []const u8, args: anytype) u8 { + log.debug(format, args); + if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); + + return 0; +} + +pub fn writeUndefined(log: anytype, comptime format: []const u8, args: anytype) void { + log.debug(format, args); + if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); +}