package izapple2 import ( "fmt" "time" ) const ( // CPUClockMhz is the actual Apple II clock speed CPUClockMhz = 14.318 / 14 cpuClockEuroMhz = 14.238 / 14 ) const ( maxWaitDuration = 100 * time.Millisecond cpuSpinLoops = 100 ) // Run starts the Apple2 emulation func (a *Apple2) Run() { a.Start(false) } // Start the Apple2 emulation, can start paused func (a *Apple2) Start(paused bool) { // Start the processor a.cpu.Reset() referenceTime := time.Now() speedReferenceTime := referenceTime speedReferenceCycles := uint64(0) a.paused = paused for { // Run 6502 steps if !a.paused { for i := 0; i < cpuSpinLoops; i++ { // Conditional tracing //pc, _ := a.cpu.GetPCAndSP() //a.cpu.SetTrace(pc >= 0xc700 && pc < 0xc800) // Execution a.cpu.ExecuteInstruction() // Special tracing a.executionTrace() } if a.cycleBreakpoint != 0 && a.cpu.GetCycles() >= a.cycleBreakpoint { a.breakPoint = true a.cycleBreakpoint = 0 a.paused = true } } else { time.Sleep(200 * time.Millisecond) } // Execute meta commands commandsPending := true for commandsPending { select { case command := <-a.commandChannel: switch command.getId() { case CommandKill: return case CommandPause: if !a.paused { a.paused = true } case CommandStart: if a.paused { a.paused = false referenceTime = time.Now() speedReferenceTime = referenceTime } case CommandPauseUnpause: a.paused = !a.paused referenceTime = time.Now() speedReferenceTime = referenceTime default: // Execute the other commands a.executeCommand(command) } default: commandsPending = false } } if a.cycleDurationNs != 0 && a.fastRequestsCounter <= 0 { // Wait until next 6502 step has to run clockDuration := time.Since(referenceTime) simulatedDuration := time.Duration(float64(a.cpu.GetCycles()) * a.cycleDurationNs) waitDuration := simulatedDuration - clockDuration if waitDuration > maxWaitDuration || -waitDuration > maxWaitDuration { // We have to wait too long or are too much behind. Let's fast forward referenceTime = referenceTime.Add(-waitDuration) waitDuration = 0 } if waitDuration > 0 { time.Sleep(waitDuration) } } if a.showSpeed && a.cpu.GetCycles()-speedReferenceCycles > 1000000 { // Calculate speed in MHz every million cycles newTime := time.Now() newCycles := a.cpu.GetCycles() elapsedCycles := float64(newCycles - speedReferenceCycles) freq := 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds()) fmt.Printf("Freq: %f Mhz\n", freq) speedReferenceTime = newTime speedReferenceCycles = newCycles } } } func (a *Apple2) reset() { a.cpu.Reset() a.mmu.reset() for _, c := range a.cards { if c != nil { c.reset() } } } func (a *Apple2) executionTrace() { for _, v := range a.tracers { v.inspect() } } func (a *Apple2) dumpDebugInfo() { // See "Apple II Monitors Peeled" pageZeroSymbols := map[int]string{ 0x36: "CSWL", 0x37: "CSWH", 0x38: "KSWL", 0x39: "KSWH", 0xe2: "ACJVAFLDL", // Apple Pascal 0xe3: "ACJVAFLDH", // Apple Pascal 0xec: "JVBFOLDL", // Apple Pascal 0xed: "JVBFOLDH", // Apple Pascal 0xee: "JVAFOLDL", // Apple Pascal 0xef: "JVAFOLDH", // Apple Pascal } fmt.Printf("Page zero values:\n") for _, k := range []int{0x36, 0x37, 0x38, 0x39, 0xe2, 0xe3, 0xec, 0xed, 0xee, 0xef} { d := a.mmu.physicalMainRAM.data[k] fmt.Printf(" %v(0x%x): 0x%02x\n", pageZeroSymbols[k], k, d) } pc := uint16(0xc700) for pc < 0xc800 { line, newPc := a.cpu.DisasmInstruction(pc) fmt.Println(line) pc = newPc } }