-
Notifications
You must be signed in to change notification settings - Fork 2
/
simulate.go
126 lines (100 loc) · 2.44 KB
/
simulate.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package dodosim
import (
"strings"
"time"
)
type Simulator struct {
Renderer Renderer
Speaker Speaker
Input chan string
Ticker *time.Ticker
IntervalCallback func() bool
Complete func(*Cpu)
CyclesPerFrame func(cycles uint64)
}
func Simulate(s *Simulator, firmware, game []byte) {
bus := new(Bus)
bus.New()
ram := new(Ram)
bus.Add(ram)
rom := new(Rom)
ssd1305 := new(Ssd1305)
ssd1305.New(ram, s.Renderer)
bus.Add(ssd1305)
gamepad := new(Gamepad)
gamepad.New()
fram := new(Fram)
fram.New(game, nil)
via := new(Via)
via.New(gamepad, fram, s.Speaker)
bus.Add(via)
acia := new(Acia)
bus.Add(acia)
for i, b := range firmware {
rom[i] = b
}
bus.Add(rom)
bus.BuildMap()
cpu := new(Cpu)
cpu.Reset(bus)
BuildTable()
var cycles uint64 = 0
syncer := time.NewTicker(50 * time.Millisecond).C
var lastOp uint8 = 0
var waitTester int = 0
var waitingForInterrupt bool = false
var missedFrames int = 0
L:
for {
opcode := bus.Read(cpu.PC)
cpu.PC++
cpu.Status |= Constant
c := Execute(cpu, bus, opcode)
cycles += uint64(c)
if (lastOp == 0xA5 && opcode == 0xF0) || (lastOp == 0xF0 && opcode == 0xA5) {
waitTester++
} else {
waitTester = 0
}
// If Lda, Beq sequences happens 5 times in a row then assume we are waiting for interrupt
if waitTester == 10 { // Getting in here tells us that a complete game cycle was performed
s.CyclesPerFrame(uint64(missedFrames*50000) + cycles)
missedFrames = 0
cycles = 0
waitTester = 0
waitingForInterrupt = true
} else if cycles >= 50000 { // If we hit 50000 cycles then that means we need to pause for a whole additional interrupt cycle
cycles = 0
waitingForInterrupt = true
missedFrames++
}
if waitingForInterrupt {
<-syncer
cpu.Irq(bus)
waitingForInterrupt = false
}
lastOp = opcode
select {
case v, ok := <-s.Input:
if !ok {
break
} else {
gamepad.A = strings.Contains(v, "A")
gamepad.B = strings.Contains(v, "B")
gamepad.U = strings.Contains(v, "U")
gamepad.D = strings.Contains(v, "D")
gamepad.L = strings.Contains(v, "L")
gamepad.R = strings.Contains(v, "R")
if strings.Contains(v, "X") {
s.Complete(cpu)
return
}
}
case <-s.Ticker.C:
if !s.IntervalCallback() {
break L
}
default:
}
}
}