mirror of
https://github.com/freewilll/apple2-go.git
synced 2025-01-03 05:29:25 +00:00
Initial commit
This commit is contained in:
commit
4b2a4c3730
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
test-cpu
|
||||||
|
cpu.prof
|
||||||
|
mem.prof
|
||||||
|
mos6502go.test
|
6102
6502_functional_test.a65
Normal file
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
BIN
6502_functional_test.bin.gz
Normal file
Binary file not shown.
47
cmd/test-cpu.go
Normal file
47
cmd/test-cpu.go
Normal 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
109
cmd/test-vid.go
Normal 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
817
cpu/cpu.go
Normal 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
96
cpu/disasm.go
Normal 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
315
cpu/opcodes.go
Normal 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
25
cpu_test.go
Normal 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
BIN
pr-latin1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
24
utils/utils.go
Normal file
24
utils/utils.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user