Added very basic audio processing

Every frame sends a bunch of audio samples in a channel. This channel is
consumed by the audio Read() function which is called asynchronously. There's
all kinds of timing issues where the audio/video are not aligned.

Issues:
- There's a large delay between the audio being produced and it being played
- Something with the timing is wrong. The first not of lemonade stand and the
  system beep are both incorrect. Changing the CPU frequency fixes it for one
  but not for the other. This means something must be wrong in the cycle
  counting.

Also added FPS display that can be toggled with ctrl-alt-F.
This commit is contained in:
Will Angenent 2018-05-14 10:33:49 +01:00
parent 23f7dc04b2
commit 8284073beb
6 changed files with 304 additions and 139 deletions

108
audio/audio.go Normal file
View File

@ -0,0 +1,108 @@
package audio
import (
"errors"
"mos6502go/system"
ebiten_audio "github.com/hajimehoshi/ebiten/audio"
)
var (
audioContext *ebiten_audio.Context
player *ebiten_audio.Player
firstAudio bool
Mute bool
)
type stream struct{}
func (s *stream) Read(data []byte) (int, error) {
dataLen := len(data)
if firstAudio {
// The first time, drain the audio queue
firstAudio = false
for i := 0; i < len(system.AudioChannel); i++ {
<-system.AudioChannel
}
return dataLen, nil
}
if dataLen%4 != 0 {
return 0, errors.New("dataLen % 4 must be 0")
}
if Mute {
return dataLen, nil
}
samples := dataLen / 4
for i := 0; i < dataLen; i++ {
data[i] = 0
}
for i := 0; i < samples; i++ {
b := <-system.AudioChannel
data[4*i] = byte(b)
data[4*i+1] = byte(b >> 8)
data[4*i+2] = byte(b)
data[4*i+3] = byte(b >> 8)
}
return dataLen, nil
}
func (s *stream) Close() error {
return nil
}
func Init() {
system.AudioCycles = 0
firstAudio = true
Mute = false
var err error
audioContext, err = ebiten_audio.NewContext(system.AudioSampleRate)
if err != nil {
panic(err)
}
// Pass the (infinite) stream to audio.NewPlayer.
// After calling Play, the stream never ends as long as the player object lives.
// var err error
player, err = ebiten_audio.NewPlayer(audioContext, &stream{})
if err != nil {
panic(err)
}
player.Play()
}
func Click() {
ForwardToFrameCycle()
system.AudioAttenuationCounter = 4000
system.LastAudioValue = ^system.LastAudioValue
}
func attenuate(sample uint16) uint16 {
if system.AudioAttenuationCounter == 0 {
return 0
} else {
system.AudioAttenuationCounter--
return sample
}
}
func ForwardToFrameCycle() {
ratio := float64(system.AudioSampleRate) / system.CpuFrequency
samples := uint64(ratio * float64(system.FrameCycles-system.LastAudioCycles))
var i uint64
for i = 0; i < samples; i++ {
b := attenuate(system.LastAudioValue)
system.AudioChannel <- b
}
system.LastAudioCycles = system.FrameCycles
}

View File

