This commit is contained in:
Luigi Thirty 2017-07-20 02:50:28 -04:00
parent 0c73b4751b
commit 0d6988541d
7 changed files with 285 additions and 1 deletions

View File

@ -11,6 +11,10 @@
2AD458D01F205EB700F05121 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* ViewController.swift */; };
2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; };
2AD458D51F205EB700F05121 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D31F205EB700F05121 /* Main.storyboard */; };
2AD458DF1F205F4500F05121 /* CPUState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458DE1F205F4500F05121 /* CPUState.swift */; };
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */; };
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E21F20661300F05121 /* CPUInstructions.swift */; };
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E41F2070DF00F05121 /* Opcodes.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -21,6 +25,10 @@
2AD458D41F205EB700F05121 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
2AD458D61F205EB700F05121 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2AD458D71F205EB700F05121 /* FruitMachine.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FruitMachine.entitlements; sourceTree = "<group>"; };
2AD458DE1F205F4500F05121 /* CPUState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUState.swift; sourceTree = "<group>"; };
2AD458E01F2064CB00F05121 /* MemoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryInterface.swift; sourceTree = "<group>"; };
2AD458E21F20661300F05121 /* CPUInstructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUInstructions.swift; sourceTree = "<group>"; };
2AD458E41F2070DF00F05121 /* Opcodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Opcodes.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -53,6 +61,7 @@
2AD458CC1F205EB700F05121 /* FruitMachine */ = {
isa = PBXGroup;
children = (
2AD458DD1F205F0D00F05121 /* M6502 */,
2AD458CD1F205EB700F05121 /* AppDelegate.swift */,
2AD458CF1F205EB700F05121 /* ViewController.swift */,
2AD458D11F205EB700F05121 /* Assets.xcassets */,
@ -63,6 +72,17 @@
path = FruitMachine;
sourceTree = "<group>";
};
2AD458DD1F205F0D00F05121 /* M6502 */ = {
isa = PBXGroup;
children = (
2AD458DE1F205F4500F05121 /* CPUState.swift */,
2AD458E21F20661300F05121 /* CPUInstructions.swift */,
2AD458E41F2070DF00F05121 /* Opcodes.swift */,
2AD458E01F2064CB00F05121 /* MemoryInterface.swift */,
);
path = M6502;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -133,8 +153,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */,
2AD458D01F205EB700F05121 /* ViewController.swift in Sources */,
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */,
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */,
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */,
2AD458DF1F205F4500F05121 /* CPUState.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -10,5 +10,13 @@
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>2AD458C91F205EB700F05121</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,51 @@
//
// CPUInstructions.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/20/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
enum AddressingMode {
case accumulator
case immediate
case implied
case relative
case absolute
case zeropage
case indirect
case absolute_indexed_x
case absolute_indexed_y
case zeropage_indexed_x
case zeropage_indexed_y
case indexed_indirect
case indirect_indexed
}
class CPUInstruction: NSObject {
let mnemonic: String //The mnemonic for this instruction.
let cycles: Int //How many cycles does this instruction take?
let bytes: Int //How many bytes long is this instruction?
let addressingMode: AddressingMode //The addressing mode of this instruction.
let action: (CPUState, AddressingMode) -> Void //A closure that describes this function's action.
init(mnemonic: String, cycles: Int, bytes: Int, addressingMode: AddressingMode, action: @escaping (CPUState, AddressingMode) -> Void) {
self.mnemonic = mnemonic
self.cycles = cycles
self.bytes = bytes
self.addressingMode = addressingMode
self.action = action
}
}
let InstructionTable: [UInt8:CPUInstruction] = [
0xA5: CPUInstruction.init(mnemonic: "LDA", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.LDA),
0xA9: CPUInstruction.init(mnemonic: "LDA", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.LDA),
0xAD: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.LDA),
0xB5: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.LDA),
0xB9: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.LDA),
0xBD: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.LDA),
]

View File

@ -0,0 +1,93 @@
//
// CPUState.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/19/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
enum CPUExceptions : Error {
case invalidInstruction
}
struct StatusRegister {
var negative: Bool //N - 0x80
var overflow: Bool //V - 0x40
// - 0x20
var brk: Bool //B - 0x10
var decimal: Bool //D - 0x08
var irq_disable: Bool //I - 0x04
var zero: Bool //Z - 0x02
var carry: Bool //C - 0x01
mutating func setState(state: UInt8) {
negative = (state & 0x80 == 0x80)
overflow = (state & 0x40 == 0x40)
brk = (state & 0x10 == 0x10)
decimal = (state & 0x08 == 0x08)
irq_disable = (state & 0x04 == 0x04)
zero = (state & 0x02 == 0x02)
carry = (state & 0x01 == 0x01)
}
}
class CPUState: NSObject {
static var sharedInstance = CPUState()
var cycles: Int
var instruction_register: UInt8
var accumulator: UInt8
var index_x: UInt8
var index_y: UInt8
var stack_pointer: UInt8
var program_counter: UInt16
var status_register: StatusRegister
var page_boundary_crossed: Bool
var memoryInterface: MemoryInterface
override init() {
cycles = 0
instruction_register = 0
accumulator = 0
index_x = 0
index_y = 0
stack_pointer = 0
program_counter = 0
status_register = StatusRegister(negative: false, overflow: false, brk: false, decimal: false, irq_disable: false, zero: false, carry: false)
memoryInterface = MemoryInterface()
page_boundary_crossed = false
}
func executeNextInstruction() throws {
instruction_register = memoryInterface.memory[Int(program_counter)]
let operation = InstructionTable[instruction_register]
if(operation == nil) {
throw CPUExceptions.invalidInstruction
}
operation!.action(CPUState.sharedInstance, operation!.addressingMode)
self.cycles += operation!.cycles
if(self.page_boundary_crossed) {
self.cycles += 1
self.page_boundary_crossed = false
}
}
func setNegativeFlag() {
status_register.negative = (accumulator & 0x80 == 0x80)
}
func setZeroFlag() {
status_register.zero = (accumulator == 0)
}
}

View File

@ -0,0 +1,17 @@
//
// MemoryInterface.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/20/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
class MemoryInterface: NSObject {
var memory: [UInt8]
override init() {
memory = [UInt8](repeating: 0x00, count: 65536)
}
}

View File

@ -0,0 +1,79 @@
//
// Opcodes.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/20/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
/* Addressing mode helper functions */
func PC_PLUS_1(state: CPUState) -> UInt16 {
return state.program_counter + 1
}
func OPERAND_IMMEDIATE(state: CPUState) -> UInt8 {
//Operand = PC+1
return MEMORY_READ_UINT8(state: state, address: state.program_counter + 1)
}
func OPERAND_ZEROPAGE_INDEXED_X(state: CPUState) -> UInt8 {
//Operand = (PC+1) + X
return MEMORY_READ_UINT8(state: state, address: (state.program_counter + 1) + UInt16(state.index_x))
}
func OPERAND_ZEROPAGE_INDEXED_Y(state: CPUState) -> UInt8 {
//Operand = (PC+1) + Y
return MEMORY_READ_UINT8(state: state, address: (state.program_counter + 1) + UInt16(state.index_y))
}
func OPERAND_ABSOLUTE(state: CPUState) -> UInt16 {
//Operand = L:(PC+1) H:(PC+2)
let low: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 1)
let high: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 2)
return UInt16(high << 8 | low)
}
func OPERAND_ABSOLUTE_INDEXED_X(state: CPUState) -> UInt16 {
//Operand = L:(PC+1)+X H:(PC+2)+X
let low: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 1 + UInt16(state.index_x))
let high: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 2 + UInt16(state.index_x))
return UInt16(high << 8 | low)
}
func OPERAND_ABSOLUTE_INDEXED_Y(state: CPUState) -> UInt16 {
//Operand = L:(PC+1)+Y H:(PC+2)+Y
let low: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 1 + UInt16(state.index_y))
let high: UInt8 = MEMORY_READ_UINT8(state: state, address: state.program_counter + 2 + UInt16(state.index_y))
return UInt16(high << 8 | low)
}
func MEMORY_READ_UINT8(state: CPUState, address: UInt16) -> UInt8 {
return state.memoryInterface.memory[Int(address)]
}
class Opcodes: NSObject {
static func LDA(state: CPUState, addressingMode: AddressingMode) -> Void {
switch addressingMode {
case .immediate:
state.accumulator = OPERAND_IMMEDIATE(state: state)
case .zeropage:
state.accumulator = OPERAND_ZEROPAGE_INDEXED_X(state: state)
case .zeropage_indexed_x:
state.accumulator = MEMORY_READ_UINT8(state: state, address: UInt16(0x0000 + OPERAND_ZEROPAGE_INDEXED_X(state: state)))
case .absolute:
state.accumulator = MEMORY_READ_UINT8(state: state, address: OPERAND_ABSOLUTE(state: state))
case .absolute_indexed_x:
state.accumulator = MEMORY_READ_UINT8(state: state, address: OPERAND_ABSOLUTE_INDEXED_X(state: state))
case .absolute_indexed_y:
state.accumulator = MEMORY_READ_UINT8(state: state, address: OPERAND_ABSOLUTE_INDEXED_Y(state: state))
default:
print("Unhandled addressing mode \(addressingMode) for LDA")
}
state.setZeroFlag();
state.setNegativeFlag();
}
}

View File

@ -9,10 +9,22 @@
import Cocoa
class ViewController: NSViewController {
let CPU = CPUState.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
CPU.memoryInterface.memory[0] = 0xAD
CPU.memoryInterface.memory[1] = 0x00
CPU.memoryInterface.memory[2] = 0x00
do {
try CPU.executeNextInstruction()
} catch CPUExceptions.invalidInstruction {
print("*** 6502 Exception: Invalid instruction 0xXX at 0xXXXX")
} catch {
print(error)
}
// Do any additional setup after loading the view.
}