GBRb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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