PPU: Implement registers

This commit is contained in:
Andrew Glaze
2024-08-19 11:21:24 -04:00
parent 9c5cb22223
commit 05927d8e91
13 changed files with 371 additions and 8 deletions

84
Sources/Data/Bus.swift Normal file
View File

@@ -0,0 +1,84 @@
class Bus {
var cpuVram: [UInt8] = .init(repeating: 0, count: 2048)
var prgRom: [UInt8]
var ppu: NesPPU
fileprivate let RAM : UInt16 = 0x0000
fileprivate let RAM_MIRRORS_END: UInt16 = 0x1FFF
fileprivate let PPU_REGISTERS: UInt16 = 0x2000
fileprivate let PPU_REGISTERS_MIRRORS_END: UInt16 = 0x3FFF
fileprivate let ROM_ADDRESS_START: UInt16 = 0x8000
fileprivate let ROM_ADDRESS_END: UInt16 = 0xFFFF
init(_ rom: Rom) {
ppu = NesPPU(rom.character, rom.screenMirror)
self.prgRom = rom.program
}
}
extension Bus: Memory {
func memRead(_ addr: UInt16) -> UInt8 {
switch addr {
case RAM...RAM_MIRRORS_END:
let mirrorDownAddr = addr & 0b00000111_11111111
return self.cpuVram[Int(mirrorDownAddr)]
case 0x2000, 0x2001, 0x2003, 0x2005, 0x2006, 0x4014:
fatalError("Attempt to read from write-only PPU address \(addr)")
case 0x2002:
return ppu.readStatus()
case 0x2004:
return ppu.readOamData()
case 0x2007:
return ppu.readData()
case 0x2008...PPU_REGISTERS_MIRRORS_END:
let mirrorDownAddr = addr & 0b00100000_00000111;
return self.memRead(mirrorDownAddr)
case ROM_ADDRESS_START...ROM_ADDRESS_END:
return readProgramRom(addr)
default:
print("Ignoring mem access at \(addr)")
return 0
}
}
func memWrite(_ addr: UInt16, data: UInt8) {
switch addr {
case RAM...RAM_MIRRORS_END:
let mirrorDownAddr = addr & 0b11111111111
self.cpuVram[Int(mirrorDownAddr)] = data
case 0x2000:
ppu.writeToCtrl(data)
case 0x2001:
ppu.writeToMask(data)
case 0x2002:
fatalError("Attempt to write to PPU status register")
case 0x2003:
ppu.writeToOamAddr(data)
case 0x2004:
ppu.writeToOamData(data)
case 0x2005:
ppu.writeToScroll(data)
case 0x2006:
ppu.writeToPPUAddr(data)
case 0x2007:
ppu.writeToData(data)
case 0x2008...PPU_REGISTERS_MIRRORS_END:
let mirrorDownAddr = addr & 0b00100000_00000111
memWrite(mirrorDownAddr, data: data)
case ROM_ADDRESS_START...ROM_ADDRESS_END:
fatalError("Attempt to write to Cartridge ROM space: \(addr)")
default:
print("Ignorming mem-write at \(addr)")
}
}
func readProgramRom(_ addr: UInt16) -> UInt8 {
var addr = addr - 0x8000
if prgRom.count == 0x4000 && addr >= 0x4000 {
// rom mirroring
addr = addr % 0x4000
}
return prgRom[Int(addr)]
}
}

50
Sources/Data/Rom.swift Normal file
View File

@@ -0,0 +1,50 @@
struct Rom {
fileprivate let PRG_ROM_PAGE_SIZE = 16384
fileprivate let CHR_ROM_PAGE_SIZE = 8192
var program: [UInt8]
var character: [UInt8]
var mapper: UInt8
var screenMirror: Mirroring
init(_ raw: [UInt8]) throws {
guard raw[0..<4] == [0x4E, 0x45, 0x53, 0x1A] else { throw HeaderParseError.notINES("File is not in iNES file format.") }
mapper = (raw[7] & 0b1111_0000) | (raw[6] >> 4)
let inesVer = (raw[7] >> 2) & 0b11
guard inesVer == 0 else { throw HeaderParseError.iNes2("iNES2.0 format not supported.") }
let fourScreen = raw[6] & 0b1000 != 0
let vertMirroring = raw[6] & 0b1 != 0
screenMirror = switch (fourScreen, vertMirroring) {
case (true, _):
Mirroring.fourScreen
case (false, true):
Mirroring.vertical
case (false, false):
Mirroring.horizontal
}
let programSize = Int(raw[4]) * PRG_ROM_PAGE_SIZE
let characterSize = Int(raw[5]) * CHR_ROM_PAGE_SIZE
let skipTrainer = raw[6] & 0b100 != 0
let programStart = 16 + (skipTrainer ? 512 : 0)
let characterStart = programStart + programSize
program = Array(raw[programStart..<(programStart + programSize)])
character = Array(raw[characterStart..<(characterStart + characterSize)])
}
}
enum Mirroring {
case vertical
case horizontal
case fourScreen
}
enum HeaderParseError: Error {
case notINES(String)
case iNes2(String)
}