FruitMachine-Swift/FruitMachine/M6502/CPU.swift

284 lines
6.8 KiB
Swift
Raw Normal View History

2017-07-20 06:50:28 +00:00
//
// 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
func asString() -> String {
var str = ""
if(negative) {
str += "N"
} else {
str += "-"
}
if(overflow) {
str += "V"
} else {
str += "-"
}
str += "|" //0x20 is unassigned
if(brk) {
str += "B"
} else {
str += "-"
}
if(decimal) {
str += "D"
} else {
str += "-"
}
if(irq_disable) {
str += "I"
} else {
str += "-"
}
if(zero) {
str += "Z"
} else {
str += "-"
}
if(carry) {
str += "C"
} else {
str += "-"
}
return str
}
2017-07-22 02:13:53 +00:00
mutating func fromByte(state: UInt8) {
2017-07-20 06:50:28 +00:00
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)
}
2017-07-21 07:25:48 +00:00
func asByte() -> UInt8 {
2017-07-24 00:40:48 +00:00
var val: UInt8 = 0x20 //unused bit is hardwired to 1
2017-07-21 07:25:48 +00:00
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
}
2017-07-20 06:50:28 +00:00
}
final class CPU: NSObject {
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
}
2017-07-24 00:40:48 +00:00
let NMI_VECTOR: UInt16 = 0xFFFA
let RESET_VECTOR: UInt16 = 0xFFFC
let IRQ_VECTOR: UInt16 = 0xFFFE
2017-07-27 23:22:13 +00:00
static let sharedInstance = CPU()
var isRunning: Bool
2017-07-20 06:50:28 +00:00
var cycles: Int
var cyclesInBatch: Int
2017-07-20 06:50:28 +00:00
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
2017-07-24 07:29:12 +00:00
var branch_was_taken: Bool
2017-07-20 06:50:28 +00:00
var memoryInterface: MemoryInterface
var breakpoints: [UInt16]
2017-07-20 06:50:28 +00:00
override init() {
2017-07-27 23:22:13 +00:00
isRunning = false
2017-07-20 06:50:28 +00:00
cycles = 0
cyclesInBatch = 0
2017-07-20 06:50:28 +00:00
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()
//Some instructions incur a 1-cycle penalty if crossing a page boundary.
2017-07-20 06:50:28 +00:00
page_boundary_crossed = false
//Branches incur a 1-cycle penalty if taken plus the page boundary penalty if necessary.
branch_was_taken = false
breakpoints = [UInt16]()
2017-07-20 06:50:28 +00:00
}
2017-07-21 07:25:48 +00:00
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
}
2017-07-22 19:46:02 +00:00
func getInstructionBytes(atAddress: UInt16) -> [UInt8] {
var bytes = [UInt8]()
let instruction = memoryInterface.readByte(offset: atAddress)
let operation = InstructionTable[instruction]
if(operation != nil){
for offset in 0...operation!.bytes {
bytes.append(memoryInterface.readByte(offset: atAddress + UInt16(offset)))
}
}
return bytes
}
2017-07-21 07:25:48 +00:00
final func executeNextInstruction() throws {
2017-07-22 02:13:53 +00:00
instruction_register = memoryInterface.readByte(offset: program_counter)
2017-07-20 06:50:28 +00:00
let operation = InstructionTable[instruction_register]
if(operation == nil) {
throw CPUExceptions.invalidInstruction
}
2017-07-24 07:29:12 +00:00
operation!.action(CPU.sharedInstance, operation!.addressingMode)
2017-07-20 06:50:28 +00:00
self.cycles += operation!.cycles
if(self.page_boundary_crossed) {
self.cycles += 1
self.page_boundary_crossed = false
}
if(self.branch_was_taken) {
self.cycles += 1
self.branch_was_taken = false
}
2017-07-21 07:25:48 +00:00
2017-07-31 21:09:37 +00:00
self.program_counter = UInt16(Int(self.program_counter) + operation!.bytes)
2017-07-20 06:50:28 +00:00
}
2017-07-27 23:22:13 +00:00
func outOfCycles() -> Bool {
if(cycles > cyclesInBatch) {
return true
} else {
return false
}
}
2017-07-24 00:40:48 +00:00
func performReset() {
program_counter = memoryInterface.readWord(offset: RESET_VECTOR)
stack_pointer = 0xFF
status_register.irq_disable = true
}
2017-07-22 02:13:53 +00:00
func updateNegativeFlag(value: UInt8) {
status_register.negative = (value & 0x80 == 0x80)
2017-07-20 06:50:28 +00:00
}
2017-07-22 02:13:53 +00:00
func updateZeroFlag(value: UInt8) {
status_register.zero = (value == 0)
2017-07-20 06:50:28 +00:00
}
2017-07-21 07:25:48 +00:00
2017-07-27 23:22:13 +00:00
/* Running */
final func cpuStep() {
2017-07-27 23:22:13 +00:00
do {
try executeNextInstruction()
} catch CPUExceptions.invalidInstruction {
isRunning = false
} catch {
print(error)
}
}
func runCyclesBatch() {
isRunning = true
while(!outOfCycles()) {
2017-07-27 23:22:13 +00:00
cpuStep()
if (breakpoints.contains(program_counter)) {
isRunning = false
}
}
}
2017-07-20 06:50:28 +00:00
}