mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-11-21 17:46:23 -06:00
PPU: impl NMI interrupt
This commit is contained in:
parent
05927d8e91
commit
830bc5f65a
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -34,4 +34,8 @@ struct ControlRegister: OptionSet {
|
||||
32
|
||||
}
|
||||
}
|
||||
|
||||
func generateVblankNMI() -> Bool {
|
||||
self.contains(.GENERATE_NMI)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user