feat: improve DMA Transfer support
This commit is contained in:
		@@ -35,6 +35,11 @@ pub fn DmaController(comptime id: u2) type {
 | 
				
			|||||||
        /// Internal. Word Count
 | 
					        /// Internal. Word Count
 | 
				
			||||||
        _word_count: if (id == 3) u16 else u14,
 | 
					        _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 {
 | 
					        pub fn init() Self {
 | 
				
			||||||
            return .{
 | 
					            return .{
 | 
				
			||||||
                .id = id,
 | 
					                .id = id,
 | 
				
			||||||
@@ -47,6 +52,7 @@ pub fn DmaController(comptime id: u2) type {
 | 
				
			|||||||
                ._sad = 0,
 | 
					                ._sad = 0,
 | 
				
			||||||
                ._dad = 0,
 | 
					                ._dad = 0,
 | 
				
			||||||
                ._word_count = 0,
 | 
					                ._word_count = 0,
 | 
				
			||||||
 | 
					                .enabled = false,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,6 +76,9 @@ pub fn DmaController(comptime id: u2) type {
 | 
				
			|||||||
                self._sad = self.sad;
 | 
					                self._sad = self.sad;
 | 
				
			||||||
                self._dad = self.dad;
 | 
					                self._dad = self.dad;
 | 
				
			||||||
                self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count;
 | 
					                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;
 | 
					            self.cnt.raw = halfword;
 | 
				
			||||||
@@ -80,7 +89,9 @@ pub fn DmaController(comptime id: u2) type {
 | 
				
			|||||||
            self.writeCntHigh(@truncate(u16, word >> 16));
 | 
					            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 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;
 | 
					            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) {
 | 
					            switch (sad_adj) {
 | 
				
			||||||
                .Increment => self._sad += offset,
 | 
					                .Increment => self._sad +%= offset,
 | 
				
			||||||
                .Decrement => self._sad -= offset,
 | 
					                .Decrement => self._sad -%= offset,
 | 
				
			||||||
                .Fixed => {},
 | 
					                .Fixed => {},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // TODO: Figure out correct behaviour on Illegal Source Addr Control Type
 | 
					                // 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) {
 | 
					            switch (dad_adj) {
 | 
				
			||||||
                .Increment, .IncrementReload => self._dad += offset,
 | 
					                .Increment, .IncrementReload => self._dad +%= offset,
 | 
				
			||||||
                .Decrement => self._dad -= offset,
 | 
					                .Decrement => self._dad -%= offset,
 | 
				
			||||||
                .Fixed => {},
 | 
					                .Fixed => {},
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self._word_count -= 1;
 | 
					            self._word_count -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (self._word_count == 0) {
 | 
					            if (self._word_count == 0) {
 | 
				
			||||||
                if (self.cnt.irq.read()) {
 | 
					                if (!self.cnt.repeat.read()) {
 | 
				
			||||||
                    switch (id) {
 | 
					                    // If we're not repeating, Fire the IRQs and disable the DMA
 | 
				
			||||||
                        0 => bus.io.irq.dma0.set(),
 | 
					                    if (self.cnt.irq.read()) {
 | 
				
			||||||
                        1 => bus.io.irq.dma0.set(),
 | 
					                        switch (id) {
 | 
				
			||||||
                        2 => bus.io.irq.dma0.set(),
 | 
					                            0 => bus.io.irq.dma0.set(),
 | 
				
			||||||
                        3 => 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) {
 | 
					const Adjustment = enum(u2) {
 | 
				
			||||||
    Increment = 0,
 | 
					    Increment = 0,
 | 
				
			||||||
    Decrement = 1,
 | 
					    Decrement = 1,
 | 
				
			||||||
    Fixed = 2,
 | 
					    Fixed = 2,
 | 
				
			||||||
    IncrementReload = 3,
 | 
					    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 {
 | 
					    fn handleDMATransfers(self: *Self) bool {
 | 
				
			||||||
        const dma0 = &self.bus.io.dma0;
 | 
					        if (self.bus.io.dma0.step(self.bus)) return self.bus.io.dma0.isBlocking();
 | 
				
			||||||
        const dma1 = &self.bus.io.dma1;
 | 
					        if (self.bus.io.dma1.step(self.bus)) return self.bus.io.dma1.isBlocking();
 | 
				
			||||||
        const dma2 = &self.bus.io.dma2;
 | 
					        if (self.bus.io.dma2.step(self.bus)) return self.bus.io.dma2.isBlocking();
 | 
				
			||||||
        const dma3 = &self.bus.io.dma3;
 | 
					        if (self.bus.io.dma3.step(self.bus)) return self.bus.io.dma3.isBlocking();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const pollBlankingDma = @import("bus/dma.zig").pollBlankingDma;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Bus = @import("Bus.zig");
 | 
					const Bus = @import("Bus.zig");
 | 
				
			||||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
 | 
					const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -68,6 +70,9 @@ pub const Scheduler = struct {
 | 
				
			|||||||
                                irq.vblank.set();
 | 
					                                irq.vblank.set();
 | 
				
			||||||
                                cpu.handleInterrupt();
 | 
					                                cpu.handleInterrupt();
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // See if Vblank DMA is present and not enabled
 | 
				
			||||||
 | 
					                            pollBlankingDma(bus, .VBlank);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (scanline == 227) stat.vblank.unset();
 | 
					                        if (scanline == 227) stat.vblank.unset();
 | 
				
			||||||
@@ -84,6 +89,9 @@ pub const Scheduler = struct {
 | 
				
			|||||||
                        cpu.handleInterrupt();
 | 
					                        cpu.handleInterrupt();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // See if Hblank DMA is present and not enabled
 | 
				
			||||||
 | 
					                    pollBlankingDma(bus, .HBlank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    bus.ppu.dispstat.hblank.set();
 | 
					                    bus.ppu.dispstat.hblank.set();
 | 
				
			||||||
                    self.push(.HBlank, self.tick + (68 * 4));
 | 
					                    self.push(.HBlank, self.tick + (68 * 4));
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -96,6 +104,9 @@ pub const Scheduler = struct {
 | 
				
			|||||||
                        cpu.handleInterrupt();
 | 
					                        cpu.handleInterrupt();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // See if Hblank DMA is present and not enabled
 | 
				
			||||||
 | 
					                    pollBlankingDma(bus, .HBlank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    bus.ppu.dispstat.hblank.set();
 | 
					                    bus.ppu.dispstat.hblank.set();
 | 
				
			||||||
                    self.push(.HBlank, self.tick + (68 * 4));
 | 
					                    self.push(.HBlank, self.tick + (68 * 4));
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user