Compare commits
No commits in common. "7e15e83d387c2cdde84ff590666b5c45cecceb0a" and "460f8308a792d27d3ca74b1c76e79f80232ef6d7" have entirely different histories.
7e15e83d38
...
460f8308a7
10
README.md
10
README.md
|
@ -22,10 +22,10 @@ An in-progress Gameboy Advance Emulator written in Zig ⚡!
|
|||
- [ ] `line_timing.gba`
|
||||
- [ ] `lyc_midline.gba`
|
||||
- [ ] `window_midframe.gba`
|
||||
- [x] [ladystarbreeze's GBA Test Collection](https://github.com/ladystarbreeze/GBA-Test-Collection)
|
||||
- [ ] [ladystarbreeze's GBA Test Collection](https://github.com/ladystarbreeze/GBA-Test-Collection)
|
||||
- [x] `retAddr.gba`
|
||||
- [x] `helloWorld.gba`
|
||||
- [x] `helloAudio.gba`
|
||||
- [ ] `helloAudio.gba`
|
||||
- [x] [`armwrestler-gba-fixed.gba`](https://github.com/destoer/armwrestler-gba-fixed)
|
||||
- [x] [FuzzARM](https://github.com/DenSinH/FuzzARM)
|
||||
|
||||
|
@ -36,7 +36,7 @@ An in-progress Gameboy Advance Emulator written in Zig ⚡!
|
|||
* [ARM7TDMI Data Sheet](https://www.dca.fee.unicamp.br/cursos/EA871/references/ARM/ARM7TDMIDataSheet.pdf)
|
||||
|
||||
## Compiling
|
||||
Most recently built on Zig [0.10.0-dev.2624+d506275a0](https://github.com/ziglang/zig/tree/d506275a0)
|
||||
Most recently built on Zig [0.10.0-dev.2424+b3672e073](https://github.com/ziglang/zig/tree/b3672e073)
|
||||
|
||||
### Dependencies
|
||||
* [SDL.zig](https://github.com/MasterQ32/SDL.zig)
|
||||
|
@ -63,8 +63,8 @@ Key | Button
|
|||
--- | ---
|
||||
<kbd>X</kbd> | A
|
||||
<kbd>Z</kbd> | B
|
||||
<kbd>A</kbd> | L
|
||||
<kbd>S</kbd> | R
|
||||
<kbd>A</kbd> | Left Shoulder
|
||||
<kbd>S</kbd> | Right Shoulder
|
||||
<kbd>Return</kbd> | Start
|
||||
<kbd>RShift</kbd> | Select
|
||||
Arrow Keys | D-Pad
|
||||
|
|
81
src/apu.zig
81
src/apu.zig
|
@ -64,16 +64,10 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void {
|
|||
|
||||
switch (T) {
|
||||
u32 => switch (byte) {
|
||||
0x60 => apu.ch1.setSoundCnt(value),
|
||||
0x64 => apu.ch1.setSoundCntX(&apu.fs, @truncate(u16, value)),
|
||||
0x68 => apu.ch2.setSoundCntL(@truncate(u16, value)),
|
||||
0x6C => apu.ch2.setSoundCntH(&apu.fs, @truncate(u16, value)),
|
||||
0x70 => apu.ch3.setSoundCnt(value),
|
||||
0x74 => apu.ch3.setSoundCntX(&apu.fs, @truncate(u16, value)),
|
||||
0x78 => apu.ch4.setSoundCntL(@truncate(u16, value)),
|
||||
0x7C => apu.ch4.setSoundCntH(&apu.fs, @truncate(u16, value)),
|
||||
|
||||
0x80 => apu.setSoundCnt(value),
|
||||
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
|
||||
|
@ -81,7 +75,7 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void {
|
|||
else => writeUndefined(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||
},
|
||||
u16 => switch (byte) {
|
||||
0x60 => apu.ch1.setSoundCntL(@truncate(u8, value)), // SOUND1CNT_L
|
||||
0x60 => apu.ch1.sweep.raw = @truncate(u8, value), // SOUND1CNT_L
|
||||
0x62 => apu.ch1.setSoundCntH(value),
|
||||
0x64 => apu.ch1.setSoundCntX(&apu.fs, value),
|
||||
|
||||
|
@ -104,7 +98,7 @@ pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void {
|
|||
else => writeUndefined(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||
},
|
||||
u8 => switch (byte) {
|
||||
0x60 => apu.ch1.setSoundCntL(value),
|
||||
0x60 => apu.ch1.sweep.raw = value, // NR10
|
||||
0x62 => apu.ch1.setNr11(value),
|
||||
0x63 => apu.ch1.setNr12(value),
|
||||
0x64 => apu.ch1.setNr13(value),
|
||||
|
@ -202,12 +196,6 @@ pub const Apu = struct {
|
|||
self.ch4.reset();
|
||||
}
|
||||
|
||||
/// SOUNDCNT
|
||||
fn setSoundCnt(self: *Self, value: u32) void {
|
||||
self.psg_cnt.raw = @truncate(u16, value);
|
||||
self.setSoundCntH(@truncate(u16, value >> 16));
|
||||
}
|
||||
|
||||
/// SOUNDCNT_H_L
|
||||
fn setSoundCntHL(self: *Self, value: u8) void {
|
||||
const merged = (self.dma_cnt.raw & 0xFF00) | value;
|
||||
|
@ -220,7 +208,6 @@ pub const Apu = struct {
|
|||
self.setSoundCntH(merged);
|
||||
}
|
||||
|
||||
/// SOUNDCNT_H
|
||||
pub fn setSoundCntH(self: *Self, value: u16) void {
|
||||
const new: io.DmaSoundControl = .{ .raw = value };
|
||||
|
||||
|
@ -333,8 +320,7 @@ pub const Apu = struct {
|
|||
const final_right = (tmp_right << 5) | (tmp_right >> 6);
|
||||
|
||||
if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
||||
const new_sample_rate = Self.sampleRate(self.bias.sampling_cycle.read());
|
||||
log.info("Sample Rate changed from {}Hz to {}Hz", .{ Self.sampleRate(self.sampling_cycle), new_sample_rate });
|
||||
log.info("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
||||
|
||||
// Sample Rate Changed, Create a new Resampler since i can't figure out how to change
|
||||
// the parameters of the old one
|
||||
|
@ -342,7 +328,7 @@ pub const Apu = struct {
|
|||
defer SDL.SDL_FreeAudioStream(old);
|
||||
|
||||
self.sampling_cycle = self.bias.sampling_cycle.read();
|
||||
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, @intCast(c_int, new_sample_rate), SDL.AUDIO_U16, 2, host_sample_rate) orelse unreachable;
|
||||
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_U16, 2, host_sample_rate) orelse unreachable;
|
||||
}
|
||||
|
||||
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]u16{ final_left, final_right }, 2 * @sizeOf(u16));
|
||||
|
@ -350,11 +336,11 @@ pub const Apu = struct {
|
|||
}
|
||||
|
||||
fn sampleTicks(self: *const Self) u64 {
|
||||
return (1 << 24) / Self.sampleRate(self.bias.sampling_cycle.read());
|
||||
return (1 << 24) / self.sampleRate();
|
||||
}
|
||||
|
||||
fn sampleRate(cycle: u2) u64 {
|
||||
return @as(u64, 1) << (15 + @as(u6, cycle));
|
||||
fn sampleRate(self: *const Self) u64 {
|
||||
return @as(u64, 1) << (15 + @as(u6, self.bias.sampling_cycle.read()));
|
||||
}
|
||||
|
||||
pub fn tickFrameSequencer(self: *Self, late: u64) void {
|
||||
|
@ -400,6 +386,17 @@ pub const Apu = struct {
|
|||
if (self.chB.len() <= 15) cpu.bus.dma[2].requestSoundDma(0x0400_00A4);
|
||||
}
|
||||
}
|
||||
|
||||
fn highPass(self: *Self, sample: f32, enabled: bool) f32 {
|
||||
return if (enabled) blk: {
|
||||
const out = sample - self.capacitor;
|
||||
const charge_factor =
|
||||
std.math.pow(f32, 0.999958, @intToFloat(f32, (1 << 22) / self.sampleRate()));
|
||||
|
||||
self.capacitor = sample - out * charge_factor;
|
||||
break :blk out;
|
||||
} else 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
const ToneSweep = struct {
|
||||
|
@ -433,14 +430,11 @@ const ToneSweep = struct {
|
|||
enabled: bool,
|
||||
shadow: u11,
|
||||
|
||||
calc_performed: bool,
|
||||
|
||||
pub fn init() This {
|
||||
return .{
|
||||
.timer = 0,
|
||||
.enabled = false,
|
||||
.shadow = 0,
|
||||
.calc_performed = false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -450,7 +444,6 @@ const ToneSweep = struct {
|
|||
if (this.timer == 0) {
|
||||
const period = ch1.sweep.period.read();
|
||||
this.timer = if (period == 0) 8 else period;
|
||||
if (!this.calc_performed) this.calc_performed = true;
|
||||
|
||||
if (this.enabled and period != 0) {
|
||||
const new_freq = this.calcFrequency(ch1);
|
||||
|
@ -496,8 +489,6 @@ const ToneSweep = struct {
|
|||
|
||||
fn reset(self: *Self) void {
|
||||
self.sweep.raw = 0;
|
||||
self.sweep_dev.calc_performed = false;
|
||||
|
||||
self.duty.raw = 0;
|
||||
self.envelope.raw = 0;
|
||||
self.freq.raw = 0;
|
||||
|
@ -530,32 +521,11 @@ const ToneSweep = struct {
|
|||
return @as(i16, self.sample);
|
||||
}
|
||||
|
||||
/// NR10, NR11, NR12
|
||||
fn setSoundCnt(self: *Self, value: u32) void {
|
||||
self.setSoundCntL(@truncate(u8, value));
|
||||
self.setSoundCntH(@truncate(u16, value >> 16));
|
||||
}
|
||||
|
||||
/// NR10
|
||||
pub fn getSoundCntL(self: *const Self) u8 {
|
||||
return self.sweep.raw & 0x7F;
|
||||
}
|
||||
|
||||
/// NR10
|
||||
fn setSoundCntL(self: *Self, value: u8) void {
|
||||
const new = io.Sweep{ .raw = value };
|
||||
|
||||
if (self.sweep.direction.read() and !new.direction.read()) {
|
||||
// Sweep Negate bit has been cleared
|
||||
// If At least 1 Sweep Calculation has been made since
|
||||
// the last trigger, the channel is immediately disabled
|
||||
|
||||
if (self.sweep_dev.calc_performed) self.enabled = false;
|
||||
}
|
||||
|
||||
self.sweep.raw = value;
|
||||
}
|
||||
|
||||
/// NR11, NR12
|
||||
pub fn getSoundCntH(self: *const Self) u16 {
|
||||
return @as(u16, self.envelope.raw) << 8 | (self.duty.raw & 0xC0);
|
||||
|
@ -619,7 +589,6 @@ const ToneSweep = struct {
|
|||
const sw_period = self.sweep.period.read();
|
||||
const sw_shift = self.sweep.shift.read();
|
||||
|
||||
self.sweep_dev.calc_performed = false;
|
||||
self.sweep_dev.shadow = self.freq.frequency.read();
|
||||
self.sweep_dev.timer = if (sw_period == 0) 8 else sw_period;
|
||||
self.sweep_dev.enabled = sw_period != 0 or sw_shift != 0;
|
||||
|
@ -820,12 +789,6 @@ const Wave = struct {
|
|||
self.len_dev.tick(self.freq.length_enable.read(), &self.enabled);
|
||||
}
|
||||
|
||||
/// NR30, NR31, NR32
|
||||
fn setSoundCnt(self: *Self, value: u32) void {
|
||||
self.setSoundCntL(@truncate(u8, value));
|
||||
self.setSoundCntH(@truncate(u16, value >> 16));
|
||||
}
|
||||
|
||||
/// NR30
|
||||
pub fn setSoundCntL(self: *Self, value: u8) void {
|
||||
self.select.raw = value;
|
||||
|
|
|
@ -98,6 +98,9 @@ fn DmaController(comptime id: u2) type {
|
|||
const sad_mask: u32 = if (id == 0) 0x07FF_FFFF else 0x0FFF_FFFF;
|
||||
const dad_mask: u32 = if (id != 3) 0x07FF_FFFF else 0x0FFF_FFFF;
|
||||
|
||||
/// Determines whether DMAController is for DMA0, DMA1, DMA2 or DMA3
|
||||
/// Note: Determined at comptime
|
||||
id: u2,
|
||||
/// Write-only. The first address in a DMA transfer. (DMASAD)
|
||||
/// Note: use writeSrc instead of manipulating src_addr directly
|
||||
sad: u32,
|
||||
|
@ -123,10 +126,11 @@ fn DmaController(comptime id: u2) type {
|
|||
/// 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
|
||||
in_progress: bool,
|
||||
active: bool,
|
||||
|
||||
pub fn init() Self {
|
||||
return .{
|
||||
.id = id,
|
||||
.sad = 0,
|
||||
.dad = 0,
|
||||
.word_count = 0,
|
||||
|
@ -137,7 +141,7 @@ fn DmaController(comptime id: u2) type {
|
|||
._dad = 0,
|
||||
._word_count = 0,
|
||||
._fifo_word_count = 4,
|
||||
.in_progress = false,
|
||||
.active = false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -163,7 +167,7 @@ fn DmaController(comptime id: u2) type {
|
|||
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.in_progress = new.start_timing.read() == 0b00;
|
||||
self.active = new.start_timing.read() == 0b00;
|
||||
}
|
||||
|
||||
self.cnt.raw = halfword;
|
||||
|
@ -175,7 +179,7 @@ fn DmaController(comptime id: u2) type {
|
|||
}
|
||||
|
||||
pub fn step(self: *Self, cpu: *Arm7tdmi) void {
|
||||
const is_fifo = (id == 1 or id == 2) and self.cnt.start_timing.read() == 0b11;
|
||||
const is_fifo = (self.id == 1 or self.id == 2) and self.cnt.start_timing.read() == 0b11;
|
||||
const sad_adj = Self.adjustment(self.cnt.sad_adj.read());
|
||||
const dad_adj = if (is_fifo) .Fixed else Self.adjustment(self.cnt.dad_adj.read());
|
||||
|
||||
|
@ -205,63 +209,57 @@ fn DmaController(comptime id: u2) type {
|
|||
self._word_count -= 1;
|
||||
|
||||
if (self._word_count == 0) {
|
||||
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 => cpu.bus.io.irq.dma0.set(),
|
||||
1 => cpu.bus.io.irq.dma1.set(),
|
||||
2 => cpu.bus.io.irq.dma2.set(),
|
||||
3 => cpu.bus.io.irq.dma3.set(),
|
||||
1 => cpu.bus.io.irq.dma0.set(),
|
||||
2 => cpu.bus.io.irq.dma0.set(),
|
||||
3 => cpu.bus.io.irq.dma0.set(),
|
||||
}
|
||||
|
||||
cpu.handleInterrupt();
|
||||
}
|
||||
|
||||
// If we're not repeating, Fire the IRQs and disable the DMA
|
||||
if (!self.cnt.repeat.read()) 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.in_progress = false;
|
||||
self.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pollBlankingDma(self: *Self, comptime kind: DmaKind) void {
|
||||
if (self.in_progress) return; // If there's an ongoing DMA Transfer, exit early
|
||||
if (self.active) return;
|
||||
|
||||
// No ongoing DMA Transfer, We want to check if we should repeat an existing one
|
||||
// Determined by the repeat bit and whether the DMA is in the right start_timing
|
||||
switch (kind) {
|
||||
.VBlank => self.in_progress = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b01,
|
||||
.HBlank => self.in_progress = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b10,
|
||||
.HBlank => self.active = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b10,
|
||||
.VBlank => self.active = self.cnt.enabled.read() and self.cnt.start_timing.read() == 0b01,
|
||||
.Immediate, .Special => {},
|
||||
}
|
||||
|
||||
// If we determined that the repeat bit is set (and now the Hblank / Vblank DMA is now in progress)
|
||||
// Reload internal word count latch
|
||||
// Reload internal DAD latch if we are in IncrementRelaod
|
||||
if (self.in_progress) {
|
||||
if (self.cnt.repeat.read() and self.active) {
|
||||
self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count;
|
||||
if (Self.adjustment(self.cnt.dad_adj.read()) == .IncrementReload) self._dad = self.dad;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn requestSoundDma(self: *Self, _: u32) void {
|
||||
pub fn requestSoundDma(self: *Self, fifo_addr: u32) void {
|
||||
comptime std.debug.assert(id == 1 or id == 2);
|
||||
if (self.in_progress) return; // APU must wait their turn
|
||||
|
||||
// DMA May not be configured for handling DMAs
|
||||
if (self.cnt.start_timing.read() != 0b11) return;
|
||||
const is_enabled = self.cnt.enabled.read();
|
||||
const is_special = self.cnt.start_timing.read() == 0b11;
|
||||
const is_repeating = self.cnt.repeat.read();
|
||||
const is_fifo = self.dad == fifo_addr;
|
||||
|
||||
// We Assume the Repeat Bit is Set
|
||||
// We Assume that DAD is set to 0x0400_00A0 or 0x0400_00A4 (fifo_addr)
|
||||
// We Assume DMACNT_L is set to 4
|
||||
|
||||
// FIXME: Safe to just assume whatever DAD is set to is the FIFO Address?
|
||||
// self._dad = fifo_addr;
|
||||
self.cnt.repeat.set();
|
||||
if (is_enabled and is_special and is_repeating and is_fifo) {
|
||||
self._word_count = 4;
|
||||
self.in_progress = true;
|
||||
self.cnt.transfer_type.set();
|
||||
self.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn adjustment(idx: u2) Adjustment {
|
||||
|
|
|
@ -52,7 +52,7 @@ 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_00B0...0x0400_00DC => dma.read(T, &bus.dma, address),
|
||||
0x0400_00B8...0x0400_00DC => dma.read(T, &bus.dma, address),
|
||||
|
||||
// Timers
|
||||
0x0400_0100...0x0400_010C => timer.read(T, &bus.tim, address),
|
||||
|
@ -83,10 +83,10 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
|||
0x0400_004C => readTodo("Read {} from MOSAIC", .{T}),
|
||||
|
||||
// Sound
|
||||
0x0400_0060...0x0400_009E => apu.read(T, &bus.apu, address),
|
||||
0x0400_0060...0x0400_009F => apu.read(T, &bus.apu, address),
|
||||
|
||||
// DMA Transfers
|
||||
0x0400_00B0...0x0400_00DE => dma.read(T, &bus.dma, address),
|
||||
0x0400_00BA...0x0400_00DE => dma.read(T, &bus.dma, address),
|
||||
|
||||
// Timers
|
||||
0x0400_0100...0x0400_010E => timer.read(T, &bus.tim, address),
|
||||
|
@ -119,7 +119,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...0x0400_00A7 => apu.read(T, &bus.apu, address),
|
||||
0x0400_0060...0x0400_0089 => apu.read(T, &bus.apu, address),
|
||||
|
||||
// Serial Communication 1
|
||||
0x0400_0128 => readTodo("Read {} from SIOCNT_L", .{T}),
|
||||
|
@ -171,7 +171,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_0058...0x0400_005C => {}, // Unused
|
||||
|
||||
// Sound
|
||||
0x0400_0060...0x0400_00A4 => apu.write(T, &bus.apu, address, value),
|
||||
0x0400_0080...0x0400_00A4 => apu.write(T, &bus.apu, address, value),
|
||||
0x0400_00A8, 0x0400_00AC => {}, // Unused
|
||||
|
||||
// DMA Transfers
|
||||
|
@ -190,8 +190,6 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
|
||||
// Keypad Input
|
||||
0x0400_0130 => log.debug("Wrote 0x{X:0>8} to KEYINPUT and KEYCNT", .{value}),
|
||||
0x0400_0134 => log.debug("Wrote 0x{X:0>8} to RCNT and IR", .{value}),
|
||||
0x0400_0138, 0x0400_013C => {}, // Unused
|
||||
|
||||
// Serial Communication 2
|
||||
0x0400_0140 => log.debug("Wrote 0x{X:0>8} to JOYCNT", .{value}),
|
||||
|
@ -199,7 +197,6 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_0154 => log.debug("Wrote 0x{X:0>8} to JOY_TRANS", .{value}),
|
||||
0x0400_0158 => log.debug("Wrote 0x{X:0>8} to JOYSTAT (?)", .{value}),
|
||||
0x0400_0144...0x0400_014C, 0x0400_015C => {}, // Unused
|
||||
0x0400_0160...0x0400_01FC => {},
|
||||
|
||||
// Interrupts
|
||||
0x0400_0200 => bus.io.setIrqs(value),
|
||||
|
@ -254,7 +251,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_004E, 0x0400_0056 => {}, // Not used
|
||||
|
||||
// Sound
|
||||
0x0400_0060...0x0400_009E => apu.write(T, &bus.apu, address, value),
|
||||
0x0400_0060...0x0400_009F => apu.write(T, &bus.apu, address, value),
|
||||
|
||||
// Dma Transfers
|
||||
0x0400_00B0...0x0400_00DE => dma.write(T, &bus.dma, address, value),
|
||||
|
@ -304,7 +301,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...0x0400_00A7 => apu.write(T, &bus.apu, 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}),
|
||||
|
@ -435,10 +432,10 @@ const InterruptRequest = extern union {
|
|||
vblank: Bit(u16, 0),
|
||||
hblank: Bit(u16, 1),
|
||||
coincidence: Bit(u16, 2),
|
||||
tim0: Bit(u16, 3),
|
||||
tim1: Bit(u16, 4),
|
||||
tim2: Bit(u16, 5),
|
||||
tim3: Bit(u16, 6),
|
||||
tim0_overflow: Bit(u16, 3),
|
||||
tim1_overflow: Bit(u16, 4),
|
||||
tim2_overflow: Bit(u16, 5),
|
||||
tim3_overflow: Bit(u16, 6),
|
||||
serial: Bit(u16, 7),
|
||||
dma0: Bit(u16, 8),
|
||||
dma1: Bit(u16, 9),
|
||||
|
|
|
@ -144,10 +144,10 @@ fn Timer(comptime id: u2) type {
|
|||
|
||||
if (self.cnt.irq.read()) {
|
||||
switch (id) {
|
||||
0 => io.irq.tim0.set(),
|
||||
1 => io.irq.tim1.set(),
|
||||
2 => io.irq.tim2.set(),
|
||||
3 => io.irq.tim3.set(),
|
||||
0 => io.irq.tim0_overflow.set(),
|
||||
1 => io.irq.tim1_overflow.set(),
|
||||
2 => io.irq.tim2_overflow.set(),
|
||||
3 => io.irq.tim3_overflow.set(),
|
||||
}
|
||||
|
||||
cpu.handleInterrupt();
|
||||
|
|
|
@ -277,22 +277,22 @@ pub const Arm7tdmi = struct {
|
|||
const dma2 = &self.bus.dma[2];
|
||||
const dma3 = &self.bus.dma[3];
|
||||
|
||||
if (dma0.in_progress) {
|
||||
if (dma0.active) {
|
||||
dma0.step(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dma1.in_progress) {
|
||||
if (dma1.active) {
|
||||
dma1.step(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dma2.in_progress) {
|
||||
if (dma2.active) {
|
||||
dma2.step(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dma3.in_progress) {
|
||||
if (dma3.active) {
|
||||
dma3.step(self);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue