add nes cartridge subsystem and 41 ines mapper variants
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
const types = @import("types.zig");
|
||||
const Mirroring = types.Mirroring;
|
||||
|
||||
const Mmc4 = @This();
|
||||
|
||||
prg_bank: u8 = 0,
|
||||
|
||||
chr_banks_fd: [2]u8 = [_]u8{ 0, 0 },
|
||||
chr_banks_fe: [2]u8 = [_]u8{ 0, 0 },
|
||||
|
||||
latch: [2]u8 = [_]u8{ 0xfe, 0xfe },
|
||||
|
||||
mirroring_mode: Mirroring = .vertical,
|
||||
prg_bank_count: usize,
|
||||
chr_bank_count: usize,
|
||||
|
||||
pub fn init(
|
||||
prg_size: usize,
|
||||
chr_size: usize,
|
||||
) Mmc4 {
|
||||
return .{
|
||||
.prg_bank_count = prg_size / 0x4000,
|
||||
.chr_bank_count = if (chr_size > 0) chr_size / 0x1000 else 2,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn cpuMapRead(
|
||||
self: *const Mmc4,
|
||||
address: u16,
|
||||
) ?usize {
|
||||
if (address < 0x8000) return null;
|
||||
|
||||
const offset = @as(usize, address & 0x3fff);
|
||||
const last_bank = self.prg_bank_count - 1;
|
||||
|
||||
if (address < 0xc000) {
|
||||
const bank = @as(usize, self.prg_bank) % self.prg_bank_count;
|
||||
return bank * 0x4000 + offset;
|
||||
} else {
|
||||
return last_bank * 0x4000 + offset;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpuWrite(
|
||||
self: *Mmc4,
|
||||
address: u16,
|
||||
value: u8,
|
||||
) void {
|
||||
if (address < 0xa000) return;
|
||||
|
||||
switch (address & 0xf000) {
|
||||
0xa000 => self.prg_bank = value & 0x0f,
|
||||
0xb000 => self.chr_banks_fd[0] = value & 0x1f,
|
||||
0xc000 => self.chr_banks_fe[0] = value & 0x1f,
|
||||
0xd000 => self.chr_banks_fd[1] = value & 0x1f,
|
||||
0xe000 => self.chr_banks_fe[1] = value & 0x1f,
|
||||
0xf000 => self.mirroring_mode = if ((value & 1) != 0) .horizontal else .vertical,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ppuMapRead(
|
||||
self: *Mmc4,
|
||||
address: u16,
|
||||
) ?usize {
|
||||
if (address >= 0x2000) return null;
|
||||
|
||||
const slot = address / 0x1000;
|
||||
const offset = @as(usize, address & 0x0fff);
|
||||
|
||||
const bank_raw = if (self.latch[slot] == 0xfd)
|
||||
self.chr_banks_fd[slot]
|
||||
else
|
||||
self.chr_banks_fe[slot];
|
||||
|
||||
const bank = @as(usize, bank_raw) % self.chr_bank_count;
|
||||
const result = bank * 0x1000 + offset;
|
||||
|
||||
// Check PPU tile latch triggers ($0FD0-$0FDF, $0FE0-$0FEF, $1FD0-$1FDF, $1FE0-$1FEF)
|
||||
if (address == 0x0fd0 or address == 0x0fe0) {
|
||||
self.latch[0] = @truncate(address >> 4);
|
||||
} else if (address == 0x1fd0 or address == 0x1fe0) {
|
||||
self.latch[1] = @truncate(address >> 4);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn ppuMapWrite(
|
||||
self: *const Mmc4,
|
||||
address: u16,
|
||||
) ?usize {
|
||||
if (address >= 0x2000) return null;
|
||||
|
||||
const slot = address / 0x1000;
|
||||
const offset = @as(usize, address & 0x0fff);
|
||||
|
||||
const bank_raw = if (self.latch[slot] == 0xfd)
|
||||
self.chr_banks_fd[slot]
|
||||
else
|
||||
self.chr_banks_fe[slot];
|
||||
|
||||
const bank = @as(usize, bank_raw) % self.chr_bank_count;
|
||||
return bank * 0x1000 + offset;
|
||||
}
|
||||
|
||||
pub fn mirroring(
|
||||
self: *const Mmc4,
|
||||
) Mirroring {
|
||||
return self.mirroring_mode;
|
||||
}
|
||||
|
||||
pub fn irqAsserted(
|
||||
_: *const Mmc4,
|
||||
) bool {
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user