PPU: impl NMI interrupt

This commit is contained in:
Andrew Glaze 2024-08-19 22:05:08 -04:00
parent 05927d8e91
commit 830bc5f65a
6 changed files with 180 additions and 38 deletions

View File

@ -14,6 +14,31 @@ enum AddressingMode {
case NoneAddressing case NoneAddressing
} }
enum Interrupt {
case NMI
var vectorAddr: UInt16 {
switch self {
case .NMI:
0xfffa
}
}
var bFlagMask: UInt8 {
switch self {
case .NMI:
0b00100000
}
}
var cpuCycles: UInt8 {
switch self {
case .NMI:
2
}
}
}
struct CPUFlags: OptionSet { struct CPUFlags: OptionSet {
var rawValue: UInt8 var rawValue: UInt8
@ -44,41 +69,43 @@ class CPU {
} }
func getOpperandAddress(_ mode: AddressingMode) -> UInt16 { func getOpperandAddress(_ mode: AddressingMode) -> (UInt16, Bool) {
switch mode { switch mode {
case .Immediate: case .Immediate:
return programCounter return (programCounter, false)
default: default:
return getAbsoluteAddress(mode, addr: programCounter) return getAbsoluteAddress(mode, addr: programCounter)
} }
} }
func getAbsoluteAddress(_ mode: AddressingMode, addr: UInt16) -> UInt16 { func getAbsoluteAddress(_ mode: AddressingMode, addr: UInt16) -> (UInt16, Bool) {
switch mode { switch mode {
case .ZeroPage: case .ZeroPage:
return UInt16(memRead(addr)) return (UInt16(memRead(addr)), false)
case .Absolute: case .Absolute:
return memReadU16(addr) return (memReadU16(addr), false)
case .ZeroPage_X: case .ZeroPage_X:
let pos = memRead(addr) let pos = memRead(addr)
let addr = pos &+ register_x let addr = pos &+ register_x
return UInt16(addr) return (UInt16(addr), false)
case .ZeroPage_Y: case .ZeroPage_Y:
let pos = memRead(addr) let pos = memRead(addr)
let addr = pos &+ register_y let addr = pos &+ register_y
return UInt16(addr) return (UInt16(addr), false)
case .Absolute_X: case .Absolute_X:
let base = memReadU16(addr) let base = memReadU16(addr)
return base &+ UInt16(register_x) let addr = base &+ UInt16(register_x)
return (addr, isPageCross(base, addr))
case .Absolute_Y: case .Absolute_Y:
let base = memReadU16(addr) let base = memReadU16(addr)
return base &+ UInt16(register_y) let addr = base &+ UInt16(register_y)
return (addr, isPageCross(base, addr))
case .Indirect_X: case .Indirect_X:
let base = memRead(addr) let base = memRead(addr)
let ptr = UInt8(base) &+ register_x let ptr = UInt8(base) &+ register_x
let lo = memRead(UInt16(ptr)) let lo = memRead(UInt16(ptr))
let hi = memRead(UInt16(ptr &+ 1)) let hi = memRead(UInt16(ptr &+ 1))
return UInt16(hi) << 8 | UInt16(lo) return (UInt16(hi) << 8 | UInt16(lo), false)
case .Indirect_Y: case .Indirect_Y:
let base = memRead(addr) let base = memRead(addr)
@ -86,12 +113,16 @@ class CPU {
let hi = memRead(UInt16(base &+ 1)) let hi = memRead(UInt16(base &+ 1))
let deref_base = UInt16(hi) << 8 | UInt16(lo) let deref_base = UInt16(hi) << 8 | UInt16(lo)
let deref = deref_base &+ UInt16(register_y) let deref = deref_base &+ UInt16(register_y)
return deref return (deref, isPageCross(deref, deref_base))
default: default:
fatalError("mode \(mode) is not implemented") fatalError("mode \(mode) is not implemented")
} }
} }
func isPageCross(_ lhs: UInt16, _ rhs: UInt16) -> Bool {
lhs & 0xff00 != rhs & 0xff00
}
func reset() { func reset() {
register_a = 0 register_a = 0
register_x = 0 register_x = 0
@ -124,13 +155,36 @@ class CPU {
func run(onCycle: @escaping () -> (), onComplete: @escaping () -> ()) { func run(onCycle: @escaping () -> (), onComplete: @escaping () -> ()) {
let opcodes = OPCODES_MAP let opcodes = OPCODES_MAP
//_ = Timer.scheduledTimer(withTimeInterval: 0.00007, repeats: true) { [self] timer in
while true { while true {
if let _nmi = bus.pollNMI() {
interrupt(.NMI)
}
processOpcodes(onCycle: onCycle, opcodes: opcodes) { processOpcodes(onCycle: onCycle, opcodes: opcodes) {
onComplete() onComplete()
} }
} }
//} }
func interrupt(_ interrupt: Interrupt) {
stackPushU16(programCounter)
var flag = status
if interrupt.bFlagMask & 0b010000 == 1 {
flag.insert(.break1)
} else {
flag.remove(.break1)
}
if interrupt.bFlagMask & 0b100000 == 1 {
flag.insert(.break2)
} else {
flag.remove(.break2)
}
stackPush(flag.rawValue)
status.insert(.interruptDisable)
bus.tick(interrupt.cpuCycles)
programCounter = memReadU16(interrupt.vectorAddr)
} }
func processOpcodes(onCycle: () -> (), opcodes: [UInt8: OpCode], onComplete: () -> ()) { func processOpcodes(onCycle: () -> (), opcodes: [UInt8: OpCode], onComplete: () -> ()) {
@ -264,10 +318,10 @@ class CPU {
case 0x24, 0x2c: case 0x24, 0x2c:
bit(opcode.mode) bit(opcode.mode)
case 0x86, 0x96, 0x8e: case 0x86, 0x96, 0x8e:
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
memWrite(addr, data: register_x) memWrite(addr, data: register_x)
case 0x84, 0x94, 0x8c: case 0x84, 0x94, 0x8c:
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
memWrite(addr, data: register_y) memWrite(addr, data: register_y)
case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: case 0xa2, 0xa6, 0xb6, 0xae, 0xbe:
ldx(opcode.mode) ldx(opcode.mode)
@ -301,8 +355,11 @@ class CPU {
onComplete() onComplete()
/// NOP Read /// NOP Read
case 0x04, 0x44, 0x64, 0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4, 0x0c, 0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc: case 0x04, 0x44, 0x64, 0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4, 0x0c, 0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc:
let addr = getOpperandAddress(opcode.mode) let (addr, pageCross) = getOpperandAddress(opcode.mode)
let _ = self.memRead(addr) let _ = self.memRead(addr)
if pageCross {
bus.tick(1)
}
// Do nothing // Do nothing
/// NOP /// NOP
case 0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa: case 0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa:
@ -320,23 +377,23 @@ class CPU {
{ /* 2 byte NOP immediate, do nothing */ }() { /* 2 byte NOP immediate, do nothing */ }()
/// LAX /// LAX
case 0xa7, 0xb7, 0xaf, 0xbf, 0xa3, 0xb3: case 0xa7, 0xb7, 0xaf, 0xbf, 0xa3, 0xb3:
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
let data = memRead(addr) let data = memRead(addr)
setRegisterA(data) setRegisterA(data)
register_x = register_a register_x = register_a
/// SAX /// SAX
case 0x87, 0x97, 0x8f, 0x83: case 0x87, 0x97, 0x8f, 0x83:
let data = register_a & register_x let data = register_a & register_x
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
memWrite(addr, data: data) memWrite(addr, data: data)
// Unoffical SBC // Unoffical SBC
case 0xeb: case 0xeb:
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
let data = self.memRead(addr) let data = self.memRead(addr)
subFromRegisterA(data) subFromRegisterA(data)
/// DCP /// DCP
case 0xc7, 0xd7, 0xCF, 0xdF, 0xdb, 0xd3, 0xc3: case 0xc7, 0xd7, 0xCF, 0xdF, 0xdb, 0xd3, 0xc3:
let addr = getOpperandAddress(opcode.mode) let (addr, _) = getOpperandAddress(opcode.mode)
var data = memRead(addr) var data = memRead(addr)
data = data &- 1 data = data &- 1
memWrite(addr, data: data) memWrite(addr, data: data)
@ -363,6 +420,8 @@ class CPU {
default: fatalError("TODO!") default: fatalError("TODO!")
} }
bus.tick(opcode.cycles)
if programCounterState == programCounter { if programCounterState == programCounter {
programCounter += UInt16(opcode.len - 1) programCounter += UInt16(opcode.len - 1)
} }
@ -461,7 +520,7 @@ class CPU {
} }
func compare(mode: AddressingMode, compare_with: UInt8) { func compare(mode: AddressingMode, compare_with: UInt8) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
if data <= compare_with { if data <= compare_with {
status.insert(.carry) status.insert(.carry)
@ -470,14 +529,23 @@ class CPU {
} }
updateZeroAndNegativeFlags(compare_with &- data) updateZeroAndNegativeFlags(compare_with &- data)
if pageCross {
bus.tick(1)
}
} }
func branch(_ condition: Bool) { func branch(_ condition: Bool) {
if condition { if condition {
bus.tick(1)
let addr = memRead(programCounter) let addr = memRead(programCounter)
let jump: Int8 = Int8(bitPattern: addr) let jump: Int8 = Int8(bitPattern: addr)
let jump_addr = programCounter &+ 1 &+ UInt16(bitPattern: Int16(jump)) let jump_addr = programCounter &+ 1 &+ UInt16(bitPattern: Int16(jump))
if (programCounter &+ 1) & 0xff00 != jump_addr & 0xff00 {
bus.tick(1)
}
programCounter = jump_addr programCounter = jump_addr
} }
} }

View File

@ -1,46 +1,64 @@
extension CPU { extension CPU {
func lda(_ mode: AddressingMode) { func lda(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let value = memRead(addr) let value = memRead(addr)
setRegisterA(value) setRegisterA(value)
if pageCross {
bus.tick(1)
}
} }
func ldy(_ mode: AddressingMode) { func ldy(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
register_y = data register_y = data
updateZeroAndNegativeFlags(register_y) updateZeroAndNegativeFlags(register_y)
if pageCross {
bus.tick(1)
}
} }
func ldx(_ mode: AddressingMode) { func ldx(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
register_x = data register_x = data
updateZeroAndNegativeFlags(register_x) updateZeroAndNegativeFlags(register_x)
if pageCross {
bus.tick(1)
}
} }
func sta(_ mode: AddressingMode) { func sta(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
memWrite(addr, data: register_a) memWrite(addr, data: register_a)
} }
func and(_ mode: AddressingMode) { func and(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
self.setRegisterA(data & register_a) self.setRegisterA(data & register_a)
if pageCross {
bus.tick(1)
}
} }
func eor(_ mode: AddressingMode) { func eor(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
setRegisterA(data ^ register_a) setRegisterA(data ^ register_a)
if pageCross {
bus.tick(1)
}
} }
func ora(_ mode: AddressingMode) { func ora(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
setRegisterA(data | register_a) setRegisterA(data | register_a)
if pageCross {
bus.tick(1)
}
} }
func tax() { func tax() {
@ -59,16 +77,22 @@ extension CPU {
} }
func sbc(_ mode: AddressingMode) { func sbc(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
let res = Int8(bitPattern: data) &* -1 let res = Int8(bitPattern: data) &* -1
addToRegisterA(UInt8(bitPattern: res &- 1)) addToRegisterA(UInt8(bitPattern: res &- 1))
if pageCross {
bus.tick(1)
}
} }
func adc(_ mode: AddressingMode) { func adc(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, pageCross) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
addToRegisterA(data) addToRegisterA(data)
if pageCross {
bus.tick(1)
}
} }
func aslAccumulator() { func aslAccumulator() {
@ -83,7 +107,7 @@ extension CPU {
} }
func asl(_ mode: AddressingMode) -> UInt8 { func asl(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
if data >> 7 == 1 { if data >> 7 == 1 {
setCarryFlag() setCarryFlag()
@ -108,7 +132,7 @@ extension CPU {
} }
func lsr(_ mode: AddressingMode) -> UInt8 { func lsr(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
if data & 1 == 1 { if data & 1 == 1 {
setCarryFlag() setCarryFlag()
@ -138,7 +162,7 @@ extension CPU {
} }
func rol(_ mode: AddressingMode) -> UInt8 { func rol(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
let oldCarry = status.contains(.carry) let oldCarry = status.contains(.carry)
@ -174,7 +198,7 @@ extension CPU {
} }
func ror(_ mode: AddressingMode) -> UInt8 { func ror(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
let oldCarry = status.contains(.carry) let oldCarry = status.contains(.carry)
if data & 1 == 1 { if data & 1 == 1 {
@ -192,7 +216,7 @@ extension CPU {
} }
func inc(_ mode: AddressingMode) -> UInt8 { func inc(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
data = data &+ 1 data = data &+ 1
memWrite(addr, data: data) memWrite(addr, data: data)
@ -211,7 +235,7 @@ extension CPU {
} }
func dec(_ mode: AddressingMode) -> UInt8 { func dec(_ mode: AddressingMode) -> UInt8 {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
var data = memRead(addr) var data = memRead(addr)
data = data &- 1 data = data &- 1
memWrite(addr, data: data) memWrite(addr, data: data)
@ -238,7 +262,7 @@ extension CPU {
} }
func bit(_ mode: AddressingMode) { func bit(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode) let (addr, _) = getOpperandAddress(mode)
let data = memRead(addr) let data = memRead(addr)
let and = register_a & data let and = register_a & data
if and == 0 { if and == 0 {

View File

@ -7,7 +7,7 @@ func dumpCpuState(_ cpu: CPU) -> String {
(UInt16(0), UInt8(0)) (UInt16(0), UInt8(0))
default: default:
{ {
let addr = cpu.getAbsoluteAddress(opcode.mode, addr: cpu.programCounter + 1) let (addr, _) = cpu.getAbsoluteAddress(opcode.mode, addr: cpu.programCounter + 1)
return (addr, cpu.memRead(addr)) return (addr, cpu.memRead(addr))
}() }()

View File

@ -2,6 +2,7 @@ class Bus {
var cpuVram: [UInt8] = .init(repeating: 0, count: 2048) var cpuVram: [UInt8] = .init(repeating: 0, count: 2048)
var prgRom: [UInt8] var prgRom: [UInt8]
var ppu: NesPPU var ppu: NesPPU
var cycles: Int = 0
fileprivate let RAM : UInt16 = 0x0000 fileprivate let RAM : UInt16 = 0x0000
fileprivate let RAM_MIRRORS_END: UInt16 = 0x1FFF fileprivate let RAM_MIRRORS_END: UInt16 = 0x1FFF
@ -14,6 +15,15 @@ class Bus {
ppu = NesPPU(rom.character, rom.screenMirror) ppu = NesPPU(rom.character, rom.screenMirror)
self.prgRom = rom.program self.prgRom = rom.program
} }
func tick(_ cycles: UInt8) {
self.cycles += Int(cycles)
self.ppu.tick(cycles * 3)
}
func pollNMI() -> UInt8? {
ppu.nmiInterrupt
}
} }

View File

@ -15,17 +15,53 @@ class NesPPU {
var oamAddr: UInt8 = 0 var oamAddr: UInt8 = 0
var oamData = [UInt8](repeating: 0, count: 64 * 4) var oamData = [UInt8](repeating: 0, count: 64 * 4)
var scanline: UInt16 = 0
var cycles: Int = 0
var nmiInterrupt: UInt8?
init(_ chrRom: [UInt8], _ mirroring: Mirroring) { init(_ chrRom: [UInt8], _ mirroring: Mirroring) {
self.chrRom = chrRom self.chrRom = chrRom
self.mirroring = mirroring self.mirroring = mirroring
} }
func tick(_ cycles: UInt8) -> Bool {
self.cycles += Int(cycles)
if self.cycles >= 341 {
self.cycles = self.cycles - 341
scanline += 1
if scanline == 241 {
if self.ctrl.generateVblankNMI() {
self.status.setVblankStatus(true)
status.setSpriteZeroHit(false)
if ctrl.generateVblankNMI() {
nmiInterrupt = 1
}
}
}
if scanline >= 262 {
scanline = 0
nmiInterrupt = nil
status.setSpriteZeroHit(false)
self.status.resetVblankStatus()
return true
}
}
return false
}
func writeToPPUAddr(_ value: UInt8) { func writeToPPUAddr(_ value: UInt8) {
addr.update(value) addr.update(value)
} }
func writeToCtrl(_ value: UInt8) { func writeToCtrl(_ value: UInt8) {
let beforeNmiStatus = ctrl.generateVblankNMI()
ctrl.rawValue = value ctrl.rawValue = value
if !beforeNmiStatus && ctrl.generateVblankNMI() && status.isInVblank() {
nmiInterrupt = 1
}
} }
func incrememtVramAddr() { func incrememtVramAddr() {

View File

@ -34,4 +34,8 @@ struct ControlRegister: OptionSet {
32 32
} }
} }
func generateVblankNMI() -> Bool {
self.contains(.GENERATE_NMI)
}
} }