Compare commits

...

2 Commits

7 changed files with 39 additions and 34 deletions

View File

@ -29,9 +29,10 @@ dma: DmaControllers,
tim: Timers,
iwram: Iwram,
ewram: Ewram,
io: Io,
sched: *Scheduler,
pub fn init(alloc: Allocator, sched: *Scheduler, rom_path: []const u8, bios_path: ?[]const u8, save_path: ?[]const u8) !Self {
return Self{
.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(),
.tim = Timers.init(sched),
.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 {
const page = @truncate(u8, address >> 24);
const align_addr = alignAddress(T, address);
self.sched.tick += 1;
return switch (page) {
// 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 {
const page = @truncate(u8, address >> 24);
const align_addr = alignAddress(T, address);
self.sched.tick += 1;
switch (page) {
// General Internal Memory

View File

@ -29,6 +29,11 @@ pub fn deinit(self: Self) void {
pub fn read(self: *const Self, comptime T: type, addr: usize) T {
if (self.buf) |buf| {
if (addr > buf.len) {
log.err("Tried to read {} from {X:0>8} (open bus)", .{ T, addr });
return 0;
}
return switch (T) {
u32 => (@as(u32, buf[addr + 3]) << 24) | (@as(u32, buf[addr + 2]) << 16) | (@as(u32, buf[addr + 1]) << 8) | (@as(u32, buf[addr])),
u16 => (@as(u16, buf[addr + 1]) << 8) | @as(u16, buf[addr]),

View File

@ -81,13 +81,13 @@ fn Timer(comptime id: u2) type {
// Reload on Rising edge
self._counter = self._reload;
if (!new.cascade.read()) self.scheduleOverflow();
if (!new.cascade.read()) self.scheduleOverflow(0);
}
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
const io = &cpu.bus.io;
const tim = &cpu.bus.tim;
@ -107,21 +107,15 @@ fn Timer(comptime id: u2) type {
switch (id) {
0 => if (tim._1.cnt.cascade.read()) {
tim._1._counter +%= 1;
if (tim._1._counter == 0)
tim._1.handleOverflow(cpu);
if (tim._1._counter == 0) tim._1.handleOverflow(cpu, late);
},
1 => if (tim._2.cnt.cascade.read()) {
tim._2._counter +%= 1;
if (tim._2._counter == 0)
tim._2.handleOverflow(cpu);
if (tim._2._counter == 0) tim._2.handleOverflow(cpu, late);
},
2 => if (tim._3.cnt.cascade.read()) {
tim._3._counter +%= 1;
if (tim._3._counter == 0)
tim._3.handleOverflow(cpu);
if (tim._3._counter == 0) tim._3.handleOverflow(cpu, late);
},
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
if (!self.cnt.cascade.read()) {
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();
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 {

View File

@ -245,12 +245,15 @@ pub const Arm7tdmi = struct {
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 (self.handleDMATransfers()) return 1;
if (self.handleDMATransfers()) return;
// 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()) {
const opcode = self.thumbFetch();
@ -265,8 +268,6 @@ pub const Arm7tdmi = struct {
arm_lut[armIdx(opcode)](self, self.bus, opcode);
}
}
return 1;
}
pub fn handleInterrupt(self: *Self) void {

View File

@ -46,8 +46,7 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
const frame_end = sched.tick + cycles_per_frame;
while (sched.tick < frame_end) {
sched.tick += 1;
_ = cpu.step();
cpu.step();
while (sched.tick >= sched.nextTimestamp()) {
sched.handleEvent(cpu, bus);

View File

@ -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
if (self.dispstat.hblank_irq.read()) {
cpu.bus.io.irq.hblank.set();
@ -369,10 +369,10 @@ pub const Ppu = struct {
pollBlankingDma(cpu.bus, .HBlank);
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)
const old_scanline = self.vcount.scanline.read();
const scanline = (old_scanline + 1) % 228;
@ -391,7 +391,7 @@ pub const Ppu = struct {
if (scanline < 160) {
// Transitioning to another Draw
self.sched.push(.Draw, self.sched.now() + (240 * 4));
self.sched.push(.Draw, self.sched.now() + (240 * 4) - late);
} else {
// Transitioning to a Vblank
if (scanline == 160) {
@ -407,7 +407,7 @@ pub const Ppu = struct {
}
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);
}
}
};

View File

@ -31,6 +31,8 @@ pub const Scheduler = struct {
pub fn handleEvent(self: *Self, cpu: *Arm7tdmi, bus: *Bus) void {
if (self.queue.removeOrNull()) |event| {
const late = self.tick - event.tick;
switch (event.kind) {
.HeatDeath => {
log.err("A u64 overflowered. This *actually* should never happen.", .{});
@ -39,18 +41,18 @@ pub const Scheduler = struct {
.Draw => {
// The end of a VDraw
bus.ppu.drawScanline();
bus.ppu.handleHDrawEnd(cpu);
bus.ppu.handleHDrawEnd(cpu, late);
},
.TimerOverflow => |id| {
switch (id) {
0 => bus.tim._0.handleOverflow(cpu),
1 => bus.tim._1.handleOverflow(cpu),
2 => bus.tim._2.handleOverflow(cpu),
3 => bus.tim._3.handleOverflow(cpu),
0 => bus.tim._0.handleOverflow(cpu, late),
1 => bus.tim._1.handleOverflow(cpu, late),
2 => bus.tim._2.handleOverflow(cpu, late),
3 => bus.tim._3.handleOverflow(cpu, late),
}
},
.HBlank => bus.ppu.handleHBlankEnd(cpu), // The end of a HBlank
.VBlank => bus.ppu.handleHDrawEnd(cpu), // The end of a VBlank
.HBlank => bus.ppu.handleHBlankEnd(cpu, late), // The end of a HBlank
.VBlank => bus.ppu.handleHDrawEnd(cpu, late), // The end of a VBlank
}
}
}