mirror of
https://github.com/freewilll/apple2-go.git
synced 2024-06-25 16:29:51 +00:00
Every frame sends a bunch of audio samples in a channel. This channel is consumed by the audio Read() function which is called asynchronously. There's all kinds of timing issues where the audio/video are not aligned. Issues: - There's a large delay between the audio being produced and it being played - Something with the timing is wrong. The first not of lemonade stand and the system beep are both incorrect. Changing the CPU frequency fixes it for one but not for the other. This means something must be wrong in the cycle counting. Also added FPS display that can be toggled with ctrl-alt-F.
855 lines
20 KiB
Go
855 lines
20 KiB
Go
package cpu
|
|
|
|
import (
|
|
"fmt"
|
|
"mos6502go/mmu"
|
|
"mos6502go/system"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
CpuFlagC byte = 1 << iota // 0x01
|
|
CpuFlagZ // 0x02
|
|
CpuFlagI // 0x04
|
|
CpuFlagD // 0x08
|
|
CpuFlagB // 0x10
|
|
CpuFlagR // 0x20
|
|
CpuFlagV // 0x40
|
|
CpuFlagN // 0x80
|
|
)
|
|
|
|
var State struct {
|
|
A uint8
|
|
X uint8
|
|
Y uint8
|
|
PC uint16
|
|
SP uint8
|
|
P uint8
|
|
}
|
|
|
|
func Init() {
|
|
system.RunningTests = false
|
|
system.RunningFunctionalTests = false
|
|
system.RunningInterruptTests = false
|
|
|
|
State.A = 0
|
|
State.X = 0
|
|
State.Y = 0
|
|
State.P = CpuFlagR | CpuFlagB | CpuFlagZ
|
|
State.SP = 0xff
|
|
system.PendingInterrupt = false
|
|
system.PendingNMI = false
|
|
}
|
|
|
|
func setC(value bool) {
|
|
if value {
|
|
State.P |= CpuFlagC
|
|
} else {
|
|
State.P &= ^CpuFlagC
|
|
}
|
|
}
|
|
|
|
func setV(value bool) {
|
|
if value {
|
|
State.P |= CpuFlagV
|
|
} else {
|
|
State.P &= ^CpuFlagV
|
|
}
|
|
}
|
|
|
|
func setN(value uint8) {
|
|
if (value & 0x80) != 0 {
|
|
State.P |= CpuFlagN
|
|
} else {
|
|
State.P &= ^CpuFlagN
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func push8(value uint8) {
|
|
mmu.PageTable[mmu.StackPage][State.SP] = value
|
|
State.SP -= 1
|
|
State.SP &= 0xff
|
|
}
|
|
|
|
func push16(value uint16) {
|
|
mmu.PageTable[mmu.StackPage][State.SP] = uint8(value >> 8)
|
|
mmu.PageTable[mmu.StackPage][State.SP-1] = uint8(value & 0xff)
|
|
State.SP -= 2
|
|
State.SP &= 0xff
|
|
}
|
|
|
|
func pop8() uint8 {
|
|
State.SP += 1
|
|
State.SP &= 0xff
|
|
return mmu.PageTable[mmu.StackPage][State.SP]
|
|
}
|
|
|
|
func pop16() uint16 {
|
|
State.SP += 2
|
|
State.SP &= 0xff
|
|
msb := uint16(mmu.PageTable[mmu.StackPage][State.SP])
|
|
lsb := uint16(mmu.PageTable[mmu.StackPage][State.SP-1])
|
|
return lsb + msb<<8
|
|
}
|
|
|
|
func branch(instructionName string, 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 {
|
|
fmt.Printf("Trap at $%04x\n", relativeAddress)
|
|
os.Exit(0)
|
|
}
|
|
|
|
samePage := (State.PC & 0xff00) != (relativeAddress & 0xff00)
|
|
if samePage {
|
|
system.FrameCycles += 1
|
|
} else {
|
|
system.FrameCycles += 2
|
|
}
|
|
State.PC = relativeAddress
|
|
} else {
|
|
State.PC += 2
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
}
|
|
|
|
// These instructions take the same amount of system.FrameCycles
|
|
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))
|
|
}
|
|
}
|
|
|
|
func load(addressMode byte) uint8 {
|
|
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
|
|
setN(value)
|
|
setZ(value)
|
|
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
|
|
return value
|
|
}
|
|
|
|
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 += 1
|
|
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
|
|
}
|
|
|
|
func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool, wantedCycles uint64) {
|
|
system.FrameCycles = 0
|
|
|
|
for {
|
|
if (wantedCycles != 0) && (system.FrameCycles >= wantedCycles) {
|
|
return
|
|
}
|
|
|
|
if system.RunningTests && (State.PC == 0x3869) {
|
|
fmt.Println("Functional tests passed")
|
|
return
|
|
}
|
|
|
|
if system.RunningTests && (State.PC == 0x0af5) {
|
|
fmt.Println("Interrupt tests passed")
|
|
return
|
|
}
|
|
|
|
if system.PendingInterrupt && ((State.P & CpuFlagI) == 0) {
|
|
irq()
|
|
system.PendingInterrupt = false
|
|
continue
|
|
}
|
|
|
|
if system.PendingNMI {
|
|
nmi()
|
|
system.PendingNMI = false
|
|
continue
|
|
}
|
|
|
|
if showInstructions {
|
|
PrintInstruction(true)
|
|
}
|
|
|
|
opcode := mmu.ReadMemory(State.PC)
|
|
addressMode := OpCodes[opcode].AddressingMode.Mode
|
|
|
|
if breakAddress != nil && State.PC == *breakAddress {
|
|
fmt.Printf("Break at $%04x\n", *breakAddress)
|
|
PrintInstruction(true)
|
|
os.Exit(0)
|
|
}
|
|
|
|
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 {
|
|
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 {
|
|
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:
|
|
State.X = (State.X + 1) & 0xff
|
|
setN(State.X)
|
|
setZ(State.X)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0xC8:
|
|
State.Y = (State.Y + 1) & 0xff
|
|
setN(State.Y)
|
|
setZ(State.Y)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0xca:
|
|
State.X = (State.X - 1) & 0xff
|
|
setN(State.X)
|
|
setZ(State.X)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0x88:
|
|
State.Y = (State.Y - 1) & 0xff
|
|
setN(State.Y)
|
|
setZ(State.Y)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
|
|
// Branch instructions
|
|
case 0x10:
|
|
branch("BPL", !isN())
|
|
case 0x30:
|
|
branch("BMI", isN())
|
|
case 0x50:
|
|
branch("BVC", !isV())
|
|
case 0x70:
|
|
branch("BVS", isV())
|
|
case 0x90:
|
|
branch("BCC", !isC())
|
|
case 0xb0:
|
|
branch("BCS", isC())
|
|
case 0xd0:
|
|
branch("BNE", !isZ())
|
|
case 0xf0:
|
|
branch("BEQ", isZ())
|
|
|
|
// Flag setting
|
|
case 0x18:
|
|
setC(false)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0x38:
|
|
setC(true)
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0x58:
|
|
State.P &= ^CpuFlagI
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0x78:
|
|
State.P |= CpuFlagI
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0xb8:
|
|
State.P &= ^CpuFlagV
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0xd8:
|
|
State.P &= ^CpuFlagD
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
case 0xf8:
|
|
State.P |= CpuFlagD
|
|
State.PC++
|
|
system.FrameCycles += 2
|
|
|
|
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
|
|
|
|
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\n", opcode)
|
|
return
|
|
}
|
|
}
|
|
}
|