forked from janogonzalez/mimey
-
Notifications
You must be signed in to change notification settings - Fork 2
/
test_emulator.rb
181 lines (155 loc) · 4.67 KB
/
test_emulator.rb
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
require 'mimey'
require 'json'
require 'pp'
class ReferenceImplementationTester
attr_reader :step_counter
def initialize
@step_counter = Mimey::StepCounter.new
end
def run_test
reference_result.each do |line|
message = JSON.parse(line)
next unless step_data = message["step"]
r_data = %w{a b c d e f h l pc}.map do |reg|
step_data["r"][reg]
end
registers = Mimey::StepCounter::Registers.new(*r_data)
gpu_r_data = %w{intfired line raster mode modeclocks scrn bg_palette bgtilebase bgmapbase lcdon bgon}.map do |reg|
if reg == "bg_palette" then
step_data["gpu_r"][reg]
else
step_data["gpu_r"][reg]
end
end
gpu_registers = Mimey::StepCounter::GPURegisters.new(*gpu_r_data)
step = Mimey::StepCounter::Step.new(
step_data["total_steps"], step_data["last_op"], registers, gpu_registers
)
step_counter << step
end
puts "finished parsing reference implementation"
GC.start
end
def reference_result
cache_path = "cache/#{reference_hash}.txt"
if File.exist?(cache_path) then
puts "using cache"
node_result = File.read(cache_path)
else
puts "not found cache for #{reference_hash}, running nodejs"
node_result = `node reference/emulator.js`
File.write(cache_path, node_result)
end
node_result.split("\n")
end
def reference_hash
Digest::MD5.hexdigest(File.read('reference/emulator.js'))
end
end
class StepCounterComparer
def initialize(node_step_counter, ruby_step_counter)
@node_step_counter = node_step_counter
@ruby_step_counter = ruby_step_counter
end
def compare
if step_counter.steps.length != node_step_counter.steps.length then
puts "warning, different step count between reference implementation and ruby implementation"
puts "ruby: #{step_counter.steps.length}, node: #{node_step_counter.steps.length}"
end
first_different_step, index = step_counter.compare_with(node_step_counter)
if first_different_step then
when_different(first_different_step, index)
:different
else
when_equal
:equal
end
end
def when_different(first_different_step, index)
puts "found first different step! (found at step ##{index + 1})"
nod_step = node_step_counter.steps[index]
rub_step = step_counter.steps[index]
puts ""
puts "ruby: #{rub_step.inspect_different_variables(nod_step)}" if rub_step
puts "node: #{nod_step.inspect_different_variables(rub_step)}" if nod_step
puts ""
implementations = {ruby: step_counter, node: node_step_counter}
implementations.each do |impl, counter|
(index - 5 .. index + 5).each do |step_id|
if step_id == index then
puts "----> #{counter.steps[step_id]} <----"
else
puts "#{impl}: #{counter.steps[step_id]}"
end
end
puts ""
end
end
def when_equal
puts "ran #{step_counter.steps.length} steps"
puts "OMG I couldn't find any errors!"
end
private
attr_reader :ruby_step_counter, :node_step_counter
alias_method :step_counter, :ruby_step_counter
end
class EmulatorTester
def initialize
@emulator = Mimey::Emulator.new
# emulator.debug_mode = true
# emulator.step_by_step = true
emulator.load_rom("./test_roms/opus5.gb")
emulator.reset
end
def run_normally(frames: 9)
frames.times do
emulator.frame
render_screen(strict: true)
end
end
def run_test
run_node_test
ruby_step_counter = Mimey::StepCounter.new
emulator.step_counter = ruby_step_counter
run_ruby_test
step_counter_comparer = StepCounterComparer.new(ruby_step_counter, node_tester.step_counter)
case step_counter_comparer.compare
when :equal
render_screen(test_nils: true)
end
end
def run_node_test
puts "running node test"
@node_tester = ReferenceImplementationTester.new
node_tester.run_test
end
def run_ruby_test
puts "running ruby implementation"
9.times { emulator.frame; GC.start}
end
def render_screen(test_nils: false, strict: false)
screen = Mimey::LcdScreen.new
scrn = emulator.gpu.scrn
if test_nils && scrn.compact.length != scrn.length then
puts "warning, the screen has some nils"
puts scrn.length
puts scrn.compact.length
puts scrn[0..100].inspect
puts scrn[0..100].compact.inspect
end
if strict then
if scrn.first.nil? then
return
end
end
# clear screen
puts "\e[H\e[2J"
screen.screen = scrn
screen.render
end
attr_reader :emulator
private
attr_reader :node_tester
end
emulator_tester = EmulatorTester.new
emulator_tester.run_normally(frames: 20)