mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-11-21 09:46:23 -06:00
add LDA, STA, TAX, INX, BRK
This commit is contained in:
commit
f0b3dcd95b
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal 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
15
Package.swift
Normal 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
182
Sources/CPU.swift
Normal 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
7
Sources/main.swift
Normal 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
38
Sources/opcodes.swift
Normal 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
72
Tests/CPU.swift
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user