mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2025-09-11 04:52:04 -05:00
PPU: Implement registers
This commit is contained in:
528
Sources/CPU/CPU.swift
Normal file
528
Sources/CPU/CPU.swift
Normal file
@@ -0,0 +1,528 @@
|
||||
import Foundation
|
||||
import SDL
|
||||
|
||||
enum AddressingMode {
|
||||
case Immediate
|
||||
case ZeroPage
|
||||
case ZeroPage_X
|
||||
case ZeroPage_Y
|
||||
case Absolute
|
||||
case Absolute_X
|
||||
case Absolute_Y
|
||||
case Indirect_X
|
||||
case Indirect_Y
|
||||
case NoneAddressing
|
||||
}
|
||||
|
||||
struct CPUFlags: OptionSet {
|
||||
var rawValue: UInt8
|
||||
|
||||
static let carry = CPUFlags(rawValue: 0b00000001)
|
||||
static let zero = CPUFlags(rawValue: 0b00000010)
|
||||
static let interruptDisable = CPUFlags(rawValue: 0b00000100)
|
||||
static let decimalMode = CPUFlags(rawValue: 0b00001000)
|
||||
static let break1 = CPUFlags(rawValue: 0b00010000)
|
||||
static let break2 = CPUFlags(rawValue: 0b00100000)
|
||||
static let overflow = CPUFlags(rawValue: 0b01000000)
|
||||
static let negative = CPUFlags(rawValue: 0b10000000)
|
||||
}
|
||||
|
||||
let STACK: UInt16 = 0x0100
|
||||
let STACK_RESET: UInt8 = 0xfd
|
||||
|
||||
class CPU {
|
||||
var register_a: UInt8 = 0
|
||||
var register_x: UInt8 = 0
|
||||
var register_y: UInt8 = 0
|
||||
var stackPointer: UInt8 = STACK_RESET
|
||||
var status: CPUFlags = [.interruptDisable, .break2]
|
||||
var programCounter: UInt16 = 0
|
||||
var bus: Bus
|
||||
|
||||
init(bus: Bus) {
|
||||
self.bus = bus
|
||||
}
|
||||
|
||||
|
||||
func getOpperandAddress(_ mode: AddressingMode) -> UInt16 {
|
||||
switch mode {
|
||||
case .Immediate:
|
||||
return programCounter
|
||||
default:
|
||||
return getAbsoluteAddress(mode, addr: programCounter)
|
||||
}
|
||||
}
|
||||
|
||||
func getAbsoluteAddress(_ mode: AddressingMode, addr: UInt16) -> UInt16 {
|
||||
switch mode {
|
||||
case .ZeroPage:
|
||||
return UInt16(memRead(addr))
|
||||
case .Absolute:
|
||||
return memReadU16(addr)
|
||||
case .ZeroPage_X:
|
||||
let pos = memRead(addr)
|
||||
let addr = pos &+ register_x
|
||||
return UInt16(addr)
|
||||
case .ZeroPage_Y:
|
||||
let pos = memRead(addr)
|
||||
let addr = pos &+ register_y
|
||||
return UInt16(addr)
|
||||
case .Absolute_X:
|
||||
let base = memReadU16(addr)
|
||||
return base &+ UInt16(register_x)
|
||||
case .Absolute_Y:
|
||||
let base = memReadU16(addr)
|
||||
return base &+ UInt16(register_y)
|
||||
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)
|
||||
case .Indirect_Y:
|
||||
let base = memRead(addr)
|
||||
|
||||
let lo = memRead(UInt16(base))
|
||||
let hi = memRead(UInt16(base &+ 1))
|
||||
let deref_base = UInt16(hi) << 8 | UInt16(lo)
|
||||
let deref = deref_base &+ UInt16(register_y)
|
||||
return deref
|
||||
default:
|
||||
fatalError("mode \(mode) is not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
register_a = 0
|
||||
register_x = 0
|
||||
register_y = 0
|
||||
stackPointer = STACK_RESET
|
||||
status = [.interruptDisable, .break2]
|
||||
|
||||
programCounter = self.memReadU16(0xFFFC)
|
||||
}
|
||||
|
||||
//func loadAndRun(_ program: [UInt8]) {
|
||||
//load(program)
|
||||
//reset()
|
||||
//run()
|
||||
//}
|
||||
|
||||
//func load(_ program: [UInt8]) {
|
||||
//bus = Bus(try! Rom(program))
|
||||
//for i in 0..<program.count {
|
||||
//memWrite(0x8000 + UInt16(i), data: program[i])
|
||||
//}
|
||||
//memory[0x0600 ..< (0x0600 + program.count)] = program[0..<program.count]
|
||||
|
||||
//memWriteU16(0xFFFC, data: 0x0600)
|
||||
//}
|
||||
|
||||
func run() {
|
||||
run(onCycle: {}, onComplete: {})
|
||||
}
|
||||
|
||||
func run(onCycle: @escaping () -> (), onComplete: @escaping () -> ()) {
|
||||
let opcodes = OPCODES_MAP
|
||||
//_ = Timer.scheduledTimer(withTimeInterval: 0.00007, repeats: true) { [self] timer in
|
||||
while true {
|
||||
processOpcodes(onCycle: onCycle, opcodes: opcodes) {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
func processOpcodes(onCycle: () -> (), opcodes: [UInt8: OpCode], onComplete: () -> ()) {
|
||||
onCycle()
|
||||
let code = memRead(programCounter)
|
||||
programCounter += 1
|
||||
|
||||
let programCounterState = programCounter
|
||||
guard let opcode = opcodes[code] else {fatalError("OpCode \(code) not recgonized!")}
|
||||
// print(programCounter, opcode.mnemonic)
|
||||
|
||||
switch code {
|
||||
/// LDA
|
||||
case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1:
|
||||
lda(opcode.mode)
|
||||
/// STA
|
||||
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91:
|
||||
sta(opcode.mode)
|
||||
case 0xd8:
|
||||
status.remove(.decimalMode)
|
||||
case 0x58:
|
||||
status.remove(.interruptDisable)
|
||||
case 0xb8:
|
||||
status.remove(.overflow)
|
||||
case 0x18:
|
||||
clearCarryFlag()
|
||||
case 0x38:
|
||||
setCarryFlag()
|
||||
case 0x78:
|
||||
status.insert(.interruptDisable)
|
||||
case 0xf8:
|
||||
status.insert(.decimalMode)
|
||||
case 0x48:
|
||||
stackPush(register_a)
|
||||
case 0x68:
|
||||
pla()
|
||||
case 0x08:
|
||||
php()
|
||||
case 0x28:
|
||||
plp()
|
||||
case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71:
|
||||
adc(opcode.mode)
|
||||
case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1:
|
||||
sbc(opcode.mode)
|
||||
case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31:
|
||||
and(opcode.mode)
|
||||
case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51:
|
||||
eor(opcode.mode)
|
||||
case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11:
|
||||
ora(opcode.mode)
|
||||
case 0x4a:
|
||||
lsrAccumulator()
|
||||
case 0x46, 0x56, 0x4e, 0x5e:
|
||||
_ = lsr(opcode.mode)
|
||||
case 0x0a:
|
||||
aslAccumulator()
|
||||
case 0x06, 0x16, 0x0e, 0x1e:
|
||||
_ = asl(opcode.mode)
|
||||
case 0x2a:
|
||||
rolAccumulator()
|
||||
case 0x26, 0x36, 0x2e, 0x3e:
|
||||
_ = rol(opcode.mode)
|
||||
case 0x6a:
|
||||
rorAccumulator()
|
||||
case 0x66, 0x76, 0x6e, 0x7e:
|
||||
_ = ror(opcode.mode)
|
||||
case 0xe6, 0xf6, 0xee, 0xfe:
|
||||
_ = inc(opcode.mode)
|
||||
case 0xc8:
|
||||
iny()
|
||||
case 0xc6, 0xd6, 0xce, 0xde:
|
||||
_ = dec(opcode.mode)
|
||||
case 0xca:
|
||||
dex()
|
||||
case 0x88:
|
||||
dey()
|
||||
case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1:
|
||||
compare(mode: opcode.mode, compare_with: register_a)
|
||||
case 0xc0, 0xc4, 0xcc:
|
||||
compare(mode: opcode.mode, compare_with: register_y)
|
||||
case 0xe0, 0xe4, 0xec:
|
||||
compare(mode: opcode.mode, compare_with: register_x)
|
||||
case 0x4c:
|
||||
let memAddr = memReadU16(programCounter)
|
||||
programCounter = memAddr
|
||||
case 0x6c:
|
||||
let memAddr = memReadU16(programCounter)
|
||||
//6502 bug mode with with page boundary:
|
||||
// if address $3000 contains $40, $30FF contains $80, and $3100 contains $50,
|
||||
// the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended
|
||||
// i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000
|
||||
let indirectRef: UInt16
|
||||
if memAddr & 0x00ff == 0x00ff {
|
||||
let lo = memRead(memAddr)
|
||||
let hi = memRead(memAddr & 0xff00)
|
||||
indirectRef = UInt16(hi) << 8 | UInt16(lo)
|
||||
} else {
|
||||
indirectRef = memReadU16(memAddr)
|
||||
}
|
||||
|
||||
programCounter = indirectRef
|
||||
|
||||
case 0x20:
|
||||
stackPushU16(programCounter + 2 - 1)
|
||||
let targetAddr = memReadU16(programCounter)
|
||||
programCounter = targetAddr
|
||||
case 0x60:
|
||||
programCounter = stackPopU16() + 1
|
||||
case 0x40:
|
||||
status.rawValue = stackPop()
|
||||
status.remove(.break1)
|
||||
status.insert(.break2)
|
||||
|
||||
programCounter = stackPopU16()
|
||||
case 0xd0:
|
||||
branch(!status.contains(.zero))
|
||||
case 0x70:
|
||||
branch(status.contains(.overflow))
|
||||
case 0x50:
|
||||
branch(!status.contains(.overflow))
|
||||
case 0x10:
|
||||
branch(!status.contains(.negative))
|
||||
case 0x30:
|
||||
branch(status.contains(.negative))
|
||||
case 0xf0:
|
||||
branch(status.contains(.zero))
|
||||
case 0xb0:
|
||||
branch(status.contains(.carry))
|
||||
case 0x90:
|
||||
branch(!status.contains(.carry))
|
||||
case 0x24, 0x2c:
|
||||
bit(opcode.mode)
|
||||
case 0x86, 0x96, 0x8e:
|
||||
let addr = getOpperandAddress(opcode.mode)
|
||||
memWrite(addr, data: register_x)
|
||||
case 0x84, 0x94, 0x8c:
|
||||
let addr = getOpperandAddress(opcode.mode)
|
||||
memWrite(addr, data: register_y)
|
||||
case 0xa2, 0xa6, 0xb6, 0xae, 0xbe:
|
||||
ldx(opcode.mode)
|
||||
case 0xa0, 0xa4, 0xb4, 0xac, 0xbc:
|
||||
ldy(opcode.mode)
|
||||
case 0xea:
|
||||
return
|
||||
case 0xa8:
|
||||
register_y = register_a
|
||||
updateZeroAndNegativeFlags(register_y)
|
||||
case 0xba:
|
||||
register_x = stackPointer
|
||||
updateZeroAndNegativeFlags(register_x)
|
||||
case 0x8a:
|
||||
register_a = register_x
|
||||
updateZeroAndNegativeFlags(register_a)
|
||||
case 0x9a:
|
||||
stackPointer = register_x
|
||||
case 0x98:
|
||||
register_a = register_y
|
||||
updateZeroAndNegativeFlags(register_a)
|
||||
/// TAX
|
||||
case 0xaa:
|
||||
tax()
|
||||
/// INX
|
||||
case 0xe8:
|
||||
inx()
|
||||
/// BRK
|
||||
case 0x00:
|
||||
//timer.invalidate()
|
||||
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 _ = self.memRead(addr)
|
||||
// Do nothing
|
||||
/// NOP
|
||||
case 0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa:
|
||||
{ /* Do Nothing */}()
|
||||
/// RRA
|
||||
case 0x67, 0x77, 0x6f, 0x7f, 0x7b, 0x63, 0x73:
|
||||
let data = ror(opcode.mode)
|
||||
addToRegisterA(data)
|
||||
/// ISB
|
||||
case 0xe7, 0xf7, 0xef, 0xff, 0xfb, 0xe3, 0xf3:
|
||||
let data = inc(opcode.mode)
|
||||
subFromRegisterA(data)
|
||||
/// SKB
|
||||
case 0x80, 0x82, 0x89, 0xc2, 0xe2:
|
||||
{ /* 2 byte NOP immediate, do nothing */ }()
|
||||
/// LAX
|
||||
case 0xa7, 0xb7, 0xaf, 0xbf, 0xa3, 0xb3:
|
||||
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)
|
||||
memWrite(addr, data: data)
|
||||
// Unoffical SBC
|
||||
case 0xeb:
|
||||
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)
|
||||
var data = memRead(addr)
|
||||
data = data &- 1
|
||||
memWrite(addr, data: data)
|
||||
if data <= register_a {
|
||||
status.insert(.carry)
|
||||
}
|
||||
let tmp = register_a &- data
|
||||
updateZeroAndNegativeFlags(tmp)
|
||||
/// SLO
|
||||
case 0x07, 0x17, 0x0F, 0x1f, 0x1b, 0x03, 0x13:
|
||||
let data = asl(opcode.mode)
|
||||
orWithRegisterA(data)
|
||||
/// RLA
|
||||
case 0x27, 0x37, 0x2F, 0x3F, 0x3b, 0x33, 0x23:
|
||||
let data = rol(opcode.mode)
|
||||
andWithRegisterA(data)
|
||||
/// SRE
|
||||
case 0x47, 0x57, 0x4F, 0x5f, 0x5b, 0x43, 0x53:
|
||||
let data = lsr(opcode.mode)
|
||||
xorWithRegisterA(data)
|
||||
|
||||
|
||||
|
||||
default: fatalError("TODO!")
|
||||
}
|
||||
|
||||
if programCounterState == programCounter {
|
||||
programCounter += UInt16(opcode.len - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func updateZeroAndNegativeFlags(_ result: UInt8) {
|
||||
if result == 0 {
|
||||
status.insert(.zero)
|
||||
} else {
|
||||
status.remove(.zero)
|
||||
}
|
||||
|
||||
if result & 0b1000_0000 != 0 {
|
||||
status.insert(.negative)
|
||||
} else {
|
||||
status.remove(.negative)
|
||||
}
|
||||
}
|
||||
|
||||
func setRegisterA(_ value: UInt8) {
|
||||
register_a = value
|
||||
updateZeroAndNegativeFlags(register_a)
|
||||
}
|
||||
|
||||
func setCarryFlag() {
|
||||
status.insert(.carry)
|
||||
}
|
||||
|
||||
func clearCarryFlag() {
|
||||
status.remove(.carry)
|
||||
}
|
||||
|
||||
/// note: ignoring decimal mode
|
||||
/// http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
|
||||
func addToRegisterA(_ data: UInt8) {
|
||||
let shouldCarry = status.contains(.carry) ? 1 : 0
|
||||
let sum = UInt16(register_a) + UInt16(data) + UInt16(shouldCarry)
|
||||
|
||||
let carry = sum > 0xff
|
||||
|
||||
if carry {
|
||||
status.insert(.carry)
|
||||
} else {
|
||||
status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = UInt8(truncatingIfNeeded: sum)
|
||||
|
||||
if (data ^ result) & (result ^ register_a) & 0x80 != 0 {
|
||||
status.insert(.overflow)
|
||||
} else {
|
||||
status.remove(.overflow)
|
||||
}
|
||||
|
||||
setRegisterA(result)
|
||||
}
|
||||
|
||||
func subFromRegisterA(_ data: UInt8) {
|
||||
addToRegisterA(UInt8(bitPattern: (Int8(bitPattern: data) &* -1) &- 1))
|
||||
}
|
||||
|
||||
func orWithRegisterA(_ data: UInt8) {
|
||||
setRegisterA(data | self.register_a)
|
||||
}
|
||||
|
||||
func xorWithRegisterA(_ data: UInt8) {
|
||||
setRegisterA(data ^ self.register_a)
|
||||
}
|
||||
|
||||
func andWithRegisterA(_ data: UInt8) {
|
||||
setRegisterA(data & register_a)
|
||||
}
|
||||
|
||||
func stackPop() -> UInt8 {
|
||||
stackPointer = stackPointer &+ 1
|
||||
return memRead(STACK + UInt16(stackPointer))
|
||||
}
|
||||
|
||||
func stackPush(_ data: UInt8) {
|
||||
memWrite(STACK + UInt16(stackPointer), data: data)
|
||||
stackPointer = stackPointer &- 1
|
||||
}
|
||||
|
||||
func stackPopU16() -> UInt16 {
|
||||
let lo = UInt16(stackPop())
|
||||
let hi = UInt16(stackPop())
|
||||
|
||||
return hi << 8 | lo
|
||||
}
|
||||
|
||||
func stackPushU16(_ data: UInt16) {
|
||||
let hi = UInt8(data >> 8)
|
||||
let lo = UInt8(data & 0xff)
|
||||
stackPush(hi)
|
||||
stackPush(lo)
|
||||
}
|
||||
|
||||
func compare(mode: AddressingMode, compare_with: UInt8) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
if data <= compare_with {
|
||||
status.insert(.carry)
|
||||
} else {
|
||||
status.remove(.carry)
|
||||
}
|
||||
|
||||
updateZeroAndNegativeFlags(compare_with &- data)
|
||||
}
|
||||
|
||||
func branch(_ condition: Bool) {
|
||||
if condition {
|
||||
let addr = memRead(programCounter)
|
||||
let jump: Int8 = Int8(bitPattern: addr)
|
||||
let jump_addr = programCounter &+ 1 &+ UInt16(bitPattern: Int16(jump))
|
||||
|
||||
programCounter = jump_addr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CPU: Memory {
|
||||
func memRead(_ addr: UInt16) -> UInt8 {
|
||||
return bus.memRead(addr)
|
||||
}
|
||||
|
||||
func memWrite(_ addr: UInt16, data: UInt8) {
|
||||
bus.memWrite(addr, data: data)
|
||||
}
|
||||
|
||||
func memReadU16(_ addr: UInt16) -> UInt16 {
|
||||
return bus.memReadU16(addr)
|
||||
}
|
||||
|
||||
func memWriteU16(_ addr: UInt16, data: UInt16) {
|
||||
bus.memWriteU16(addr, data: data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protocol Memory {
|
||||
func memRead(_ addr: UInt16) -> UInt8
|
||||
|
||||
func memWrite(_ addr: UInt16, data: UInt8)
|
||||
|
||||
func memReadU16(_ addr: UInt16) -> UInt16
|
||||
|
||||
func memWriteU16(_ addr: UInt16, data: UInt16)
|
||||
}
|
||||
|
||||
extension Memory {
|
||||
func memReadU16(_ addr: UInt16) -> UInt16 {
|
||||
let lo = UInt16(memRead(addr))
|
||||
let hi = UInt16(memRead(addr + 1))
|
||||
return (hi << 8) | lo
|
||||
}
|
||||
|
||||
func memWriteU16(_ addr: UInt16, data: UInt16) {
|
||||
let hi = UInt8(data >> 8)
|
||||
let lo = UInt8(data & 0xff)
|
||||
self.memWrite(addr, data: lo)
|
||||
self.memWrite(addr + 1, data: hi)
|
||||
}
|
||||
}
|
262
Sources/CPU/CPU_Instructions.swift
Normal file
262
Sources/CPU/CPU_Instructions.swift
Normal file
@@ -0,0 +1,262 @@
|
||||
extension CPU {
|
||||
func lda(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let value = memRead(addr)
|
||||
|
||||
setRegisterA(value)
|
||||
}
|
||||
|
||||
func ldy(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
register_y = data
|
||||
updateZeroAndNegativeFlags(register_y)
|
||||
}
|
||||
|
||||
func ldx(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
register_x = data
|
||||
updateZeroAndNegativeFlags(register_x)
|
||||
}
|
||||
|
||||
func sta(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
memWrite(addr, data: register_a)
|
||||
}
|
||||
|
||||
func and(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
self.setRegisterA(data & register_a)
|
||||
}
|
||||
|
||||
func eor(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
setRegisterA(data ^ register_a)
|
||||
}
|
||||
|
||||
func ora(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
setRegisterA(data | register_a)
|
||||
}
|
||||
|
||||
func tax() {
|
||||
register_x = register_a
|
||||
updateZeroAndNegativeFlags(register_x)
|
||||
}
|
||||
|
||||
func inx() {
|
||||
register_x = register_x &+ 1
|
||||
updateZeroAndNegativeFlags(register_x)
|
||||
}
|
||||
|
||||
func iny() {
|
||||
register_y = register_y &+ 1
|
||||
updateZeroAndNegativeFlags(register_y)
|
||||
}
|
||||
|
||||
func sbc(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
let res = Int8(bitPattern: data) &* -1
|
||||
addToRegisterA(UInt8(bitPattern: res &- 1))
|
||||
}
|
||||
|
||||
func adc(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
addToRegisterA(data)
|
||||
}
|
||||
|
||||
func aslAccumulator() {
|
||||
var data = register_a
|
||||
if data >> 7 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data << 1
|
||||
setRegisterA(data)
|
||||
}
|
||||
|
||||
func asl(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
if data >> 7 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data << 1
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func lsrAccumulator() {
|
||||
var data = register_a
|
||||
if data & 1 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data >> 1
|
||||
setRegisterA(data)
|
||||
}
|
||||
|
||||
func lsr(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
if data & 1 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data >> 1
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func rolAccumulator() {
|
||||
var data = register_a
|
||||
let oldCarry = status.contains(.carry)
|
||||
|
||||
if data >> 7 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data << 1
|
||||
if oldCarry {
|
||||
data = data | 1
|
||||
}
|
||||
setRegisterA(data)
|
||||
}
|
||||
|
||||
func rol(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
let oldCarry = status.contains(.carry)
|
||||
|
||||
if data >> 7 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
|
||||
data = data << 1
|
||||
if oldCarry {
|
||||
data = data | 1
|
||||
}
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func rorAccumulator() {
|
||||
var data = register_a
|
||||
let oldCarry = status.contains(.carry)
|
||||
|
||||
if data & 1 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data >> 1
|
||||
if oldCarry {
|
||||
data = data | 0b10000000
|
||||
}
|
||||
setRegisterA(data)
|
||||
}
|
||||
|
||||
func ror(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
let oldCarry = status.contains(.carry)
|
||||
if data & 1 == 1 {
|
||||
setCarryFlag()
|
||||
} else {
|
||||
clearCarryFlag()
|
||||
}
|
||||
data = data >> 1
|
||||
if oldCarry {
|
||||
data = data | 0b10000000
|
||||
}
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func inc(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
data = data &+ 1
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func dey() {
|
||||
register_y = register_y &- 1
|
||||
updateZeroAndNegativeFlags(register_y)
|
||||
}
|
||||
|
||||
func dex() {
|
||||
register_x = register_x &- 1
|
||||
updateZeroAndNegativeFlags(register_x)
|
||||
}
|
||||
|
||||
func dec(_ mode: AddressingMode) -> UInt8 {
|
||||
let addr = getOpperandAddress(mode)
|
||||
var data = memRead(addr)
|
||||
data = data &- 1
|
||||
memWrite(addr, data: data)
|
||||
updateZeroAndNegativeFlags(data)
|
||||
return data
|
||||
}
|
||||
|
||||
func pla() {
|
||||
let data = stackPop()
|
||||
setRegisterA(data)
|
||||
}
|
||||
|
||||
func plp() {
|
||||
status.rawValue = stackPop()
|
||||
status.remove(.break1)
|
||||
status.insert(.break2)
|
||||
}
|
||||
|
||||
func php() {
|
||||
var flags = status
|
||||
flags.insert(.break1)
|
||||
flags.insert(.break2)
|
||||
stackPush(flags.rawValue)
|
||||
}
|
||||
|
||||
func bit(_ mode: AddressingMode) {
|
||||
let addr = getOpperandAddress(mode)
|
||||
let data = memRead(addr)
|
||||
let and = register_a & data
|
||||
if and == 0 {
|
||||
status.insert(.zero)
|
||||
} else {
|
||||
status.remove(.zero)
|
||||
}
|
||||
|
||||
if data & 0b10000000 > 0 {
|
||||
status.insert(.negative)
|
||||
} else {
|
||||
status.remove(.negative)
|
||||
}
|
||||
|
||||
if data & 0b01000000 > 0 {
|
||||
status.insert(.overflow)
|
||||
} else {
|
||||
status.remove(.overflow)
|
||||
}
|
||||
}
|
||||
}
|
107
Sources/CPU/Trace.swift
Normal file
107
Sources/CPU/Trace.swift
Normal file
@@ -0,0 +1,107 @@
|
||||
func dumpCpuState(_ cpu: CPU) -> String {
|
||||
guard let opcode = OPCODES_MAP[cpu.memRead(cpu.programCounter)] else { fatalError("Not an opcode: \(cpu.memRead(cpu.programCounter))") }
|
||||
var hexOpcode = [String(format: "%02X", cpu.memRead(cpu.programCounter))]
|
||||
|
||||
let (memAddr, storedVal) = switch opcode.mode {
|
||||
case .Immediate, .NoneAddressing:
|
||||
(UInt16(0), UInt8(0))
|
||||
default:
|
||||
{
|
||||
let addr = cpu.getAbsoluteAddress(opcode.mode, addr: cpu.programCounter + 1)
|
||||
|
||||
return (addr, cpu.memRead(addr))
|
||||
}()
|
||||
}
|
||||
|
||||
let operand = switch opcode.len {
|
||||
case 1:
|
||||
{
|
||||
return switch opcode.code {
|
||||
case 0x0a, 0x4a, 0x2a, 0x6a:
|
||||
"A "
|
||||
default:
|
||||
""
|
||||
}
|
||||
}()
|
||||
case 2:
|
||||
{
|
||||
let address = cpu.memRead(cpu.programCounter + 1)
|
||||
hexOpcode.append(String(format: "%02X", address))
|
||||
|
||||
return switch opcode.mode {
|
||||
case .Immediate:
|
||||
String(format: "#$%02X", address)
|
||||
case .ZeroPage:
|
||||
String(format: "$%02X = %02X", memAddr, storedVal)
|
||||
case .ZeroPage_X:
|
||||
String(format: "$%02X,X @ %02X = %02X", address, memAddr, storedVal)
|
||||
case .ZeroPage_Y:
|
||||
String(format: "$%02X,Y @ %02X = %02X", address, memAddr, storedVal)
|
||||
case .Indirect_X:
|
||||
String(format: "($%02X,X) @ %02X = %04X = %02X", address, address &+ cpu.register_x, memAddr, storedVal)
|
||||
case .Indirect_Y:
|
||||
String(format: "($%02X),Y = %04X @ %04X = %02X", address, memAddr &- UInt16(cpu.register_y), memAddr, storedVal)
|
||||
case .NoneAddressing:
|
||||
String(format: "$%04X", Int(cpu.programCounter) + 2 &+ Int(Int8(bitPattern: address)))
|
||||
default:
|
||||
fatalError("Unexpected addressing mode \(opcode.mode) has ops-length of 2. code: \(opcode.mnemonic)")
|
||||
}
|
||||
}()
|
||||
case 3:
|
||||
{
|
||||
let addressLo = cpu.memRead(cpu.programCounter + 1)
|
||||
let addressHi = cpu.memRead(cpu.programCounter + 2)
|
||||
hexOpcode.append(String(format: "%02X", addressLo))
|
||||
hexOpcode.append(String(format: "%02X", addressHi))
|
||||
|
||||
let address = cpu.memReadU16(cpu.programCounter + 1)
|
||||
|
||||
return switch opcode.mode {
|
||||
case .NoneAddressing:
|
||||
{
|
||||
// jmp indirect
|
||||
if (opcode.code == 0x6c) {
|
||||
let jmpAddr = if address & 0x00FF == 0x00FF {
|
||||
{
|
||||
let lo = cpu.memRead(address)
|
||||
let hi = cpu.memRead(address & 0xFF00)
|
||||
return UInt16(hi) << 8 | UInt16(lo)
|
||||
}()
|
||||
} else {
|
||||
cpu.memReadU16(address)
|
||||
}
|
||||
|
||||
return String(format: "($%04X) = %04X", address, jmpAddr)
|
||||
} else {
|
||||
return String(format: "$%04X", address)
|
||||
}
|
||||
}()
|
||||
case .Absolute:
|
||||
String(format: "$%04X = %02X", memAddr, storedVal)
|
||||
case .Absolute_X:
|
||||
String(format: "$%04X,X @ %04X = %02X", address, memAddr, storedVal)
|
||||
case .Absolute_Y:
|
||||
String(format: "$%04X,Y @ %04X = %02X", address, memAddr, storedVal)
|
||||
default:
|
||||
fatalError("Unexpected addressing mode \(opcode.mode) has ops-length of 3. code: \(opcode.mnemonic)")
|
||||
}
|
||||
}()
|
||||
default:
|
||||
""
|
||||
}
|
||||
|
||||
let hexString = hexOpcode.joined(separator: " ").padding(toLength: 8, withPad: " ", startingAt: 0)
|
||||
let asm = "\(String(format: "%04X", cpu.programCounter)) \(hexString) \(opcode.mnemonic.leftPadding(toLength: 4, withPad: " ")) \(operand)".padding(toLength: 47, withPad: " ", startingAt: 0)
|
||||
return String(format: "\(asm) A:%02X X:%02X Y:%02X P:%02X SP:%02X", cpu.register_a, cpu.register_x, cpu.register_y, cpu.status.rawValue, cpu.stackPointer)
|
||||
}
|
||||
|
||||
extension String {
|
||||
func leftPadding(toLength: Int, withPad character: Character) -> String {
|
||||
let stringLength = self.count
|
||||
if stringLength < toLength {
|
||||
return String(repeatElement(character, count: toLength - stringLength)) + self
|
||||
} else {
|
||||
return String(self.suffix(toLength))
|
||||
}
|
||||
}
|
||||
}
|
334
Sources/CPU/opcodes.swift
Normal file
334
Sources/CPU/opcodes.swift
Normal file
@@ -0,0 +1,334 @@
|
||||
struct OpCode {
|
||||
let code: UInt8
|
||||
let mnemonic: String
|
||||
let len: UInt8
|
||||
let cycles: UInt8
|
||||
let mode: AddressingMode
|
||||
}
|
||||
|
||||
let CPU_OP_CODES: [OpCode] = [
|
||||
OpCode(code: 0x00, mnemonic: "BRK", len: 1, cycles: 7, mode: .NoneAddressing),
|
||||
OpCode(code: 0xea, mnemonic: "NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
/// Arithmetic
|
||||
OpCode(code: 0x69, mnemonic: "ADC", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x65, mnemonic: "ADC", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x75, mnemonic: "ADC", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x6d, mnemonic: "ADC", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x7d, mnemonic: "ADC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0x79, mnemonic: "ADC", len: 3, cycles: 5 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0x61, mnemonic: "ADC", len: 2, cycles: 5, mode: .Indirect_X),
|
||||
OpCode(code: 0x71, mnemonic: "ADC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0xe9, mnemonic: "SBC", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xe5, mnemonic: "SBC", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xf5, mnemonic: "SBC", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xed, mnemonic: "SBC", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xfd, mnemonic: "SBC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0xf9, mnemonic: "SBC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0xe1, mnemonic: "SBC", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0xf1, mnemonic: "SBC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x29, mnemonic: "AND", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x25, mnemonic: "AND", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x35, mnemonic: "AND", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x2d, mnemonic: "AND", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x3d, mnemonic: "AND", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0x39, mnemonic: "AND", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0x21, mnemonic: "AND", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0x31, mnemonic: "AND", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x49, mnemonic: "EOR", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x45, mnemonic: "EOR", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x55, mnemonic: "EOR", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x4d, mnemonic: "EOR", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x5d, mnemonic: "EOR", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0x59, mnemonic: "EOR", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0x41, mnemonic: "EOR", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0x51, mnemonic: "EOR", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x09, mnemonic: "ORA", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x05, mnemonic: "ORA", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x15, mnemonic: "ORA", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x0d, mnemonic: "ORA", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x1d, mnemonic: "ORA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0x19, mnemonic: "ORA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0x01, mnemonic: "ORA", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0x11, mnemonic: "ORA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
/// Shifts
|
||||
OpCode(code: 0x0a, mnemonic: "ASL", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x06, mnemonic: "ASL", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x16, mnemonic: "ASL", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x0e, mnemonic: "ASL", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x1e, mnemonic: "ASL", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0x4a, mnemonic: "LSR", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x46, mnemonic: "LSR", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x56, mnemonic: "LSR", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x4e, mnemonic: "LSR", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x5e, mnemonic: "LSR", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0x2a, mnemonic: "ROL", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x26, mnemonic: "ROL", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x36, mnemonic: "ROL", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x2e, mnemonic: "ROL", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x3e, mnemonic: "ROL", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0x6a, mnemonic: "ROR", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x66, mnemonic: "ROR", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x76, mnemonic: "ROR", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x6e, mnemonic: "ROR", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x7e, mnemonic: "ROR", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0xe6, mnemonic: "INC", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0xf6, mnemonic: "INC", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xee, mnemonic: "INC", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0xfe, mnemonic: "INC", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0xe8, mnemonic: "INX", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xc8, mnemonic: "INY", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0xc6, mnemonic: "DEC", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0xd6, mnemonic: "DEC", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xce, mnemonic: "DEC", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0xde, mnemonic: "DEC", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0xca, mnemonic: "DEX", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x88, mnemonic: "DEY", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0xc9, mnemonic: "CMP", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xc5, mnemonic: "CMP", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xd5, mnemonic: "CMP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xcd, mnemonic: "CMP", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xdd, mnemonic: "CMP", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0xd9, mnemonic: "CMP", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0xc1, mnemonic: "CMP", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0xd1, mnemonic: "CMP", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0xc0, mnemonic: "CPY", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xc4, mnemonic: "CPY", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xcc, mnemonic: "CPY", len: 3, cycles: 4, mode: .Absolute),
|
||||
|
||||
OpCode(code: 0xe0, mnemonic: "CPX", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xe4, mnemonic: "CPX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xec, mnemonic: "CPX", len: 3, cycles: 4, mode: .Absolute),
|
||||
|
||||
/// Branching
|
||||
OpCode(code: 0x4c, mnemonic: "JMP", len: 3, cycles: 3, mode: .NoneAddressing), // AddressingMode that acts as Immidiate
|
||||
OpCode(code: 0x6c, mnemonic: "JMP", len: 3, cycles: 5, mode: .NoneAddressing), // AddressingMode.Indirect with 6502 bug
|
||||
|
||||
OpCode(code: 0x20, mnemonic: "JSR", len: 3, cycles: 6, mode: .NoneAddressing),
|
||||
OpCode(code: 0x60, mnemonic: "RTS", len: 1, cycles: 6, mode: .NoneAddressing),
|
||||
OpCode(code: 0x40, mnemonic: "RTI", len: 1, cycles: 6, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0xd0, mnemonic: "BNE", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0x70, mnemonic: "BVS", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0x50, mnemonic: "BVC", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0x30, mnemonic: "BMI", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0xf0, mnemonic: "BEQ", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0xb0, mnemonic: "BCS", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0x90, mnemonic: "BCC", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
OpCode(code: 0x10, mnemonic: "BPL", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0x24, mnemonic: "BIT", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x2c, mnemonic: "BIT", len: 3, cycles: 4, mode: .Absolute),
|
||||
|
||||
/// Stores, Loads
|
||||
OpCode(code: 0xa9, mnemonic: "LDA", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xa5, mnemonic: "LDA", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xb5, mnemonic: "LDA", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xad, mnemonic: "LDA", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xbd, mnemonic: "LDA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
OpCode(code: 0xb9, mnemonic: "LDA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
OpCode(code: 0xa1, mnemonic: "LDA", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0xb1, mnemonic: "LDA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0xa2, mnemonic: "LDX", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xa6, mnemonic: "LDX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xb6, mnemonic: "LDX", len: 2, cycles: 4, mode: .ZeroPage_Y),
|
||||
OpCode(code: 0xae, mnemonic: "LDX", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xbe, mnemonic: "LDX", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y),
|
||||
|
||||
OpCode(code: 0xa0, mnemonic: "LDY", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xa4, mnemonic: "LDY", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xb4, mnemonic: "LDY", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xac, mnemonic: "LDY", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xbc, mnemonic: "LDY", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0x85, mnemonic: "STA", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x95, mnemonic: "STA", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x8d, mnemonic: "STA", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x9d, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_X),
|
||||
OpCode(code: 0x99, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_Y),
|
||||
OpCode(code: 0x81, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0x91, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x86, mnemonic: "STX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x96, mnemonic: "STX", len: 2, cycles: 4, mode: .ZeroPage_Y),
|
||||
OpCode(code: 0x8e, mnemonic: "STX", len: 3, cycles: 4, mode: .Absolute),
|
||||
|
||||
OpCode(code: 0x84, mnemonic: "STY", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x94, mnemonic: "STY", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x8c, mnemonic: "STY", len: 3, cycles: 4, mode: .Absolute),
|
||||
|
||||
/// Flag Clears
|
||||
OpCode(code: 0xd8, mnemonic: "CLD", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x58, mnemonic: "CLI", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xb8, mnemonic: "CLV", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x18, mnemonic: "CLC", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x38, mnemonic: "SEC", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x78, mnemonic: "SEI", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xf8, mnemonic: "SED", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0xaa, mnemonic: "TAX", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xa8, mnemonic: "TAY", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xba, mnemonic: "TSX", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x8a, mnemonic: "TXA", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x9a, mnemonic: "TXS", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x98, mnemonic: "TYA", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
/// Stack
|
||||
OpCode(code: 0x48, mnemonic: "PHA", len: 1, cycles: 3, mode: .NoneAddressing),
|
||||
OpCode(code: 0x68, mnemonic: "PLA", len: 1, cycles: 4, mode: .NoneAddressing),
|
||||
OpCode(code: 0x08, mnemonic: "PHP", len: 1, cycles: 3, mode: .NoneAddressing),
|
||||
OpCode(code: 0x28, mnemonic: "PLP", len: 1, cycles: 4, mode: .NoneAddressing),
|
||||
|
||||
/// Undocumented
|
||||
|
||||
OpCode(code: 0xc7, mnemonic: "*DCP", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0xd7, mnemonic: "*DCP", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xCF, mnemonic: "*DCP", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0xdF, mnemonic: "*DCP", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0xdb, mnemonic: "*DCP", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0xd3, mnemonic: "*DCP", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
OpCode(code: 0xc3, mnemonic: "*DCP", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
|
||||
|
||||
OpCode(code: 0x27, mnemonic: "*RLA", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x37, mnemonic: "*RLA", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x2F, mnemonic: "*RLA", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x3F, mnemonic: "*RLA", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0x3b, mnemonic: "*RLA", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0x33, mnemonic: "*RLA", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
OpCode(code: 0x23, mnemonic: "*RLA", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
|
||||
OpCode(code: 0x07, mnemonic: "*SLO", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x17, mnemonic: "*SLO", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x0F, mnemonic: "*SLO", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x1f, mnemonic: "*SLO", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0x1b, mnemonic: "*SLO", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0x03, mnemonic: "*SLO", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
OpCode(code: 0x13, mnemonic: "*SLO", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x47, mnemonic: "*SRE", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x57, mnemonic: "*SRE", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x4F, mnemonic: "*SRE", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x5f, mnemonic: "*SRE", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0x5b, mnemonic: "*SRE", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0x43, mnemonic: "*SRE", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
OpCode(code: 0x53, mnemonic: "*SRE", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
|
||||
|
||||
OpCode(code: 0x80, mnemonic: "*NOP", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x82, mnemonic: "*NOP", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x89, mnemonic: "*NOP", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xc2, mnemonic: "*NOP", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0xe2, mnemonic: "*NOP", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
|
||||
OpCode(code: 0xCB, mnemonic: "*AXS", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
OpCode(code: 0x6B, mnemonic: "*ARR", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
OpCode(code: 0xeb, mnemonic: "*SBC", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
OpCode(code: 0x0b, mnemonic: "*ANC", len: 2, cycles: 2, mode: .Immediate),
|
||||
OpCode(code: 0x2b, mnemonic: "*ANC", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
OpCode(code: 0x4b, mnemonic: "*ALR", len: 2, cycles: 2, mode: .Immediate),
|
||||
|
||||
OpCode(code: 0x04, mnemonic: "*NOP", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x44, mnemonic: "*NOP", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x64, mnemonic: "*NOP", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x14, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x34, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x54, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x74, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xd4, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xf4, mnemonic: "*NOP", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x0c, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x1c, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
OpCode(code: 0x3c, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
OpCode(code: 0x5c, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
OpCode(code: 0x7c, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
OpCode(code: 0xdc, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
OpCode(code: 0xfc, mnemonic: "*NOP", len: 3, cycles: 4, mode: .Absolute_X),
|
||||
|
||||
OpCode(code: 0x67, mnemonic: "*RRA", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0x77, mnemonic: "*RRA", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0x6f, mnemonic: "*RRA", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0x7f, mnemonic: "*RRA", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0x7b, mnemonic: "*RRA", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0x63, mnemonic: "*RRA", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
OpCode(code: 0x73, mnemonic: "*RRA", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
|
||||
|
||||
OpCode(code: 0xe7, mnemonic: "*ISB", len: 2, cycles: 5, mode: .ZeroPage),
|
||||
OpCode(code: 0xf7, mnemonic: "*ISB", len: 2, cycles: 6, mode: .ZeroPage_X),
|
||||
OpCode(code: 0xef, mnemonic: "*ISB", len: 3, cycles: 6, mode: .Absolute),
|
||||
OpCode(code: 0xff, mnemonic: "*ISB", len: 3, cycles: 7, mode: .Absolute_X),
|
||||
OpCode(code: 0xfb, mnemonic: "*ISB", len: 3, cycles: 7, mode: .Absolute_Y),
|
||||
OpCode(code: 0xe3, mnemonic: "*ISB", len: 2, cycles: 8, mode: .Indirect_X),
|
||||
OpCode(code: 0xf3, mnemonic: "*ISB", len: 2, cycles: 8, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x02, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x12, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x22, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x32, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x42, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x52, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x62, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x72, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x92, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xb2, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xd2, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xf2, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0x1a, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x3a, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x5a, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0x7a, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xda, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
OpCode(code: 0xfa, mnemonic: "*NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||
|
||||
OpCode(code: 0xab, mnemonic: "*LXA", len: 2, cycles: 3, mode: .Immediate), //todo: highly unstable and not used
|
||||
OpCode(code: 0x8b, mnemonic: "*XAA", len: 2, cycles: 3, mode: .Immediate), //todo: highly unstable and not used
|
||||
OpCode(code: 0xbb, mnemonic: "*LAS", len: 3, cycles: 2, mode: .Absolute_Y), //todo: highly unstable and not used
|
||||
OpCode(code: 0x9b, mnemonic: "*TAS", len: 3, cycles: 2, mode: .Absolute_Y), //todo: highly unstable and not used
|
||||
OpCode(code: 0x93, mnemonic: "*AHX", len: 2, cycles: 8, mode: .Indirect_Y), //todo: highly unstable and not used
|
||||
OpCode(code: 0x9f, mnemonic: "*AHX", len: 3, cycles: 4, mode: .Absolute_Y), //todo: highly unstable and not used
|
||||
OpCode(code: 0x9e, mnemonic: "*SHX", len: 3, cycles: 4, mode: .Absolute_Y), //todo: highly unstable and not used
|
||||
OpCode(code: 0x9c, mnemonic: "*SHY", len: 3, cycles: 4, mode: .Absolute_X), //todo: highly unstable and not used
|
||||
|
||||
OpCode(code: 0xa7, mnemonic: "*LAX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0xb7, mnemonic: "*LAX", len: 2, cycles: 4, mode: .ZeroPage_Y),
|
||||
OpCode(code: 0xaf, mnemonic: "*LAX", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0xbf, mnemonic: "*LAX", len: 3, cycles: 4, mode: .Absolute_Y),
|
||||
OpCode(code: 0xa3, mnemonic: "*LAX", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
OpCode(code: 0xb3, mnemonic: "*LAX", len: 2, cycles: 5, mode: .Indirect_Y),
|
||||
|
||||
OpCode(code: 0x87, mnemonic: "*SAX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||
OpCode(code: 0x97, mnemonic: "*SAX", len: 2, cycles: 4, mode: .ZeroPage_Y),
|
||||
OpCode(code: 0x8f, mnemonic: "*SAX", len: 3, cycles: 4, mode: .Absolute),
|
||||
OpCode(code: 0x83, mnemonic: "*SAX", len: 2, cycles: 6, mode: .Indirect_X),
|
||||
]
|
||||
|
||||
let OPCODES_MAP: [UInt8: OpCode] = {
|
||||
var map: [UInt8:OpCode] = [:]
|
||||
for cpuop in CPU_OP_CODES {
|
||||
guard map[cpuop.code] == nil else {fatalError("Duplicate opcode \(cpuop) and \(map[cpuop.code]!)")}
|
||||
map[cpuop.code] = cpuop
|
||||
}
|
||||
return map
|
||||
}()
|
Reference in New Issue
Block a user