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.
data/lib/gbrb/cpu.rb ADDED
@@ -0,0 +1,7 @@
1
+ module GBRb
2
+ module CPU
3
+ class CPUError < StandardError; end
4
+ class UnknownRegister < CPUError; end
5
+ class InstructionNotImplemented < CPUError; end
6
+ end
7
+ end
@@ -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