mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-09-19 08:51:02 -05:00
impl the rest of the instructions
This commit is contained in:
parent
f0b3dcd95b
commit
c441014511
14
Package.resolved
Normal file
14
Package.resolved
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "swiftsdl2",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/ctreffs/SwiftSDL2.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "30a2886bd68e43fc19ba29b63ffe230ac0e4db7a",
|
||||||
|
"version" : "1.4.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
|
}
|
@ -5,11 +5,18 @@ import PackageDescription
|
|||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "SwiftNES",
|
name: "SwiftNES",
|
||||||
|
platforms: [.macOS("11.0")],
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/ctreffs/SwiftSDL2.git", from: "1.4.0")
|
||||||
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||||
// Targets can depend on other targets in this package and products from dependencies.
|
// Targets can depend on other targets in this package and products from dependencies.
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "SwiftNES"),
|
name: "SwiftNES",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "SDL", package: "SwiftSDL2")
|
||||||
|
]),
|
||||||
.testTarget(name: "SwiftNESTests", dependencies: ["SwiftNES"])
|
.testTarget(name: "SwiftNESTests", dependencies: ["SwiftNES"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
enum AddressingMode {
|
enum AddressingMode {
|
||||||
case Immediate
|
case Immediate
|
||||||
case ZeroPage
|
case ZeroPage
|
||||||
@ -13,12 +11,28 @@ enum AddressingMode {
|
|||||||
case NoneAddressing
|
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 {
|
class CPU {
|
||||||
var register_a: UInt8 = 0
|
var register_a: UInt8 = 0
|
||||||
var register_x: UInt8 = 0
|
var register_x: UInt8 = 0
|
||||||
var register_y: UInt8 = 0
|
var register_y: UInt8 = 0
|
||||||
|
var stackPointer: UInt8 = STACK_RESET
|
||||||
var status: UInt8 = 0
|
var status: CPUFlags = [.interruptDisable, .break2]
|
||||||
var programCounter: UInt16 = 0
|
var programCounter: UInt16 = 0
|
||||||
private var memory = [UInt8](repeating: 0, count: 0xFFFF)
|
private var memory = [UInt8](repeating: 0, count: 0xFFFF)
|
||||||
|
|
||||||
@ -65,32 +79,12 @@ class CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func memRead(_ addr: UInt16) -> UInt8 {
|
|
||||||
memory[Int(addr)]
|
|
||||||
}
|
|
||||||
|
|
||||||
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: addr, data: lo)
|
|
||||||
self.memWrite(addr: addr + 1, data: hi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func memWrite(addr: UInt16, data: UInt8) {
|
|
||||||
memory[Int(addr)] = data
|
|
||||||
}
|
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
register_a = 0
|
register_a = 0
|
||||||
register_x = 0
|
register_x = 0
|
||||||
register_y = 0
|
register_y = 0
|
||||||
status = 0
|
stackPointer = STACK_RESET
|
||||||
|
status = [.interruptDisable, .break2]
|
||||||
|
|
||||||
programCounter = self.memReadU16(0xFFFC)
|
programCounter = self.memReadU16(0xFFFC)
|
||||||
}
|
}
|
||||||
@ -102,14 +96,21 @@ class CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func load(_ program: [UInt8]) {
|
func load(_ program: [UInt8]) {
|
||||||
memory[0x8000 ..< (0x8000 + program.count)] = program[0..<program.count]
|
memory[0x0600 ..< (0x0600 + program.count)] = program[0..<program.count]
|
||||||
memWriteU16(addr: 0xFFFC, data: 0x8000)
|
memWriteU16(0xFFFC, data: 0x0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
func run() {
|
func run() {
|
||||||
|
run {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(callback: () -> ()) {
|
||||||
let opcodes = OPCODES_MAP
|
let opcodes = OPCODES_MAP
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
|
callback()
|
||||||
let code = memRead(programCounter)
|
let code = memRead(programCounter)
|
||||||
programCounter += 1
|
programCounter += 1
|
||||||
|
|
||||||
@ -123,11 +124,151 @@ class CPU {
|
|||||||
/// STA
|
/// STA
|
||||||
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91:
|
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91:
|
||||||
sta(opcode.mode)
|
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 & 0x00ff)
|
||||||
|
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.remove(.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:
|
||||||
|
continue
|
||||||
|
case 0xa8:
|
||||||
|
register_y = register_x
|
||||||
|
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
|
/// TAX
|
||||||
case 0xAA:
|
case 0xaa:
|
||||||
tax()
|
tax()
|
||||||
/// INX
|
/// INX
|
||||||
case 0xE8:
|
case 0xe8:
|
||||||
inx()
|
inx()
|
||||||
/// BRK
|
/// BRK
|
||||||
case 0x00:
|
case 0x00:
|
||||||
@ -143,40 +284,136 @@ class CPU {
|
|||||||
|
|
||||||
func updateZeroAndNegativeFlags(_ result: UInt8) {
|
func updateZeroAndNegativeFlags(_ result: UInt8) {
|
||||||
if result == 0 {
|
if result == 0 {
|
||||||
status = status | 0b0000_0010
|
status.insert(.zero)
|
||||||
} else {
|
} else {
|
||||||
status = status & 0b1111_1101
|
status.remove(.zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result & 0b1000_0000 != 0 {
|
if result & 0b1000_0000 != 0 {
|
||||||
status = status | 0b1000_0000
|
status.insert(.negative)
|
||||||
} else {
|
} else {
|
||||||
status = status & 0b0111_1111
|
status.remove(.negative)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func lda(_ mode: AddressingMode) {
|
func setRegisterA(_ value: UInt8) {
|
||||||
let addr = getOpperandAddress(mode)
|
|
||||||
let value = memRead(addr)
|
|
||||||
|
|
||||||
register_a = value
|
register_a = value
|
||||||
updateZeroAndNegativeFlags(register_a)
|
updateZeroAndNegativeFlags(register_a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tax() {
|
func setCarryFlag() {
|
||||||
register_x = register_a
|
status.insert(.carry)
|
||||||
updateZeroAndNegativeFlags(register_x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func inx() {
|
func clearCarryFlag() {
|
||||||
register_x = register_x &+ 1
|
status.remove(.carry)
|
||||||
updateZeroAndNegativeFlags(register_x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sta(_ mode: AddressingMode) {
|
/// 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(sum)
|
||||||
|
|
||||||
|
if (data ^ result) & (result ^ register_a) & 0x80 != 0 {
|
||||||
|
status.insert(.overflow)
|
||||||
|
} else {
|
||||||
|
status.remove(.overflow)
|
||||||
|
}
|
||||||
|
|
||||||
|
setRegisterA(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 addr = getOpperandAddress(mode)
|
||||||
memWrite(addr: addr, data: register_a)
|
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)
|
||||||
|
// print(addr)
|
||||||
|
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 {
|
||||||
|
memory[Int(addr)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func memWrite(_ addr: UInt16, data: UInt8) {
|
||||||
|
memory[Int(addr)] = 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_Instructions.swift
Normal file
262
Sources/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(data) &* -1
|
||||||
|
addToRegisterA(UInt8(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.remove(.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
Sources/Util.swift
Normal file
0
Sources/Util.swift
Normal file
@ -1,7 +1,59 @@
|
|||||||
// The Swift Programming Language
|
// The Swift Programming Language
|
||||||
// https://docs.swift.org/swift-book
|
// https://docs.swift.org/swift-book
|
||||||
|
|
||||||
let cpu = CPU()
|
import SDL
|
||||||
let program: [UInt8] = [0xa9, 0xc0, 0xaa, 0xe8, 0x00]
|
|
||||||
cpu.loadAndRun(program)
|
guard SDL_Init(SDL_INIT_VIDEO) == 0 else {
|
||||||
print(cpu.status)
|
fatalError("SDL could not initialize! SDL_Error: \(String(cString: SDL_GetError()))")
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = SDL_CreateWindow(
|
||||||
|
"Snake Game",
|
||||||
|
Int32(SDL_WINDOWPOS_CENTERED_MASK), Int32(SDL_WINDOWPOS_CENTERED_MASK),
|
||||||
|
Int32(32.0 * 10.0), Int32(32.0 * 10.0),
|
||||||
|
SDL_WINDOW_SHOWN.rawValue)
|
||||||
|
|
||||||
|
let canvas = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC.rawValue)
|
||||||
|
|
||||||
|
SDL_RenderSetScale(canvas, 10.0, 10.0)
|
||||||
|
|
||||||
|
|
||||||
|
let gameCode: [UInt8] = [
|
||||||
|
0x20, 0x06, 0x06, 0x20, 0x38, 0x06, 0x20, 0x0d, 0x06, 0x20, 0x2a, 0x06, 0x60, 0xa9, 0x02, 0x85,
|
||||||
|
0x02, 0xa9, 0x04, 0x85, 0x03, 0xa9, 0x11, 0x85, 0x10, 0xa9, 0x10, 0x85, 0x12, 0xa9, 0x0f, 0x85,
|
||||||
|
0x14, 0xa9, 0x04, 0x85, 0x11, 0x85, 0x13, 0x85, 0x15, 0x60, 0xa5, 0xfe, 0x85, 0x00, 0xa5, 0xfe,
|
||||||
|
0x29, 0x03, 0x18, 0x69, 0x02, 0x85, 0x01, 0x60, 0x20, 0x4d, 0x06, 0x20, 0x8d, 0x06, 0x20, 0xc3,
|
||||||
|
0x06, 0x20, 0x19, 0x07, 0x20, 0x20, 0x07, 0x20, 0x2d, 0x07, 0x4c, 0x38, 0x06, 0xa5, 0xff, 0xc9,
|
||||||
|
0x77, 0xf0, 0x0d, 0xc9, 0x64, 0xf0, 0x14, 0xc9, 0x73, 0xf0, 0x1b, 0xc9, 0x61, 0xf0, 0x22, 0x60,
|
||||||
|
0xa9, 0x04, 0x24, 0x02, 0xd0, 0x26, 0xa9, 0x01, 0x85, 0x02, 0x60, 0xa9, 0x08, 0x24, 0x02, 0xd0,
|
||||||
|
0x1b, 0xa9, 0x02, 0x85, 0x02, 0x60, 0xa9, 0x01, 0x24, 0x02, 0xd0, 0x10, 0xa9, 0x04, 0x85, 0x02,
|
||||||
|
0x60, 0xa9, 0x02, 0x24, 0x02, 0xd0, 0x05, 0xa9, 0x08, 0x85, 0x02, 0x60, 0x60, 0x20, 0x94, 0x06,
|
||||||
|
0x20, 0xa8, 0x06, 0x60, 0xa5, 0x00, 0xc5, 0x10, 0xd0, 0x0d, 0xa5, 0x01, 0xc5, 0x11, 0xd0, 0x07,
|
||||||
|
0xe6, 0x03, 0xe6, 0x03, 0x20, 0x2a, 0x06, 0x60, 0xa2, 0x02, 0xb5, 0x10, 0xc5, 0x10, 0xd0, 0x06,
|
||||||
|
0xb5, 0x11, 0xc5, 0x11, 0xf0, 0x09, 0xe8, 0xe8, 0xe4, 0x03, 0xf0, 0x06, 0x4c, 0xaa, 0x06, 0x4c,
|
||||||
|
0x35, 0x07, 0x60, 0xa6, 0x03, 0xca, 0x8a, 0xb5, 0x10, 0x95, 0x12, 0xca, 0x10, 0xf9, 0xa5, 0x02,
|
||||||
|
0x4a, 0xb0, 0x09, 0x4a, 0xb0, 0x19, 0x4a, 0xb0, 0x1f, 0x4a, 0xb0, 0x2f, 0xa5, 0x10, 0x38, 0xe9,
|
||||||
|
0x20, 0x85, 0x10, 0x90, 0x01, 0x60, 0xc6, 0x11, 0xa9, 0x01, 0xc5, 0x11, 0xf0, 0x28, 0x60, 0xe6,
|
||||||
|
0x10, 0xa9, 0x1f, 0x24, 0x10, 0xf0, 0x1f, 0x60, 0xa5, 0x10, 0x18, 0x69, 0x20, 0x85, 0x10, 0xb0,
|
||||||
|
0x01, 0x60, 0xe6, 0x11, 0xa9, 0x06, 0xc5, 0x11, 0xf0, 0x0c, 0x60, 0xc6, 0x10, 0xa5, 0x10, 0x29,
|
||||||
|
0x1f, 0xc9, 0x1f, 0xf0, 0x01, 0x60, 0x4c, 0x35, 0x07, 0xa0, 0x00, 0xa5, 0xfe, 0x91, 0x00, 0x60,
|
||||||
|
0xa6, 0x03, 0xa9, 0x00, 0x81, 0x10, 0xa2, 0x00, 0xa9, 0x01, 0x81, 0x10, 0x60, 0xa2, 0x00, 0xea,
|
||||||
|
0xea, 0xca, 0xd0, 0xfb, 0x60
|
||||||
|
]
|
||||||
|
|
||||||
|
print(gameCode[32])
|
||||||
|
|
||||||
|
|
||||||
|
var cpu = CPU()
|
||||||
|
cpu.load(gameCode)
|
||||||
|
cpu.reset()
|
||||||
|
|
||||||
|
cpu.run {
|
||||||
|
// TODO:
|
||||||
|
// read user input and write it to mem[0xFF]
|
||||||
|
// update mem[0xFE] with new Random Number
|
||||||
|
// read mem mapped screen state
|
||||||
|
// render screen state
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -8,9 +8,133 @@ struct OpCode {
|
|||||||
|
|
||||||
let CPU_OP_CODES: [OpCode] = [
|
let CPU_OP_CODES: [OpCode] = [
|
||||||
OpCode(code: 0x00, mnemonic: "BRK", len: 1, cycles: 7, mode: .NoneAddressing),
|
OpCode(code: 0x00, mnemonic: "BRK", len: 1, cycles: 7, mode: .NoneAddressing),
|
||||||
OpCode(code: 0xaa, mnemonic: "TAX", len: 1, cycles: 2, mode: .NoneAddressing),
|
OpCode(code: 0xea, mnemonic: "NOP", len: 1, cycles: 2, mode: .NoneAddressing),
|
||||||
OpCode(code: 0xe8, mnemonic: "INX", 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: 2, 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: 0xa9, mnemonic: "LDA", len: 2, cycles: 2, mode: .Immediate),
|
||||||
OpCode(code: 0xa5, mnemonic: "LDA", len: 2, cycles: 3, mode: .ZeroPage),
|
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: 0xb5, mnemonic: "LDA", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||||
@ -20,18 +144,61 @@ let CPU_OP_CODES: [OpCode] = [
|
|||||||
OpCode(code: 0xa1, mnemonic: "LDA", len: 2, cycles: 6, mode: .Indirect_X),
|
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: 0xb1, mnemonic: "LDA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y),
|
||||||
|
|
||||||
OpCode(code: 0xa5, mnemonic: "STA", len: 2, cycles: 3, mode: .ZeroPage),
|
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: 0x95, mnemonic: "STA", len: 2, cycles: 4, mode: .ZeroPage_X),
|
||||||
OpCode(code: 0x8d, mnemonic: "STA", len: 3, cycles: 4, mode: .Absolute),
|
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: 0x9d, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_X),
|
||||||
OpCode(code: 0x99, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_Y),
|
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: 0x81, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_X),
|
||||||
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: 0x96, mnemonic: "STX", len: 2, cycles: 4, mode: .Absolute_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: .Absolute_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),
|
||||||
]
|
]
|
||||||
|
|
||||||
let OPCODES_MAP: [UInt8: OpCode] = {
|
let OPCODES_MAP: [UInt8: OpCode] = {
|
||||||
var map: [UInt8:OpCode] = [:]
|
var map: [UInt8:OpCode] = [:]
|
||||||
for cpuop in CPU_OP_CODES {
|
for cpuop in CPU_OP_CODES {
|
||||||
|
guard map[cpuop.code] == nil else {fatalError("Duplicate opcode \(cpuop) and \(map[cpuop.code]!)")}
|
||||||
map[cpuop.code] = cpuop
|
map[cpuop.code] = cpuop
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
|
@ -7,13 +7,13 @@ class CPUTests: XCTestCase {
|
|||||||
cpu.loadAndRun([0xa9, 0x05, 0x00])
|
cpu.loadAndRun([0xa9, 0x05, 0x00])
|
||||||
|
|
||||||
XCTAssertEqual(cpu.register_a, 0x05)
|
XCTAssertEqual(cpu.register_a, 0x05)
|
||||||
XCTAssert(cpu.status & 0b0000_0010 == 0b00)
|
XCTAssert(cpu.status.rawValue & 0b0000_0010 == 0b00)
|
||||||
XCTAssert(cpu.status & 0b1000_0000 == 0)
|
XCTAssert(cpu.status.rawValue & 0b1000_0000 == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_lda_from_memory() {
|
func test_lda_from_memory() {
|
||||||
let cpu = CPU()
|
let cpu = CPU()
|
||||||
cpu.memWrite(addr: 0x10, data: 0x55)
|
cpu.memWrite(0x10, data: 0x55)
|
||||||
|
|
||||||
cpu.loadAndRun([0xa5, 0x10, 0x00])
|
cpu.loadAndRun([0xa5, 0x10, 0x00])
|
||||||
|
|
||||||
@ -23,13 +23,13 @@ class CPUTests: XCTestCase {
|
|||||||
func test_0xa9_lda_zero_flag() {
|
func test_0xa9_lda_zero_flag() {
|
||||||
let cpu = CPU()
|
let cpu = CPU()
|
||||||
cpu.loadAndRun([0xa9, 0x00, 0x00])
|
cpu.loadAndRun([0xa9, 0x00, 0x00])
|
||||||
XCTAssert(cpu.status & 0b0000_0010 == 0b10)
|
XCTAssert(cpu.status.rawValue & 0b0000_0010 == 0b10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_0xa9_lda_neg_flag() {
|
func test_0xa9_lda_neg_flag() {
|
||||||
let cpu = CPU()
|
let cpu = CPU()
|
||||||
cpu.loadAndRun([0xa9, 0xFF, 0x00])
|
cpu.loadAndRun([0xa9, 0xFF, 0x00])
|
||||||
XCTAssert(cpu.status & 0b1000_0000 == 0b1000_0000)
|
XCTAssert(cpu.status.rawValue & 0b1000_0000 == 0b1000_0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_0xaa_tax_move_a_to_x() {
|
func test_0xaa_tax_move_a_to_x() {
|
||||||
|
Loading…
Reference in New Issue
Block a user