Compare commits
2 Commits
76789aa8bc
...
c100d64fcb
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | c100d64fcb | |
Rekai Nyangadzayi Musuka | 5da84aff36 |
|
@ -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
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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 {
|
||||
|
|
11
src/cpu.zig
11
src/cpu.zig
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue