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

View File

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

View File

@ -7,7 +7,7 @@ func dumpCpuState(_ cpu: CPU) -> String {
(UInt16(0), UInt8(0))
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))
}()

View File

@ -2,6 +2,7 @@ class Bus {
var cpuVram: [UInt8] = .init(repeating: 0, count: 2048)
var prgRom: [UInt8]
var ppu: NesPPU
var cycles: Int = 0
fileprivate let RAM : UInt16 = 0x0000
fileprivate let RAM_MIRRORS_END: UInt16 = 0x1FFF
@ -14,6 +15,15 @@ class Bus {
ppu = NesPPU(rom.character, rom.screenMirror)
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 oamData = [UInt8](repeating: 0, count: 64 * 4)
var scanline: UInt16 = 0
var cycles: Int = 0
var nmiInterrupt: UInt8?
init(_ chrRom: [UInt8], _ mirroring: Mirroring) {
self.chrRom = chrRom
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) {
addr.update(value)
}
func writeToCtrl(_ value: UInt8) {
let beforeNmiStatus = ctrl.generateVblankNMI()
ctrl.rawValue = value
if !beforeNmiStatus && ctrl.generateVblankNMI() && status.isInVblank() {
nmiInterrupt = 1
}
}
func incrememtVramAddr() {

View File

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