more opcodes

This commit is contained in:
Luigi Thirty 2017-07-21 03:25:48 -04:00
parent 0d6988541d
commit 9bc97a7022
7 changed files with 318 additions and 70 deletions

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */; };
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CD1F205EB700F05121 /* AppDelegate.swift */; };
2AD458D01F205EB700F05121 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* ViewController.swift */; };
2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; };
@ -18,6 +19,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerExtensions.swift; sourceTree = "<group>"; };
2AD458CA1F205EB700F05121 /* FruitMachine.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FruitMachine.app; sourceTree = BUILT_PRODUCTS_DIR; };
2AD458CD1F205EB700F05121 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
2AD458CF1F205EB700F05121 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -79,6 +81,7 @@
2AD458E21F20661300F05121 /* CPUInstructions.swift */,
2AD458E41F2070DF00F05121 /* Opcodes.swift */,
2AD458E01F2064CB00F05121 /* MemoryInterface.swift */,
2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */,
);
path = M6502;
sourceTree = "<group>";
@ -156,6 +159,7 @@
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */,
2AD458D01F205EB700F05121 /* ViewController.swift in Sources */,
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */,
2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */,
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */,
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */,
2AD458DF1F205F4500F05121 /* CPUState.swift in Sources */,
@ -329,6 +333,7 @@
2AD458DC1F205EB700F05121 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};

View File

@ -41,11 +41,59 @@ class CPUInstruction: NSObject {
}
}
//indexed_indirect = LDA ($00,X)
//indirect_indexed = LDA ($00),Y
let InstructionTable: [UInt8:CPUInstruction] = [
0xA5: CPUInstruction.init(mnemonic: "LDA", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.LDA),
//LD instructions
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),
0xA5: CPUInstruction.init(mnemonic: "LDA", cycles: 3, bytes: 2, addressingMode: .zeropage, 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),
0xAD: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.LDA),
0xBD: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.LDA),
0xB9: CPUInstruction.init(mnemonic: "LDA", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.LDA),
0xA1: CPUInstruction.init(mnemonic: "LDA", cycles: 6, bytes: 2, addressingMode: .indexed_indirect, action: Opcodes.LDA),
0xB1: CPUInstruction.init(mnemonic: "LDA", cycles: 5, bytes: 2, addressingMode: .indirect_indexed, action: Opcodes.LDA),
0xA2: CPUInstruction.init(mnemonic: "LDX", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.LDX),
0xA6: CPUInstruction.init(mnemonic: "LDX", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.LDX),
0xB6: CPUInstruction.init(mnemonic: "LDX", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_y, action: Opcodes.LDX),
0xAE: CPUInstruction.init(mnemonic: "LDX", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.LDX),
0xBE: CPUInstruction.init(mnemonic: "LDX", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.LDX),
0xA0: CPUInstruction.init(mnemonic: "LDY", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.LDY),
0xA4: CPUInstruction.init(mnemonic: "LDY", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.LDY),
0xB4: CPUInstruction.init(mnemonic: "LDY", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.LDY),
0xAC: CPUInstruction.init(mnemonic: "LDY", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.LDY),
0xBC: CPUInstruction.init(mnemonic: "LDY", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.LDY),
//Register functions
0x88: CPUInstruction.init(mnemonic: "DEY", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.DEY),
0x8A: CPUInstruction.init(mnemonic: "TXA", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TXA),
0x98: CPUInstruction.init(mnemonic: "TYA", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TYA),
0xA8: CPUInstruction.init(mnemonic: "TAY", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TAY),
0xAA: CPUInstruction.init(mnemonic: "TAX", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TAX),
0xC8: CPUInstruction.init(mnemonic: "INY", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.INY),
0xCA: CPUInstruction.init(mnemonic: "DEX", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.DEX),
0xE8: CPUInstruction.init(mnemonic: "INX", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.INX),
//Processor flag instructions
0x18: CPUInstruction.init(mnemonic: "CLC", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.CLC),
0x38: CPUInstruction.init(mnemonic: "SEC", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.SEC),
0x58: CPUInstruction.init(mnemonic: "CLI", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.CLI),
0x78: CPUInstruction.init(mnemonic: "SEI", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.SEI),
0xB8: CPUInstruction.init(mnemonic: "CLV", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.CLV),
0xD8: CPUInstruction.init(mnemonic: "CLD", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.CLD),
0xF8: CPUInstruction.init(mnemonic: "SED", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.SED),
//Stack instructions
0x9A: CPUInstruction.init(mnemonic: "TXS", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TXS),
0xBA: CPUInstruction.init(mnemonic: "TSX", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TSX),
0x48: CPUInstruction.init(mnemonic: "PHA", cycles: 3, bytes: 1, addressingMode: .implied, action: Opcodes.PHA),
0x68: CPUInstruction.init(mnemonic: "PLA", cycles: 4, bytes: 1, addressingMode: .implied, action: Opcodes.PLA),
0x08: CPUInstruction.init(mnemonic: "PHP", cycles: 3, bytes: 1, addressingMode: .implied, action: Opcodes.PHP),
0x28: CPUInstruction.init(mnemonic: "PLP", cycles: 4, bytes: 1, addressingMode: .implied, action: Opcodes.PLP),
0xEA: CPUInstruction.init(mnemonic: "NOP", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.NOP),
]

