From 0a5df26c310077f747436632bd312e3249810853 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 21 Oct 2022 05:12:49 -0300 Subject: [PATCH] chore: move timer, apu and dma i/o addr matching outside of io.zig --- src/apu.zig | 211 +++++++++++++++++++++++++++++++++++++--------- src/bus/dma.zig | 97 +++++++++++++++++++-- src/bus/io.zig | 161 ++++------------------------------- src/bus/timer.zig | 80 +++++++++++++++--- 4 files changed, 348 insertions(+), 201 deletions(-) diff --git a/src/apu.zig b/src/apu.zig index 15002e2..6cdc788 100644 --- a/src/apu.zig +++ b/src/apu.zig @@ -13,6 +13,117 @@ const log = std.log.scoped(.APU); pub const host_sample_rate = 1 << 15; +pub fn read(comptime T: type, apu: *const Apu, addr: u32) T { + const byte = @truncate(u8, addr); + + return switch (T) { + u16 => switch (byte) { + 0x60 => apu.ch1.sweep.raw, // SOUND1CNT_L + 0x62 => apu.ch1.getSoundCntH(), + 0x64 => apu.ch1.freq.raw, // SOUND1CNT_X + + 0x68 => apu.ch2.getSoundCntL(), + 0x6C => apu.ch2.freq.raw, // SOUND2CNT_H + + 0x70 => apu.ch3.select.raw, // SOUND3CNT_L + 0x74 => apu.ch3.freq.raw, // SOUND3CNT_X + + 0x78 => apu.ch4.getSoundCntL(), + 0x7C => apu.ch4.getSoundCntH(), + + 0x80 => apu.dma_cnt.raw, // SOUNDCNT_L + 0x88 => apu.bias.raw, // SOUNDBIAS + else => @panic("TODO: Unexpected APU u16 read"), + }, + u8 => switch (byte) { + 0x60 => apu.ch1.sweep.raw, // NR10 + 0x63 => apu.ch1.envelope.raw, // NR12 + 0x69 => apu.ch2.envelope.raw, // NR22 + 0x73 => apu.ch3.vol.raw, // NR32 + 0x79 => apu.ch4.envelope.raw, // NR42 + 0x7C => apu.ch4.poly.raw, // NR43 + 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"), + }, + u32 => @panic("TODO: Unexpected APU u32 read"), + else => @compileError("APU: Unsupported read width"), + }; +} + +pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void { + const byte = @truncate(u8, addr); + + switch (T) { + u32 => switch (byte) { + 0x80 => { + apu.psg_cnt.raw = @truncate(u16, value); // SOUNDCNT_L + apu.dma_cnt.raw = @truncate(u16, value >> 16); // SOUNDCNT_H + }, + // WAVE_RAM + 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"), + }, + u16 => switch (byte) { + 0x60 => apu.ch1.sweep.raw = @truncate(u8, value), // SOUND1CNT_L + 0x62 => apu.ch1.setSoundCntH(value), + 0x64 => apu.ch1.setSoundCntX(&apu.fs, value), + + 0x68 => apu.ch2.setSoundCntL(value), + 0x6C => apu.ch2.setSoundCntH(&apu.fs, value), + + 0x70 => apu.ch3.setSoundCntL(@truncate(u8, value)), + 0x72 => apu.ch3.setSoundCntH(value), + 0x74 => apu.ch3.setSoundCntX(&apu.fs, value), + + 0x78 => apu.ch4.setSoundCntL(value), + 0x7C => apu.ch4.setSoundCntH(&apu.fs, value), + + 0x80 => apu.psg_cnt.raw = value, // SOUNDCNT_L + 0x82 => apu.setSoundCntH(value), + 0x84 => apu.setSoundCntX(value >> 7 & 1 == 1), + 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"), + }, + u8 => switch (byte) { + 0x60 => apu.ch1.sweep.raw = value, // NR10 + 0x62 => apu.ch1.setNr11(value), + 0x63 => apu.ch1.setNr12(value), + 0x64 => apu.ch1.setNr13(value), + 0x65 => apu.ch1.setNr14(&apu.fs, value), + + 0x68 => apu.ch2.setNr21(value), + 0x69 => apu.ch2.setNr22(value), + 0x6C => apu.ch2.setNr23(value), + 0x6D => apu.ch2.setNr24(&apu.fs, value), + + 0x70 => apu.ch3.setSoundCntL(value), // NR30 + 0x72 => apu.ch3.setNr31(value), + 0x73 => apu.ch3.vol.raw = value, // NR32 + 0x74 => apu.ch3.setNr33(value), + 0x75 => apu.ch3.setNr34(&apu.fs, value), + + 0x78 => apu.ch4.setNr41(value), + 0x79 => apu.ch4.setNr42(value), + 0x7C => apu.ch4.poly.raw = value, // NR 43 + 0x7D => apu.ch4.setNr44(&apu.fs, value), + + 0x80 => apu.setNr50(value), + 0x81 => apu.setNr51(value), + 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 => @compileError("APU: Unsupported write width"), + } +} + pub const Apu = struct { const Self = @This(); @@ -76,7 +187,7 @@ pub const Apu = struct { self.ch4.reset(); } - pub fn setDmaCnt(self: *Self, value: u16) void { + pub fn setSoundCntH(self: *Self, value: u16) void { const new: io.DmaSoundControl = .{ .raw = value }; // Reinitializing instead of resetting is fine because @@ -106,7 +217,7 @@ pub const Apu = struct { } /// NR52 - pub fn soundCntX(self: *const Self) u8 { + pub fn getSoundCntX(self: *const Self) u8 { const apu_enable: u8 = @boolToInt(self.cnt.apu_enable.read()); const ch1_enable: u8 = @boolToInt(self.ch1.enabled); @@ -118,16 +229,16 @@ pub const Apu = struct { } /// NR50 - pub fn setSoundCntLLow(self: *Self, byte: u8) void { + pub fn setNr50(self: *Self, byte: u8) void { self.psg_cnt.raw = (self.psg_cnt.raw & 0xFF00) | byte; } /// NR51 - pub fn setSoundCntLHigh(self: *Self, byte: u8) void { + pub fn setNr51(self: *Self, byte: u8) void { self.psg_cnt.raw = @as(u16, byte) << 8 | (self.psg_cnt.raw & 0xFF); } - pub fn setBiasHigh(self: *Self, byte: u8) void { + pub fn setSoundBiasH(self: *Self, byte: u8) void { self.bias.raw = (@as(u16, byte) << 8) | (self.bias.raw & 0xFF); } @@ -389,37 +500,42 @@ const ToneSweep = struct { return @as(i16, self.sample); } + /// NR11, NR12 + pub fn getSoundCntH(self: *const Self) u16 { + return @as(u16, self.envelope.raw) << 8 | self.duty.raw; + } + /// NR11, NR12 pub fn setSoundCntH(self: *Self, value: u16) void { - self.setDuty(@truncate(u8, value)); - self.setEnvelope(@truncate(u8, value >> 8)); + self.setNr11(@truncate(u8, value)); + self.setNr12(@truncate(u8, value >> 8)); } /// NR11 - pub fn setDuty(self: *Self, value: u8) void { + pub fn setNr11(self: *Self, value: u8) void { self.duty.raw = value; self.len_dev.timer = @as(u7, 64) - @truncate(u6, value); } /// NR12 - pub fn setEnvelope(self: *Self, value: u8) void { + pub fn setNr12(self: *Self, value: u8) void { self.envelope.raw = value; if (!self.isDacEnabled()) self.enabled = false; } /// NR13, NR14 - pub fn setFreq(self: *Self, fs: *const FrameSequencer, value: u16) void { - self.setFreqLow(@truncate(u8, value)); - self.setFreqHigh(fs, @truncate(u8, value >> 8)); + pub fn setSoundCntX(self: *Self, fs: *const FrameSequencer, value: u16) void { + self.setNr13(@truncate(u8, value)); + self.setNr14(fs, @truncate(u8, value >> 8)); } /// NR13 - pub fn setFreqLow(self: *Self, byte: u8) void { + pub fn setNr13(self: *Self, byte: u8) void { self.freq.raw = (self.freq.raw & 0xFF00) | byte; } /// NR14 - pub fn setFreqHigh(self: *Self, fs: *const FrameSequencer, byte: u8) void { + pub fn setNr14(self: *Self, fs: *const FrameSequencer, byte: u8) void { var new: io.Frequency = .{ .raw = (@as(u16, byte) << 8) | (self.freq.raw & 0xFF) }; if (new.trigger.read()) { @@ -524,36 +640,41 @@ const Tone = struct { } /// NR21, NR22 - pub fn setSoundCntH(self: *Self, value: u16) void { - self.setDuty(@truncate(u8, value)); - self.setEnvelope(@truncate(u8, value >> 8)); + pub fn getSoundCntL(self: *const Self) u16 { + return @as(u16, self.envelope.raw) << 8 | self.duty.raw; + } + + /// NR21, NR22 + pub fn setSoundCntL(self: *Self, value: u16) void { + self.setNr21(@truncate(u8, value)); + self.setNr22(@truncate(u8, value >> 8)); } /// NR21 - pub fn setDuty(self: *Self, value: u8) void { + pub fn setNr21(self: *Self, value: u8) void { self.duty.raw = value; self.len_dev.timer = @as(u7, 64) - @truncate(u6, value); } /// NR22 - pub fn setEnvelope(self: *Self, value: u8) void { + pub fn setNr22(self: *Self, value: u8) void { self.envelope.raw = value; if (!self.isDacEnabled()) self.enabled = false; } /// NR23, NR24 - pub fn setFreq(self: *Self, fs: *const FrameSequencer, value: u16) void { - self.setFreqLow(@truncate(u8, value)); - self.setFreqHigh(fs, @truncate(u8, value >> 8)); + pub fn setSoundCntH(self: *Self, fs: *const FrameSequencer, value: u16) void { + self.setNr23(@truncate(u8, value)); + self.setNr24(fs, @truncate(u8, value >> 8)); } /// NR23 - pub fn setFreqLow(self: *Self, byte: u8) void { + pub fn setNr23(self: *Self, byte: u8) void { self.freq.raw = (self.freq.raw & 0xFF00) | byte; } /// NR24 - pub fn setFreqHigh(self: *Self, fs: *const FrameSequencer, byte: u8) void { + pub fn setNr24(self: *Self, fs: *const FrameSequencer, byte: u8) void { var new: io.Frequency = .{ .raw = (@as(u16, byte) << 8) | (self.freq.raw & 0xFF) }; if (new.trigger.read()) { @@ -633,36 +754,36 @@ const Wave = struct { } /// NR30 - pub fn setWaveSelect(self: *Self, value: u8) void { + pub fn setSoundCntL(self: *Self, value: u8) void { self.select.raw = value; if (!self.select.enabled.read()) self.enabled = false; } /// NR31, NR32 pub fn setSoundCntH(self: *Self, value: u16) void { - self.setLength(@truncate(u8, value)); + self.setNr31(@truncate(u8, value)); self.vol.raw = (@truncate(u8, value >> 8)); } /// NR31 - pub fn setLength(self: *Self, len: u8) void { + pub fn setNr31(self: *Self, len: u8) void { self.length = len; self.len_dev.timer = 256 - @as(u9, len); } /// NR33, NR34 - pub fn setFreq(self: *Self, fs: *const FrameSequencer, value: u16) void { - self.setFreqLow(@truncate(u8, value)); - self.setFreqHigh(fs, @truncate(u8, value >> 8)); + pub fn setSoundCntX(self: *Self, fs: *const FrameSequencer, value: u16) void { + self.setNr33(@truncate(u8, value)); + self.setNr34(fs, @truncate(u8, value >> 8)); } /// NR33 - pub fn setFreqLow(self: *Self, byte: u8) void { + pub fn setNr33(self: *Self, byte: u8) void { self.freq.raw = (self.freq.raw & 0xFF00) | byte; } /// NR34 - pub fn setFreqHigh(self: *Self, fs: *const FrameSequencer, byte: u8) void { + pub fn setNr34(self: *Self, fs: *const FrameSequencer, byte: u8) void { var new: io.Frequency = .{ .raw = (@as(u16, byte) << 8) | (self.freq.raw & 0xFF) }; if (new.trigger.read()) { @@ -757,32 +878,42 @@ const Noise = struct { self.env_dev.tick(self.envelope); } + /// NR41, NR42 + pub fn getSoundCntL(self: *const Self) u16 { + return @as(u16, self.envelope.raw) << 8 | self.len; + } + + /// NR41, NR42 + pub fn setSoundCntL(self: *Self, value: u16) void { + self.setNr41(@truncate(u8, value)); + self.setNr42(@truncate(u8, value >> 8)); + } + /// NR41 - pub fn setLength(self: *Self, len: u8) void { + pub fn setNr41(self: *Self, len: u8) void { self.len = @truncate(u6, len); self.len_dev.timer = @as(u7, 64) - @truncate(u6, len); } /// NR42 - pub fn setEnvelope(self: *Self, value: u8) void { + pub fn setNr42(self: *Self, value: u8) void { self.envelope.raw = value; if (!self.isDacEnabled()) self.enabled = false; } - /// NR41, NR42 - pub fn setSoundCntL(self: *Self, value: u16) void { - self.setLength(@truncate(u8, value)); - self.setEnvelope(@truncate(u8, value >> 8)); + /// NR43, NR44 + pub fn getSoundCntH(self: *const Self) u16 { + return @as(u16, self.poly.raw) << 8 | self.cnt.raw; } /// NR43, NR44 pub fn setSoundCntH(self: *Self, fs: *const FrameSequencer, value: u16) void { self.poly.raw = @truncate(u8, value); - self.setCnt(fs, @truncate(u8, value >> 8)); + self.setNr44(fs, @truncate(u8, value >> 8)); } /// NR44 - pub fn setCnt(self: *Self, fs: *const FrameSequencer, byte: u8) void { + pub fn setNr44(self: *Self, fs: *const FrameSequencer, byte: u8) void { var new: io.NoiseControl = .{ .raw = byte }; if (new.trigger.read()) { diff --git a/src/bus/dma.zig b/src/bus/dma.zig index b116ebe..4d328e9 100644 --- a/src/bus/dma.zig +++ b/src/bus/dma.zig @@ -11,6 +11,83 @@ pub fn create() DmaTuple { return .{ DmaController(0).init(), DmaController(1).init(), DmaController(2).init(), DmaController(3).init() }; } +pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) T { + const byte = @truncate(u8, addr); + + return switch (T) { + u32 => switch (byte) { + 0xB8 => @as(T, dma.*[0].cnt.raw) << 16, + 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"), + }, + 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"), + }, + u8 => @panic("TODO: Unexpected u8 DMA read"), + else => @compileError("DMA: Unsupported read width"), + }; +} + +pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void { + const byte = @truncate(u8, addr); + + switch (T) { + u32 => switch (byte) { + 0xB0 => dma.*[0].setSad(value), + 0xB4 => dma.*[0].setDad(value), + 0xB8 => dma.*[0].setCnt(value), + 0xBC => dma.*[1].setSad(value), + 0xC0 => dma.*[1].setDad(value), + 0xC4 => dma.*[1].setCnt(value), + 0xC8 => dma.*[2].setSad(value), + 0xCC => dma.*[2].setDad(value), + 0xD0 => dma.*[2].setCnt(value), + 0xD4 => dma.*[3].setSad(value), + 0xD8 => dma.*[3].setDad(value), + 0xDC => dma.*[3].setCnt(value), + else => @panic("TODO: Unexpected u32 DMA write"), + }, + u16 => switch (byte) { + 0xB0 => dma.*[0].setSad(setU32L(dma.*[0].sad, value)), + 0xB2 => dma.*[0].setSad(setU32H(dma.*[0].sad, value)), + 0xB4 => dma.*[0].setDad(setU32L(dma.*[0].dad, value)), + 0xB6 => dma.*[0].setDad(setU32H(dma.*[0].dad, value)), + 0xB8 => dma.*[0].setCntL(value), + 0xBA => dma.*[0].setCntH(value), + + 0xBC => dma.*[1].setSad(setU32L(dma.*[1].sad, value)), + 0xBE => dma.*[1].setSad(setU32H(dma.*[1].sad, value)), + 0xC0 => dma.*[1].setDad(setU32L(dma.*[1].dad, value)), + 0xC2 => dma.*[1].setDad(setU32H(dma.*[1].dad, value)), + 0xC4 => dma.*[1].setCntL(value), + 0xC6 => dma.*[1].setCntH(value), + + 0xC8 => dma.*[2].setSad(setU32L(dma.*[2].sad, value)), + 0xCA => dma.*[2].setSad(setU32H(dma.*[2].sad, value)), + 0xCC => dma.*[2].setDad(setU32L(dma.*[2].dad, value)), + 0xCE => dma.*[2].setDad(setU32H(dma.*[2].dad, value)), + 0xD0 => dma.*[2].setCntL(value), + 0xD2 => dma.*[2].setCntH(value), + + 0xD4 => dma.*[3].setSad(setU32L(dma.*[3].sad, value)), + 0xD6 => dma.*[3].setSad(setU32H(dma.*[3].sad, value)), + 0xD8 => dma.*[3].setDad(setU32L(dma.*[3].dad, value)), + 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"), + }, + u8 => @panic("TODO: Unexpected u8 DMA write"), + else => @compileError("DMA: Unsupported write width"), + } +} + /// Function that creates a DMAController. Determines unique DMA Controller behaiour at compile-time fn DmaController(comptime id: u2) type { return struct { @@ -66,19 +143,19 @@ fn DmaController(comptime id: u2) type { }; } - pub fn writeSad(self: *Self, addr: u32) void { + pub fn setSad(self: *Self, addr: u32) void { self.sad = addr & sad_mask; } - pub fn writeDad(self: *Self, addr: u32) void { + pub fn setDad(self: *Self, addr: u32) void { self.dad = addr & dad_mask; } - pub fn writeWordCount(self: *Self, halfword: u16) void { + pub fn setCntL(self: *Self, halfword: u16) void { self.word_count = @truncate(@TypeOf(self.word_count), halfword); } - pub fn writeCntHigh(self: *Self, halfword: u16) void { + pub fn setCntH(self: *Self, halfword: u16) void { const new = DmaControl{ .raw = halfword }; if (!self.cnt.enabled.read() and new.enabled.read()) { @@ -94,9 +171,9 @@ fn DmaController(comptime id: u2) type { self.cnt.raw = halfword; } - pub fn writeCnt(self: *Self, word: u32) void { + pub fn setCnt(self: *Self, word: u32) void { self.word_count = @truncate(@TypeOf(self.word_count), word); - self.writeCntHigh(@truncate(u16, word >> 16)); + self.setCntH(@truncate(u16, word >> 16)); } pub fn step(self: *Self, cpu: *Arm7tdmi) bool { @@ -218,3 +295,11 @@ const DmaKind = enum(u2) { VBlank, Special, }; + +fn setU32L(left: u32, right: u16) u32 { + return (left & 0xFFFF_0000) | right; +} + +fn setU32H(left: u32, right: u16) u32 { + return (left & 0x0000_FFFF) | (@as(u32, right) << 16); +} diff --git a/src/bus/io.zig b/src/bus/io.zig index 4ec3ddd..a4b5cc0 100644 --- a/src/bus/io.zig +++ b/src/bus/io.zig @@ -7,6 +7,10 @@ const Bus = @import("../Bus.zig"); const DmaController = @import("dma.zig").DmaController; const Scheduler = @import("../scheduler.zig").Scheduler; +const timer = @import("timer.zig"); +const dma = @import("dma.zig"); +const apu = @import("../apu.zig"); + const log = std.log.scoped(.@"I/O"); pub const Io = struct { @@ -46,16 +50,10 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0006 => @as(T, bus.ppu.bg[0].cnt.raw) << 16 | bus.ppu.vcount.raw, // DMA Transfers - 0x0400_00B8 => @as(T, bus.dma[0].cnt.raw) << 16, - 0x0400_00C4 => @as(T, bus.dma[1].cnt.raw) << 16, - 0x0400_00D0 => @as(T, bus.dma[1].cnt.raw) << 16, - 0x0400_00DC => @as(T, bus.dma[3].cnt.raw) << 16, + 0x0400_00B8...0x0400_00DC => dma.read(T, &bus.dma, address), // Timers - 0x0400_0100 => @as(T, bus.tim[0].cnt.raw) << 16 | bus.tim[0].counter(), - 0x0400_0104 => @as(T, bus.tim[1].cnt.raw) << 16 | bus.tim[1].counter(), - 0x0400_0108 => @as(T, bus.tim[2].cnt.raw) << 16 | bus.tim[2].counter(), - 0x0400_010C => @as(T, bus.tim[3].cnt.raw) << 16 | bus.tim[3].counter(), + 0x0400_0100...0x0400_010C => timer.read(T, &bus.tim, address), // Serial Communication 1 0x0400_0128 => unimplementedRead("Read {} from SIOCNT and SIOMLT_SEND", .{T}), @@ -83,33 +81,13 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_004C => unimplementedRead("Read {} from MOSAIC", .{T}), // Sound - 0x0400_0060 => bus.apu.ch1.sweep.raw, - 0x0400_0062 => @as(u16, bus.apu.ch1.envelope.raw) << 8 | bus.apu.ch1.duty.raw, - 0x0400_0064 => bus.apu.ch1.freq.raw, - 0x0400_0068 => @as(u16, bus.apu.ch2.envelope.raw) << 8 | bus.apu.ch2.duty.raw, - 0x0400_006C => bus.apu.ch2.freq.raw, - 0x0400_0070 => bus.apu.ch3.select.raw, - 0x0400_0074 => bus.apu.ch3.freq.raw, - 0x0400_0078 => @as(u16, bus.apu.ch4.envelope.raw) << 8 | bus.apu.ch4.len, - 0x0400_007C => @as(u16, bus.apu.ch4.poly.raw) << 8 | bus.apu.ch4.cnt.raw, - 0x0400_0080 => bus.apu.dma_cnt.raw, - 0x0400_0088 => bus.apu.bias.raw, + 0x0400_0060...0x0400_0088 => apu.read(T, &bus.apu, address), // DMA Transfers - 0x0400_00BA => bus.dma[0].cnt.raw, - 0x0400_00C6 => bus.dma[1].cnt.raw, - 0x0400_00D2 => bus.dma[2].cnt.raw, - 0x0400_00DE => bus.dma[3].cnt.raw, + 0x0400_00BA...0x0400_00DE => dma.read(T, &bus.dma, address), // Timers - 0x0400_0100 => bus.tim[0].counter(), - 0x0400_0102 => bus.tim[0].cnt.raw, - 0x0400_0104 => bus.tim[1].counter(), - 0x0400_0106 => bus.tim[1].cnt.raw, - 0x0400_0108 => bus.tim[2].counter(), - 0x0400_010A => bus.tim[2].cnt.raw, - 0x0400_010C => bus.tim[3].counter(), - 0x0400_010E => bus.tim[3].cnt.raw, + 0x0400_0100...0x0400_010E => timer.read(T, &bus.tim, address), // Serial Communication 1 0x0400_0128 => unimplementedRead("Read {} from SIOCNT", .{T}), @@ -139,15 +117,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_000B => @truncate(T, bus.ppu.bg[1].cnt.raw >> 8), // Sound - 0x0400_0060 => bus.apu.ch1.sweep.raw, - 0x0400_0063 => bus.apu.ch1.envelope.raw, - 0x0400_0069 => bus.apu.ch2.envelope.raw, - 0x0400_0073 => bus.apu.ch3.vol.raw, - 0x0400_0079 => bus.apu.ch4.envelope.raw, - 0x0400_007C => bus.apu.ch4.poly.raw, - 0x0400_0081 => @truncate(T, bus.apu.psg_cnt.raw >> 8), - 0x0400_0084 => bus.apu.soundCntX(), - 0x0400_0089 => @truncate(T, bus.apu.bias.raw >> 8), + 0x0400_0060...0x0400_0089 => apu.read(T, &bus.apu, address), // Serial Communication 1 0x0400_0128 => unimplementedRead("Read {} from SIOCNT_L", .{T}), @@ -199,35 +169,15 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0058...0x0400_005C => {}, // Unused // Sound - 0x0400_0080 => { - bus.apu.psg_cnt.raw = @truncate(u16, value); - bus.apu.dma_cnt.raw = @truncate(u16, value >> 16); - }, - 0x0400_0090...0x0400_009F => bus.apu.ch3.wave_dev.write(T, bus.apu.ch3.select, address, value), - 0x0400_00A0 => bus.apu.chA.push(value), - 0x0400_00A4 => bus.apu.chB.push(value), + 0x0400_0080...0x0400_00A4 => apu.write(T, &bus.apu, address, value), 0x0400_00A8, 0x0400_00AC => {}, // Unused // DMA Transfers - 0x0400_00B0 => bus.dma[0].writeSad(value), - 0x0400_00B4 => bus.dma[0].writeDad(value), - 0x0400_00B8 => bus.dma[0].writeCnt(value), - 0x0400_00BC => bus.dma[1].writeSad(value), - 0x0400_00C0 => bus.dma[1].writeDad(value), - 0x0400_00C4 => bus.dma[1].writeCnt(value), - 0x0400_00C8 => bus.dma[2].writeSad(value), - 0x0400_00CC => bus.dma[2].writeDad(value), - 0x0400_00D0 => bus.dma[2].writeCnt(value), - 0x0400_00D4 => bus.dma[3].writeSad(value), - 0x0400_00D8 => bus.dma[3].writeDad(value), - 0x0400_00DC => bus.dma[3].writeCnt(value), + 0x0400_00B0...0x0400_00DC => dma.write(T, &bus.dma, address, value), 0x0400_00E0...0x0400_00FC => {}, // Unused // Timers - 0x0400_0100 => bus.tim[0].writeCnt(value), - 0x0400_0104 => bus.tim[1].writeCnt(value), - 0x0400_0108 => bus.tim[2].writeCnt(value), - 0x0400_010C => bus.tim[3].writeCnt(value), + 0x0400_0100...0x0400_010C => timer.write(T, &bus.tim, address, value), 0x0400_0110...0x0400_011C => {}, // Unused // Serial Communication 1 @@ -299,64 +249,13 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_004E, 0x0400_0056 => {}, // Not used // Sound - 0x0400_0060 => bus.apu.ch1.sweep.raw = @truncate(u8, value), // Channel 1 - 0x0400_0062 => bus.apu.ch1.setSoundCntH(value), - 0x0400_0064 => bus.apu.ch1.setFreq(&bus.apu.fs, value), - - 0x0400_0068 => bus.apu.ch2.setSoundCntH(value), // Channel 2 - 0x0400_006C => bus.apu.ch2.setFreq(&bus.apu.fs, value), - - 0x0400_0070 => bus.apu.ch3.setWaveSelect(@truncate(u8, value)), // Channel 3 - 0x0400_0072 => bus.apu.ch3.setSoundCntH(value), - 0x0400_0074 => bus.apu.ch3.setFreq(&bus.apu.fs, value), - - 0x0400_0078 => bus.apu.ch4.setSoundCntL(value), // Channel 4 - 0x0400_007C => bus.apu.ch4.setSoundCntH(&bus.apu.fs, value), - - 0x0400_0080 => bus.apu.psg_cnt.raw = value, - 0x0400_0082 => bus.apu.setDmaCnt(value), - 0x0400_0084 => bus.apu.setSoundCntX(value >> 7 & 1 == 1), - 0x0400_0088 => bus.apu.bias.raw = value, - 0x0400_0090...0x0400_009F => bus.apu.ch3.wave_dev.write(T, bus.apu.ch3.select, address, value), + 0x0400_0060...0x0400_009F => apu.write(T, &bus.apu, address, value), // Dma Transfers - 0x0400_00B0 => bus.dma[0].writeSad(bus.dma[0].sad & 0xFFFF_0000 | value), - 0x0400_00B2 => bus.dma[0].writeSad(bus.dma[0].sad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00B4 => bus.dma[0].writeDad(bus.dma[0].dad & 0xFFFF_0000 | value), - 0x0400_00B6 => bus.dma[0].writeDad(bus.dma[0].dad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00B8 => bus.dma[0].writeWordCount(value), - 0x0400_00BA => bus.dma[0].writeCntHigh(value), - - 0x0400_00BC => bus.dma[1].writeSad(bus.dma[1].sad & 0xFFFF_0000 | value), - 0x0400_00BE => bus.dma[1].writeSad(bus.dma[1].sad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00C0 => bus.dma[1].writeDad(bus.dma[1].dad & 0xFFFF_0000 | value), - 0x0400_00C2 => bus.dma[1].writeDad(bus.dma[1].dad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00C4 => bus.dma[1].writeWordCount(value), - 0x0400_00C6 => bus.dma[1].writeCntHigh(value), - - 0x0400_00C8 => bus.dma[2].writeSad(bus.dma[2].sad & 0xFFFF_0000 | value), - 0x0400_00CA => bus.dma[2].writeSad(bus.dma[2].sad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00CC => bus.dma[2].writeDad(bus.dma[2].dad & 0xFFFF_0000 | value), - 0x0400_00CE => bus.dma[2].writeDad(bus.dma[2].dad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00D0 => bus.dma[2].writeWordCount(value), - 0x0400_00D2 => bus.dma[2].writeCntHigh(value), - - 0x0400_00D4 => bus.dma[3].writeSad(bus.dma[3].sad & 0xFFFF_0000 | value), - 0x0400_00D6 => bus.dma[3].writeSad(bus.dma[3].sad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00D8 => bus.dma[3].writeDad(bus.dma[3].dad & 0xFFFF_0000 | value), - 0x0400_00DA => bus.dma[3].writeDad(bus.dma[3].dad & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_00DC => bus.dma[3].writeWordCount(value), - 0x0400_00DE => bus.dma[3].writeCntHigh(value), + 0x0400_00B0...0x0400_00DE => dma.write(T, &bus.dma, address, value), // Timers - 0x0400_0100 => bus.tim[0].setReload(value), - 0x0400_0102 => bus.tim[0].writeCntHigh(value), - 0x0400_0104 => bus.tim[1].setReload(value), - 0x0400_0106 => bus.tim[1].writeCntHigh(value), - 0x0400_0108 => bus.tim[2].setReload(value), - 0x0400_010A => bus.tim[2].writeCntHigh(value), - 0x0400_010C => bus.tim[3].setReload(value), - 0x0400_010E => bus.tim[3].writeCntHigh(value), + 0x0400_0100...0x0400_010E => timer.write(T, &bus.tim, address, value), 0x0400_0114 => {}, // TODO: Gyakuten Saiban writes 0x8000 to 0x0400_0114 0x0400_0110 => {}, // Not Used, @@ -399,33 +298,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0054 => log.debug("Wrote 0x{X:0>2} to BLDY_L", .{value}), // Sound - 0x0400_0060 => bus.apu.ch1.sweep.raw = value, // Channel 1 - 0x0400_0062 => bus.apu.ch1.duty.raw = value, - 0x0400_0063 => bus.apu.ch1.envelope.raw = value, - 0x0400_0064 => bus.apu.ch1.setFreqLow(value), - 0x0400_0065 => bus.apu.ch1.setFreqHigh(&bus.apu.fs, value), - - 0x0400_0068 => bus.apu.ch2.duty.raw = value, // Channel 2 - 0x0400_0069 => bus.apu.ch2.envelope.raw = value, - 0x0400_006C => bus.apu.ch2.setFreqLow(value), - 0x0400_006D => bus.apu.ch2.setFreqHigh(&bus.apu.fs, value), - - 0x0400_0070 => bus.apu.ch3.setWaveSelect(value), // Channel 3 - 0x0400_0072 => bus.apu.ch3.setLength(value), - 0x0400_0073 => bus.apu.ch3.vol.raw = value, - 0x0400_0074 => bus.apu.ch3.setFreqLow(value), - 0x0400_0075 => bus.apu.ch3.setFreqHigh(&bus.apu.fs, value), - - 0x0400_0078 => bus.apu.ch4.setLength(value), // Channel 4 - 0x0400_0079 => bus.apu.ch4.setEnvelope(value), - 0x0400_007C => bus.apu.ch4.poly.raw = value, - 0x0400_007D => bus.apu.ch4.setCnt(&bus.apu.fs, value), - - 0x0400_0080 => bus.apu.setSoundCntLLow(value), - 0x0400_0081 => bus.apu.setSoundCntLHigh(value), - 0x0400_0084 => bus.apu.setSoundCntX(value >> 7 & 1 == 1), - 0x0400_0089 => bus.apu.setBiasHigh(value), - 0x0400_0090...0x0400_009F => bus.apu.ch3.wave_dev.write(T, bus.apu.ch3.select, address, value), + 0x0400_0060...0x0400_009F => apu.write(T, &bus.apu, address, value), // Serial Communication 1 0x0400_0120 => log.debug("Wrote 0x{X:0>2} to SIODATA32_L_L", .{value}), diff --git a/src/bus/timer.zig b/src/bus/timer.zig index 77feeb6..28c9528 100644 --- a/src/bus/timer.zig +++ b/src/bus/timer.zig @@ -13,17 +13,71 @@ pub fn create(sched: *Scheduler) TimerTuple { return .{ Timer(0).init(sched), Timer(1).init(sched), Timer(2).init(sched), Timer(3).init(sched) }; } +pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) T { + const nybble = @truncate(u4, addr); + + return switch (T) { + u32 => switch (nybble) { + 0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].getCntL(), + 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"), + }, + u16 => switch (nybble) { + 0x0 => tim.*[0].getCntL(), + 0x2 => tim.*[0].cnt.raw, + 0x4 => tim.*[1].getCntL(), + 0x6 => tim.*[1].cnt.raw, + 0x8 => tim.*[2].getCntL(), + 0xA => tim.*[2].cnt.raw, + 0xC => tim.*[3].getCntL(), + 0xE => tim.*[3].cnt.raw, + else => @panic("TODO: u16 timer unexpected nybble"), + }, + u8 => @panic("TODO: u8 timer unexpected read"), + else => @compileError("TIMX: Unsupported read width"), + }; +} + +pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void { + const nybble = @truncate(u4, addr); + + return switch (T) { + u32 => switch (nybble) { + 0x0 => tim.*[0].setCnt(value), + 0x4 => tim.*[1].setCnt(value), + 0x8 => tim.*[2].setCnt(value), + 0xC => tim.*[3].setCnt(value), + else => @panic("TODO: u32 timer unexpected nybble"), + }, + u16 => switch (nybble) { + 0x0 => tim.*[0].setCntL(value), + 0x2 => tim.*[0].setCntH(value), + 0x4 => tim.*[1].setCntL(value), + 0x6 => tim.*[1].setCntH(value), + 0x8 => tim.*[2].setCntL(value), + 0xA => tim.*[2].setCntH(value), + 0xC => tim.*[3].setCntL(value), + 0xE => tim.*[3].setCntH(value), + else => @panic("TODO: u16 timer unexpected nybble"), + }, + u8 => @panic("TODO: u8 timer unexpected write"), + else => @compileError("TIMX: Unsupported write width"), + }; +} + fn Timer(comptime id: u2) type { return struct { const Self = @This(); - /// Read Only, Internal. Please use self.counter() + /// Read Only, Internal. Please use self.getCntL() _counter: u16, - /// Write Only, Internal. Please use self.setReload() + /// Write Only, Internal. Please use self.setCntL() _reload: u16, - /// Write Only, Internal. Please use self.WriteCntHigh() + /// Write Only, Internal. Please use self.setCntH() cnt: TimerControl, /// Internal. @@ -42,22 +96,26 @@ fn Timer(comptime id: u2) type { }; } - pub fn counter(self: *const Self) u16 { + /// TIMCNT_L + pub fn getCntL(self: *const Self) u16 { if (self.cnt.cascade.read() or !self.cnt.enabled.read()) return self._counter; return self._counter +% @truncate(u16, (self.sched.now() - self._start_timestamp) / self.frequency()); } - pub fn writeCnt(self: *Self, word: u32) void { - self.setReload(@truncate(u16, word)); - self.writeCntHigh(@truncate(u16, word >> 16)); - } - - pub fn setReload(self: *Self, halfword: u16) void { + /// TIMCNT_L + pub fn setCntL(self: *Self, halfword: u16) void { self._reload = halfword; } - pub fn writeCntHigh(self: *Self, halfword: u16) void { + /// TIMCNT_L & TIMCNT_H + pub fn setCnt(self: *Self, word: u32) void { + self.setCntL(@truncate(u16, word)); + self.setCntH(@truncate(u16, word >> 16)); + } + + /// TIMCNT_H + pub fn setCntH(self: *Self, halfword: u16) void { const new = TimerControl{ .raw = halfword }; // If Timer happens to be enabled, It will either be resheduled or disabled