Initial commit

This commit is contained in:
Will Angenent 2018-04-29 20:41:11 +01:00
commit 4b2a4c3730
11 changed files with 7539 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
test-cpu
cpu.prof
mem.prof
mos6502go.test

6102
6502_functional_test.a65 Normal file

File diff suppressed because it is too large Load Diff

BIN
6502_functional_test.bin.gz Normal file

Binary file not shown.

47
cmd/test-cpu.go Normal file
View File

@ -0,0 +1,47 @@
package main
import (
"encoding/hex"
"flag"
"mos6502go/cpu"
"mos6502go/utils"
)
func main() {
cpu.InitDisasm()
var s cpu.State
s.Init()
bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz")
if err != nil {
panic(err)
}
for i := 0; i < len(bytes); i++ {
s.Memory[i] = bytes[i]
}
showInstructions := flag.Bool("show-instructions", false, "Show instructions code while running")
breakAddressString := flag.String("break", "", "Break on address")
flag.Parse()
var breakAddress *uint16
if *breakAddressString != "" {
breakAddressValue, err := hex.DecodeString(*breakAddressString)
if err != nil {
panic(err)
}
var foo uint16
if len(breakAddressValue) == 1 {
foo = uint16(breakAddressValue[0])
} else if len(breakAddressValue) == 2 {
foo = uint16(breakAddressValue[0])*uint16(0x100) + uint16(breakAddressValue[1])
} else {
panic("Invalid break address")
}
breakAddress = &foo
}
cpu.Run(&s, *showInstructions, breakAddress)
}

109
cmd/test-vid.go Normal file
View File

