diff --git a/audio/audio.go b/audio/audio.go new file mode 100644 index 0000000..b7e1dcd --- /dev/null +++ b/audio/audio.go @@ -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 +} diff --git a/cmd/test-apple-iie-boot.go b/cmd/test-apple-iie-boot.go index fb62281..f44f326 100644 --- a/cmd/test-apple-iie-boot.go +++ b/cmd/test-apple-iie-boot.go @@ -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() diff --git a/cpu/cpu.go b/cpu/cpu.go index e51378c..ea83d9a 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -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) diff --git a/mmu/io.go b/mmu/io.go index cdc0bbc..7ceb198 100644 --- a/mmu/io.go +++ b/mmu/io.go @@ -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: diff --git a/system/system.go b/system/system.go index b80abd8..964ebac 100644 --- a/system/system.go +++ b/system/system.go @@ -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 diff --git a/video/video.go b/video/video.go index 6675aa0..e1fb60b 100644 --- a/video/video.go +++ b/video/video.go @@ -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 }