apple2-go/cpu/cpu.go

910 lines
22 KiB
Go

package cpu
import (
"fmt"
"os"
"github.com/freewilll/apple2-go/mmu"
"github.com/freewilll/apple2-go/system"
)
const (
cpuFlagC byte = 1 << iota // 0x01 carry
cpuFlagZ // 0x02 zero
cpuFlagI // 0x04 interrupt disable
cpuFlagD // 0x08 decimal mode
cpuFlagB // 0x10 break
cpuFlagR // 0x20 reserved (unused)
cpuFlagV // 0x40 overflow
cpuFlagN // 0x80 sign/negative
)
// State contains the CPU state
var State struct {
A uint8 // accumulator
X uint8 // X register
Y uint8 // Y register
PC uint16 // program counter
SP uint8 // stack pointer
P uint8 // processor flags
}
// Init sets up the CPU registers, interrupts and disable testing code
func Init() {
system.RunningTests = false
system.RunningFunctionalTests = false
system.RunningInterruptTests = false
system.PendingInterrupt = false
system.PendingNMI = false
State.A = 0
State.X = 0
State.Y = 0
State.P = cpuFlagR | cpuFlagB | cpuFlagZ
State.SP = 0xff
}
// setC sets the carry flag
func setC(value bool) {
if value {
State.P |= cpuFlagC
} else {
State.P &= ^cpuFlagC
}
}
// setV sets the overflow flag
func setV(value bool) {
if value {
State.P |= cpuFlagV
} else {
State.P &= ^cpuFlagV
}
}
// setN sets the sign/negative flag if the value is negative (>=0x80)
func setN(value uint8) {
if (value & 0x80) != 0 {
State.P |= cpuFlagN
} else {
State.P &= ^cpuFlagN
}
}
// setZ sets the zero flag if the value is zero
func setZ(value uint8) {
if value == 0 {
State.P |= cpuFlagZ
} else {
State.P &= ^cpuFlagZ
}
}
func isC() bool {
return (State.P & cpuFlagC) != 0
}
func isZ() bool {
return (State.P & cpuFlagZ) != 0
}
func isD() bool {
return (State.P & cpuFlagD) != 0
}
func isV() bool {
return (State.P & cpuFlagV) != 0
}
func isN() bool {
return (State.P & cpuFlagN) != 0
}
// push8 pushes an 8 bit value to the stack
func push8(value uint8) {
mmu.WritePageTable[mmu.StackPage][State.SP] = value
State.SP--
State.SP &= 0xff
}
// push16 pushes a 16 bit value to the stack
func push16(value uint16) {
mmu.WritePageTable[mmu.StackPage][State.SP] = uint8(value >> 8)
mmu.WritePageTable[mmu.StackPage][State.SP-1] = uint8(value & 0xff)
State.SP -= 2
State.SP &= 0xff
}
// pop8 pulls an 8 bit value from the stack
func pop8() uint8 {
State.SP++
State.SP &= 0xff
return mmu.ReadPageTable[mmu.StackPage][State.SP]
}
// pop16 pulls a 16 bit value from the stack
func pop16() uint16 {
State.SP += 2
State.SP &= 0xff
msb := uint16(mmu.ReadPageTable[mmu.StackPage][State.SP])
lsb := uint16(mmu.ReadPageTable[mmu.StackPage][State.SP-1])
return lsb + msb<<8
}
// branch handles a branch instruction
func branch(doBranch bool) {
value := mmu.ReadMemory(State.PC + 1)
var relativeAddress uint16
if (value & 0x80) == 0 {
relativeAddress = State.PC + uint16(value) + 2
} else {
relativeAddress = State.PC + uint16(value) + 2 - 0x100
}
system.FrameCycles += 2
if doBranch {
if system.RunningTests && State.PC == relativeAddress {
// Catch an infinite loop and exit
fmt.Printf("Trap at $%04x\n", relativeAddress)
os.Exit(0)
}
// The number of cycles depends on if a page boundary was crossed
samePage := (State.PC & 0xff00) == (relativeAddress & 0xff00)
if samePage {
system.FrameCycles++
} else {
system.FrameCycles += 2
}
State.PC = relativeAddress
} else {
State.PC += 2
}
}
// getAddressFromAddressMode gets the address an instruction is referring to
func getAddressFromAddressMode(addressMode byte) (result uint16, pageBoundaryCrossed bool) {
switch addressMode {
case amZeroPage:
result = uint16(mmu.ReadMemory(State.PC + 1))
case amZeroPageX:
result = (uint16(mmu.ReadMemory(State.PC+1)) + uint16(State.X)) & 0xff
case amZeroPageY:
result = (uint16(mmu.ReadMemory(State.PC+1)) + uint16(State.Y)) & 0xff
case amAbsolute:
result = uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
case amAbsoluteX:
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(State.X)) & 0xff00)
result = value + uint16(State.X)
case amAbsoluteY:
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(State.Y)) & 0xff00)
result = value + uint16(State.Y)
case amIndirectX:
zeroPageAddress := (mmu.ReadMemory(State.PC+1) + State.X) & 0xff
result = uint16(mmu.ReadMemory(uint16(zeroPageAddress))) + uint16(mmu.ReadMemory(uint16(zeroPageAddress)+1))<<8
case amIndirectY:
address := uint16(mmu.ReadMemory(State.PC + 1))
lsb := uint16(mmu.ReadMemory(address))
msb := uint16(mmu.ReadMemory(address + 1))
value := lsb + msb<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(State.Y)) & 0xff00)
result = value + uint16(State.Y)
default:
panic(fmt.Sprintf("Unknown address mode %d in getAddressFromAddressMode()", addressMode))
}
return result, pageBoundaryCrossed
}
// readMemoryWithAddressMode reads memory using a particular address mode
func readMemoryWithAddressMode(addressMode byte) (result uint8, pageBoundaryCrossed bool) {
switch addressMode {
case amImmediate:
result = mmu.ReadMemory(State.PC + 1)
State.PC += 2
case amZeroPage:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 2
case amZeroPageX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 2
case amZeroPageY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 2
case amAbsolute:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 3
case amAbsoluteX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 3
case amAbsoluteY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 3
case amIndirectX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 2
case amIndirectY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(addressMode)
result = mmu.ReadMemory(address)
State.PC += 2
default:
result = 0
State.PC++
}
return result, pageBoundaryCrossed
}
// store handles STA, STX and STY
func store(regValue uint8, addressMode byte) {
address, _ := getAddressFromAddressMode(addressMode)
mmu.WriteMemory(address, regValue)
switch addressMode {
case amZeroPage:
State.PC += 2
system.FrameCycles += 3
case amZeroPageX:
State.PC += 2
system.FrameCycles += 4
case amZeroPageY:
State.PC += 2
system.FrameCycles += 4
case amAbsolute:
State.PC += 3
system.FrameCycles += 4
case amAbsoluteX:
State.PC += 3
system.FrameCycles += 5
case amAbsoluteY:
State.PC += 3
system.FrameCycles += 5
case amIndirect:
State.PC += 2
system.FrameCycles += 6
case amIndirectX:
State.PC += 2
system.FrameCycles += 6
case amIndirectY:
State.PC += 2
system.FrameCycles += 6
default:
panic(fmt.Sprintf("Unknown address mode %d in store()", addressMode))
}
}
// advanceCyclesForAcculumatorOperation advances the number of cycles for common accumulator operations
func advanceCyclesForAcculumatorOperation(addressMode byte, pageBoundaryCrossed bool) {
extraCycle := uint64(0)
if pageBoundaryCrossed {
extraCycle = 1
}
switch addressMode {
case amImmediate:
system.FrameCycles += 2
case amZeroPage:
system.FrameCycles += 3
case amZeroPageX:
system.FrameCycles += 4
case amZeroPageY:
system.FrameCycles += 4
case amAbsolute:
system.FrameCycles += 4
case amAbsoluteX:
system.FrameCycles += 4 + extraCycle
case amAbsoluteY:
system.FrameCycles += 4 + extraCycle
case amIndirectX:
system.FrameCycles += 6
case amIndirectY:
system.FrameCycles += 5 + extraCycle
default:
panic(fmt.Sprintf("Unknown address mode %d in advanceCyclesForAcculumatorOperation()", addressMode))
}
}
// LDA, LDX, LDY
func load(addressMode byte) uint8 {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
setN(value)
setZ(value)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
return value
}
// CMP, CPX, CPY
func cmp(regValue uint8, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var result uint16
result = uint16(regValue) - uint16(value)
setC(result < 0x100)
setN(uint8(result))
setZ(uint8(result & 0xff))
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func ora(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A |= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func and(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A &= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func eor(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A ^= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func adc(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var temp uint16
temp = uint16(State.A) + uint16(value)
var carry uint8
if isC() {
carry = 1
}
if carry > 0 {
temp++
}
// This is not valid in decimal mode
setZ(uint8(temp & 0xff))
if isD() {
if ((State.A & 0xf) + (value & 0xf) + carry) > 9 {
temp += 6
}
setN(uint8(temp))
setV((((State.A ^ value) & 0x80) == 0) && (((State.A ^ uint8(temp)) & 0x80) != 0))
if temp > 0x99 {
temp += 96
}
setC(temp > 0x99)
} else {
setN(uint8(temp))
setV((((State.A ^ value) & 0x80) == 0) && (((State.A ^ uint8(temp)) & 0x80) != 0))
setC(temp > 0xff)
}
State.A = uint8(temp & 0xff)
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func sbc(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var temp uint16
temp = uint16(State.A) - uint16(value)
var carry uint8
if isC() {
carry = 0
} else {
carry = 1
}
if carry > 0 {
temp--
}
setN(uint8(temp))
// This is not valid in decimal mode
setZ(uint8(temp & 0xff))
setV((((State.A ^ uint8(temp)) & 0x80) != 0) && (((State.A ^ value) & 0x80) != 0))
if isD() {
if ((int8(State.A) & 0xf) - int8(carry)) < (int8(value) & 0xf) {
temp -= 6
}
if temp > 0x99 {
temp -= 96
}
}
setC(temp < 0x100)
State.A = uint8(temp & 0xff)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func bit(address uint16) {
value := mmu.ReadMemory(address)
setN(value)
setV((value & 0x40) != 0)
setZ(value & State.A)
}
// Read the address/value for an ASL, LSR, ROR, ROL
func preProcessShift(addressMode byte) (address uint16, value uint8) {
if addressMode == amAccumulator {
value = State.A
} else {
address, _ = getAddressFromAddressMode(addressMode)
value = mmu.ReadMemory(address)
}
if addressMode == amAccumulator {
value = State.A
} else {
address, _ = getAddressFromAddressMode(addressMode)
value = mmu.ReadMemory(address)
}
return
}
// Store the result of a ASL, LSR, ROR, ROL and advance PC and system.FrameCycles
func postProcessShift(addressMode byte, address uint16, value uint8) {
switch addressMode {
case amAccumulator:
State.A = value
State.PC++
system.FrameCycles += 2
case amZeroPage:
mmu.WriteMemory(address, value)
State.PC += 2
system.FrameCycles += 5
case amZeroPageX:
mmu.WriteMemory(address, value)
State.PC += 2
system.FrameCycles += 6
case amAbsolute:
mmu.WriteMemory(address, value)
State.PC += 3
system.FrameCycles += 6
case amAbsoluteX:
mmu.WriteMemory(address, value)
State.PC += 3
system.FrameCycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in postProcessShift()", addressMode))
}
}
func postProcessIncDec(addressMode byte) {
switch addressMode {
case amZeroPage:
State.PC += 2
system.FrameCycles += 5
case amZeroPageX:
State.PC += 2
system.FrameCycles += 6
case amAbsolute:
State.PC += 3
system.FrameCycles += 6
case amAbsoluteX:
State.PC += 3
system.FrameCycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in INC", addressMode))
}
}
func brk() {
push16(State.PC + 2)
State.P |= cpuFlagB
push8(State.P)
State.P |= cpuFlagI
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
system.FrameCycles += 7
}
func irq() {
push16(State.PC)
State.P &= ^cpuFlagB
push8(State.P)
State.P |= cpuFlagI
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
system.FrameCycles += 7
}
func nmi() {
push16(State.PC)
State.P &= ^cpuFlagB
push8(State.P)
State.P |= cpuFlagI
State.PC = uint16(mmu.ReadMemory(0xfffb))<<8 + uint16(mmu.ReadMemory(0xfffa))
system.FrameCycles += 7
}
// Run runs the CPU until either wantedCycles has been reached (if non-zero) or the program counter reaches breakAddress.
// system.FrameCycles is the amount of cycles executed so far.
func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableFirmwareWait bool, wantedCycles uint64) {
system.FrameCycles = 0
for {
// Exit if wantedCycles is set and has been reached
if (wantedCycles != 0) && (system.FrameCycles >= wantedCycles) {
return
}
// Exit if the magic address of the functional tests has been reached
if system.RunningTests && (State.PC == 0x3869) {
fmt.Println("Functional tests passed")
return
}
// Exit if the magic address of the interupt tests has been reached
if system.RunningTests && (State.PC == 0x0af5) {
fmt.Println("Interrupt tests passed")
return
}
// Handle an IRQ f there is one pending and interrupts are enabled
if system.PendingInterrupt && ((State.P & cpuFlagI) == 0) {
irq()
system.PendingInterrupt = false
continue
}
// Handle an NMI if there is one pending
if system.PendingNMI {
nmi()
system.PendingNMI = false
continue
}
if showInstructions {
PrintInstruction(true)
}
// Handle case of breakAddress being set and being been reached
if breakAddress != nil && State.PC == *breakAddress {
if exitAtBreak {
// Exit the process completely
fmt.Printf("Break at $%04x\n", *breakAddress)
PrintInstruction(true)
os.Exit(0)
} else {
// Exit politely
return
}
}
// Decode opcode
opcode := mmu.ReadMemory(State.PC)
addressMode := opCodes[opcode].addressingMode.mode
switch opcode {
case 0x4c: // JMP $0000
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
if system.RunningTests && State.PC == value {
// Check for an infinite loop and exit if so
fmt.Printf("Trap at $%04x\n", value)
os.Exit(0)
}
State.PC = value
system.FrameCycles += 3
case 0x6c: // JMP ($0000)
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
State.PC = uint16(mmu.ReadMemory(value)) + uint16(mmu.ReadMemory(value+1))<<8
system.FrameCycles += 5
case 0x20: // JSR $0000
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
system.FrameCycles += 6
if disableFirmwareWait && value == 0xfca8 {
// Don't call the firmware wait, just move forward and pretend it happened.
State.PC += 3
State.A = 0
continue
}
push16(State.PC + 2)
State.PC = value
case 0x60: // RTS
value := pop16()
State.PC = value + 1
system.FrameCycles += 6
case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: // LDA
State.A = load(addressMode)
case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: // LDX
State.X = load(addressMode)
case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: // LDY
State.Y = load(addressMode)
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: //STA
store(State.A, addressMode)
case 0x86, 0x96, 0x8e: // STX
store(State.X, addressMode)
case 0x84, 0x94, 0x8c: //STY
store(State.Y, addressMode)
case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: // CMP
cmp(State.A, addressMode)
case 0xe0, 0xe4, 0xeC: // CPX
cmp(State.X, addressMode)
case 0xc0, 0xc4, 0xcc: // CPY
cmp(State.Y, addressMode)
case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: // ORA
ora(addressMode)
case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: // AND
and(addressMode)
case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: // EOR
eor(addressMode)
case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: // ADC
adc(addressMode)
case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: // SBC
sbc(addressMode)
// Register transfers
case 0xaa: // TAX
State.X = State.A
setN(State.X)
setZ(State.X)
State.PC++
system.FrameCycles += 2
case 0xa8: // TAY
State.Y = State.A
setN(State.Y)
setZ(State.Y)
State.PC++
system.FrameCycles += 2
case 0xba: // TSX
State.X = State.SP
setN(State.X)
setZ(State.X)
State.PC++
system.FrameCycles += 2
case 0x8a: // TXA
State.A = State.X
setN(State.A)
setZ(State.A)
State.PC++
system.FrameCycles += 2
case 0x9a: // TXS
State.SP = State.X
State.PC++
system.FrameCycles += 2
case 0x98: // TYA
State.A = State.Y
setN(State.A)
setZ(State.A)
State.PC++
system.FrameCycles += 2
case 0xE8: // INX
State.X = (State.X + 1) & 0xff
setN(State.X)
setZ(State.X)
State.PC++
system.FrameCycles += 2
case 0xC8: // INY
State.Y = (State.Y + 1) & 0xff
setN(State.Y)
setZ(State.Y)
State.PC++
system.FrameCycles += 2
case 0xca: // DEX
State.X = (State.X - 1) & 0xff
setN(State.X)
setZ(State.X)
State.PC++
system.FrameCycles += 2
case 0x88: // DEY
State.Y = (State.Y - 1) & 0xff
setN(State.Y)
setZ(State.Y)
State.PC++
system.FrameCycles += 2
// Branch instructions
case 0x10:
branch(!isN())
case 0x30:
branch(isN())
case 0x50:
branch(!isV())
case 0x70:
branch(isV())
case 0x90:
branch(!isC())
case 0xb0:
branch(isC())
case 0xd0:
branch(!isZ())
case 0xf0:
branch(isZ())
// Flag setting
case 0x18: // CLC
setC(false)
State.PC++
system.FrameCycles += 2
case 0x38: // SEC
setC(true)
State.PC++
system.FrameCycles += 2
case 0x58: // CLI
State.P &= ^cpuFlagI
State.PC++
system.FrameCycles += 2
case 0x78: // SEI
State.P |= cpuFlagI
State.PC++
system.FrameCycles += 2
case 0xb8: // CLV
State.P &= ^cpuFlagV
State.PC++
system.FrameCycles += 2
case 0xd8: // CLD
State.P &= ^cpuFlagD
State.PC++
system.FrameCycles += 2
case 0xf8: //SED
State.P |= cpuFlagD
State.PC++
system.FrameCycles += 2
// Stack operations
case 0x48: // PHA
push8(State.A)
State.PC++
system.FrameCycles += 3
case 0x68: // PLA
State.A = pop8()
setN(State.A)
setZ(State.A)
State.PC++
system.FrameCycles += 4
case 0x08: // PHP
// From http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit#the_B_flag_and_the_various_mechanisms
// software instructions BRK & PHP will push the B flag as being 1.
push8(State.P | cpuFlagB)
State.PC++
system.FrameCycles += 3
case 0x28: // PLP
// cpuFlagR is always supposed to be 1
State.P = pop8() | cpuFlagR
State.PC++
system.FrameCycles += 4
case 0xea:
State.PC++
system.FrameCycles += 2
case 0x00: // BRK
brk()
case 0x40: // RTI
State.P = pop8() | cpuFlagR
value := pop16()
State.PC = value
system.FrameCycles += 6
case 0x24: // BIT $00
address := mmu.ReadMemory(State.PC + 1)
bit(uint16(address))
State.PC += 2
system.FrameCycles += 3
case 0x2C: // BIT $0000
address := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
bit(address)
State.PC += 3
system.FrameCycles += 4
// Shifts and rotations
case 0x0a, 0x06, 0x16, 0x0e, 0x1e: // ASL
address, value := preProcessShift(addressMode)
setC((value & 0x80) != 0)
value = (value << 1) & 0xff
setZ(value)
setN(value)
postProcessShift(addressMode, address, value)
case 0x4a, 0x46, 0x56, 0x4e, 0x5e: // LSR
address, value := preProcessShift(addressMode)
setC((value & 0x01) != 0)
value >>= 1
setZ(value)
setN(value)
postProcessShift(addressMode, address, value)
case 0x2a, 0x26, 0x36, 0x2e, 0x3e: // ROL
address, value := preProcessShift(addressMode)
value16 := uint16(value)
value16 <<= 1
if (State.P & cpuFlagC) != 0 {
value16 |= 0x01
}
setC((value16 & 0x100) != 0)
value = uint8(value16 & 0xff)
setZ(value)
setN(value)
postProcessShift(addressMode, address, value)
case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR
address, value := preProcessShift(addressMode)
value16 := uint16(value)
if (State.P & cpuFlagC) != 0 {
value16 |= 0x100
}
setC((value16 & 0x01) != 0)
value = uint8(value16 >> 1)
setZ(value)
setN(value)
postProcessShift(addressMode, address, value)
case 0xe6, 0xf6, 0xee, 0xfe: // INC
address, _ := getAddressFromAddressMode(addressMode)
value := mmu.ReadMemory(address)
value = (value + 1) & 0xff
setZ(value)
setN(value)
mmu.WriteMemory(address, value)
postProcessIncDec(addressMode)
case 0xc6, 0xd6, 0xce, 0xde: // DEC
address, _ := getAddressFromAddressMode(addressMode)
value := mmu.ReadMemory(address)
value = (value - 1) & 0xff
setZ(value)
setN(value)
mmu.WriteMemory(address, value)
postProcessIncDec(addressMode)
default:
fmt.Printf("Unknown opcode $%02x at %04x\n", opcode, State.PC)
return
}
}
}
// SetColdStartReset nukes the checkum byte for the reset vector. When this is called, the apple boot firmware will
// conclude that the reset vector is invalid and do a cold start.
func SetColdStartReset() {
mmu.WriteMemory(uint16(0x3f4), uint8(0))
}
// Reset sets the CPU and memory states so that a next call to cpu.Run() calls the firmware reset code
func Reset() {
mmu.InitROM() // Set upper memory area for reading from ROM
mmu.InitRAM()
bootVector := 0xfffc
lsb := mmu.ReadPageTable[bootVector>>8][bootVector&0xff]
msb := mmu.ReadPageTable[(bootVector+1)>>8][(bootVector+1)&0xff]
State.PC = uint16(lsb) + uint16(msb)<<8
}