@ -0,0 +1,109 @@
package main
import (
"image"
"log"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
)
const (
screenSizeFactor = 1 // Factor by which the whole screen is resized
textVideoMemory = 0x400 // Base location of page 1 text video memory
flashFrames = 8 // Number of frames when FLASH mode is toggled
)
var (
ebitenImage *ebiten.Image
memory [0x100000]byte
flashCounter int
flashOn bool
)
func drawTextScreen(screen *ebiten.Image) error {
flashCounter--
if flashCounter < 0 {
flashCounter = flashFrames
flashOn = !flashOn
}
if ebiten.IsRunningSlowly() {
return nil
}
for y := 0; y < 24; y++ {
base := 128*(y%8) + 40*(y/8)
for x := 0; x < 40; x++ {
offset := textVideoMemory + base + x
value := memory[offset]
inverted := false
if (value & 0xc0) == 0 {
inverted = true
} else if (value & 0x80) == 0 {
value = value & 0x3f
inverted = flashOn
}
if !inverted {
value = value & 0x7f
}
if value < 0x20 {
value += 0x40
}
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(screenSizeFactor, screenSizeFactor)
op.GeoM.Translate(screenSizeFactor*7*float64(x), screenSizeFactor*8*float64(y))
fontRow := value % 16
fontCol := value / 16
var fontX = (int)(15 + fontCol*12)
var fontY = (int)(32 + fontRow*11)
r := image.Rect(fontX, fontY, fontX+7, fontY+8)
op.SourceRect = &r
if !inverted {
op.ColorM.Scale(-1, -1, -1, 1)
op.ColorM.Translate(1, 1, 1, 0)
}
op.ColorM.Scale(0.20, 0.75, 0.20, 1)
if err := screen.DrawImage(ebitenImage, op); err != nil {
return err
}
}
}
return nil
}
func update(screen *ebiten.Image) error {
return drawTextScreen(screen)
}
func addTestTextScreenData() {
// Clear screen
for i := 0; i < 0x400; i++ {
memory[textVideoMemory+i] = 160
}
for i := 0; i < 255; i++ {
memory[textVideoMemory+i] = byte(i)
}
}
func main() {
addTestTextScreenData()
var err error
ebitenImage, _, err = ebitenutil.NewImageFromFile("./pr-latin1.png", ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
ebiten.Run(update, 280*screenSizeFactor, 192*screenSizeFactor, 2, "Apple //")
}

817
cpu/cpu.go Normal file
View File

@ -0,0 +1,817 @@
package cpu
import (
"fmt"
"os"
)
const (
CpuFlagC byte = 1 << iota
CpuFlagZ
CpuFlagI
CpuFlagD
CpuFlagB
CpuFlagR
CpuFlagV
CpuFlagN
)
const runningTests = true
type State struct {
Memory [0x10000]uint8
A uint8
X uint8
Y uint8
PC uint16
SP uint8
P uint8
}
func (s *State) Init() {
s.A = 0
s.X = 0
s.Y = 0
s.P = CpuFlagR | CpuFlagB | CpuFlagZ
s.PC = 0x800
s.SP = 0xff
}
func (s *State) setC(value bool) {
if value {
s.P |= CpuFlagC
} else {
s.P &= ^CpuFlagC
}
}
func (s *State) setV(value bool) {
if value {
s.P |= CpuFlagV
} else {
s.P &= ^CpuFlagV
}
}
func (s *State) setN(value uint8) {
if (value & 0x80) != 0 {
s.P |= CpuFlagN
} else {
s.P &= ^CpuFlagN
}
}
func (s *State) setZ(value uint8) {
if value == 0 {
s.P |= CpuFlagZ
} else {
s.P &= ^CpuFlagZ
}
}
func (s *State) isC() bool {
return (s.P & CpuFlagC) != 0
}
func (s *State) isZ() bool {
return (s.P & CpuFlagZ) != 0
}
func (s *State) isD() bool {
return (s.P & CpuFlagD) != 0
}
func (s *State) isV() bool {
return (s.P & CpuFlagV) != 0
}
func (s *State) isN() bool {
return (s.P & CpuFlagN) != 0
}
func push8(s *State, value uint8) {
s.Memory[0x100+uint16(s.SP)] = value
s.SP -= 1
s.SP &= 0xff
}
func push16(s *State, value uint16) {
s.Memory[0x100+uint16(s.SP)] = uint8(value >> 8)
s.Memory[0xff+uint16(s.SP)] = uint8(value & 0xff)
s.SP -= 2
s.SP &= 0xff
}
func pop8(s *State) uint8 {
s.SP += 1
s.SP &= 0xff
return s.Memory[0x100+uint16(s.SP)]
}
func pop16(s *State) uint16 {
s.SP += 2
s.SP &= 0xff
lsb := uint16(s.Memory[0xff+uint16(s.SP)])
msb := uint16(s.Memory[0x100+uint16(s.SP)])
return lsb + msb<<8
}
func readMemory(s *State, address uint16) uint8 {
return s.Memory[address]
}
func writeMemory(s *State, address uint16, value uint8) {
s.Memory[address] = value
if runningTests && address == 0x200 {
testNumber := s.Memory[0x200]
if testNumber == 0xf0 {
fmt.Println("Opcode testing completed")
} else {
fmt.Printf("Test %d OK\n", s.Memory[0x200])
}
}
}
func branch(s *State, cycles *int, instructionName string, doBranch bool) {
value := s.Memory[s.PC+1]
var relativeAddress uint16
if (value & 0x80) == 0 {
relativeAddress = s.PC + uint16(value) + 2
} else {
relativeAddress = s.PC + uint16(value) + 2 - 0x100
}
*cycles += 2
if doBranch {
if runningTests && s.PC == relativeAddress {
fmt.Printf("Trap at $%04x\n", relativeAddress)
os.Exit(0)
}
samePage := (s.PC & 0xff00) != (relativeAddress & 0xff00)
if samePage {
*cycles += 1
} else {
*cycles += 2
}
s.PC = relativeAddress
} else {
s.PC += 2
}
}
func getAddressFromAddressMode(s *State, addressMode byte) (result uint16, pageBoundaryCrossed bool) {
switch addressMode {
case AmZeroPage:
result = uint16(s.Memory[s.PC+1])
case AmZeroPageX:
result = (uint16(s.Memory[s.PC+1]) + uint16(s.X)) & 0xff
case AmZeroPageY:
result = (uint16(s.Memory[s.PC+1]) + uint16(s.Y)) & 0xff
case AmAbsolute:
result = uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
case AmAbsoluteX:
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.X)) & 0xff00)
result = value + uint16(s.X)
case AmAbsoluteY:
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
result = value + uint16(s.Y)
case AmIndirectX:
zeroPageAddress := (s.Memory[s.PC+1] + s.X) & 0xff
result = uint16(s.Memory[zeroPageAddress]) + uint16(s.Memory[zeroPageAddress+1])<<8
case AmIndirectY:
address := s.Memory[s.PC+1]
lsb := uint16(s.Memory[address])
msb := uint16(s.Memory[address+1])
value := lsb + msb<<8
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
result = value + uint16(s.Y)
default:
panic(fmt.Sprintf("Unknown address mode %d in getAddressFromAddressMode()", addressMode))
}
return result, pageBoundaryCrossed
}
func readMemoryWithAddressMode(s *State, addressMode byte) (result uint8, pageBoundaryCrossed bool) {
switch addressMode {
case AmImmediate:
result = s.Memory[s.PC+1]
s.PC += 2
case AmZeroPage:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 2
case AmZeroPageX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 2
case AmZeroPageY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 2
case AmAbsolute:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 3
case AmAbsoluteX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 3
case AmAbsoluteY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 3
case AmIndirectX:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 2
case AmIndirectY:
var address uint16
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
result = s.Memory[address]
s.PC += 2
default:
result = 0
s.PC++
}
return result, pageBoundaryCrossed
}
// STA, STX and STY
func store(s *State, cycles *int, regValue uint8, addressMode byte) {
address, _ := getAddressFromAddressMode(s, addressMode)
writeMemory(s, address, regValue)
switch addressMode {
case AmZeroPage:
s.PC += 2
*cycles += 3
case AmZeroPageX:
s.PC += 2
*cycles += 4
case AmZeroPageY:
s.PC += 2
*cycles += 4
case AmAbsolute:
s.PC += 3
*cycles += 4
case AmAbsoluteX:
s.PC += 3
*cycles += 5
case AmAbsoluteY:
s.PC += 3
*cycles += 5
case AmIndirect:
s.PC += 2
*cycles += 6
case AmIndirectX:
s.PC += 2
*cycles += 6
case AmIndirectY:
s.PC += 2
*cycles += 6
default:
panic(fmt.Sprintf("Unknown address mode %d in store()", addressMode))
}
}
// These instructions take the same amount of cycles
func advanceCyclesForAcculumatorOperation(cycles *int, addressMode byte, pageBoundaryCrossed bool) {
extraCycle := 0
if pageBoundaryCrossed {
extraCycle = 1
}
switch addressMode {
case AmImmediate:
*cycles += 2
case AmZeroPage:
*cycles += 3
case AmZeroPageX:
*cycles += 4
case AmZeroPageY:
*cycles += 4
case AmAbsolute:
*cycles += 4
case AmAbsoluteX:
*cycles += 4 + extraCycle
case AmAbsoluteY:
*cycles += 4 + extraCycle
case AmIndirectX:
*cycles += 6
case AmIndirectY:
*cycles += 5 + extraCycle
default:
panic(fmt.Sprintf("Unknown address mode %d in advanceCyclesForAcculumatorOperation()", addressMode))
}
}
func load(s *State, cycles *int, addressMode byte) uint8 {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
s.setN(value)
s.setZ(value)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
return value
}
func cmp(s *State, cycles *int, regValue uint8, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
var result uint16
result = uint16(regValue) - uint16(value)
s.setC(result < 0x100)
s.setN(uint8(result))
s.setZ(uint8(result & 0xff))
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func ora(s *State, cycles *int, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
s.A |= value
s.setN(s.A)
s.setZ(s.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func and(s *State, cycles *int, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
s.A &= value
s.setN(s.A)
s.setZ(s.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func eor(s *State, cycles *int, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
s.A ^= value
s.setN(s.A)
s.setZ(s.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func adc(s *State, cycles *int, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
var temp uint16
temp = uint16(s.A) + uint16(value)
var carry uint8
if s.isC() {
carry = 1
}
if carry > 0 {
temp++
}
// This is not valid in decimal mode
s.setZ(uint8(temp & 0xff))
if s.isD() {
if ((s.A & 0xf) + (value & 0xf) + carry) > 9 {
temp += 6
}
s.setN(uint8(temp))
s.setV((((s.A ^ value) & 0x80) == 0) && (((s.A ^ uint8(temp)) & 0x80) != 0))
if temp > 0x99 {
temp += 96
}
s.setC(temp > 0x99)
} else {
s.setN(uint8(temp))
s.setV((((s.A ^ value) & 0x80) == 0) && (((s.A ^ uint8(temp)) & 0x80) != 0))
s.setC(temp > 0xff)
}
s.A = uint8(temp & 0xff)
s.setN(s.A)
s.setZ(s.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func sbc(s *State, cycles *int, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(s, addressMode)
var temp uint16
temp = uint16(s.A) - uint16(value)
var carry uint8
if s.isC() {
carry = 0
} else {
carry = 1
}
if carry > 0 {
temp--
}
s.setN(uint8(temp))
// This is not valid in decimal mode
s.setZ(uint8(temp & 0xff))
s.setV((((s.A ^ uint8(temp)) & 0x80) != 0) && (((s.A ^ value) & 0x80) != 0))
if s.isD() {
if ((int8(s.A) & 0xf) - int8(carry)) < (int8(value) & 0xf) {
temp -= 6
}
if temp > 0x99 {
temp -= 96
}
}
s.setC(temp < 0x100)
s.A = uint8(temp & 0xff)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
}
func bit(s *State, address uint16) {
value := s.Memory[address]
s.setN(value)
s.setV((value & 0x40) != 0)
s.setZ(value & s.A)
}
// Read the address/value for an ASL, LSR, ROR, ROL
func preProcessShift(s *State, cycles *int, addressMode byte) (address uint16, value uint8) {
if addressMode == AmAccumulator {
value = s.A
} else {
address, _ = getAddressFromAddressMode(s, addressMode)
value = s.Memory[address]
}
if addressMode == AmAccumulator {
value = s.A
} else {
address, _ = getAddressFromAddressMode(s, addressMode)
value = s.Memory[address]
}
return
}
// Store the result of a ASL, LSR, ROR, ROL and advance PC and cycles
func postProcessShift(s *State, cycles *int, addressMode byte, address uint16, value uint8) {
switch addressMode {
case AmAccumulator:
s.A = value
s.PC += 1
*cycles += 2
case AmZeroPage:
writeMemory(s, address, value)
s.PC += 2
*cycles += 5
case AmZeroPageX:
writeMemory(s, address, value)
s.PC += 2
*cycles += 6
case AmAbsolute:
writeMemory(s, address, value)
s.PC += 3
*cycles += 6
case AmAbsoluteX:
writeMemory(s, address, value)
s.PC += 3
*cycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in postProcessShift()", addressMode))
}
}
func postProcessIncDec(s *State, cycles *int, addressMode byte) {
switch addressMode {
case AmZeroPage:
s.PC += 2
*cycles += 5
case AmZeroPageX:
s.PC += 2
*cycles += 6
case AmAbsolute:
s.PC += 3
*cycles += 6
case AmAbsoluteX:
s.PC += 3
*cycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in INC", addressMode))
}
}
func Run(s *State, showInstructions bool, breakAddress *uint16) {
cycles := 0
for {
if runningTests && (s.PC == 0x3869) {
fmt.Println("Functional tests passed")
return
}
if showInstructions {
PrintInstruction(s)
}
opcode := s.Memory[s.PC]
addressMode := OpCodes[opcode].AddressingMode.Mode
if breakAddress != nil && s.PC == *breakAddress {
fmt.Printf("Break at $%04x\n", *breakAddress)
os.Exit(0)
}
switch opcode {
case 0x4c: // JMP $0000
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
if runningTests && s.PC == value {
fmt.Printf("Trap at $%04x\n", value)
os.Exit(0)
}
s.PC = value
cycles += 3
case 0x6c: // JMP ($0000)
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
s.PC = uint16(s.Memory[value]) + uint16(s.Memory[value+1])<<8
cycles += 5
case 0x20: // JSR $0000
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
push16(s, s.PC+2)
s.PC = value
cycles += 6
case 0x60: // RTS
value := pop16(s)
s.PC = value + 1
cycles += 6
case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: // LDA
s.A = load(s, &cycles, addressMode)
case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: // LDX
s.X = load(s, &cycles, addressMode)
case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: // LDY
s.Y = load(s, &cycles, addressMode)
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: //STA
store(s, &cycles, s.A, addressMode)
case 0x86, 0x96, 0x8e: // STX
store(s, &cycles, s.X, addressMode)
case 0x84, 0x94, 0x8c: //STY
store(s, &cycles, s.Y, addressMode)
case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: // CMP
cmp(s, &cycles, s.A, addressMode)
case 0xe0, 0xe4, 0xeC: // CPX
cmp(s, &cycles, s.X, addressMode)
case 0xc0, 0xc4, 0xcc: // CPY
cmp(s, &cycles, s.Y, addressMode)
case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: // ORA
ora(s, &cycles, addressMode)
case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: // AND
and(s, &cycles, addressMode)
case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: // EOR
eor(s, &cycles, addressMode)
case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: // ADC
adc(s, &cycles, addressMode)
case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: // SBC
sbc(s, &cycles, addressMode)
// Register transfers
case 0xaa: // TAX
s.X = s.A
s.setN(s.X)
s.setZ(s.X)
s.PC++
cycles += 2
case 0xa8: // TAY
s.Y = s.A
s.setN(s.Y)
s.setZ(s.Y)
s.PC++
cycles += 2
case 0xba: // TSX
s.X = s.SP
s.setN(s.X)
s.setZ(s.X)
s.PC++
cycles += 2
case 0x8a: // TXA
s.A = s.X
s.setN(s.A)
s.setZ(s.A)
s.PC++
cycles += 2
case 0x9a: // TXS
s.SP = s.X
s.PC++
cycles += 2
case 0x98: // TYA
s.A = s.Y
s.setN(s.A)
s.setZ(s.A)
s.PC++
cycles += 2
case 0xE8:
s.X = (s.X + 1) & 0xff
s.setN(s.X)
s.setZ(s.X)
s.PC++
cycles += 2
case 0xC8:
s.Y = (s.Y + 1) & 0xff
s.setN(s.Y)
s.setZ(s.Y)
s.PC++
cycles += 2
case 0xca:
s.X = (s.X - 1) & 0xff
s.setN(s.X)
s.setZ(s.X)
s.PC++
cycles += 2
case 0x88:
s.Y = (s.Y - 1) & 0xff
s.setN(s.Y)
s.setZ(s.Y)
s.PC++
cycles += 2
// Branch instructions
case 0x10:
branch(s, &cycles, "BPL", !s.isN())
case 0x30:
branch(s, &cycles, "BMI", s.isN())
case 0x50:
branch(s, &cycles, "BVC", !s.isV())
case 0x70:
branch(s, &cycles, "BVS", s.isV())
case 0x90:
branch(s, &cycles, "BCC", !s.isC())
case 0xb0:
branch(s, &cycles, "BCS", s.isC())
case 0xd0:
branch(s, &cycles, "BNE", !s.isZ())
case 0xf0:
branch(s, &cycles, "BEQ", s.isZ())
// Flag setting
case 0x18:
s.setC(false)
s.PC++
cycles += 2
case 0x38:
s.setC(true)
s.PC++
cycles += 2
case 0x58:
s.P &= ^CpuFlagI
s.PC++
cycles += 2
case 0x78:
s.P |= CpuFlagI
s.PC++
cycles += 2
case 0xb8:
s.P &= ^CpuFlagV
s.PC++
cycles += 2
case 0xd8:
s.P &= ^CpuFlagD
s.PC++
cycles += 2
case 0xf8:
s.P |= CpuFlagD
s.PC++
cycles += 2
case 0x48: // PHA
push8(s, s.A)
s.PC++
cycles += 3
case 0x68: // PLA
s.A = pop8(s)
s.setN(s.A)
s.setZ(s.A)
s.PC++
cycles += 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(s, s.P|CpuFlagB)
s.PC++
cycles += 3
case 0x28: // PLP
// CpuFlagR is always supposed to be 1
s.P = pop8(s) | CpuFlagR
s.PC++
cycles += 4
case 0xea:
s.PC++
cycles += 2
case 0x00: // BRK
push16(s, s.PC+2)
s.P |= CpuFlagB
push8(s, s.P)
s.P |= CpuFlagI
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
cycles += 7
case 0x40: // RTI
s.P = pop8(s) | CpuFlagR
value := pop16(s)
s.PC = value
cycles += 6
case 0x24: // BIT $00
address := s.Memory[s.PC+1]
bit(s, uint16(address))
s.PC += 2
cycles += 3
case 0x2C: // BIT $0000
address := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
bit(s, address)
s.PC += 3
cycles += 4
case 0x0a, 0x06, 0x16, 0x0e, 0x1e: // ASL
address, value := preProcessShift(s, &cycles, addressMode)
s.setC((value & 0x80) != 0)
value = (value << 1) & 0xff
s.setZ(value)
s.setN(value)
postProcessShift(s, &cycles, addressMode, address, value)
case 0x4a, 0x46, 0x56, 0x4e, 0x5e: // LSR
address, value := preProcessShift(s, &cycles, addressMode)
s.setC((value & 0x01) != 0)
value >>= 1
s.setZ(value)
s.setN(value)
postProcessShift(s, &cycles, addressMode, address, value)
case 0x2a, 0x26, 0x36, 0x2e, 0x3e: // ROL
address, value := preProcessShift(s, &cycles, addressMode)
value16 := uint16(value)
value16 <<= 1
if (s.P & CpuFlagC) != 0 {
value16 |= 0x01
}
s.setC((value16 & 0x100) != 0)
value = uint8(value16 & 0xff)
s.setZ(value)
s.setN(value)
postProcessShift(s, &cycles, addressMode, address, value)
case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR
address, value := preProcessShift(s, &cycles, addressMode)
value16 := uint16(value)
if (s.P & CpuFlagC) != 0 {
value16 |= 0x100
}
s.setC((value16 & 0x01) != 0)
value = uint8(value16 >> 1)
s.setZ(value)
s.setN(value)
postProcessShift(s, &cycles, addressMode, address, value)
case 0xe6, 0xf6, 0xee, 0xfe: // INC
address, _ := getAddressFromAddressMode(s, addressMode)
value := s.Memory[address]
value = (value + 1) & 0xff
s.setZ(value)
s.setN(value)
writeMemory(s, address, value)
postProcessIncDec(s, &cycles, addressMode)
case 0xc6, 0xd6, 0xce, 0xde: // DEC
address, _ := getAddressFromAddressMode(s, addressMode)
value := s.Memory[address]
value = (value - 1) & 0xff
s.setZ(value)
s.setN(value)
writeMemory(s, address, value)
postProcessIncDec(s, &cycles, addressMode)
default:
fmt.Printf("Unknown opcode $%02x\n", opcode)
return
}
}
}

96
cpu/disasm.go Normal file
View File

@ -0,0 +1,96 @@
package cpu
import (
"fmt"
"strings"
)
func printFlag(p byte, flag uint8, code string) {
if (p & flag) == 0 {
fmt.Print(code)
} else {
fmt.Printf("%s", strings.ToUpper(code))
}
}
func printInstruction(s *State, instruction string) {
fmt.Printf("%04x %-24s A=%02x X=%02x Y=%02x S=%02x P=%02x ",
s.PC,
instruction,
s.A,
s.X,
s.Y,
s.SP,
s.P,
)
printFlag(s.P, CpuFlagN, "n")
printFlag(s.P, CpuFlagV, "v")
fmt.Print("-") // CpuFlagR flag that's always 1
printFlag(s.P, CpuFlagB, "b")
printFlag(s.P, CpuFlagD, "d")
printFlag(s.P, CpuFlagI, "i")
printFlag(s.P, CpuFlagZ, "z")
printFlag(s.P, CpuFlagC, "c")
fmt.Println("")
}
func PrintInstruction(s *State) {
opcodeValue := s.Memory[s.PC]
opcode := OpCodes[opcodeValue]
mnemonic := opcode.Mnemonic
size := opcode.AddressingMode.OperandSize
stringFormat := opcode.AddressingMode.StringFormat
var value uint16
if size == 0 {
printInstruction(s, fmt.Sprintf("%02x %s", opcodeValue, mnemonic))
return
}
var opcodes string
var suffix string
if opcode.AddressingMode.Mode == AmRelative {
value = uint16(s.Memory[s.PC+1])
var relativeAddress uint16
if (value & 0x80) == 0 {
relativeAddress = s.PC + 2 + uint16(value)
} else {
relativeAddress = s.PC + 2 + uint16(value) - 0x100
}
suffix = fmt.Sprintf(stringFormat, relativeAddress)
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
} else if size == 1 {
value = uint16(s.Memory[s.PC+1])
suffix = fmt.Sprintf(stringFormat, value)
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
} else if size == 2 {
lower := s.Memory[s.PC+1]
higher := s.Memory[s.PC+2]
value = uint16(lower) + uint16(higher)*0x100
suffix = fmt.Sprintf(stringFormat, value)
opcodes = fmt.Sprintf("%02x %02x %02x ", opcodeValue, lower, higher)
}
printInstruction(s, fmt.Sprintf("%s %s %s", opcodes, mnemonic, suffix))
}
func DumpMemory(s *State, offset uint16) {
var i uint16
for i = 0; i < 0x100; i++ {
if (i & 0xf) == 8 {
fmt.Print(" ")
}
if (i & 0xf) == 0 {
if i > 0 {
fmt.Print("\n")
}
fmt.Printf("%04x ", offset+i)
}
fmt.Printf(" %02x", s.Memory[offset+i])
}
fmt.Print("\n")
}

315
cpu/opcodes.go Normal file
View File

@ -0,0 +1,315 @@
package cpu
const (
AmNone byte = 1 + iota
AmAccumulator //
AmImplied //
AmRelative //
AmExpansion //
AmImmediate // #$00
AmZeroPage // $00
AmZeroPageX // $00,X
AmZeroPageY // $00,Y
AmAbsolute // $0000
AmAbsoluteX // $0000,X
AmAbsoluteY // $0000,Y
AmIndirect // ($0000)
AmIndirectX // ($00,X)
AmIndirectY // ($00),Y
)
type AddressingMode struct {
Mode byte
OperandSize byte
StringFormat string
}
type OpCode struct {
Mnemonic string
AddressingMode AddressingMode
}
var AddressingModes map[byte]AddressingMode
var OpCodes [0x100]OpCode
func InitAddressingModes() {
AddressingModes = make(map[byte]AddressingMode)
AddressingModes[AmAccumulator] = AddressingMode{Mode: AmAccumulator, OperandSize: 0, StringFormat: ""}
AddressingModes[AmImplied] = AddressingMode{Mode: AmImplied, OperandSize: 0, StringFormat: ""}
AddressingModes[AmRelative] = AddressingMode{Mode: AmRelative, OperandSize: 1, StringFormat: "$%04x"}
AddressingModes[AmExpansion] = AddressingMode{Mode: AmExpansion, OperandSize: 0, StringFormat: ""}
AddressingModes[AmImmediate] = AddressingMode{Mode: AmImmediate, OperandSize: 1, StringFormat: "#$%02x"}
AddressingModes[AmZeroPage] = AddressingMode{Mode: AmZeroPage, OperandSize: 1, StringFormat: "$%02x"}
AddressingModes[AmZeroPageX] = AddressingMode{Mode: AmZeroPageX, OperandSize: 1, StringFormat: "$%02x,X"}
AddressingModes[AmZeroPageY] = AddressingMode{Mode: AmZeroPageY, OperandSize: 1, StringFormat: "$%02x,Y"}
AddressingModes[AmAbsolute] = AddressingMode{Mode: AmAbsolute, OperandSize: 2, StringFormat: "$%04x"}
AddressingModes[AmAbsoluteX] = AddressingMode{Mode: AmAbsoluteX, OperandSize: 2, StringFormat: "$%04x,X"}
AddressingModes[AmAbsoluteY] = AddressingMode{Mode: AmAbsoluteY, OperandSize: 2, StringFormat: "$%04x,Y"}
AddressingModes[AmIndirect] = AddressingMode{Mode: AmIndirect, OperandSize: 2, StringFormat: "($%04x)"}
AddressingModes[AmIndirectX] = AddressingMode{Mode: AmIndirectX, OperandSize: 1, StringFormat: "($%02x,X)"}
AddressingModes[AmIndirectY] = AddressingMode{Mode: AmIndirectY, OperandSize: 1, StringFormat: "($%02x),Y"}
}
func InitOpCodes() {
OpCodes[0x00] = OpCode{Mnemonic: "BRK", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x01] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0x02] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x03] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x04] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x05] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x06] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x07] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x08] = OpCode{Mnemonic: "PHP", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x09] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0x0A] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAccumulator]}
OpCodes[0x0B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x0C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x0D] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x0E] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x0F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x10] = OpCode{Mnemonic: "BPL", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0x11] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0x12] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x13] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x14] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x15] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x16] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x17] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x18] = OpCode{Mnemonic: "CLC", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x19] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0x1A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x1B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x1C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x1D] = OpCode{Mnemonic: "ORA", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x1E] = OpCode{Mnemonic: "ASL", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x1F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x20] = OpCode{Mnemonic: "JSR", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x21] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0x22] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x23] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x24] = OpCode{Mnemonic: "BIT", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x25] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x26] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x27] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x28] = OpCode{Mnemonic: "PLP", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x29] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0x2A] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAccumulator]}
OpCodes[0x2B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x2C] = OpCode{Mnemonic: "BIT", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x2D] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x2E] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x2F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x30] = OpCode{Mnemonic: "BMI", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0x31] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0x32] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x33] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x34] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x35] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x36] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x37] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x38] = OpCode{Mnemonic: "SEC", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x39] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0x3A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x3B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x3C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x3D] = OpCode{Mnemonic: "AND", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x3E] = OpCode{Mnemonic: "ROL", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x3F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x40] = OpCode{Mnemonic: "RTI", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x41] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0x42] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x43] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x44] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x45] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x46] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x47] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x48] = OpCode{Mnemonic: "PHA", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x49] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0x4A] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAccumulator]}
OpCodes[0x4B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x4C] = OpCode{Mnemonic: "JMP", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x4D] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x4E] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x4F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x50] = OpCode{Mnemonic: "BVC", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0x51] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0x52] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x53] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x54] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x55] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x56] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x57] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x58] = OpCode{Mnemonic: "CLI", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x59] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0x5A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x5B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x5C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x5D] = OpCode{Mnemonic: "EOR", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x5E] = OpCode{Mnemonic: "LSR", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x5F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x60] = OpCode{Mnemonic: "RTS", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x61] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0x62] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x63] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x64] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x65] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x66] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x67] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x68] = OpCode{Mnemonic: "PLA", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x69] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0x6A] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAccumulator]}
OpCodes[0x6B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x6C] = OpCode{Mnemonic: "JMP", AddressingMode: AddressingModes[AmIndirect]}
OpCodes[0x6D] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x6E] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x6F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x70] = OpCode{Mnemonic: "BVS", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0x71] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0x72] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x73] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x74] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x75] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x76] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x77] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x78] = OpCode{Mnemonic: "SEI", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x79] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0x7A] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x7B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x7C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x7D] = OpCode{Mnemonic: "ADC", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x7E] = OpCode{Mnemonic: "ROR", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x7F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x80] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x81] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0x82] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x83] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x84] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x85] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x86] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0x87] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x88] = OpCode{Mnemonic: "DEY", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x89] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x8A] = OpCode{Mnemonic: "TXA", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x8B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x8C] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x8D] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x8E] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0x8F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x90] = OpCode{Mnemonic: "BCC", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0x91] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0x92] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x93] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x94] = OpCode{Mnemonic: "STY", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x95] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0x96] = OpCode{Mnemonic: "STX", AddressingMode: AddressingModes[AmZeroPageY]}
OpCodes[0x97] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x98] = OpCode{Mnemonic: "TYA", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x99] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0x9A] = OpCode{Mnemonic: "TXS", AddressingMode: AddressingModes[AmNone]}
OpCodes[0x9B] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x9C] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x9D] = OpCode{Mnemonic: "STA", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0x9E] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0x9F] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xA0] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xA1] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0xA2] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xA3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xA4] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xA5] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xA6] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xA7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xA8] = OpCode{Mnemonic: "TAY", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xA9] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xAA] = OpCode{Mnemonic: "TAX", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xAB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xAC] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xAD] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xAE] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xAF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xB0] = OpCode{Mnemonic: "BCS", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0xB1] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0xB2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xB3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xB4] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xB5] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xB6] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmZeroPageY]}
OpCodes[0xB7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xB8] = OpCode{Mnemonic: "CLV", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xB9] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0xBA] = OpCode{Mnemonic: "TSX", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xBB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xBC] = OpCode{Mnemonic: "LDY", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xBD] = OpCode{Mnemonic: "LDA", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xBE] = OpCode{Mnemonic: "LDX", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0xBF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xC0] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xC1] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0xC2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xC3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xC4] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xC5] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xC6] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xC7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xC8] = OpCode{Mnemonic: "INY", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xC9] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xCA] = OpCode{Mnemonic: "DEX", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xCB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xCC] = OpCode{Mnemonic: "CPY", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xCD] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xCE] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xCF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xD0] = OpCode{Mnemonic: "BNE", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0xD1] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0xD2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xD3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xD4] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xD5] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xD6] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xD7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xD8] = OpCode{Mnemonic: "CLD", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xD9] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0xDA] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xDB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xDC] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xDD] = OpCode{Mnemonic: "CMP", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xDE] = OpCode{Mnemonic: "DEC", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xDF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xE0] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xE1] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmIndirectX]}
OpCodes[0xE2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xE3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xE4] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xE5] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xE6] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmZeroPage]}
OpCodes[0xE7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xE8] = OpCode{Mnemonic: "INX", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xE9] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmImmediate]}
OpCodes[0xEA] = OpCode{Mnemonic: "NOP", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xEB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xEC] = OpCode{Mnemonic: "CPX", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xED] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xEE] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmAbsolute]}
OpCodes[0xEF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xF0] = OpCode{Mnemonic: "BEQ", AddressingMode: AddressingModes[AmRelative]}
OpCodes[0xF1] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmIndirectY]}
OpCodes[0xF2] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xF3] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xF4] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xF5] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xF6] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmZeroPageX]}
OpCodes[0xF7] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xF8] = OpCode{Mnemonic: "SED", AddressingMode: AddressingModes[AmNone]}
OpCodes[0xF9] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsoluteY]}
OpCodes[0xFA] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xFB] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xFC] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
OpCodes[0xFD] = OpCode{Mnemonic: "SBC", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xFE] = OpCode{Mnemonic: "INC", AddressingMode: AddressingModes[AmAbsoluteX]}
OpCodes[0xFF] = OpCode{Mnemonic: "???", AddressingMode: AddressingModes[AmExpansion]}
}
func InitDisasm() {
InitAddressingModes()
InitOpCodes()
}

25
cpu_test.go Normal file
View File

@ -0,0 +1,25 @@
package test_cpu
import (
"mos6502go/cpu"
"mos6502go/utils"
"testing"
)
func TestFunctionalTests(*testing.T) {
cpu.InitDisasm()
var s cpu.State
s.Init()
bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz")
if err != nil {
panic(err)
}
for i := 0; i < len(bytes); i++ {
s.Memory[i] = bytes[i]
}
cpu.Run(&s, false, nil)
}

BIN
pr-latin1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

24
utils/utils.go Normal file
View File

@ -0,0 +1,24 @@
package utils
import (
"compress/gzip"
"io/ioutil"
"os"
)
func ReadMemoryFromFile(filename string) (data []byte, err error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
reader, err := gzip.NewReader(f)
if err != nil {
return nil, err
}
defer reader.Close()
data, err = ioutil.ReadAll(reader)
return
}