-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathstep.go
91 lines (78 loc) · 2.34 KB
/
step.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright 2012 Lawrence Kesteloot
package main
// The main code that emulates the Z80.
import (
"log"
"runtime"
"time"
)
// Steps through one instruction.
func (vm *vm) step() {
// Log PC for retroactive disassembly.
if historicalPcCount > 0 {
vm.historicalPcPtr = (vm.historicalPcPtr + 1) % historicalPcCount
vm.historicalPc[vm.historicalPcPtr] = vm.z80.PC()
}
// Execute a single instruction.
vm.z80.DoOpcode()
// Dispatch scheduled events.
vm.events.dispatch(vm.clock)
// Handle non-maskable interrupts.
if (vm.nmiLatch&vm.nmiMask) != 0 && !vm.nmiSeen {
if printDebug {
log.Print("Non-maskable interrupt %02X", vm.nmiLatch)
}
vm.z80.NonMaskableInterrupt()
vm.nmiSeen = true
// Simulate the reset button being released.
vm.resetButtonInterrupt(false)
}
// Handle interrupts.
if (vm.irqLatch & vm.irqMask) != 0 {
if printDebug {
log.Print("Maskable interrupt %02X", vm.irqLatch)
}
vm.z80.Interrupt()
}
// Print something periodically.
if vm.clock > vm.previousDumpClock+cpuHz {
now := time.Now()
if vm.previousDumpClock > 0 {
elapsed := now.Sub(vm.previousDumpTime)
computerTime := float64(vm.clock-vm.previousDumpClock) / float64(cpuHz)
log.Printf("Computer time: %.1fs, elapsed: %.1fs, mult: %.1f, slept: %dms (%d,%d)",
computerTime, elapsed.Seconds(), computerTime/elapsed.Seconds(),
vm.sleptSinceDump/time.Millisecond,
vm.cassetteRiseInterruptCount,
vm.cassetteFallInterruptCount)
vm.sleptSinceDump = 0
vm.cassetteRiseInterruptCount = 0
vm.cassetteFallInterruptCount = 0
}
vm.previousDumpTime = now
vm.previousDumpClock = vm.clock
}
// Slow down CPU if we're going too fast.
if !goFullSpeed && !*profiling && vm.clock > vm.previousAdjustClock+1000 {
now := time.Now().UnixNano()
elapsedReal := time.Duration(now - vm.startTime)
elapsedFake := time.Duration(vm.clock * cpuPeriodNs)
aheadNs := elapsedFake - elapsedReal
if aheadNs > 0 {
time.Sleep(aheadNs)
vm.sleptSinceDump += aheadNs
} else {
// Yield periodically so that we can get messages from other
// goroutines like the one sending us commands.
runtime.Gosched()
}
vm.previousAdjustClock = vm.clock
}
// Set off a timer interrupt.
if vm.clock > vm.previousTimerClock+timerCycles {
vm.handleTimer()
vm.previousTimerClock = vm.clock
}
// Update cassette state.
vm.updateCassette()
}