From 0d6988541ddf8e377af47694314de619f423f21a Mon Sep 17 00:00:00 2001 From: Luigi Thirty Date: Thu, 20 Jul 2017 02:50:28 -0400 Subject: [PATCH] cool --- FruitMachine.xcodeproj/project.pbxproj | 24 +++++ .../xcschemes/xcschememanagement.plist | 8 ++ FruitMachine/M6502/CPUInstructions.swift | 51 ++++++++++ FruitMachine/M6502/CPUState.swift | 93 +++++++++++++++++++ FruitMachine/M6502/MemoryInterface.swift | 17 ++++ FruitMachine/M6502/Opcodes.swift | 79 ++++++++++++++++ FruitMachine/ViewController.swift | 14 ++- 7 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 FruitMachine/M6502/CPUInstructions.swift create mode 100644 FruitMachine/M6502/CPUState.swift create mode 100644 FruitMachine/M6502/MemoryInterface.swift create mode 100644 FruitMachine/M6502/Opcodes.swift diff --git a/FruitMachine.xcodeproj/project.pbxproj b/FruitMachine.xcodeproj/project.pbxproj index cdbf47a..32f7053 100644 --- a/FruitMachine.xcodeproj/project.pbxproj +++ b/FruitMachine.xcodeproj/project.pbxproj @@ -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 = ""; }; 2AD458D61F205EB700F05121 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2AD458D71F205EB700F05121 /* FruitMachine.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FruitMachine.entitlements; sourceTree = ""; }; + 2AD458DE1F205F4500F05121 /* CPUState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUState.swift; sourceTree = ""; }; + 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryInterface.swift; sourceTree = ""; }; + 2AD458E21F20661300F05121 /* CPUInstructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUInstructions.swift; sourceTree = ""; }; + 2AD458E41F2070DF00F05121 /* Opcodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Opcodes.swift; sourceTree = ""; }; /* 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 = ""; }; + 2AD458DD1F205F0D00F05121 /* M6502 */ = { + isa = PBXGroup; + children = ( + 2AD458DE1F205F4500F05121 /* CPUState.swift */, + 2AD458E21F20661300F05121 /* CPUInstructions.swift */, + 2AD458E41F2070DF00F05121 /* Opcodes.swift */, + 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */, + ); + path = M6502; + sourceTree = ""; + }; /* 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; }; diff --git a/FruitMachine.xcodeproj/xcuserdata/luigi.xcuserdatad/xcschemes/xcschememanagement.plist b/FruitMachine.xcodeproj/xcuserdata/luigi.xcuserdatad/xcschemes/xcschememanagement.plist index 5c99a29..3b1ef1a 100644 --- a/FruitMachine.xcodeproj/xcuserdata/luigi.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/FruitMachine.xcodeproj/xcuserdata/luigi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,13 @@ 0 + SuppressBuildableAutocreation + + 2AD458C91F205EB700F05121 + + primary + + + diff --git a/FruitMachine/M6502/CPUInstructions.swift b/FruitMachine/M6502/CPUInstructions.swift new file mode 100644 index 0000000..13f71f6 --- /dev/null +++ b/FruitMachine/M6502/CPUInstructions.swift @@ -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), +] diff --git a/FruitMachine/M6502/CPUState.swift b/FruitMachine/M6502/CPUState.swift new file mode 100644 index 0000000..5e63dd4 --- /dev/null +++ b/FruitMachine/M6502/CPUState.swift @@ -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) + } +} diff --git a/FruitMachine/M6502/MemoryInterface.swift b/FruitMachine/M6502/MemoryInterface.swift new file mode 100644 index 0000000..1ff0820 --- /dev/null +++ b/FruitMachine/M6502/MemoryInterface.swift @@ -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) + } +} diff --git a/FruitMachine/M6502/Opcodes.swift b/FruitMachine/M6502/Opcodes.swift new file mode 100644 index 0000000..3f27cd0 --- /dev/null +++ b/FruitMachine/M6502/Opcodes.swift @@ -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(); + } +} diff --git a/FruitMachine/ViewController.swift b/FruitMachine/ViewController.swift index 051715b..1cf55c8 100644 --- a/FruitMachine/ViewController.swift +++ b/FruitMachine/ViewController.swift @@ -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. }