mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-11-23 01:46:46 -06:00
implement undocumented opcodes
This commit is contained in:
parent
af72573d13
commit
5f770589c8
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ DerivedData/
|
|||||||
.netrc
|
.netrc
|
||||||
|
|
||||||
*.nes
|
*.nes
|
||||||
|
*.log
|
||||||
|
@ -37,46 +37,57 @@ class CPU {
|
|||||||
var stackPointer: UInt8 = STACK_RESET
|
var stackPointer: UInt8 = STACK_RESET
|
||||||
var status: CPUFlags = [.interruptDisable, .break2]
|
var status: CPUFlags = [.interruptDisable, .break2]
|
||||||
var programCounter: UInt16 = 0
|
var programCounter: UInt16 = 0
|
||||||
var bus: Bus?
|
var bus: Bus
|
||||||
|
|
||||||
|
init(bus: Bus) {
|
||||||
|
self.bus = bus
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func getOpperandAddress(_ mode: AddressingMode) -> UInt16 {
|
func getOpperandAddress(_ mode: AddressingMode) -> UInt16 {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Immediate:
|
case .Immediate:
|
||||||
return programCounter
|
return programCounter
|
||||||
|
default:
|
||||||
|
return getAbsoluteAddress(mode, addr: programCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAbsoluteAddress(_ mode: AddressingMode, addr: UInt16) -> UInt16 {
|
||||||
|
switch mode {
|
||||||
case .ZeroPage:
|
case .ZeroPage:
|
||||||
return UInt16(memRead(programCounter))
|
return UInt16(memRead(addr))
|
||||||
case .Absolute:
|
case .Absolute:
|
||||||
return memReadU16(programCounter)
|
return memReadU16(addr)
|
||||||
case .ZeroPage_X:
|
case .ZeroPage_X:
|
||||||
let pos = memRead(programCounter)
|
let pos = memRead(addr)
|
||||||
let addr = pos &+ register_x
|
let addr = pos &+ register_x
|
||||||
return UInt16(addr)
|
return UInt16(addr)
|
||||||
case .ZeroPage_Y:
|
case .ZeroPage_Y:
|
||||||
let pos = memRead(programCounter)
|
let pos = memRead(addr)
|
||||||
let addr = pos &+ register_y
|
let addr = pos &+ register_y
|
||||||
return UInt16(addr)
|
return UInt16(addr)
|
||||||
case .Absolute_X:
|
case .Absolute_X:
|
||||||
let base = memReadU16(programCounter)
|
let base = memReadU16(addr)
|
||||||
return base &+ UInt16(register_x)
|
return base &+ UInt16(register_x)
|
||||||
case .Absolute_Y:
|
case .Absolute_Y:
|
||||||
let base = memReadU16(programCounter)
|
let base = memReadU16(addr)
|
||||||
return base &+ UInt16(register_y)
|
return base &+ UInt16(register_y)
|
||||||
case .Indirect_X:
|
case .Indirect_X:
|
||||||
let base = memRead(programCounter)
|
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)
|
||||||
case .Indirect_Y:
|
case .Indirect_Y:
|
||||||
let base = memRead(programCounter)
|
let base = memRead(addr)
|
||||||
|
|
||||||
let lo = memRead(UInt16(base))
|
let lo = memRead(UInt16(base))
|
||||||
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
|
||||||
case .NoneAddressing:
|
default:
|
||||||
fatalError("mode \(mode) is not implemented")
|
fatalError("mode \(mode) is not implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,17 +102,21 @@ class CPU {
|
|||||||
programCounter = self.memReadU16(0xFFFC)
|
programCounter = self.memReadU16(0xFFFC)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAndRun(_ program: [UInt8]) {
|
//func loadAndRun(_ program: [UInt8]) {
|
||||||
load(program)
|
//load(program)
|
||||||
reset()
|
//reset()
|
||||||
run()
|
//run()
|
||||||
}
|
//}
|
||||||
|
|
||||||
func load(_ program: [UInt8]) {
|
//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]
|
//memory[0x0600 ..< (0x0600 + program.count)] = program[0..<program.count]
|
||||||
bus = Bus(try! Rom(program))
|
|
||||||
memWriteU16(0xFFFC, data: 0x0600)
|
//memWriteU16(0xFFFC, data: 0x0600)
|
||||||
}
|
//}
|
||||||
|
|
||||||
func run() {
|
func run() {
|
||||||
run(onCycle: {}, onComplete: {})
|
run(onCycle: {}, onComplete: {})
|
||||||
@ -208,7 +223,7 @@ class CPU {
|
|||||||
let indirectRef: UInt16
|
let indirectRef: UInt16
|
||||||
if memAddr & 0x00ff == 0x00ff {
|
if memAddr & 0x00ff == 0x00ff {
|
||||||
let lo = memRead(memAddr)
|
let lo = memRead(memAddr)
|
||||||
let hi = memRead(memAddr & 0x00ff)
|
let hi = memRead(memAddr & 0xff00)
|
||||||
indirectRef = UInt16(hi) << 8 | UInt16(lo)
|
indirectRef = UInt16(hi) << 8 | UInt16(lo)
|
||||||
} else {
|
} else {
|
||||||
indirectRef = memReadU16(memAddr)
|
indirectRef = memReadU16(memAddr)
|
||||||
@ -225,7 +240,7 @@ class CPU {
|
|||||||
case 0x40:
|
case 0x40:
|
||||||
status.rawValue = stackPop()
|
status.rawValue = stackPop()
|
||||||
status.remove(.break1)
|
status.remove(.break1)
|
||||||
status.remove(.break2)
|
status.insert(.break2)
|
||||||
|
|
||||||
programCounter = stackPopU16()
|
programCounter = stackPopU16()
|
||||||
case 0xd0:
|
case 0xd0:
|
||||||
@ -259,7 +274,7 @@ class CPU {
|
|||||||
case 0xea:
|
case 0xea:
|
||||||
return
|
return
|
||||||
case 0xa8:
|
case 0xa8:
|
||||||
register_y = register_x
|
register_y = register_a
|
||||||
updateZeroAndNegativeFlags(register_y)
|
updateZeroAndNegativeFlags(register_y)
|
||||||
case 0xba:
|
case 0xba:
|
||||||
register_x = stackPointer
|
register_x = stackPointer
|
||||||
@ -282,6 +297,67 @@ class CPU {
|
|||||||
case 0x00:
|
case 0x00:
|
||||||
timer.invalidate()
|
timer.invalidate()
|
||||||
onComplete()
|
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!")
|
default: fatalError("TODO!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +418,22 @@ class CPU {
|
|||||||
setRegisterA(result)
|
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 {
|
func stackPop() -> UInt8 {
|
||||||
stackPointer = stackPointer &+ 1
|
stackPointer = stackPointer &+ 1
|
||||||
return memRead(STACK + UInt16(stackPointer))
|
return memRead(STACK + UInt16(stackPointer))
|
||||||
@ -391,19 +483,19 @@ class CPU {
|
|||||||
|
|
||||||
extension CPU: Memory {
|
extension CPU: Memory {
|
||||||
func memRead(_ addr: UInt16) -> UInt8 {
|
func memRead(_ addr: UInt16) -> UInt8 {
|
||||||
return bus!.memRead(addr)
|
return bus.memRead(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memWrite(_ addr: UInt16, data: UInt8) {
|
func memWrite(_ addr: UInt16, data: UInt8) {
|
||||||
bus!.memWrite(addr, data: data)
|
bus.memWrite(addr, data: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memReadU16(_ addr: UInt16) -> UInt16 {
|
func memReadU16(_ addr: UInt16) -> UInt16 {
|
||||||
return bus!.memReadU16(addr)
|
return bus.memReadU16(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memWriteU16(_ addr: UInt16, data: UInt16) {
|
func memWriteU16(_ addr: UInt16, data: UInt16) {
|
||||||
bus!.memWriteU16(addr, data: data)
|
bus.memWriteU16(addr, data: data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ extension CPU {
|
|||||||
func plp() {
|
func plp() {
|
||||||
status.rawValue = stackPop()
|
status.rawValue = stackPop()
|
||||||
status.remove(.break1)
|
status.remove(.break1)
|
||||||
status.remove(.break2)
|
status.insert(.break2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func php() {
|
func php() {
|
||||||
|
107
Sources/Trace.swift
Normal file
107
Sources/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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -94,21 +94,23 @@ func readScreenState(_ cpu: CPU, frame: inout [UInt8]) -> Bool {
|
|||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let rom = NSData(contentsOfFile: "snake.nes") else { fatalError("Rom not found") }
|
guard let rom = NSData(contentsOfFile: "nestest.nes") else { fatalError("Rom not found") }
|
||||||
var gameCode = [UInt8](repeating: 0, count: rom.length)
|
var gameCode = [UInt8](repeating: 0, count: rom.length)
|
||||||
rom.getBytes(&gameCode, length: rom.length)
|
rom.getBytes(&gameCode, length: rom.length)
|
||||||
|
|
||||||
|
let bus = Bus(try! Rom(gameCode))
|
||||||
|
|
||||||
var cpu = CPU()
|
var cpu = CPU(bus: bus)
|
||||||
cpu.load(gameCode)
|
//cpu.load(gameCode)
|
||||||
cpu.reset()
|
cpu.reset()
|
||||||
|
cpu.programCounter = 0xC000
|
||||||
|
|
||||||
var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32)
|
var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32)
|
||||||
var rng = SystemRandomNumberGenerator()
|
var rng = SystemRandomNumberGenerator()
|
||||||
|
|
||||||
cpu.run(onCycle: {
|
cpu.run(onCycle: {
|
||||||
|
print(dumpCpuState(cpu))
|
||||||
handleUserInput(cpu, event: &event)
|
handleUserInput(cpu, event: &event)
|
||||||
cpu.memWrite(0xfe, data: UInt8.random(in: 1...16, using: &rng))
|
//cpu.memWrite(0xfe, data: UInt8.random(in: 1...16, using: &rng))
|
||||||
|
|
||||||
if readScreenState(cpu, frame: &screenState) {
|
if readScreenState(cpu, frame: &screenState) {
|
||||||
SDL_UpdateTexture(texture, nil, screenState, 32 * 3)
|
SDL_UpdateTexture(texture, nil, screenState, 32 * 3)
|
||||||
|
@ -16,7 +16,7 @@ let CPU_OP_CODES: [OpCode] = [
|
|||||||
OpCode(code: 0x75, mnemonic: "ADC", len: 2, cycles: 4, mode: .ZeroPage_X),
|
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: 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: 0x7d, mnemonic: "ADC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X),
|
||||||
OpCode(code: 0x79, mnemonic: "ADC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Absolute_Y),
|
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: 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: 0x71, mnemonic: "ADC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||||
|
|
||||||
@ -165,11 +165,11 @@ let CPU_OP_CODES: [OpCode] = [
|
|||||||
OpCode(code: 0x91, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_Y),
|
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: 0x86, mnemonic: "STX", len: 2, cycles: 3, mode: .ZeroPage),
|
||||||
OpCode(code: 0x96, mnemonic: "STX", len: 2, cycles: 4, mode: .Absolute_Y),
|
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: 0x8e, mnemonic: "STX", len: 3, cycles: 4, mode: .Absolute),
|
||||||
|
|
||||||
OpCode(code: 0x84, mnemonic: "STY", len: 2, cycles: 3, mode: .ZeroPage),
|
OpCode(code: 0x84, mnemonic: "STY", len: 2, cycles: 3, mode: .ZeroPage),
|
||||||
OpCode(code: 0x94, mnemonic: "STY", len: 2, cycles: 4, mode: .Absolute_X),
|
OpCode(code: 0x94, mnemonic: "STY", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||||
OpCode(code: 0x8c, mnemonic: "STY", len: 3, cycles: 4, mode: .Absolute),
|
OpCode(code: 0x8c, mnemonic: "STY", len: 3, cycles: 4, mode: .Absolute),
|
||||||
|
|
||||||
/// Flag Clears
|
/// Flag Clears
|
||||||
@ -193,6 +193,135 @@ let CPU_OP_CODES: [OpCode] = [
|
|||||||
OpCode(code: 0x68, mnemonic: "PLA", len: 1, cycles: 4, 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: 0x08, mnemonic: "PHP", len: 1, cycles: 3, mode: .NoneAddressing),
|
||||||
OpCode(code: 0x28, mnemonic: "PLP", len: 1, cycles: 4, 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] = {
|
let OPCODES_MAP: [UInt8: OpCode] = {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import SwiftNES
|
@testable import SwiftNES
|
||||||
|
|
||||||
|
/*
|
||||||
class CPUTests: XCTestCase {
|
class CPUTests: XCTestCase {
|
||||||
func test_lda() {
|
func test_lda() {
|
||||||
let cpu = CPU()
|
let cpu = CPU()
|
||||||
@ -364,3 +365,4 @@ extension CPU {
|
|||||||
expect.wait(sender)
|
expect.wait(sender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
71
Tests/TestRom.swift
Normal file
71
Tests/TestRom.swift
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import SwiftNES
|
||||||
|
|
||||||
|
class TestRom: XCTestCase {
|
||||||
|
func testFormatTrace() {
|
||||||
|
let expect = XCTestExpectation()
|
||||||
|
let bus = Bus(try! Rom(getRom()))
|
||||||
|
bus.memWrite(100, data: 0xa2)
|
||||||
|
bus.memWrite(101, data: 0x01)
|
||||||
|
bus.memWrite(102, data: 0xca)
|
||||||
|
bus.memWrite(103, data: 0x88)
|
||||||
|
bus.memWrite(104, data: 0x00)
|
||||||
|
let cpu = CPU(bus: bus)
|
||||||
|
cpu.programCounter = 0x64
|
||||||
|
cpu.register_a = 1
|
||||||
|
cpu.register_x = 2
|
||||||
|
cpu.register_y = 3
|
||||||
|
|
||||||
|
var result: [String] = []
|
||||||
|
cpu.run(
|
||||||
|
onCycle: { result.append(dumpCpuState(cpu)) },
|
||||||
|
onComplete: { expect.fulfill() }
|
||||||
|
)
|
||||||
|
wait(for: [expect], timeout: 0.5)
|
||||||
|
XCTAssertEqual(
|
||||||
|
"0064 A2 01 LDX #$01 A:01 X:02 Y:03 P:24 SP:FD",
|
||||||
|
result[0]
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
"0066 CA DEX A:01 X:01 Y:03 P:24 SP:FD",
|
||||||
|
result[1]
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
"0067 88 DEY A:01 X:00 Y:03 P:26 SP:FD",
|
||||||
|
result[2]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFormatMemAccess() {
|
||||||
|
let expect = XCTestExpectation()
|
||||||
|
let bus = Bus(try! Rom(getRom()))
|
||||||
|
bus.memWrite(100, data: 0x11)
|
||||||
|
bus.memWrite(101, data: 0x33
|
||||||
|
)
|
||||||
|
bus.memWrite(0x33, data: 00)
|
||||||
|
bus.memWrite(0x34, data: 04)
|
||||||
|
|
||||||
|
bus.memWrite(0x400, data: 0xAA)
|
||||||
|
let cpu = CPU(bus: bus)
|
||||||
|
cpu.programCounter = 0x64
|
||||||
|
cpu.register_y = 0
|
||||||
|
|
||||||
|
var result: [String] = []
|
||||||
|
cpu.run(
|
||||||
|
onCycle: { result.append(dumpCpuState(cpu)) },
|
||||||
|
onComplete: { expect.fulfill() }
|
||||||
|
)
|
||||||
|
wait(for: [expect], timeout: 0.5)
|
||||||
|
XCTAssertEqual(
|
||||||
|
"0064 11 33 ORA ($33),Y = 0400 @ 0400 = AA A:00 X:00 Y:00 P:24 SP:FD",
|
||||||
|
result[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRom() -> [UInt8] {
|
||||||
|
guard let rom = NSData(contentsOfFile: "nestest.nes") else { fatalError("Rom not found") }
|
||||||
|
var gameCode = [UInt8](repeating: 0, count: rom.length)
|
||||||
|
rom.getBytes(&gameCode, length: rom.length)
|
||||||
|
return gameCode;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user