feat: improve DMA Transfer support
This commit is contained in:
		@@ -35,6 +35,11 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
        /// Internal. Word Count
 | 
			
		||||
        _word_count: if (id == 3) u16 else u14,
 | 
			
		||||
 | 
			
		||||
        /// Some DMA Transfers are enabled during Hblank / VBlank and / or
 | 
			
		||||
        /// have delays. Thefore bit 15 of DMACNT isn't actually something
 | 
			
		||||
        /// we can use to control when we do or do not execute a step in a DMA Transfer
 | 
			
		||||
        enabled: bool,
 | 
			
		||||
 | 
			
		||||
        pub fn init() Self {
 | 
			
		||||
            return .{
 | 
			
		||||
                .id = id,
 | 
			
		||||
@@ -47,6 +52,7 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
                ._sad = 0,
 | 
			
		||||
                ._dad = 0,
 | 
			
		||||
                ._word_count = 0,
 | 
			
		||||
                .enabled = false,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -70,6 +76,9 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
                self._sad = self.sad;
 | 
			
		||||
                self._dad = self.dad;
 | 
			
		||||
                self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count;
 | 
			
		||||
 | 
			
		||||
                // Only a Start Timing of 00 has a DMA Transfer immediately begin
 | 
			
		||||
                self.enabled = new.start_timing.read() == 0b00;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.cnt.raw = halfword;
 | 
			
		||||
@@ -80,7 +89,9 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
            self.writeCntHigh(@truncate(u16, word >> 16));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn step(self: *Self, bus: *Bus) void {
 | 
			
		||||
        pub fn step(self: *Self, bus: *Bus) bool {
 | 
			
		||||
            if (!self.enabled or !self.cnt.enabled.read()) return false;
 | 
			
		||||
 | 
			
		||||
            const sad_adj = std.meta.intToEnum(Adjustment, self.cnt.sad_adj.read()) catch unreachable;
 | 
			
		||||
            const dad_adj = std.meta.intToEnum(Adjustment, self.cnt.dad_adj.read()) catch unreachable;
 | 
			
		||||
 | 
			
		||||
@@ -96,8 +107,8 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (sad_adj) {
 | 
			
		||||
                .Increment => self._sad += offset,
 | 
			
		||||
                .Decrement => self._sad -= offset,
 | 
			
		||||
                .Increment => self._sad +%= offset,
 | 
			
		||||
                .Decrement => self._sad -%= offset,
 | 
			
		||||
                .Fixed => {},
 | 
			
		||||
 | 
			
		||||
                // TODO: Figure out correct behaviour on Illegal Source Addr Control Type
 | 
			
		||||
@@ -105,32 +116,77 @@ pub fn DmaController(comptime id: u2) type {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (dad_adj) {
 | 
			
		||||
                .Increment, .IncrementReload => self._dad += offset,
 | 
			
		||||
                .Decrement => self._dad -= offset,
 | 
			
		||||
                .Increment, .IncrementReload => self._dad +%= offset,
 | 
			
		||||
                .Decrement => self._dad -%= offset,
 | 
			
		||||
                .Fixed => {},
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self._word_count -= 1;
 | 
			
		||||
 | 
			
		||||
            if (self._word_count == 0) {
 | 
			
		||||
                if (self.cnt.irq.read()) {
 | 
			
		||||
                    switch (id) {
 | 
			
		||||
                        0 => bus.io.irq.dma0.set(),
 | 
			
		||||
                        1 => bus.io.irq.dma0.set(),
 | 
			
		||||
                        2 => bus.io.irq.dma0.set(),
 | 
			
		||||
                        3 => bus.io.irq.dma0.set(),
 | 
			
		||||
                if (!self.cnt.repeat.read()) {
 | 
			
		||||
                    // If we're not repeating, Fire the IRQs and disable the DMA
 | 
			
		||||
                    if (self.cnt.irq.read()) {
 | 
			
		||||
                        switch (id) {
 | 
			
		||||
                            0 => bus.io.irq.dma0.set(),
 | 
			
		||||
                            1 => bus.io.irq.dma0.set(),
 | 
			
		||||
                            2 => bus.io.irq.dma0.set(),
 | 
			
		||||
                            3 => bus.io.irq.dma0.set(),
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    self.cnt.enabled.unset();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                self.cnt.enabled.unset();
 | 
			
		||||
                // We want to disable our internal enabled flag regardless of repeat
 | 
			
		||||
                // because we only want to step A DMA that repeats during it's specific
 | 
			
		||||
                // timing window
 | 
			
		||||
                self.enabled = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn isBlocking(self: *const Self) bool {
 | 
			
		||||
            // A DMA Transfer is Blocking if it is Immediate
 | 
			
		||||
            return self.cnt.start_timing.read() == 0b00;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn pollBlankingDma(self: *Self, comptime kind: DmaKind) void {
 | 
			
		||||
            if (self.enabled) return;
 | 
			
		||||
 | 
			
		||||
            switch (kind) {
 | 
			
		||||
                .HBlank => self.enabled = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b10,
 | 
			
		||||
                .VBlank => self.enabled = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b01,
 | 
			
		||||
                .Immediate, .Special => {},
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (self.cnt.repeat.read() and self.enabled) {
 | 
			
		||||
                self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count;
 | 
			
		||||
 | 
			
		||||
                const dad_adj = std.meta.intToEnum(Adjustment, self.cnt.dad_adj.read()) catch unreachable;
 | 
			
		||||
                if (dad_adj == .IncrementReload) self._dad = self.dad;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn pollBlankingDma(bus: *Bus, comptime kind: DmaKind) void {
 | 
			
		||||
    bus.io.dma0.pollBlankingDma(kind);
 | 
			
		||||
    bus.io.dma1.pollBlankingDma(kind);
 | 
			
		||||
    bus.io.dma2.pollBlankingDma(kind);
 | 
			
		||||
    bus.io.dma3.pollBlankingDma(kind);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Adjustment = enum(u2) {
 | 
			
		||||
    Increment = 0,
 | 
			
		||||
    Decrement = 1,
 | 
			
		||||
    Fixed = 2,
 | 
			
		||||
    IncrementReload = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DmaKind = enum(u2) {
 | 
			
		||||
    Immediate = 0,
 | 
			
		||||
    HBlank,
 | 
			
		||||
    VBlank,
 | 
			
		||||
    Special,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								src/cpu.zig
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/cpu.zig
									
									
									
									
									
								
							@@ -296,30 +296,10 @@ pub const Arm7tdmi = struct {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handleDMATransfers(self: *Self) bool {
 | 
			
		||||
        const dma0 = &self.bus.io.dma0;
 | 
			
		||||
        const dma1 = &self.bus.io.dma1;
 | 
			
		||||
        const dma2 = &self.bus.io.dma2;
 | 
			
		||||
        const dma3 = &self.bus.io.dma3;
 | 
			
		||||
 | 
			
		||||
        if (dma0.cnt.enabled.read() and dma0.cnt.start_timing.read() == 0) {
 | 
			
		||||
            dma0.step(self.bus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dma1.cnt.enabled.read() and dma1.cnt.start_timing.read() == 0) {
 | 
			
		||||
            dma1.step(self.bus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dma2.cnt.enabled.read() and dma2.cnt.start_timing.read() == 0) {
 | 
			
		||||
            dma2.step(self.bus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dma3.cnt.enabled.read() and dma3.cnt.start_timing.read() == 0) {
 | 
			
		||||
            dma3.step(self.bus);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (self.bus.io.dma0.step(self.bus)) return self.bus.io.dma0.isBlocking();
 | 
			
		||||
        if (self.bus.io.dma1.step(self.bus)) return self.bus.io.dma1.isBlocking();
 | 
			
		||||
        if (self.bus.io.dma2.step(self.bus)) return self.bus.io.dma2.isBlocking();
 | 
			
		||||
        if (self.bus.io.dma3.step(self.bus)) return self.bus.io.dma3.isBlocking();
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
 | 
			
		||||
const pollBlankingDma = @import("bus/dma.zig").pollBlankingDma;
 | 
			
		||||
 | 
			
		||||
const Bus = @import("Bus.zig");
 | 
			
		||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
 | 
			
		||||
 | 
			
		||||
@@ -68,6 +70,9 @@ pub const Scheduler = struct {
 | 
			
		||||
                                irq.vblank.set();
 | 
			
		||||
                                cpu.handleInterrupt();
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // See if Vblank DMA is present and not enabled
 | 
			
		||||
                            pollBlankingDma(bus, .VBlank);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (scanline == 227) stat.vblank.unset();
 | 
			
		||||
@@ -84,6 +89,9 @@ pub const Scheduler = struct {
 | 
			
		||||
                        cpu.handleInterrupt();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // See if Hblank DMA is present and not enabled
 | 
			
		||||
                    pollBlankingDma(bus, .HBlank);
 | 
			
		||||
 | 
			
		||||
                    bus.ppu.dispstat.hblank.set();
 | 
			
		||||
                    self.push(.HBlank, self.tick + (68 * 4));
 | 
			
		||||
                },
 | 
			
		||||
@@ -96,6 +104,9 @@ pub const Scheduler = struct {
 | 
			
		||||
                        cpu.handleInterrupt();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // See if Hblank DMA is present and not enabled
 | 
			
		||||
                    pollBlankingDma(bus, .HBlank);
 | 
			
		||||
 | 
			
		||||
                    bus.ppu.dispstat.hblank.set();
 | 
			
		||||
                    self.push(.HBlank, self.tick + (68 * 4));
 | 
			
		||||
                },
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user