chore: tick scheduler on memory access
This commit is contained in:
parent
5da84aff36
commit
c100d64fcb
|
@ -29,9 +29,10 @@ dma: DmaControllers,
|
||||||
tim: Timers,
|
tim: Timers,
|
||||||
iwram: Iwram,
|
iwram: Iwram,
|
||||||
ewram: Ewram,
|
ewram: Ewram,
|
||||||
|
|
||||||
io: Io,
|
io: Io,
|
||||||
|
|
||||||
|
sched: *Scheduler,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler, rom_path: []const u8, bios_path: ?[]const u8, save_path: ?[]const u8) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler, rom_path: []const u8, bios_path: ?[]const u8, save_path: ?[]const u8) !Self {
|
||||||
return Self{
|
return Self{
|
||||||
.pak = try GamePak.init(alloc, rom_path, save_path),
|
.pak = try GamePak.init(alloc, rom_path, save_path),
|
||||||
|
@ -43,6 +44,7 @@ pub fn init(alloc: Allocator, sched: *Scheduler, rom_path: []const u8, bios_path
|
||||||
.dma = DmaControllers.init(),
|
.dma = DmaControllers.init(),
|
||||||
.tim = Timers.init(sched),
|
.tim = Timers.init(sched),
|
||||||
.io = Io.init(),
|
.io = Io.init(),
|
||||||
|
.sched = sched,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ pub fn deinit(self: Self) void {
|
||||||
pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
||||||
const page = @truncate(u8, address >> 24);
|
const page = @truncate(u8, address >> 24);
|
||||||
const align_addr = alignAddress(T, address);
|
const align_addr = alignAddress(T, address);
|
||||||
|
self.sched.tick += 1;
|
||||||
|
|
||||||
return switch (page) {
|
return switch (page) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
|
@ -91,6 +94,7 @@ pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
||||||
pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
|
pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
|
||||||
const page = @truncate(u8, address >> 24);
|
const page = @truncate(u8, address >> 24);
|
||||||
const align_addr = alignAddress(T, address);
|
const align_addr = alignAddress(T, address);
|
||||||
|
self.sched.tick += 1;
|
||||||
|
|
||||||
switch (page) {
|
switch (page) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
|
|
|
@ -81,13 +81,13 @@ fn Timer(comptime id: u2) type {
|
||||||
// Reload on Rising edge
|
// Reload on Rising edge
|
||||||
self._counter = self._reload;
|
self._counter = self._reload;
|
||||||
|
|
||||||
if (!new.cascade.read()) self.scheduleOverflow();
|
if (!new.cascade.read()) self.scheduleOverflow(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cnt.raw = halfword;
|
self.cnt.raw = halfword;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleOverflow(self: *Self, cpu: *Arm7tdmi) void {
|
pub fn handleOverflow(self: *Self, cpu: *Arm7tdmi, late: u64) void {
|
||||||
// Fire IRQ if enabled
|
// Fire IRQ if enabled
|
||||||
const io = &cpu.bus.io;
|
const io = &cpu.bus.io;
|
||||||
const tim = &cpu.bus.tim;
|
const tim = &cpu.bus.tim;
|
||||||
|
@ -107,21 +107,15 @@ fn Timer(comptime id: u2) type {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
0 => if (tim._1.cnt.cascade.read()) {
|
0 => if (tim._1.cnt.cascade.read()) {
|
||||||
tim._1._counter +%= 1;
|
tim._1._counter +%= 1;
|
||||||
|
if (tim._1._counter == 0) tim._1.handleOverflow(cpu, late);
|
||||||
if (tim._1._counter == 0)
|
|
||||||
tim._1.handleOverflow(cpu);
|
|
||||||
},
|
},
|
||||||
1 => if (tim._2.cnt.cascade.read()) {
|
1 => if (tim._2.cnt.cascade.read()) {
|
||||||
tim._2._counter +%= 1;
|
tim._2._counter +%= 1;
|
||||||
|
if (tim._2._counter == 0) tim._2.handleOverflow(cpu, late);
|
||||||
if (tim._2._counter == 0)
|
|
||||||
tim._2.handleOverflow(cpu);
|
|
||||||
},
|
},
|
||||||
2 => if (tim._3.cnt.cascade.read()) {
|
2 => if (tim._3.cnt.cascade.read()) {
|
||||||
tim._3._counter +%= 1;
|
tim._3._counter +%= 1;
|
||||||
|
if (tim._3._counter == 0) tim._3.handleOverflow(cpu, late);
|
||||||
if (tim._3._counter == 0)
|
|
||||||
tim._3.handleOverflow(cpu);
|
|
||||||
},
|
},
|
||||||
3 => {}, // There is no Timer for TIM3 to "cascade" to,
|
3 => {}, // There is no Timer for TIM3 to "cascade" to,
|
||||||
}
|
}
|
||||||
|
@ -129,15 +123,15 @@ fn Timer(comptime id: u2) type {
|
||||||
// Reschedule Timer if we're not cascading
|
// Reschedule Timer if we're not cascading
|
||||||
if (!self.cnt.cascade.read()) {
|
if (!self.cnt.cascade.read()) {
|
||||||
self._counter = self._reload;
|
self._counter = self._reload;
|
||||||
self.scheduleOverflow();
|
self.scheduleOverflow(late);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scheduleOverflow(self: *Self) void {
|
fn scheduleOverflow(self: *Self, late: u64) void {
|
||||||
const when = (@as(u64, 0x10000) - self._counter) * self.frequency();
|
const when = (@as(u64, 0x10000) - self._counter) * self.frequency();
|
||||||
|
|
||||||
self._start_timestamp = self.sched.now();
|
self._start_timestamp = self.sched.now();
|
||||||
self.sched.push(.{ .TimerOverflow = id }, self.sched.now() + when);
|
self.sched.push(.{ .TimerOverflow = id }, self.sched.now() + when - late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frequency(self: *const Self) u16 {
|
fn frequency(self: *const Self) u16 {
|
||||||
|
|
11
src/cpu.zig
11
src/cpu.zig
|
@ -245,12 +245,15 @@ pub const Arm7tdmi = struct {
|
||||||
self.cpsr.raw = 0x6000001F;
|
self.cpsr.raw = 0x6000001F;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(self: *Self) u64 {
|
pub fn step(self: *Self) void {
|
||||||
// If we're processing a DMA (not Sound or Blanking) the CPU is disabled
|
// If we're processing a DMA (not Sound or Blanking) the CPU is disabled
|
||||||
if (self.handleDMATransfers()) return 1;
|
if (self.handleDMATransfers()) return;
|
||||||
|
|
||||||
// If we're halted, the cpu is disabled
|
// If we're halted, the cpu is disabled
|
||||||
if (self.bus.io.haltcnt == .Halt) return 1;
|
if (self.bus.io.haltcnt == .Halt) {
|
||||||
|
self.sched.tick += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.cpsr.t.read()) {
|
if (self.cpsr.t.read()) {
|
||||||
const opcode = self.thumbFetch();
|
const opcode = self.thumbFetch();
|
||||||
|
@ -265,8 +268,6 @@ pub const Arm7tdmi = struct {
|
||||||
arm_lut[armIdx(opcode)](self, self.bus, opcode);
|
arm_lut[armIdx(opcode)](self, self.bus, opcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleInterrupt(self: *Self) void {
|
pub fn handleInterrupt(self: *Self) void {
|
||||||
|
|
|
@ -46,8 +46,7 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
||||||
const frame_end = sched.tick + cycles_per_frame;
|
const frame_end = sched.tick + cycles_per_frame;
|
||||||
|
|
||||||
while (sched.tick < frame_end) {
|
while (sched.tick < frame_end) {
|
||||||
sched.tick += 1;
|
cpu.step();
|
||||||
_ = cpu.step();
|
|
||||||
|
|
||||||
while (sched.tick >= sched.nextTimestamp()) {
|
while (sched.tick >= sched.nextTimestamp()) {
|
||||||
sched.handleEvent(cpu, bus);
|
sched.handleEvent(cpu, bus);
|
||||||
|
|
10
src/ppu.zig
10
src/ppu.zig
|
@ -358,7 +358,7 @@ pub const Ppu = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleHDrawEnd(self: *Self, cpu: *Arm7tdmi) void {
|
pub fn handleHDrawEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void {
|
||||||
// Transitioning to a Hblank
|
// Transitioning to a Hblank
|
||||||
if (self.dispstat.hblank_irq.read()) {
|
if (self.dispstat.hblank_irq.read()) {
|
||||||
cpu.bus.io.irq.hblank.set();
|
cpu.bus.io.irq.hblank.set();
|
||||||
|
@ -369,10 +369,10 @@ pub const Ppu = struct {
|
||||||
pollBlankingDma(cpu.bus, .HBlank);
|
pollBlankingDma(cpu.bus, .HBlank);
|
||||||
|
|
||||||
self.dispstat.hblank.set();
|
self.dispstat.hblank.set();
|
||||||
self.sched.push(.HBlank, self.sched.now() + (68 * 4));
|
self.sched.push(.HBlank, self.sched.now() + (68 * 4) - late);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleHBlankEnd(self: *Self, cpu: *Arm7tdmi) void {
|
pub fn handleHBlankEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void {
|
||||||
// The End of a Hblank (During Draw or Vblank)
|
// The End of a Hblank (During Draw or Vblank)
|
||||||
const old_scanline = self.vcount.scanline.read();
|
const old_scanline = self.vcount.scanline.read();
|
||||||
const scanline = (old_scanline + 1) % 228;
|
const scanline = (old_scanline + 1) % 228;
|
||||||
|
@ -391,7 +391,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
if (scanline < 160) {
|
if (scanline < 160) {
|
||||||
// Transitioning to another Draw
|
// Transitioning to another Draw
|
||||||
self.sched.push(.Draw, self.sched.now() + (240 * 4));
|
self.sched.push(.Draw, self.sched.now() + (240 * 4) - late);
|
||||||
} else {
|
} else {
|
||||||
// Transitioning to a Vblank
|
// Transitioning to a Vblank
|
||||||
if (scanline == 160) {
|
if (scanline == 160) {
|
||||||
|
@ -407,7 +407,7 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanline == 227) self.dispstat.vblank.unset();
|
if (scanline == 227) self.dispstat.vblank.unset();
|
||||||
self.sched.push(.VBlank, self.sched.now() + (240 * 4));
|
self.sched.push(.VBlank, self.sched.now() + (240 * 4) - late);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
pub fn handleEvent(self: *Self, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn handleEvent(self: *Self, cpu: *Arm7tdmi, bus: *Bus) void {
|
||||||
if (self.queue.removeOrNull()) |event| {
|
if (self.queue.removeOrNull()) |event| {
|
||||||
|
const late = self.tick - event.tick;
|
||||||
|
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
.HeatDeath => {
|
.HeatDeath => {
|
||||||
log.err("A u64 overflowered. This *actually* should never happen.", .{});
|
log.err("A u64 overflowered. This *actually* should never happen.", .{});
|
||||||
|
@ -39,18 +41,18 @@ pub const Scheduler = struct {
|
||||||
.Draw => {
|
.Draw => {
|
||||||
// The end of a VDraw
|
// The end of a VDraw
|
||||||
bus.ppu.drawScanline();
|
bus.ppu.drawScanline();
|
||||||
bus.ppu.handleHDrawEnd(cpu);
|
bus.ppu.handleHDrawEnd(cpu, late);
|
||||||
},
|
},
|
||||||
.TimerOverflow => |id| {
|
.TimerOverflow => |id| {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
0 => bus.tim._0.handleOverflow(cpu),
|
0 => bus.tim._0.handleOverflow(cpu, late),
|
||||||
1 => bus.tim._1.handleOverflow(cpu),
|
1 => bus.tim._1.handleOverflow(cpu, late),
|
||||||
2 => bus.tim._2.handleOverflow(cpu),
|
2 => bus.tim._2.handleOverflow(cpu, late),
|
||||||
3 => bus.tim._3.handleOverflow(cpu),
|
3 => bus.tim._3.handleOverflow(cpu, late),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.HBlank => bus.ppu.handleHBlankEnd(cpu), // The end of a HBlank
|
.HBlank => bus.ppu.handleHBlankEnd(cpu, late), // The end of a HBlank
|
||||||
.VBlank => bus.ppu.handleHDrawEnd(cpu), // The end of a VBlank
|
.VBlank => bus.ppu.handleHDrawEnd(cpu, late), // The end of a VBlank
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue