GBRb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/display +9 -0
- data/bin/gbrb +12 -0
- data/gbrb.gemspec +26 -0
- data/lib/gbrb.rb +9 -0
- data/lib/gbrb/bios +256 -0
- data/lib/gbrb/cartridge.rb +15 -0
- data/lib/gbrb/cpu.rb +7 -0
- data/lib/gbrb/cpu/concatenated_register.rb +44 -0
- data/lib/gbrb/cpu/flags_register.rb +42 -0
- data/lib/gbrb/cpu/instruction.rb +648 -0
- data/lib/gbrb/cpu/register.rb +44 -0
- data/lib/gbrb/cpu/register_ensemble.rb +59 -0
- data/lib/gbrb/cpu/z80.rb +584 -0
- data/lib/gbrb/gb.rb +125 -0
- data/lib/gbrb/graphics.rb +14 -0
- data/lib/gbrb/graphics/gpu.rb +196 -0
- data/lib/gbrb/graphics/mode_clock.rb +51 -0
- data/lib/gbrb/graphics/screen_client.rb +31 -0
- data/lib/gbrb/graphics/screen_server.rb +90 -0
- data/lib/gbrb/mmu.rb +96 -0
- data/lib/gbrb/version.rb +3 -0
- data/misc/parse_tiles +27 -0
- data/perf/cpu_perf_spec.rb +23 -0
- data/spec/gbrb/cartridge_spec.rb +19 -0
- data/spec/gbrb/cpu/concatenated_register_spec.rb +36 -0
- data/spec/gbrb/cpu/flags_register_spec.rb +91 -0
- data/spec/gbrb/cpu/instruction_spec.rb +283 -0
- data/spec/gbrb/cpu/register_ensemble_spec.rb +84 -0
- data/spec/gbrb/cpu/register_spec.rb +86 -0
- data/spec/gbrb/cpu/z80_spec.rb +2534 -0
- data/spec/gbrb/graphics/mode_clock_spec.rb +82 -0
- data/spec/gbrb/mmu_spec.rb +61 -0
- data/spec/gbrb/version_spec.rb +10 -0
- data/spec/spec_helper.rb +4 -0
- metadata +154 -0
data/lib/gbrb/cpu.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
|
3
|
+
module GBRb::CPU
|
4
|
+
class ConcatenatedRegister
|
5
|
+
def initialize high, low
|
6
|
+
@high = high
|
7
|
+
@low = low
|
8
|
+
@bits = @high.bits + @low.bits
|
9
|
+
end
|
10
|
+
|
11
|
+
def store value
|
12
|
+
value &= mask - 1
|
13
|
+
@high.store value >> @high.bits
|
14
|
+
@low.store value << @low.bits >> @low.bits
|
15
|
+
end
|
16
|
+
|
17
|
+
def read
|
18
|
+
@low.read + (@high.read << @high.bits)
|
19
|
+
end
|
20
|
+
|
21
|
+
def zero?
|
22
|
+
@low.zero? && @high.zero?
|
23
|
+
end
|
24
|
+
|
25
|
+
def == other
|
26
|
+
other.read == read && other.bits == bits
|
27
|
+
end
|
28
|
+
|
29
|
+
def mask
|
30
|
+
@mask ||= 0x10 ** (@bits/4)
|
31
|
+
end
|
32
|
+
|
33
|
+
def half_mask
|
34
|
+
@half_mask ||= 0x10 ** (@bits/8)
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
@high.clear
|
39
|
+
@low.clear
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :bits
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
require_relative 'register'
|
3
|
+
|
4
|
+
module GBRb::CPU
|
5
|
+
class FlagsRegister < Register
|
6
|
+
ZERO_FLAG_POS = 0x80
|
7
|
+
ADD_SUB_FLAG_POS = 0x40
|
8
|
+
HALF_CARRY_FLAG_POS = 0x20
|
9
|
+
CARRY_FLAG_POS = 0x10
|
10
|
+
|
11
|
+
FLAGS = { zero: ZERO_FLAG_POS,
|
12
|
+
add_sub: ADD_SUB_FLAG_POS,
|
13
|
+
half_carry: HALF_CARRY_FLAG_POS,
|
14
|
+
carry: CARRY_FLAG_POS
|
15
|
+
}
|
16
|
+
|
17
|
+
FLAGS.each_pair do |flag, position|
|
18
|
+
method_name = "#{flag}_flag?".to_sym
|
19
|
+
send :define_method, method_name do
|
20
|
+
not (@value & position).zero?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
FLAGS.each_pair do |flag, position|
|
25
|
+
method_name = "set_#{flag}_flag".to_sym
|
26
|
+
send :define_method, method_name do
|
27
|
+
store @value | position
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
FLAGS.each_pair do |flag, position|
|
32
|
+
method_name = "clear_#{flag}_flag".to_sym
|
33
|
+
send :define_method, method_name do
|
34
|
+
store @value & ~position
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize value=0x0
|
39
|
+
super value, 8
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,648 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
|
3
|
+
module GBRb::CPU
|
4
|
+
class Instruction
|
5
|
+
attr_reader :i, :m, :t
|
6
|
+
|
7
|
+
def initialize m=1, t=4, immediates=0, *extra
|
8
|
+
@m = m
|
9
|
+
@t = t
|
10
|
+
@immediates = immediates
|
11
|
+
end
|
12
|
+
|
13
|
+
def immediate_count
|
14
|
+
@immediates
|
15
|
+
end
|
16
|
+
|
17
|
+
def call r, mem
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def carry? left, right, mask
|
22
|
+
(mask-1 & left).public_send(@op, mask-1 & right) & mask == mask
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Stop < Instruction; end
|
27
|
+
|
28
|
+
class Scf < Instruction
|
29
|
+
def call r, mem
|
30
|
+
r.clear_half_carry_flag
|
31
|
+
r.clear_add_sub_flag
|
32
|
+
r.set_carry_flag
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Cpl < Instruction
|
37
|
+
def call r, mem
|
38
|
+
r.set_add_sub_flag
|
39
|
+
r.set_half_carry_flag
|
40
|
+
r.a.store r.a.read ^ 0xff
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Ccf < Instruction
|
45
|
+
def call r, mem
|
46
|
+
if r.carry_flag?
|
47
|
+
r.clear_carry_flag
|
48
|
+
else
|
49
|
+
r.set_carry_flag
|
50
|
+
end
|
51
|
+
r.clear_add_sub_flag
|
52
|
+
r.clear_half_carry_flag
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Inc < Instruction
|
57
|
+
def initialize register, m=1, t=4, flags=true, indirect=false
|
58
|
+
super m, t
|
59
|
+
|
60
|
+
@register = register
|
61
|
+
@flags = flags
|
62
|
+
@op = :+
|
63
|
+
@indirect = indirect
|
64
|
+
end
|
65
|
+
|
66
|
+
def call r, mem
|
67
|
+
reg = r.public_send(@register.to_sym)
|
68
|
+
tmp = reg.read
|
69
|
+
if @indirect
|
70
|
+
v = mem.read_byte(tmp) + 1
|
71
|
+
mem.write_byte(tmp, v)
|
72
|
+
else
|
73
|
+
reg.store tmp + 1
|
74
|
+
end
|
75
|
+
if @flags
|
76
|
+
r.clear_add_sub_flag
|
77
|
+
reg.zero? ? r.set_zero_flag : r.clear_zero_flag
|
78
|
+
if carry? tmp, 0x01, reg.half_mask
|
79
|
+
r.set_half_carry_flag
|
80
|
+
else
|
81
|
+
r.clear_half_carry_flag
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Dec < Instruction
|
88
|
+
def initialize register, m=1, t=4, flags=true, indirect=false
|
89
|
+
super m, t
|
90
|
+
|
91
|
+
@register = register
|
92
|
+
@flags = flags
|
93
|
+
@op = :-
|
94
|
+
@indirect = indirect
|
95
|
+
end
|
96
|
+
|
97
|
+
def call r, mem
|
98
|
+
reg = r.public_send(@register.to_sym)
|
99
|
+
tmp = reg.read
|
100
|
+
if @indirect
|
101
|
+
v = mem.read_byte(tmp) - 1
|
102
|
+
mem.write_byte(tmp, v)
|
103
|
+
else
|
104
|
+
reg.store tmp - 0x01
|
105
|
+
end
|
106
|
+
if @flags
|
107
|
+
r.set_add_sub_flag
|
108
|
+
if reg.zero?
|
109
|
+
r.set_zero_flag
|
110
|
+
else
|
111
|
+
r.clear_zero_flag
|
112
|
+
end
|
113
|
+
if carry? tmp, 0x01, reg.half_mask
|
114
|
+
r.set_half_carry_flag
|
115
|
+
else
|
116
|
+
r.clear_half_carry_flag
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Ld < Instruction
|
123
|
+
def initialize destination, target, m=1, t=4, indirect_dest=false, indirect_target=false, immediates=0, offset=0x00
|
124
|
+
super m, t, immediates
|
125
|
+
|
126
|
+
@destination = destination
|
127
|
+
@target = target
|
128
|
+
@indirect_dest = indirect_dest
|
129
|
+
@indirect_target = indirect_target
|
130
|
+
@offset = offset
|
131
|
+
end
|
132
|
+
|
133
|
+
def call r, mem, v=nil
|
134
|
+
if not v
|
135
|
+
v = r.public_send(@target.to_sym).read
|
136
|
+
end
|
137
|
+
|
138
|
+
if @indirect_target
|
139
|
+
v = mem.read_byte(v + @offset)
|
140
|
+
target_reg = r.public_send(@destination.to_sym)
|
141
|
+
if @indirect_target == :increment
|
142
|
+
target_reg.store target_reg.read + 1
|
143
|
+
elsif @indirect_target == :decrement
|
144
|
+
target_reg.store target_reg.read - 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
if @indirect_dest
|
150
|
+
if @indirect_dest == :immediate
|
151
|
+
if @target.to_s.chars.length == 2
|
152
|
+
mem.write_word(v + @offset, r.public_send(@target.to_sym).read)
|
153
|
+
else
|
154
|
+
mem.write_byte(v + @offset, r.public_send(@target.to_sym).read)
|
155
|
+
end
|
156
|
+
else
|
157
|
+
reg = r.public_send(@destination.to_sym)
|
158
|
+
mem.write_byte(reg.read + @offset, v)
|
159
|
+
if @indirect_dest == :increment
|
160
|
+
reg.store reg.read + 1
|
161
|
+
elsif @indirect_dest == :decrement
|
162
|
+
reg.store reg.read - 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
else
|
166
|
+
r.public_send(@destination.to_sym).store v
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Ldh < Ld
|
172
|
+
def call r, mem, address
|
173
|
+
if @indirect_dest
|
174
|
+
value = r.public_send(@target.to_sym).read
|
175
|
+
mem.write_byte address + @offset, value
|
176
|
+
elsif @indirect_target
|
177
|
+
value = mem.read_byte address + @offset
|
178
|
+
r.public_send(@destination.to_sym).store value
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class Ldhlsp < Instruction
|
184
|
+
def initialize m=2, t=12, immediates=1
|
185
|
+
super
|
186
|
+
@op = :+
|
187
|
+
end
|
188
|
+
|
189
|
+
def call r, mem, offset
|
190
|
+
sign = (offset >> 7 == 1) ? :- : :+
|
191
|
+
offset = ((offset ^ 0xff) + 1) & 0xff
|
192
|
+
value = r.sp.read.public_send(sign, offset)
|
193
|
+
r.hl.store value
|
194
|
+
r.clear_zero_flag
|
195
|
+
r.clear_add_sub_flag
|
196
|
+
carry?(value, 0x00, r.hl.mask) ? r.set_carry_flag : r.clear_carry_flag
|
197
|
+
carry?(value, 0x00, r.hl.half_mask) ? r.set_half_carry_flag : r.clear_half_carry_flag
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Jump < Instruction
|
202
|
+
def initialize condition, m=2, t_high=12, t_low=8, immediates=1
|
203
|
+
super m, t_low, immediates
|
204
|
+
@t_high = t_high
|
205
|
+
@condition = condition.downcase.to_sym
|
206
|
+
end
|
207
|
+
|
208
|
+
def call r, mem, offset
|
209
|
+
do_it = case @condition
|
210
|
+
when :c
|
211
|
+
r.carry_flag?
|
212
|
+
when :none
|
213
|
+
true
|
214
|
+
when :nc
|
215
|
+
not r.carry_flag?
|
216
|
+
when :nz
|
217
|
+
not r.zero_flag?
|
218
|
+
when :z
|
219
|
+
r.zero_flag?
|
220
|
+
else
|
221
|
+
false
|
222
|
+
end
|
223
|
+
|
224
|
+
if do_it
|
225
|
+
@t = @t_high
|
226
|
+
if @immediates == 1
|
227
|
+
sign = (offset >> 7 == 1) ? :- : :+
|
228
|
+
offset = ((offset ^ 0xff) + 1) & 0xff if sign == :-
|
229
|
+
r.pc.store r.pc.read.public_send(sign, offset)
|
230
|
+
else
|
231
|
+
r.pc.store offset
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
class Rl < Instruction
|
238
|
+
def initialize target, carry=false, indirect=false, m=2, t=8
|
239
|
+
super m, t
|
240
|
+
@target = target.to_sym
|
241
|
+
@carry = carry
|
242
|
+
@indirect = indirect
|
243
|
+
end
|
244
|
+
|
245
|
+
def call r, mem
|
246
|
+
if @indirect
|
247
|
+
address = r.public_send(@target).read
|
248
|
+
v = mem.read_byte(address) << 1
|
249
|
+
else
|
250
|
+
v = r.public_send(@target).read << 1
|
251
|
+
end
|
252
|
+
|
253
|
+
carry_in = if @carry
|
254
|
+
v & 0x100 == 0x100 ? 1 : 0
|
255
|
+
else
|
256
|
+
r.carry_flag? ? 1 : 0
|
257
|
+
end
|
258
|
+
|
259
|
+
v += carry_in
|
260
|
+
|
261
|
+
if @indirect
|
262
|
+
mem.write_byte(address, v)
|
263
|
+
else
|
264
|
+
r.public_send(@target).store v
|
265
|
+
end
|
266
|
+
|
267
|
+
r.clear_add_sub_flag
|
268
|
+
r.clear_half_carry_flag
|
269
|
+
v & 0xff == 0 ? r.set_zero_flag : r.clear_zero_flag
|
270
|
+
carry_out = v >> 8
|
271
|
+
carry_out == 1 ? r.set_carry_flag : r.clear_carry_flag
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class Rr < Instruction
|
276
|
+
def initialize target, carry=false, indirect=false, m=2, t=8
|
277
|
+
super m, t
|
278
|
+
@target = target.to_sym
|
279
|
+
@carry = carry
|
280
|
+
@indirect = indirect
|
281
|
+
end
|
282
|
+
|
283
|
+
def call r, mem
|
284
|
+
if @indirect
|
285
|
+
address = r.public_send(@target).read
|
286
|
+
v = mem.read_byte(address)
|
287
|
+
else
|
288
|
+
v = r.public_send(@target).read
|
289
|
+
end
|
290
|
+
|
291
|
+
carry_out = v & 0x01
|
292
|
+
|
293
|
+
carry_in = if @carry
|
294
|
+
carry_out
|
295
|
+
else
|
296
|
+
r.carry_flag? ? 1 : 0
|
297
|
+
end
|
298
|
+
|
299
|
+
v >>= 1
|
300
|
+
|
301
|
+
v += carry_in << 7
|
302
|
+
|
303
|
+
if @indirect
|
304
|
+
original = mem.read_byte address
|
305
|
+
mem.write_byte(address, v)
|
306
|
+
else
|
307
|
+
r.public_send(@target).store v
|
308
|
+
end
|
309
|
+
|
310
|
+
r.clear_add_sub_flag
|
311
|
+
r.clear_half_carry_flag
|
312
|
+
v & 0xff == 0 ? r.set_zero_flag : r.clear_zero_flag
|
313
|
+
carry_out == 1 ? r.set_carry_flag : r.clear_carry_flag
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class Swap < Instruction
|
318
|
+
def initialize target, m=2, t=8, indirect=false
|
319
|
+
@target = target
|
320
|
+
@indirect = indirect
|
321
|
+
super m, t
|
322
|
+
end
|
323
|
+
|
324
|
+
def call r, mem
|
325
|
+
initial = r.public_send(@target).read
|
326
|
+
initial = mem.read_byte(initial) if @indirect
|
327
|
+
high = initial >> 4
|
328
|
+
low = initial & ((1 << 4) - 1)
|
329
|
+
|
330
|
+
result = (low << 4) + high
|
331
|
+
|
332
|
+
if @indirect
|
333
|
+
mem.write_byte r.public_send(@target).read, result
|
334
|
+
mem.read_byte(r.public_send(@target).read) == 0 ? r.set_zero_flag : r.clear_zero_flag
|
335
|
+
else
|
336
|
+
r.public_send(@target).store result
|
337
|
+
r.public_send(@target).read == 0 ? r.set_zero_flag : r.clear_zero_flag
|
338
|
+
end
|
339
|
+
r.clear_add_sub_flag
|
340
|
+
r.clear_half_carry_flag
|
341
|
+
r.clear_carry_flag
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
class Arithmetic < Instruction
|
346
|
+
def initialize target, m, t, indirect, immediates=0
|
347
|
+
super m, t, immediates
|
348
|
+
@target = target
|
349
|
+
@indirect = indirect
|
350
|
+
end
|
351
|
+
|
352
|
+
def carry? left, right, mask
|
353
|
+
(mask-1 & left).public_send(@op, mask-1 & right) & mask == mask
|
354
|
+
end
|
355
|
+
|
356
|
+
def call r, mem, right_value
|
357
|
+
right_value ||= r.public_send(@target.to_sym).read
|
358
|
+
right_value = mem.read_byte(right_value) if @indirect
|
359
|
+
a_value = r.a.read
|
360
|
+
if carry? a_value, right_value, r.a.half_mask
|
361
|
+
r.set_half_carry_flag
|
362
|
+
else
|
363
|
+
r.clear_half_carry_flag
|
364
|
+
end
|
365
|
+
if carry? a_value, right_value, r.a.mask
|
366
|
+
r.set_carry_flag
|
367
|
+
else
|
368
|
+
r.clear_carry_flag
|
369
|
+
end
|
370
|
+
a_value = a_value.public_send(@op, right_value) & 0xff
|
371
|
+
r.a.store a_value unless @skip_store
|
372
|
+
a_value == 0x00 ? r.set_zero_flag : r.clear_zero_flag
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
class Add < Arithmetic
|
377
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
378
|
+
super
|
379
|
+
@op = :+
|
380
|
+
end
|
381
|
+
|
382
|
+
def call r, mem, right_value=nil
|
383
|
+
super
|
384
|
+
r.clear_add_sub_flag
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class AddHl < Instruction
|
389
|
+
def initialize target, m=1, t=8
|
390
|
+
super m, t
|
391
|
+
@target = target.to_sym
|
392
|
+
@op = :+
|
393
|
+
end
|
394
|
+
|
395
|
+
def call r, mem
|
396
|
+
left = r.hl.read
|
397
|
+
right = r.public_send(@target).read
|
398
|
+
r.hl.store left + right
|
399
|
+
r.set_carry_flag if carry? left, right, r.hl.mask
|
400
|
+
r.set_half_carry_flag if carry? left, right, r.hl.half_mask
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
class Sub < Arithmetic
|
405
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
406
|
+
super
|
407
|
+
@op = :-
|
408
|
+
end
|
409
|
+
|
410
|
+
def call r, mem, right_value=nil
|
411
|
+
super
|
412
|
+
r.set_add_sub_flag
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
class Cp < Sub
|
417
|
+
def initialize *args
|
418
|
+
super
|
419
|
+
@skip_store = true
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
class Adc < Add
|
424
|
+
def call r, mem, right_value=nil
|
425
|
+
carry = r.carry_flag? ? 1 : 0
|
426
|
+
super r, mem, r.public_send(@target.to_sym).read + carry
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
class Sbc < Sub
|
431
|
+
def call r, mem, right_value=nil
|
432
|
+
carry = r.carry_flag? ? 1 : 0
|
433
|
+
super r, mem, r.public_send(@target.to_sym).read + carry
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
class Boolean < Instruction
|
438
|
+
def initialize target, m, t, indirect, immediates=0
|
439
|
+
@target = target
|
440
|
+
@indirect = indirect
|
441
|
+
super m, t, immediates
|
442
|
+
end
|
443
|
+
|
444
|
+
def call r, mem, v=nil
|
445
|
+
if v
|
446
|
+
value = v
|
447
|
+
else
|
448
|
+
value = r.public_send(@target.to_sym).read
|
449
|
+
value = mem.read_byte value if @indirect
|
450
|
+
end
|
451
|
+
r.a.store r.a.read.public_send(@op, value)
|
452
|
+
r.a.zero? ? r.set_zero_flag : r.clear_zero_flag
|
453
|
+
r.clear_carry_flag
|
454
|
+
r.clear_add_sub_flag
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
class And < Boolean
|
459
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
460
|
+
@op = :&
|
461
|
+
super
|
462
|
+
end
|
463
|
+
|
464
|
+
def call r, mem, v=nil
|
465
|
+
super
|
466
|
+
r.set_half_carry_flag
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
class Or < Boolean
|
471
|
+
def initialize target, m=1, t=4, indirect=false
|
472
|
+
@op = :|
|
473
|
+
super
|
474
|
+
end
|
475
|
+
|
476
|
+
def call r, mem
|
477
|
+
super
|
478
|
+
r.clear_half_carry_flag
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
class Xor < Boolean
|
483
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
484
|
+
@op = :^
|
485
|
+
super
|
486
|
+
end
|
487
|
+
|
488
|
+
def call r, mem, right_value=nil
|
489
|
+
super
|
490
|
+
r.clear_half_carry_flag
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
class Pop < Instruction
|
495
|
+
def initialize target, m=1, t=12
|
496
|
+
super m, t
|
497
|
+
@targets = target.to_s.chars.map{|a| a.to_sym}.reverse
|
498
|
+
end
|
499
|
+
|
500
|
+
def call r, mem
|
501
|
+
@targets.each do |target|
|
502
|
+
r.public_send(target).store mem.read_byte(r.sp.read)
|
503
|
+
r.sp.store r.sp.read + 1
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
class Push < Instruction
|
509
|
+
def initialize target, m=1, t=16
|
510
|
+
super m, t
|
511
|
+
@targets = target.to_s.chars.map{|a| a.to_sym}
|
512
|
+
end
|
513
|
+
|
514
|
+
def call r, mem
|
515
|
+
@targets.each do |target|
|
516
|
+
r.sp.store r.sp.read - 1
|
517
|
+
mem.write_byte(r.sp.read, r.public_send(target).read)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
class Rst < Instruction
|
523
|
+
def initialize offset
|
524
|
+
@offset = offset
|
525
|
+
super 1, 16
|
526
|
+
end
|
527
|
+
|
528
|
+
def call r, mem
|
529
|
+
r.sp.store r.sp.read - 2
|
530
|
+
mem.write_word r.sp.read, r.pc.read
|
531
|
+
r.pc.store 0x0000 + @offset
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
class Call < Instruction
|
536
|
+
def initialize m=3, t=24, immediates=2, condition=:none
|
537
|
+
@condition = condition
|
538
|
+
super
|
539
|
+
end
|
540
|
+
|
541
|
+
def call r, mem, addr
|
542
|
+
condition_met = case @condition
|
543
|
+
when :Z
|
544
|
+
r.zero_flag?
|
545
|
+
when :none
|
546
|
+
true
|
547
|
+
else
|
548
|
+
false
|
549
|
+
end
|
550
|
+
if condition_met
|
551
|
+
r.sp.store r.sp.read - 2
|
552
|
+
mem.write_word(r.sp.read, r.pc.read)
|
553
|
+
r.pc.store addr
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
class Ret < Instruction
|
559
|
+
def initialize m=1, t=16, t_high=16, condition=:none
|
560
|
+
@condition = condition
|
561
|
+
@t_high = t_high
|
562
|
+
super m, t, 0
|
563
|
+
end
|
564
|
+
|
565
|
+
def call r, mem
|
566
|
+
condition_met = case @condition
|
567
|
+
when :NZ
|
568
|
+
not r.zero_flag?
|
569
|
+
when :none
|
570
|
+
true
|
571
|
+
else
|
572
|
+
false
|
573
|
+
end
|
574
|
+
if condition_met
|
575
|
+
@t = @t_high
|
576
|
+
low = mem.read_byte(r.sp.read)
|
577
|
+
r.sp.store r.sp.read + 1
|
578
|
+
high = mem.read_byte(r.sp.read)
|
579
|
+
r.sp.store r.sp.read + 1
|
580
|
+
|
581
|
+
r.pc.store (high << 8) + low
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
class EnableInterrupts < Instruction; end
|
587
|
+
class DisableInterrupts < Instruction; end
|
588
|
+
|
589
|
+
class Res < Instruction
|
590
|
+
def initialize bit, target, indirect=false, m=2, t=16
|
591
|
+
super m, t
|
592
|
+
@bit = bit
|
593
|
+
@mask = 0xff - 0b10 ** bit
|
594
|
+
@target = target.to_sym
|
595
|
+
@indirect = indirect
|
596
|
+
end
|
597
|
+
|
598
|
+
def call r, mem
|
599
|
+
if @indirect
|
600
|
+
addr = r.public_send(@target).read
|
601
|
+
mem.write_byte(addr, mem.read_byte(addr) & @mask)
|
602
|
+
else
|
603
|
+
r.public_send(@target).store r.public_send(@target).read & @mask
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
class Set < Instruction
|
609
|
+
def initialize bit, target, indirect=false, m=2, t=16
|
610
|
+
super m, t
|
611
|
+
@bit = bit
|
612
|
+
@mask = 0b10 ** bit
|
613
|
+
@target = target.to_sym
|
614
|
+
@indirect = indirect
|
615
|
+
end
|
616
|
+
|
617
|
+
def call r, mem
|
618
|
+
if @indirect
|
619
|
+
addr = r.public_send(@target).read
|
620
|
+
mem.write_byte(addr, mem.read_byte(addr) | @mask)
|
621
|
+
else
|
622
|
+
r.public_send(@target).store r.public_send(@target).read | @mask
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
class Bit < Instruction
|
628
|
+
def initialize bit, target, indirect=false, m=2, t=8
|
629
|
+
super m,t
|
630
|
+
@target = target
|
631
|
+
@bit = bit
|
632
|
+
@indirect = indirect
|
633
|
+
@mask = 1 << @bit
|
634
|
+
end
|
635
|
+
|
636
|
+
def call r, mem
|
637
|
+
if @indirect
|
638
|
+
v = mem.read_byte r.public_send(@target).read
|
639
|
+
else
|
640
|
+
v = r.public_send(@target).read
|
641
|
+
end
|
642
|
+
|
643
|
+
v & @mask != @mask ? r.set_zero_flag : r.clear_zero_flag
|
644
|
+
r.set_half_carry_flag
|
645
|
+
r.clear_add_sub_flag
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|