View File

@ -31,6 +31,34 @@ struct StatusRegister {
zero = (state & 0x02 == 0x02)
carry = (state & 0x01 == 0x01)
}
func asByte() -> UInt8 {
var val: UInt8 = 0x00
if(negative) {
val |= 0x80
}
if(overflow) {
val |= 0x40
}
if(brk) {
val |= 0x10
}
if(decimal) {
val |= 0x08
}
if(irq_disable) {
val |= 0x04
}
if(zero) {
val |= 0x02
}
if(carry) {
val |= 0x01
}
return val
}
}
class CPUState: NSObject {
@ -67,6 +95,23 @@ class CPUState: NSObject {
page_boundary_crossed = false
}
func getOperandByte() -> UInt8 {
//Returns the operand byte after the current instruction byte.
return memoryInterface.readByte(offset: program_counter + 1)
}
func getOperandWord() -> UInt16 {
var word: UInt16
let low = memoryInterface.readByte(offset: program_counter + 1)
let high = memoryInterface.readByte(offset: program_counter + 2)
word = UInt16(high)
word = word << 8
word |= UInt16(low)
return word
}
func executeNextInstruction() throws {
instruction_register = memoryInterface.memory[Int(program_counter)]
let operation = InstructionTable[instruction_register]
@ -81,13 +126,16 @@ class CPUState: NSObject {
self.cycles += 1
self.page_boundary_crossed = false
}
self.program_counter = UInt16(Int(self.program_counter) + operation!.bytes)
}
func setNegativeFlag() {
func updateNegativeFlag() {
status_register.negative = (accumulator & 0x80 == 0x80)
}
func setZeroFlag() {
func updateZeroFlag() {
status_register.zero = (accumulator == 0)
}
}

View File

@ -0,0 +1,15 @@
//
// IntegerExtensions.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/20/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
extension UInt16 {
static func + (left: UInt16, right: UInt8) -> UInt16 {
return left + right
}
}

View File

@ -14,4 +14,16 @@ class MemoryInterface: NSObject {
override init() {
memory = [UInt8](repeating: 0x00, count: 65536)
}
func readByte(offset: UInt16) -> UInt8 {
return memory[Int(offset)]
}
func writeByte(offset: UInt16, value: UInt8) {
memory[Int(offset)] = value
}
func readWord(offset: UInt16) -> UInt16 {
return UInt16(memory[Int(offset)] | (memory[Int(offset+1)] << 8))
}
}

View File

@ -8,72 +8,188 @@
import Cocoa
/* Addressing mode helper functions */
func PC_PLUS_1(state: CPUState) -> UInt16 {
return state.program_counter + 1
func getOperand(state: CPUState, mode: AddressingMode) -> UInt8 {
switch (mode) {
case .immediate:
return state.getOperandByte()
case .zeropage:
return state.memoryInterface.readByte(offset: UInt16(0x0000 + state.getOperandByte()))
case .zeropage_indexed_x:
return state.memoryInterface.readByte(offset: UInt16(state.getOperandByte() + state.index_x) & 0x00FF)
case .zeropage_indexed_y:
return state.memoryInterface.readByte(offset: UInt16(state.getOperandByte() + state.index_y) & 0x00FF)
case .absolute:
let word: UInt16 = state.getOperandWord()
return state.memoryInterface.readByte(offset: word)
case .absolute_indexed_x:
return state.memoryInterface.readByte(offset: state.getOperandWord() + UInt16(state.index_x))
case .absolute_indexed_y:
return state.memoryInterface.readByte(offset: state.getOperandWord() + UInt16(state.index_y))
case .indexed_indirect:
let zp: UInt8 = state.memoryInterface.readByte(offset: UInt16(state.getOperandByte() + state.index_x))
//read from (ZP)
let pointer: UInt16 = state.memoryInterface.readWord(offset: UInt16(zp))
state.accumulator = state.memoryInterface.readByte(offset: pointer)
case .indirect_indexed:
let zp: UInt8 = state.memoryInterface.readByte(offset: UInt16(state.getOperandByte()))
let pointer: UInt16 = state.memoryInterface.readWord(offset: UInt16(zp)) + UInt16(state.index_y)
state.accumulator = state.memoryInterface.readByte(offset: pointer)
case .indirect:
//JMP is the only instruction that does this - handle it specially since it's a UInt16
break
default:
print("Called getOperand on an instruction in addressing mode \(mode)")
return 0
}
return 0 //never gets here
}
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();
state.accumulator = getOperand(state: state, mode: addressingMode)
state.updateZeroFlag()
state.updateNegativeFlag()
}
static func LDX(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_x = getOperand(state: state, mode: addressingMode)
state.updateZeroFlag()
state.updateNegativeFlag()
}
static func LDY(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_y = getOperand(state: state, mode: addressingMode)
state.updateZeroFlag()
state.updateNegativeFlag()
}
//Register instructions
static func TAX(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_x = state.accumulator
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func TXA(state: CPUState, addressingMode: AddressingMode) -> Void {
state.accumulator = state.index_x
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func DEX(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_x = state.index_x &- 1
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func INX(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_x = state.index_x &+ 1
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func TAY(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_y = state.accumulator
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func TYA(state: CPUState, addressingMode: AddressingMode) -> Void {
state.accumulator = state.index_y
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func DEY(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_y = state.index_x &- 1
state.updateZeroFlag();
state.updateNegativeFlag();
}
static func INY(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_y = state.index_x &+ 1
state.updateZeroFlag();
state.updateNegativeFlag();
}
//Processor flag instructions
static func CLC(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.carry = false
}
static func SEC(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.carry = true
}
static func CLI(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.irq_disable = false
}
static func SEI(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.irq_disable = true
}
static func CLV(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.overflow = false
}
static func CLD(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.decimal = false
}
static func SED(state: CPUState, addressingMode: AddressingMode) -> Void {
state.status_register.carry = true
}
//Stack instructions
static func TXS(state: CPUState, addressingMode: AddressingMode) -> Void {
state.stack_pointer = state.index_x
}
static func TSX(state: CPUState, addressingMode: AddressingMode) -> Void {
state.index_x = state.stack_pointer
}
static func PHA(state: CPUState, addressingMode: AddressingMode) -> Void {
state.memoryInterface.writeByte(offset: 0x0100 | UInt16(state.stack_pointer), value: state.accumulator)
state.stack_pointer = state.stack_pointer &- 1
}
static func PLA(state: CPUState, addressingMode: AddressingMode) -> Void {
state.stack_pointer = state.stack_pointer &+ 1
state.accumulator = state.memoryInterface.readByte(offset: 0x0100 | UInt16(state.stack_pointer))
}
static func PHP(state: CPUState, addressingMode: AddressingMode) -> Void {
state.memoryInterface.writeByte(offset: 0x0100 | UInt16(state.stack_pointer), value: state.status_register.asByte())
state.stack_pointer = state.stack_pointer &- 1
}
static func PLP(state: CPUState, addressingMode: AddressingMode) -> Void {
state.stack_pointer = state.stack_pointer &+ 1
state.status_register.setState(state: state.memoryInterface.readByte(offset: 0x0100 | UInt16(state.stack_pointer)))
}
static func NOP(state: CPUState, addressingMode: AddressingMode) -> Void {}
}

View File

@ -15,11 +15,15 @@ class ViewController: NSViewController {
super.viewDidLoad()
CPU.memoryInterface.memory[0] = 0xAD
CPU.memoryInterface.memory[1] = 0x00
CPU.memoryInterface.memory[2] = 0x00
CPU.memoryInterface.memory[1] = 0x34
CPU.memoryInterface.memory[2] = 0x12
CPU.memoryInterface.memory[0x1234] = 0xAA
do {
try CPU.executeNextInstruction()
try CPU.executeNextInstruction()
} catch CPUExceptions.invalidInstruction {
print("*** 6502 Exception: Invalid instruction 0xXX at 0xXXXX")
} catch {