diff --git a/Sources/CPU/CPU.swift b/Sources/CPU/CPU.swift index 46d7f15..e4c96dd 100644 --- a/Sources/CPU/CPU.swift +++ b/Sources/CPU/CPU.swift @@ -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 } } diff --git a/Sources/CPU/CPU_Instructions.swift b/Sources/CPU/CPU_Instructions.swift index 9b36b4b..e89def9 100644 --- a/Sources/CPU/CPU_Instructions.swift +++ b/Sources/CPU/CPU_Instructions.swift @@ -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 { diff --git a/Sources/CPU/Trace.swift b/Sources/CPU/Trace.swift index 102c65c..ed5f0ad 100644 --- a/Sources/CPU/Trace.swift +++ b/Sources/CPU/Trace.swift @@ -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)) }() diff --git a/Sources/Data/Bus.swift b/Sources/Data/Bus.swift index 1e0d2a3..4e6eaef 100644 --- a/Sources/Data/Bus.swift +++ b/Sources/Data/Bus.swift @@ -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 + } } diff --git a/Sources/PPU/NesPPU.swift b/Sources/PPU/NesPPU.swift index 1367f76..ee11a1c 100644 --- a/Sources/PPU/NesPPU.swift +++ b/Sources/PPU/NesPPU.swift @@ -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() { diff --git a/Sources/PPU/Registers/ControlRegister.swift b/Sources/PPU/Registers/ControlRegister.swift index c0ec2c5..09d16d7 100644 --- a/Sources/PPU/Registers/ControlRegister.swift +++ b/Sources/PPU/Registers/ControlRegister.swift @@ -34,4 +34,8 @@ struct ControlRegister: OptionSet { 32 } } + + func generateVblankNMI() -> Bool { + self.contains(.GENERATE_NMI) + } }