@ -2,12 +2,15 @@ package main
import (
"flag"
"fmt"
"github.com/hajimehoshi/ebiten"
"mos6502go/audio"
"mos6502go/cpu"
"mos6502go/keyboard"
"mos6502go/mmu"
"mos6502go/system"
"mos6502go/utils"
"mos6502go/video"
)
@ -21,6 +24,7 @@ const (
var showInstructions *bool
var disableFirmwareWait *bool
var resetKeysDown bool
var fpsKeysDown bool
var breakAddress *uint16
func reset() {
@ -30,23 +34,43 @@ func reset() {
cpu.State.PC = uint16(lsb) + uint16(msb)<<8
}
// checkResetKeys check ctrl-alt-R has been pressed. Releasing the R does a warm reset
func checkResetKeys() {
// checkSpecialKeys checks
// - ctrl-alt-R has been pressed. Releasing the R does a warm reset
// - ctrl-alt-F has been pressed, toggling FPS display
func checkSpecialKeys() {
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyR) {
resetKeysDown = true
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && !ebiten.IsKeyPressed(ebiten.KeyR) && resetKeysDown {
resetKeysDown = false
reset()
} else {
resetKeysDown = false
}
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyF) {
fpsKeysDown = true
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && !ebiten.IsKeyPressed(ebiten.KeyF) && fpsKeysDown {
fpsKeysDown = false
video.ShowFPS = !video.ShowFPS
fmt.Println("Toggled")
} else {
fpsKeysDown = false
}
}
func update(screen *ebiten.Image) error {
keyboard.Poll()
checkResetKeys()
checkSpecialKeys()
system.FrameCycles = 0
system.LastAudioCycles = 0
cpu.Run(*showInstructions, breakAddress, *disableFirmwareWait, system.CpuFrequency/60)
audio.ForwardToFrameCycle()
system.Cycles += system.FrameCycles
system.FrameCycles = 0
cpu.Run(*showInstructions, breakAddress, *disableFirmwareWait, 1024000/60)
return video.DrawScreen(screen)
}
@ -54,6 +78,7 @@ func main() {
showInstructions = flag.Bool("show-instructions", false, "Show instructions code while running")
disableFirmwareWait = flag.Bool("disable-wait", false, "Ignore JSRs to firmware wait at $FCA8")
breakAddressString := flag.String("break", "", "Break on address")
mute := flag.Bool("mute", false, "Mute sound")
diskImage := flag.String("image", "", "Disk Image")
flag.Parse()
@ -74,6 +99,9 @@ func main() {
keyboard.Init()
video.Init()
audio.Init()
audio.Mute = *mute
system.Init()
reset()

View File

@ -120,7 +120,7 @@ func pop16() uint16 {
return lsb + msb<<8
}
func branch(cycles *int, instructionName string, doBranch bool) {
func branch(instructionName string, doBranch bool) {
value := mmu.ReadMemory(State.PC + 1)
var relativeAddress uint16
@ -130,7 +130,7 @@ func branch(cycles *int, instructionName string, doBranch bool) {
relativeAddress = State.PC + uint16(value) + 2 - 0x100
}
*cycles += 2
system.FrameCycles += 2
if doBranch {
if system.RunningTests && State.PC == relativeAddress {
fmt.Printf("Trap at $%04x\n", relativeAddress)
@ -139,9 +139,9 @@ func branch(cycles *int, instructionName string, doBranch bool) {
samePage := (State.PC & 0xff00) != (relativeAddress & 0xff00)
if samePage {
*cycles += 1
system.FrameCycles += 1
} else {
*cycles += 2
system.FrameCycles += 2
}
State.PC = relativeAddress
} else {
@ -238,117 +238,117 @@ func readMemoryWithAddressMode(addressMode byte) (result uint8, pageBoundaryCros
}
// STA, STX and STY
func store(cycles *int, regValue uint8, addressMode byte) {
func store(regValue uint8, addressMode byte) {
address, _ := getAddressFromAddressMode(addressMode)
mmu.WriteMemory(address, regValue)
switch addressMode {
case AmZeroPage:
State.PC += 2
*cycles += 3
system.FrameCycles += 3
case AmZeroPageX:
State.PC += 2
*cycles += 4
system.FrameCycles += 4
case AmZeroPageY:
State.PC += 2
*cycles += 4
system.FrameCycles += 4
case AmAbsolute:
State.PC += 3
*cycles += 4
system.FrameCycles += 4
case AmAbsoluteX:
State.PC += 3
*cycles += 5
system.FrameCycles += 5
case AmAbsoluteY:
State.PC += 3
*cycles += 5
system.FrameCycles += 5
case AmIndirect:
State.PC += 2
*cycles += 6
system.FrameCycles += 6
case AmIndirectX:
State.PC += 2
*cycles += 6
system.FrameCycles += 6
case AmIndirectY:
State.PC += 2
*cycles += 6
system.FrameCycles += 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
// These instructions take the same amount of system.FrameCycles
func advanceCyclesForAcculumatorOperation(addressMode byte, pageBoundaryCrossed bool) {
extraCycle := uint64(0)
if pageBoundaryCrossed {
extraCycle = 1
}
switch addressMode {
case AmImmediate:
*cycles += 2
system.FrameCycles += 2
case AmZeroPage:
*cycles += 3
system.FrameCycles += 3
case AmZeroPageX:
*cycles += 4
system.FrameCycles += 4
case AmZeroPageY:
*cycles += 4
system.FrameCycles += 4
case AmAbsolute:
*cycles += 4
system.FrameCycles += 4
case AmAbsoluteX:
*cycles += 4 + extraCycle
system.FrameCycles += 4 + extraCycle
case AmAbsoluteY:
*cycles += 4 + extraCycle
system.FrameCycles += 4 + extraCycle
case AmIndirectX:
*cycles += 6
system.FrameCycles += 6
case AmIndirectY:
*cycles += 5 + extraCycle
system.FrameCycles += 5 + extraCycle
default:
panic(fmt.Sprintf("Unknown address mode %d in advanceCyclesForAcculumatorOperation()", addressMode))
}
}
func load(cycles *int, addressMode byte) uint8 {
func load(addressMode byte) uint8 {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
setN(value)
setZ(value)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
return value
}
func cmp(cycles *int, regValue uint8, addressMode byte) {
func cmp(regValue uint8, addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var result uint16
result = uint16(regValue) - uint16(value)
setC(result < 0x100)
setN(uint8(result))
setZ(uint8(result & 0xff))
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func ora(cycles *int, addressMode byte) {
func ora(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A |= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func and(cycles *int, addressMode byte) {
func and(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A &= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func eor(cycles *int, addressMode byte) {
func eor(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
State.A ^= value
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func adc(cycles *int, addressMode byte) {
func adc(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var temp uint16
@ -388,10 +388,10 @@ func adc(cycles *int, addressMode byte) {
setN(State.A)
setZ(State.A)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func sbc(cycles *int, addressMode byte) {
func sbc(addressMode byte) {
value, pageBoundaryCrossed := readMemoryWithAddressMode(addressMode)
var temp uint16
@ -427,7 +427,7 @@ func sbc(cycles *int, addressMode byte) {
setC(temp < 0x100)
State.A = uint8(temp & 0xff)
advanceCyclesForAcculumatorOperation(cycles, addressMode, pageBoundaryCrossed)
advanceCyclesForAcculumatorOperation(addressMode, pageBoundaryCrossed)
}
func bit(address uint16) {
@ -438,7 +438,7 @@ func bit(address uint16) {
}
// Read the address/value for an ASL, LSR, ROR, ROL
func preProcessShift(cycles *int, addressMode byte) (address uint16, value uint8) {
func preProcessShift(addressMode byte) (address uint16, value uint8) {
if addressMode == AmAccumulator {
value = State.A
} else {
@ -456,85 +456,85 @@ func preProcessShift(cycles *int, addressMode byte) (address uint16, value uint8
return
}
// Store the result of a ASL, LSR, ROR, ROL and advance PC and cycles
func postProcessShift(cycles *int, addressMode byte, address uint16, value uint8) {
// Store the result of a ASL, LSR, ROR, ROL and advance PC and system.FrameCycles
func postProcessShift(addressMode byte, address uint16, value uint8) {
switch addressMode {
case AmAccumulator:
State.A = value
State.PC += 1
*cycles += 2
system.FrameCycles += 2
case AmZeroPage:
mmu.WriteMemory(address, value)
State.PC += 2
*cycles += 5
system.FrameCycles += 5
case AmZeroPageX:
mmu.WriteMemory(address, value)
State.PC += 2
*cycles += 6
system.FrameCycles += 6
case AmAbsolute:
mmu.WriteMemory(address, value)
State.PC += 3
*cycles += 6
system.FrameCycles += 6
case AmAbsoluteX:
mmu.WriteMemory(address, value)
State.PC += 3
*cycles += 7
system.FrameCycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in postProcessShift()", addressMode))
}
}
func postProcessIncDec(cycles *int, addressMode byte) {
func postProcessIncDec(addressMode byte) {
switch addressMode {
case AmZeroPage:
State.PC += 2
*cycles += 5
system.FrameCycles += 5
case AmZeroPageX:
State.PC += 2
*cycles += 6
system.FrameCycles += 6
case AmAbsolute:
State.PC += 3
*cycles += 6
system.FrameCycles += 6
case AmAbsoluteX:
State.PC += 3
*cycles += 7
system.FrameCycles += 7
default:
panic(fmt.Sprintf("Unknown address mode %d in INC", addressMode))
}
}
func brk(cycles *int) {
func brk() {
push16(State.PC + 2)
State.P |= CpuFlagB
push8(State.P)
State.P |= CpuFlagI
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
*cycles += 7
system.FrameCycles += 7
}
func irq(cycles *int) {
func irq() {
push16(State.PC)
State.P &= ^CpuFlagB
push8(State.P)
State.P |= CpuFlagI
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
*cycles += 7
system.FrameCycles += 7
}
func nmi(cycles *int) {
func nmi() {
push16(State.PC)
State.P &= ^CpuFlagB
push8(State.P)
State.P |= CpuFlagI
State.PC = uint16(mmu.ReadMemory(0xfffb))<<8 + uint16(mmu.ReadMemory(0xfffa))
*cycles += 7
system.FrameCycles += 7
}
func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool, wantedCycles int) {
cycles := 0
func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool, wantedCycles uint64) {
system.FrameCycles = 0
for {
if (wantedCycles != 0) && (cycles >= wantedCycles) {
if (wantedCycles != 0) && (system.FrameCycles >= wantedCycles) {
return
}
@ -549,13 +549,13 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
}
if system.PendingInterrupt && ((State.P & CpuFlagI) == 0) {
irq(&cycles)
irq()
system.PendingInterrupt = false
continue
}
if system.PendingNMI {
nmi(&cycles)
nmi()
system.PendingNMI = false
continue
}
@ -582,15 +582,15 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
os.Exit(0)
}
State.PC = value
cycles += 3
system.FrameCycles += 3
case 0x6c: // JMP ($0000)
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
State.PC = uint16(mmu.ReadMemory(value)) + uint16(mmu.ReadMemory(value+1))<<8
cycles += 5
system.FrameCycles += 5
case 0x20: // JSR $0000
value := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
cycles += 6
system.FrameCycles += 6
if disableFirmwareWait && value == 0xfca8 {
State.PC += 3
@ -604,38 +604,38 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
case 0x60: // RTS
value := pop16()
State.PC = value + 1
cycles += 6
system.FrameCycles += 6
case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: // LDA
State.A = load(&cycles, addressMode)
State.A = load(addressMode)
case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: // LDX
State.X = load(&cycles, addressMode)
State.X = load(addressMode)
case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: // LDY
State.Y = load(&cycles, addressMode)
State.Y = load(addressMode)
case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: //STA
store(&cycles, State.A, addressMode)
store(State.A, addressMode)
case 0x86, 0x96, 0x8e: // STX
store(&cycles, State.X, addressMode)
store(State.X, addressMode)
case 0x84, 0x94, 0x8c: //STY
store(&cycles, State.Y, addressMode)
store(State.Y, addressMode)
case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: // CMP
cmp(&cycles, State.A, addressMode)
cmp(State.A, addressMode)
case 0xe0, 0xe4, 0xeC: // CPX
cmp(&cycles, State.X, addressMode)
cmp(State.X, addressMode)
case 0xc0, 0xc4, 0xcc: // CPY
cmp(&cycles, State.Y, addressMode)
cmp(State.Y, addressMode)
case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: // ORA
ora(&cycles, addressMode)
ora(addressMode)
case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: // AND
and(&cycles, addressMode)
and(addressMode)
case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: // EOR
eor(&cycles, addressMode)
eor(addressMode)
case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: // ADC
adc(&cycles, addressMode)
adc(addressMode)
case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: // SBC
sbc(&cycles, addressMode)
sbc(addressMode)
// Register transfers
case 0xaa: // TAX
@ -643,169 +643,169 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
setN(State.X)
setZ(State.X)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xa8: // TAY
State.Y = State.A
setN(State.Y)
setZ(State.Y)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xba: // TSX
State.X = State.SP
setN(State.X)
setZ(State.X)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x8a: // TXA
State.A = State.X
setN(State.A)
setZ(State.A)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x9a: // TXS
State.SP = State.X
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x98: // TYA
State.A = State.Y
setN(State.A)
setZ(State.A)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xE8:
State.X = (State.X + 1) & 0xff
setN(State.X)
setZ(State.X)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xC8:
State.Y = (State.Y + 1) & 0xff
setN(State.Y)
setZ(State.Y)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xca:
State.X = (State.X - 1) & 0xff
setN(State.X)
setZ(State.X)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x88:
State.Y = (State.Y - 1) & 0xff
setN(State.Y)
setZ(State.Y)
State.PC++
cycles += 2
system.FrameCycles += 2
// Branch instructions
case 0x10:
branch(&cycles, "BPL", !isN())
branch("BPL", !isN())
case 0x30:
branch(&cycles, "BMI", isN())
branch("BMI", isN())
case 0x50:
branch(&cycles, "BVC", !isV())
branch("BVC", !isV())
case 0x70:
branch(&cycles, "BVS", isV())
branch("BVS", isV())
case 0x90:
branch(&cycles, "BCC", !isC())
branch("BCC", !isC())
case 0xb0:
branch(&cycles, "BCS", isC())
branch("BCS", isC())
case 0xd0:
branch(&cycles, "BNE", !isZ())
branch("BNE", !isZ())
case 0xf0:
branch(&cycles, "BEQ", isZ())
branch("BEQ", isZ())
// Flag setting
case 0x18:
setC(false)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x38:
setC(true)
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x58:
State.P &= ^CpuFlagI
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x78:
State.P |= CpuFlagI
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xb8:
State.P &= ^CpuFlagV
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xd8:
State.P &= ^CpuFlagD
State.PC++
cycles += 2
system.FrameCycles += 2
case 0xf8:
State.P |= CpuFlagD
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x48: // PHA
push8(State.A)
State.PC++
cycles += 3
system.FrameCycles += 3
case 0x68: // PLA
State.A = pop8()
setN(State.A)
setZ(State.A)
State.PC++
cycles += 4
system.FrameCycles += 4
case 0x08: // PHP
// From http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit#the_B_flag_and_the_various_mechanisms
// software instructions BRK & PHP will push the B flag as being 1.
push8(State.P | CpuFlagB)
State.PC++
cycles += 3
system.FrameCycles += 3
case 0x28: // PLP
// CpuFlagR is always supposed to be 1
State.P = pop8() | CpuFlagR
State.PC++
cycles += 4
system.FrameCycles += 4
case 0xea:
State.PC++
cycles += 2
system.FrameCycles += 2
case 0x00: // BRK
brk(&cycles)
brk()
case 0x40: // RTI
State.P = pop8() | CpuFlagR
value := pop16()
State.PC = value
cycles += 6
system.FrameCycles += 6
case 0x24: // BIT $00
address := mmu.ReadMemory(State.PC + 1)
bit(uint16(address))
State.PC += 2
cycles += 3
system.FrameCycles += 3
case 0x2C: // BIT $0000
address := uint16(mmu.ReadMemory(State.PC+1)) + uint16(mmu.ReadMemory(State.PC+2))<<8
bit(address)
State.PC += 3
cycles += 4
system.FrameCycles += 4
case 0x0a, 0x06, 0x16, 0x0e, 0x1e: // ASL
address, value := preProcessShift(&cycles, addressMode)
address, value := preProcessShift(addressMode)
setC((value & 0x80) != 0)
value = (value << 1) & 0xff
setZ(value)
setN(value)
postProcessShift(&cycles, addressMode, address, value)
postProcessShift(addressMode, address, value)
case 0x4a, 0x46, 0x56, 0x4e, 0x5e: // LSR
address, value := preProcessShift(&cycles, addressMode)
address, value := preProcessShift(addressMode)
setC((value & 0x01) != 0)
value >>= 1
setZ(value)
setN(value)
postProcessShift(&cycles, addressMode, address, value)
postProcessShift(addressMode, address, value)
case 0x2a, 0x26, 0x36, 0x2e, 0x3e: // ROL
address, value := preProcessShift(&cycles, addressMode)
address, value := preProcessShift(addressMode)
value16 := uint16(value)
value16 <<= 1
if (State.P & CpuFlagC) != 0 {
@ -815,9 +815,9 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
value = uint8(value16 & 0xff)
setZ(value)
setN(value)
postProcessShift(&cycles, addressMode, address, value)
postProcessShift(addressMode, address, value)
case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR
address, value := preProcessShift(&cycles, addressMode)
address, value := preProcessShift(addressMode)
value16 := uint16(value)
if (State.P & CpuFlagC) != 0 {
value16 |= 0x100
@ -826,7 +826,7 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
value = uint8(value16 >> 1)
setZ(value)
setN(value)
postProcessShift(&cycles, addressMode, address, value)
postProcessShift(addressMode, address, value)
case 0xe6, 0xf6, 0xee, 0xfe: // INC
address, _ := getAddressFromAddressMode(addressMode)
@ -835,7 +835,7 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
setZ(value)
setN(value)
mmu.WriteMemory(address, value)
postProcessIncDec(&cycles, addressMode)
postProcessIncDec(addressMode)
case 0xc6, 0xd6, 0xce, 0xde: // DEC
address, _ := getAddressFromAddressMode(addressMode)
@ -844,7 +844,7 @@ func Run(showInstructions bool, breakAddress *uint16, disableFirmwareWait bool,
setZ(value)
setN(value)
mmu.WriteMemory(address, value)
postProcessIncDec(&cycles, addressMode)
postProcessIncDec(addressMode)
default:
fmt.Printf("Unknown opcode $%02x\n", opcode)

View File

@ -2,6 +2,7 @@ package mmu
import (
"fmt"
"mos6502go/audio"
"mos6502go/keyboard"
)
@ -132,6 +133,9 @@ func driveIsreadSequencing() bool {
// effect and the return value is meaningless
func readWrite(address uint16, isRead bool) bool {
switch address {
case CLR80VID:
// 80 column card hasn't been implemented yet
return true
case CLRTEXT:
VideoState.TextMode = false
return true
@ -256,8 +260,7 @@ func ReadIO(address uint16) uint8 {
// RDALTCH not implemented
return 0x0d
case SPEAKER:
// Speaker not implemented
// Not printing anything since this will generate a lot of noise
audio.Click()
case S6Q6L:
return ReadTrackData()
default:

View File

@ -1,14 +1,31 @@
package system
var (
PendingInterrupt bool
PendingNMI bool
RunningTests bool
RunningFunctionalTests bool
RunningInterruptTests bool
const (
CpuFrequency = 1024000
AudioSampleRate = 44100
)
var (
PendingInterrupt bool
PendingNMI bool
RunningTests bool
RunningFunctionalTests bool
RunningInterruptTests bool
Cycles uint64
FrameCycles uint64
AudioCycles uint64
AudioChannel chan uint16
LastAudioCycles uint64
LastAudioValue uint16
AudioAttenuationCounter uint64
)
func Init() {
Cycles = 0
AudioChannel = make(chan uint16, AudioSampleRate*4) // 1 second
LastAudioValue = 0x2000
}
// Handle a write to a magic test address that triggers an interrupt and/or an NMI
func WriteInterruptTestOpenCollector(address uint16, oldValue uint8, value uint8) {
oldInterrupt := (oldValue & 0x1) == 0x1

View File

@ -1,6 +1,7 @@
package video
import (
"fmt"
"image"
"image/color"
"mos6502go/mmu"
@ -20,6 +21,7 @@ var (
flashCounter int
flashOn bool
loresSquares [16]*ebiten.Image
ShowFPS bool
)
func Init() {
@ -57,6 +59,8 @@ func Init() {
loresSquares[0x0D].Fill(color.NRGBA{255, 255, 0, alpha})
loresSquares[0x0E].Fill(color.NRGBA{68, 255, 153, alpha})
loresSquares[0x0F].Fill(color.NRGBA{255, 255, 255, alpha})
ShowFPS = false
}
func drawText(screen *ebiten.Image, x int, y int, value uint8, flashOn bool) error {
@ -147,5 +151,10 @@ func DrawScreen(screen *ebiten.Image) error {
}
}
if ShowFPS {
msg := fmt.Sprintf(`FPS: %0.2f`, ebiten.CurrentFPS())
ebitenutil.DebugPrint(screen, msg)
}
return nil
}