add LDA, STA, TAX, INX, BRK

This commit is contained in:
Candygoblen123 2024-05-10 23:06:45 -04:00
commit f0b3dcd95b
No known key found for this signature in database
GPG Key ID: 577DA64EBEF10385
6 changed files with 322 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

15
Package.swift Normal file
View File

@ -0,0 +1,15 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SwiftNES",
targets: [
// 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.
.executableTarget(
name: "SwiftNES"),
.testTarget(name: "SwiftNESTests", dependencies: ["SwiftNES"])
]
)

182
Sources/CPU.swift Normal file
View File

@ -0,0 +1,182 @@
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
}
class CPU {
var register_a: UInt8 = 0
var register_x: UInt8 = 0
var register_y: UInt8 = 0
var status: UInt8 = 0
var programCounter: UInt16 = 0
private var memory = [UInt8](repeating: 0, count: 0xFFFF)
func getOpperandAddress(_ mode: AddressingMode) -> UInt16 {
switch mode {
case .Immediate:
return programCounter
case .ZeroPage:
return UInt16(memRead(programCounter))
case .Absolute:
return memReadU16(programCounter)
case .ZeroPage_X:
let pos = memRead(programCounter)
let addr = pos &+ register_x
return UInt16(addr)
case .ZeroPage_Y:
let pos = memRead(programCounter)
let addr = pos &+ register_y
return UInt16(addr)
case .Absolute_X:
let base = memReadU16(programCounter)
return base &+ UInt16(register_x)
case .Absolute_Y:
let base = memReadU16(programCounter)
return base &+ UInt16(register_y)
case .Indirect_X:
let base = memRead(programCounter)
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(programCounter)
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
case .NoneAddressing:
fatalError("mode \(mode) is not implemented")
}
}
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() {
register_a = 0
register_x = 0
register_y = 0
status = 0
programCounter = self.memReadU16(0xFFFC)
}
func loadAndRun(_ program: [UInt8]) {
load(program)
reset()
run()
}
func load(_ program: [UInt8]) {
memory[0x8000 ..< (0x8000 + program.count)] = program[0..<program.count]
memWriteU16(addr: 0xFFFC, data: 0x8000)
}
func run() {
let opcodes = OPCODES_MAP
while true {
let code = memRead(programCounter)
programCounter += 1
let programCounterState = programCounter
guard let opcode = opcodes[code] else {fatalError("OpCode \(code) not recgonized!")}
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)
/// TAX
case 0xAA:
tax()
/// INX
case 0xE8:
inx()
/// BRK
case 0x00:
return
default: fatalError("TODO!")
}
if programCounterState == programCounter {
programCounter += UInt16(opcode.len - 1)
}
}
}
func updateZeroAndNegativeFlags(_ result: UInt8) {
if result == 0 {
status = status | 0b0000_0010
} else {
status = status & 0b1111_1101
}
if result & 0b1000_0000 != 0 {
status = status | 0b1000_0000
} else {
status = status & 0b0111_1111
}
}
func lda(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode)
let value = memRead(addr)
register_a = value
updateZeroAndNegativeFlags(register_a)
}
func tax() {
register_x = register_a
updateZeroAndNegativeFlags(register_x)
}
func inx() {
register_x = register_x &+ 1
updateZeroAndNegativeFlags(register_x)
}
func sta(_ mode: AddressingMode) {
let addr = getOpperandAddress(mode)
memWrite(addr: addr, data: register_a)
}
}

7
Sources/main.swift Normal file
View File

@ -0,0 +1,7 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
let cpu = CPU()
let program: [UInt8] = [0xa9, 0xc0, 0xaa, 0xe8, 0x00]
cpu.loadAndRun(program)
print(cpu.status)

38
Sources/opcodes.swift Normal file
View File

@ -0,0 +1,38 @@
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: 0xaa, mnemonic: "TAX", len: 1, cycles: 2, mode: .NoneAddressing),
OpCode(code: 0xe8, mnemonic: "INX", len: 1, cycles: 2, mode: .NoneAddressing),
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: 0xa5, 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),
]
let OPCODES_MAP: [UInt8: OpCode] = {
var map: [UInt8:OpCode] = [:]
for cpuop in CPU_OP_CODES {
map[cpuop.code] = cpuop
}
return map
}()

72
Tests/CPU.swift Normal file
View File

@ -0,0 +1,72 @@
import XCTest
@testable import SwiftNES
class CPUTests: XCTestCase {
func test_0xa9_lda_immediate_load_data() {
let cpu = CPU()
cpu.loadAndRun([0xa9, 0x05, 0x00])
XCTAssertEqual(cpu.register_a, 0x05)
XCTAssert(cpu.status & 0b0000_0010 == 0b00)
XCTAssert(cpu.status & 0b1000_0000 == 0)
}
func test_lda_from_memory() {
let cpu = CPU()
cpu.memWrite(addr: 0x10, data: 0x55)
cpu.loadAndRun([0xa5, 0x10, 0x00])
XCTAssertEqual(cpu.register_a, 0x55)
}
func test_0xa9_lda_zero_flag() {
let cpu = CPU()
cpu.loadAndRun([0xa9, 0x00, 0x00])
XCTAssert(cpu.status & 0b0000_0010 == 0b10)
}
func test_0xa9_lda_neg_flag() {
let cpu = CPU()
cpu.loadAndRun([0xa9, 0xFF, 0x00])
XCTAssert(cpu.status & 0b1000_0000 == 0b1000_0000)
}
func test_0xaa_tax_move_a_to_x() {
let cpu = CPU()
cpu.load([0xaa, 0x00])
cpu.reset()
cpu.register_a = 10
cpu.run()
XCTAssertEqual(cpu.register_x, 10)
}
func test_0xe8_inx_incriment_x() {
let cpu = CPU()
cpu.load([0xe8, 0x00])
cpu.reset()
cpu.register_x = 10
cpu.run()
XCTAssertEqual(cpu.register_x, 11)
}
func test_5_ops_working_together() {
let cpu = CPU()
cpu.loadAndRun([0xa9, 0xc0, 0xaa, 0xe8, 0x00])
XCTAssertEqual(cpu.register_x, 0xc1)
}
func test_inx_overflow() {
let cpu = CPU()
cpu.load([0xe8, 0xe8, 0x00])
cpu.reset()
cpu.register_x = 0xff
cpu.run()
XCTAssertEqual(cpu.register_x, 1)
}
}