From 6154585e7711b7add1ae580ed5ce302e28d6be00 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 30 Oct 2022 00:40:57 -0300 Subject: [PATCH] feat(dma): implement all dma i/o writes --- src/core/bus/dma.zig | 70 +++++++++++++++++++++++++++----------------- src/core/bus/io.zig | 3 ++ src/util.zig | 31 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/core/bus/dma.zig b/src/core/bus/dma.zig index 8a66f6b..8df5359 100644 --- a/src/core/bus/dma.zig +++ b/src/core/bus/dma.zig @@ -8,9 +8,9 @@ const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; pub const DmaTuple = std.meta.Tuple(&[_]type{ DmaController(0), DmaController(1), DmaController(2), DmaController(3) }); const log = std.log.scoped(.DmaTransfer); -const setHi = util.setHi; -const setLo = util.setLo; -const shift = util.shift; +const getHalf = util.shift; +const setHalf = util.setHalf; +const setQuart = util.setQuart; const rotr = @import("../../util.zig").rotr; @@ -54,19 +54,19 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T { u8 => switch (byte) { 0xB0...0xB7 => null, // DMA0SAD, DMA0DAD 0xB8, 0xB9 => 0x00, // DMA0CNT_L - 0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> shift(byte)), + 0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> getHalf(byte)), 0xBC...0xC3 => null, // DMA1SAD, DMA1DAD 0xC4, 0xC5 => 0x00, // DMA1CNT_L - 0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> shift(byte)), + 0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> getHalf(byte)), 0xC8...0xCF => null, // DMA2SAD, DMA2DAD 0xD0, 0xD1 => 0x00, // DMA2CNT_L - 0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> shift(byte)), + 0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> getHalf(byte)), 0xD4...0xDB => null, // DMA3SAD, DMA3DAD 0xDC, 0xDD => 0x00, // DMA3CNT_L - 0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> shift(byte)), + 0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> getHalf(byte)), else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }), }, else => @compileError("DMA: Unsupported read width"), @@ -74,55 +74,71 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T { } pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void { - const byte = @truncate(u8, addr); + const byte_addr = @truncate(u8, addr); switch (T) { - u32 => switch (byte) { + u32 => switch (byte_addr) { 0xB0 => dma.*[0].setDmasad(value), 0xB4 => dma.*[0].setDmadad(value), 0xB8 => dma.*[0].setDmacnt(value), + 0xBC => dma.*[1].setDmasad(value), 0xC0 => dma.*[1].setDmadad(value), 0xC4 => dma.*[1].setDmacnt(value), + 0xC8 => dma.*[2].setDmasad(value), 0xCC => dma.*[2].setDmadad(value), 0xD0 => dma.*[2].setDmacnt(value), + 0xD4 => dma.*[3].setDmasad(value), 0xD8 => dma.*[3].setDmadad(value), 0xDC => dma.*[3].setDmacnt(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, - u16 => switch (byte) { - 0xB0 => dma.*[0].setDmasad(setLo(u32, dma.*[0].sad, value)), - 0xB2 => dma.*[0].setDmasad(setHi(u32, dma.*[0].sad, value)), - 0xB4 => dma.*[0].setDmadad(setLo(u32, dma.*[0].dad, value)), - 0xB6 => dma.*[0].setDmadad(setHi(u32, dma.*[0].dad, value)), + u16 => switch (byte_addr) { + 0xB0, 0xB2 => dma.*[0].setDmasad(setHalf(u32, dma.*[0].sad, byte_addr, value)), + 0xB4, 0xB6 => dma.*[0].setDmadad(setHalf(u32, dma.*[0].dad, byte_addr, value)), 0xB8 => dma.*[0].setDmacntL(value), 0xBA => dma.*[0].setDmacntH(value), - 0xBC => dma.*[1].setDmasad(setLo(u32, dma.*[1].sad, value)), - 0xBE => dma.*[1].setDmasad(setHi(u32, dma.*[1].sad, value)), - 0xC0 => dma.*[1].setDmadad(setLo(u32, dma.*[1].dad, value)), - 0xC2 => dma.*[1].setDmadad(setHi(u32, dma.*[1].dad, value)), + 0xBC, 0xBE => dma.*[1].setDmasad(setHalf(u32, dma.*[1].sad, byte_addr, value)), + 0xC0, 0xC2 => dma.*[1].setDmadad(setHalf(u32, dma.*[1].dad, byte_addr, value)), 0xC4 => dma.*[1].setDmacntL(value), 0xC6 => dma.*[1].setDmacntH(value), - 0xC8 => dma.*[2].setDmasad(setLo(u32, dma.*[2].sad, value)), - 0xCA => dma.*[2].setDmasad(setHi(u32, dma.*[2].sad, value)), - 0xCC => dma.*[2].setDmadad(setLo(u32, dma.*[2].dad, value)), - 0xCE => dma.*[2].setDmadad(setHi(u32, dma.*[2].dad, value)), + 0xC8, 0xCA => dma.*[2].setDmasad(setHalf(u32, dma.*[2].sad, byte_addr, value)), + 0xCC, 0xCE => dma.*[2].setDmadad(setHalf(u32, dma.*[2].dad, byte_addr, value)), 0xD0 => dma.*[2].setDmacntL(value), 0xD2 => dma.*[2].setDmacntH(value), - 0xD4 => dma.*[3].setDmasad(setLo(u32, dma.*[3].sad, value)), - 0xD6 => dma.*[3].setDmasad(setHi(u32, dma.*[3].sad, value)), - 0xD8 => dma.*[3].setDmadad(setLo(u32, dma.*[3].dad, value)), - 0xDA => dma.*[3].setDmadad(setHi(u32, dma.*[3].dad, value)), + 0xD4, 0xD6 => dma.*[3].setDmasad(setHalf(u32, dma.*[3].sad, byte_addr, value)), + 0xD8, 0xDA => dma.*[3].setDmadad(setHalf(u32, dma.*[3].dad, byte_addr, value)), 0xDC => dma.*[3].setDmacntL(value), 0xDE => dma.*[3].setDmacntH(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, - u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), + u8 => switch (byte_addr) { + 0xB0, 0xB1, 0xB2, 0xB3 => dma.*[0].setDmasad(setQuart(dma.*[0].sad, byte_addr, value)), + 0xB4, 0xB5, 0xB6, 0xB7 => dma.*[0].setDmadad(setQuart(dma.*[0].dad, byte_addr, value)), + 0xB8, 0xB9 => dma.*[0].setDmacntL(setHalf(u16, dma.*[0].word_count, byte_addr, value)), + 0xBA, 0xBB => dma.*[0].setDmacntH(setHalf(u16, dma.*[0].cnt.raw, byte_addr, value)), + + 0xBC, 0xBD, 0xBE, 0xBF => dma.*[1].setDmasad(setQuart(dma.*[1].sad, byte_addr, value)), + 0xC0, 0xC1, 0xC2, 0xC3 => dma.*[1].setDmadad(setQuart(dma.*[1].dad, byte_addr, value)), + 0xC4, 0xC5 => dma.*[1].setDmacntL(setHalf(u16, dma.*[1].word_count, byte_addr, value)), + 0xC6, 0xC7 => dma.*[1].setDmacntH(setHalf(u16, dma.*[1].cnt.raw, byte_addr, value)), + + 0xC8, 0xC9, 0xCA, 0xCB => dma.*[2].setDmasad(setQuart(dma.*[2].sad, byte_addr, value)), + 0xCC, 0xCD, 0xCE, 0xCF => dma.*[2].setDmadad(setQuart(dma.*[2].dad, byte_addr, value)), + 0xD0, 0xD1 => dma.*[2].setDmacntL(setHalf(u16, dma.*[2].word_count, byte_addr, value)), + 0xD2, 0xD3 => dma.*[2].setDmacntH(setHalf(u16, dma.*[2].cnt.raw, byte_addr, value)), + + 0xD4, 0xD5, 0xD6, 0xD7 => dma.*[3].setDmasad(setQuart(dma.*[3].sad, byte_addr, value)), + 0xD8, 0xD9, 0xDA, 0xDB => dma.*[3].setDmadad(setQuart(dma.*[3].dad, byte_addr, value)), + 0xDC, 0xDD => dma.*[3].setDmacntL(setHalf(u16, dma.*[3].word_count, byte_addr, value)), + 0xDE, 0xDF => dma.*[3].setDmacntH(setHalf(u16, dma.*[3].cnt.raw, byte_addr, value)), + else => util.io.write.undef(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/core/bus/io.zig b/src/core/bus/io.zig index 21c6f47..472a411 100644 --- a/src/core/bus/io.zig +++ b/src/core/bus/io.zig @@ -222,6 +222,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { // Sound 0x0400_0060...0x0400_00A7 => apu.write(T, &bus.apu, address, value), + // Dma Transfers + 0x0400_00B0...0x0400_00DF => dma.write(T, &bus.dma, address, value), + // Serial Communication 1 0x0400_0120 => log.debug("Wrote 0x{X:0>2} to SIODATA32_L_L", .{value}), 0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT_L", .{value}), diff --git a/src/util.zig b/src/util.zig index ff8192f..69258c4 100644 --- a/src/util.zig +++ b/src/util.zig @@ -283,13 +283,44 @@ pub const audio = struct { }; }; +/// Sets a quarter (8) of the bits of the u32 `left` to the value of u8 `right` +pub inline fn setQuart(left: u32, addr: u8, right: u8) u32 { + const offset = @truncate(u2, addr); + + return switch (offset) { + 0b00 => (left & 0xFFFF_FF00) | right, + 0b01 => (left & 0xFFFF_00FF) | @as(u32, right) << 8, + 0b10 => (left & 0xFF00_FFFF) | @as(u32, right) << 16, + 0b11 => (left & 0x00FF_FFFF) | @as(u32, right) << 24, + }; +} + /// Calculates the correct shift offset for an aligned/unaligned u8 read /// /// TODO: Rename this +/// TODO: Support u16 reads of u32 values pub inline fn shift(byte: u8) u4 { return @truncate(u4, byte & 1) << 3; } +// TODO: Maybe combine SetLo and SetHi, use addr alignment to deduplicate code + +pub inline fn setHalf(comptime T: type, left: T, addr: u8, right: HalfInt(T)) T { + const offset = @truncate(u1, addr >> if (T == u32) 1 else 0); + + return switch (T) { + u32 => switch (offset) { + 0b0 => (left & 0xFFFF_0000) | right, + 0b1 => (left & 0x0000_FFFF) | @as(u32, right) << 16, + }, + u16 => switch (offset) { + 0b0 => (left & 0xFF00) | right, + 0b1 => (right & 0x00FF) | @as(u16, right) << 8, + }, + else => @compileError("unsupported type"), + }; +} + /// Sets the high bits of an integer to a value pub inline fn setLo(comptime T: type, left: T, right: HalfInt(T)) T { return switch (T) {