GBRb 0.1.0 → 0.2.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/README.md +9 -1
  4. data/bin/gbrb +3 -3
  5. data/doc/images/blargg_cpu.png +0 -0
  6. data/doc/images/cpu_01.png +0 -0
  7. data/doc/images/cpu_03.png +0 -0
  8. data/doc/images/cpu_04.png +0 -0
  9. data/doc/images/cpu_05.png +0 -0
  10. data/doc/images/cpu_06.png +0 -0
  11. data/doc/images/cpu_07.png +0 -0
  12. data/doc/images/cpu_08.png +0 -0
  13. data/doc/images/cpu_09.png +0 -0
  14. data/doc/images/cpu_10.png +0 -0
  15. data/doc/images/cpu_11.png +0 -0
  16. data/doc/images/nintendo_logo.png +0 -0
  17. data/doc/images/opus5.png +0 -0
  18. data/doc/images/test.gb.png +0 -0
  19. data/doc/images/ttt.png +0 -0
  20. data/lib/gbrb.rb +7 -0
  21. data/lib/gbrb/cartridge.rb +21 -8
  22. data/lib/gbrb/cartridge/cartridge.rb +187 -0
  23. data/lib/gbrb/cpu/concatenated_register.rb +1 -1
  24. data/lib/gbrb/cpu/z80.rb +575 -498
  25. data/lib/gbrb/gb.rb +102 -32
  26. data/lib/gbrb/graphics.rb +1 -1
  27. data/lib/gbrb/graphics/gpu.rb +38 -30
  28. data/lib/gbrb/graphics/mode_clock.rb +31 -30
  29. data/lib/gbrb/graphics/screen_client.rb +3 -2
  30. data/lib/gbrb/instruction_set.rb +16 -0
  31. data/lib/gbrb/instruction_set/arithmetic.rb +238 -0
  32. data/lib/gbrb/instruction_set/bitwise.rb +64 -0
  33. data/lib/gbrb/instruction_set/boolean.rb +61 -0
  34. data/lib/gbrb/instruction_set/call.rb +40 -0
  35. data/lib/gbrb/instruction_set/carry.rb +23 -0
  36. data/lib/gbrb/instruction_set/cpl.rb +12 -0
  37. data/lib/gbrb/instruction_set/daa.rb +33 -0
  38. data/lib/gbrb/instruction_set/instruction.rb +23 -0
  39. data/lib/gbrb/instruction_set/jump.rb +47 -0
  40. data/lib/gbrb/instruction_set/ld.rb +241 -0
  41. data/lib/gbrb/instruction_set/return.rb +43 -0
  42. data/lib/gbrb/instruction_set/rotate.rb +178 -0
  43. data/lib/gbrb/instruction_set/rst.rb +16 -0
  44. data/lib/gbrb/instruction_set/special.rb +37 -0
  45. data/lib/gbrb/instruction_set/stack.rb +34 -0
  46. data/lib/gbrb/instruction_set/swap.rb +32 -0
  47. data/lib/gbrb/mmu.rb +60 -35
  48. data/lib/gbrb/options.rb +54 -0
  49. data/lib/gbrb/timer.rb +114 -0
  50. data/lib/gbrb/version.rb +1 -1
  51. data/misc/dump_diff +133 -0
  52. data/perf/cpu_perf_spec.rb +2 -2
  53. data/spec/gbrb/cartridge/cartridge_spec.rb +19 -0
  54. data/spec/gbrb/cartridge/mbc1_spec.rb +83 -0
  55. data/spec/gbrb/cpu/z80_spec.rb +92 -2
  56. data/spec/gbrb/{cpu/instruction_spec.rb → instruction_set/arithmetic_spec.rb} +21 -100
  57. data/spec/gbrb/instruction_set/boolean_spec.rb +50 -0
  58. data/spec/gbrb/instruction_set/instruction_spec.rb +22 -0
  59. data/spec/gbrb/instruction_set/ld_spec.rb +21 -0
  60. data/spec/gbrb/instruction_set/special_spec.rb +22 -0
  61. data/spec/gbrb/mmu_spec.rb +1 -1
  62. data/spec/gbrb/timer_spec.rb +26 -0
  63. metadata +53 -9
  64. data/lib/gbrb/cpu/instruction.rb +0 -648
  65. data/spec/gbrb/cartridge_spec.rb +0 -19
  66. data/spec/gbrb/graphics/mode_clock_spec.rb +0 -82
@@ -3,7 +3,7 @@ require 'benchmark'
3
3
 
4
4
  module GBRb::CPU
5
5
  describe Z80 do
6
- xit "full speed" do
6
+ it "full speed" do
7
7
  n = 1_000_000
8
8
  class Z80
9
9
  def fetch
@@ -14,7 +14,7 @@ module GBRb::CPU
14
14
 
15
15
  @cpu = Z80.new
16
16
  elapsed_time = Benchmark.realtime do
17
- n.times { @cpu.run }
17
+ n.times { @cpu.step }
18
18
  end
19
19
 
20
20
  elapsed_time.should be < 1.0
@@ -0,0 +1,19 @@
1
+ require_relative '../../spec_helper.rb'
2
+
3
+ require_relative '../../../lib/gbrb/cartridge/cartridge'
4
+
5
+ module GBRb
6
+ describe BaseCartridge do
7
+ it "is initialized with an IO object" do
8
+ c = BaseCartridge.new StringIO.new ""
9
+ end
10
+
11
+ it "separates data into banks" do
12
+ raw_data = StringIO.new(0x01.chr * MEMORY_BANK_SIZE + 0x02.chr * MEMORY_BANK_SIZE)
13
+ c = BaseCartridge.new raw_data
14
+ c.rom_bank(0).each {|b| b.should eq 0x01}
15
+ c.rom_bank(1).each {|b| b.should eq 0x02}
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,83 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/gbrb/cartridge'
3
+
4
+ module GBRb
5
+ describe MBC1 do
6
+ context 'post_initialize' do
7
+ let(:m) { MBC1.new StringIO.new '' }
8
+
9
+ it 'sets the mode' do
10
+ m.instance_eval('@mode').should eq :rom
11
+ end
12
+
13
+ it 'sets the rom bank number' do
14
+ m.instance_eval('@rom_bank_number').should eq 1
15
+ end
16
+
17
+ it 'sets the ram bank number' do
18
+ m.instance_eval('@ram_bank_number').should eq 0
19
+ end
20
+
21
+ it 'sets the ram status' do
22
+ m.instance_eval('@ram_status').should eq :disabled
23
+ end
24
+ end
25
+
26
+ context 'write_byte' do
27
+ let(:m) { MBC1.new StringIO.new '' }
28
+
29
+ context 'Swap current ROM bank' do
30
+ it 'changes rom bank' do
31
+ m.write_byte 0x2345, 7
32
+ m.instance_eval('@rom_bank_number').should eq 7
33
+ end
34
+
35
+ it 'maps 0 to bank 1' do
36
+ m.write_byte 0x2941, 0
37
+ m.instance_eval('@rom_bank_number').should eq 1
38
+ end
39
+
40
+ it 'only sets the lowest 5 bits' do
41
+ m.write_byte 0x3456, 0x33
42
+ m.instance_eval('@rom_bank_number').should eq 0x13
43
+ end
44
+
45
+ it 'sets the upper 2 bits when in rom mode' do
46
+ m.instance_eval('@mode = :rom')
47
+ m.write_byte 0x4567, 3
48
+ m.instance_eval('@rom_bank_number').should eq 0x61
49
+ end
50
+
51
+ it 'sets the ram bank when in ram mode' do
52
+ m.instance_eval('@mode = :ram')
53
+ m.write_byte 0x5678, 2
54
+ m.instance_eval('@ram_bank_number').should eq 0x02
55
+ end
56
+ end
57
+
58
+ context 'Change operating mode' do
59
+ it '0x01 is RAM' do
60
+ m.write_byte 0x6820, 0x01
61
+ m.instance_eval('@mode').should eq :ram
62
+ end
63
+
64
+ it '0x00 is ROM' do
65
+ m.write_byte 0x7481, 0x00
66
+ m.instance_eval('@mode').should eq :rom
67
+ end
68
+ end
69
+
70
+ context 'Enable/disable ram' do
71
+ it 'disables ram with 0x00' do
72
+ m.write_byte 0x1234, 0x00
73
+ m.instance_eval('@ram_status').should eq :disabled
74
+ end
75
+
76
+ it 'enables ram with 0x0a' do
77
+ m.write_byte 0x0123, 0x0a
78
+ m.instance_eval('@ram_status').should eq :enabled
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -80,7 +80,11 @@ module GBRb
80
80
  reg.clear
81
81
  reg.read.should eq 0x00
82
82
  run pop_opcode
83
- reg.read.should eq value
83
+ if register == :af
84
+ reg.read.should eq value & 0xfff0
85
+ else
86
+ reg.read.should eq value
87
+ end
84
88
  end
85
89
 
86
90
  it "push/pop bc" do
@@ -455,6 +459,13 @@ module GBRb
455
459
  run 0x28
456
460
  @z.r.pc.read.should eq 0xff96
457
461
  end
462
+
463
+ it "jr a16" do
464
+ @z.r.pc.store 0x0002
465
+ @z.mmu.write_word 0x0002, 0x4231
466
+ run 0xc3
467
+ @z.r.pc.read.should eq 0x4231
468
+ end
458
469
  end
459
470
 
460
471
  context "ld (immediate value)" do
@@ -623,6 +634,13 @@ module GBRb
623
634
  @z.mmu.read_byte(address).should eq @z.r.a.read
624
635
  @z.r.hl.read.should eq address + 1
625
636
  end
637
+
638
+ it "ld a hl+" do
639
+ address = @z.r.hl.read
640
+ run 0x2a
641
+ @z.mmu.read_byte(address).should eq @z.r.a.read
642
+ @z.r.hl.read.should eq address +1
643
+ end
626
644
  end
627
645
 
628
646
  context "ld decrement" do
@@ -633,6 +651,14 @@ module GBRb
633
651
  @z.mmu.read_byte(address).should eq @z.r.a.read
634
652
  @z.r.hl.read.should eq address - 1
635
653
  end
654
+
655
+ it "ld a hl-" do
656
+ @z.r.hl.store 0x4c
657
+ address = @z.r.hl.read
658
+ run 0x3a
659
+ @z.mmu.read_byte(address).should eq @z.r.a.read
660
+ @z.r.hl.read.should eq address - 1
661
+ end
636
662
  end
637
663
 
638
664
  context "ld (indirect destination)" do
@@ -1300,8 +1326,9 @@ module GBRb
1300
1326
  before(:each) { @z.reset }
1301
1327
 
1302
1328
  def run op
1329
+ @z.r.pc.store 7
1330
+ @z.mmu.write_byte 7, op.to_i
1303
1331
  @z.dispatch @z.decode 0xcb
1304
- @z.dispatch @z.decode op.to_i
1305
1332
  end
1306
1333
 
1307
1334
  context "#reset" do
@@ -2475,6 +2502,69 @@ module GBRb
2475
2502
  end
2476
2503
  end
2477
2504
 
2505
+ context "srl" do
2506
+ it "srl b" do
2507
+ @z.r.b.store 0b10101010
2508
+ run 0x38
2509
+ @z.r.b.read.should eq 0b01010101
2510
+ @z.r.carry_flag?.should be_false
2511
+ end
2512
+
2513
+ it "srl c" do
2514
+ @z.r.c.store 0b00000001
2515
+ run 0x39
2516
+ @z.r.c.read.should eq 0b00000000
2517
+ @z.r.carry_flag?.should be_true
2518
+ @z.r.zero_flag?.should be_true
2519
+ end
2520
+
2521
+ it "srl d" do
2522
+ @z.r.d.store 0b00000000
2523
+ @z.r.set_carry_flag
2524
+ run 0x3a
2525
+ @z.r.d.read.should eq 0b00000000
2526
+ @z.r.carry_flag?.should be_false
2527
+ @z.r.zero_flag?.should be_true
2528
+ end
2529
+
2530
+ it "srl e" do
2531
+ @z.r.e.store 0b10011001
2532
+ run 0x3b
2533
+ @z.r.e.read.should eq 0b01001100
2534
+ @z.r.carry_flag?.should be_true
2535
+ end
2536
+
2537
+ it "srl h" do
2538
+ @z.r.h.store 0b11111111
2539
+ run 0x3c
2540
+ @z.r.h.read.should eq 0b01111111
2541
+ @z.r.carry_flag?.should be_true
2542
+ end
2543
+
2544
+ it "srl l" do
2545
+ @z.r.l.store 0b11111110
2546
+ run 0x3d
2547
+ @z.r.l.read.should eq 0b01111111
2548
+ @z.r.carry_flag?.should be_false
2549
+ end
2550
+
2551
+ it "srl (hl)" do
2552
+ @z.mmu.write_byte 0x1d3b, 0b01010101
2553
+ @z.r.hl.store 0x1d3b
2554
+ run 0x3e
2555
+ @z.mmu.read_byte(0x1d3b).should eq 0b00101010
2556
+ @z.r.carry_flag?.should be_true
2557
+ @z.r.hl.read.should eq 0x1d3b
2558
+ end
2559
+
2560
+ it "srl a" do
2561
+ @z.r.a.store 0b00100110
2562
+ run 0x3f
2563
+ @z.r.a.read.should eq 0b0010011
2564
+ @z.r.carry_flag?.should be_false
2565
+ end
2566
+ end
2567
+
2478
2568
  context "swap" do
2479
2569
  it "swap b" do
2480
2570
  @z.r.b.store 0b10101010
@@ -1,26 +1,8 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/gbrb/cpu/instruction.rb'
2
+ require_relative '../../../lib/gbrb/instruction_set/arithmetic.rb'
3
3
  require_relative '../../../lib/gbrb/cpu/register.rb'
4
4
 
5
- module GBRb::CPU
6
- describe Instruction do
7
- it "has a m time" do
8
- i = Instruction.new
9
- i.m.should be_true
10
- end
11
-
12
- it "has a t time" do
13
- i = Instruction.new
14
- i.t.should be_true
15
- end
16
-
17
- it "is callable" do
18
- i = Instruction.new
19
- world = double
20
- i.call world, nil
21
- end
22
- end
23
-
5
+ module GBRb::InstructionSet
24
6
  describe Inc do
25
7
  it "clears the add sub flag" do
26
8
  fake_register = double.as_null_object
@@ -34,7 +16,7 @@ module GBRb::CPU
34
16
  end
35
17
 
36
18
  it "sets the zero flag when zero" do
37
- a = Register.new 0xff
19
+ a = GBRb::CPU::Register.new 0xff
38
20
  world = double.as_null_object
39
21
  world.should_receive(:a).at_least(1).times.and_return(a)
40
22
  world.should_receive(:set_zero_flag)
@@ -43,7 +25,7 @@ module GBRb::CPU
43
25
  end
44
26
 
45
27
  it "does not set the carry flag when value overflows register" do
46
- a = Register.new 0xff
28
+ a = GBRb::CPU::Register.new 0xff
47
29
  world = double.as_null_object
48
30
  world.should_receive(:a).at_least(1).times.and_return(a)
49
31
  world.should_not_receive(:set_carry_flag)
@@ -52,7 +34,7 @@ module GBRb::CPU
52
34
  end
53
35
 
54
36
  it "sets the half carry flag when low nibble overflows" do
55
- a = Register.new 0x0f
37
+ a = GBRb::CPU::Register.new 0x0f
56
38
  world = double.as_null_object
57
39
  world.should_receive(:a).at_least(1).times.and_return(a)
58
40
  world.should_receive(:set_half_carry_flag)
@@ -62,7 +44,7 @@ module GBRb::CPU
62
44
  end
63
45
 
64
46
  it "does not touch the flags if initialized with false" do
65
- a = Register.new 0xff
47
+ a = GBRb::CPU::Register.new 0xff
66
48
  world = double.as_null_object
67
49
  world.should_receive(:a).at_least(1).times.and_return(a)
68
50
  world.should_not_receive(:set_carry_flag)
@@ -86,7 +68,7 @@ module GBRb::CPU
86
68
  end
87
69
 
88
70
  it "sets the zero flag when zero" do
89
- a = Register.new 0x01
71
+ a = GBRb::CPU::Register.new 0x01
90
72
  world = double.as_null_object
91
73
  world.should_receive(:a).at_least(1).times.and_return(a)
92
74
  world.should_not_receive(:set_half_carry_flag)
@@ -96,7 +78,7 @@ module GBRb::CPU
96
78
  end
97
79
 
98
80
  it "does not set the carry flag when value underflows register" do
99
- a = Register.new 0x00
81
+ a = GBRb::CPU::Register.new 0x00
100
82
  world = double.as_null_object
101
83
  world.should_receive(:a).at_least(1).times.and_return(a)
102
84
  world.should_not_receive(:set_carry_flag)
@@ -105,7 +87,7 @@ module GBRb::CPU
105
87
  end
106
88
 
107
89
  it "sets the half carry flag when borrowing from high nibble" do
108
- a = Register.new 0x10
90
+ a = GBRb::CPU::Register.new 0x10
109
91
  world = double.as_null_object
110
92
  world.should_receive(:a).at_least(1).times.and_return(a)
111
93
  world.should_receive(:set_half_carry_flag)
@@ -115,7 +97,7 @@ module GBRb::CPU
115
97
  end
116
98
 
117
99
  it "does not touch the flags if initialized with false" do
118
- a = Register.new 0xff
100
+ a = GBRb::CPU::Register.new 0xff
119
101
  world = double.as_null_object
120
102
  world.should_receive(:a).at_least(1).times.and_return(a)
121
103
  world.should_not_receive(:set_half_carry_flag)
@@ -125,25 +107,9 @@ module GBRb::CPU
125
107
  end
126
108
  end
127
109
 
128
- describe Ld do
129
- it "does not set any flags" do
130
- b = Register.new
131
- c = Register.new 0x34
132
- world = double.as_null_object
133
- world.should_receive(:b).at_least(1).times.and_return(b)
134
- world.should_receive(:c).at_least(1).times.and_return(c)
135
- world.should_not_receive(:set_half_carry_flag)
136
- world.should_not_receive(:set_carry_flag)
137
- world.should_not_receive(:set_add_sub_flag)
138
- world.should_not_receive(:set_zero_flag)
139
- i = Ld.new :b, :c
140
- i.call world, nil
141
- end
142
- end
143
-
144
110
  describe Add do
145
111
  it "clears the add sub flag" do
146
- a = Register.new 0x34
112
+ a = GBRb::CPU::Register.new 0x34
147
113
  world = double.as_null_object
148
114
  world.should_receive(:clear_add_sub_flag)
149
115
  world.should_receive(:a).at_least(1).times.and_return(a)
@@ -152,7 +118,7 @@ module GBRb::CPU
152
118
  end
153
119
 
154
120
  it "sets the zero flag when zero" do
155
- a = Register.new 0x80
121
+ a = GBRb::CPU::Register.new 0x80
156
122
  world = double.as_null_object
157
123
  world.should_receive(:a).at_least(1).times.and_return(a)
158
124
  world.should_receive(:set_zero_flag)
@@ -162,7 +128,7 @@ module GBRb::CPU
162
128
  end
163
129
 
164
130
  it "sets the carry flag when value overflows register" do
165
- a = Register.new 0xff
131
+ a = GBRb::CPU::Register.new 0xff
166
132
  world = double.as_null_object
167
133
  world.should_receive(:a).at_least(1).times.and_return(a)
168
134
  i = Add.new :a
@@ -171,7 +137,7 @@ module GBRb::CPU
171
137
  end
172
138
 
173
139
  it "sets the half carry flag when low nibble overflows" do
174
- a = Register.new 0x0d
140
+ a = GBRb::CPU::Register.new 0x0d
175
141
  world = double.as_null_object
176
142
  world.should_receive(:a).at_least(1).times.and_return(a)
177
143
  world.should_receive(:set_half_carry_flag)
@@ -183,7 +149,7 @@ module GBRb::CPU
183
149
 
184
150
  describe Sub do
185
151
  it "sets the add sub flag" do
186
- a = Register.new 0x34
152
+ a = GBRb::CPU::Register.new 0x34
187
153
  world = double.as_null_object
188
154
  world.should_receive(:set_add_sub_flag)
189
155
  world.should_receive(:a).at_least(1).times.and_return(a)
@@ -192,7 +158,7 @@ module GBRb::CPU
192
158
  end
193
159
 
194
160
  it "sets the zero flag when zero" do
195
- a = Register.new 0x80
161
+ a = GBRb::CPU::Register.new 0x80
196
162
  world = double.as_null_object
197
163
  world.should_receive(:a).at_least(1).times.and_return(a)
198
164
  world.should_receive(:set_zero_flag)
@@ -202,7 +168,7 @@ module GBRb::CPU
202
168
  end
203
169
 
204
170
  it "sets the carry flag when value underflows register" do
205
- a = Register.new 0x00
171
+ a = GBRb::CPU::Register.new 0x00
206
172
  world = double.as_null_object
207
173
  world.should_receive(:a).at_least(1).times.and_return(a)
208
174
  i = Sub.new :a
@@ -210,8 +176,8 @@ module GBRb::CPU
210
176
  end
211
177
 
212
178
  it "sets the half carry flag when borrowing from high nibble" do
213
- a = Register.new 0x10
214
- b = Register.new 0x01
179
+ a = GBRb::CPU::Register.new 0x10
180
+ b = GBRb::CPU::Register.new 0x01
215
181
  world = double.as_null_object
216
182
  world.should_receive(:a).at_least(1).times.and_return(a)
217
183
  world.should_receive(:b).at_least(1).times.and_return(b)
@@ -224,8 +190,8 @@ module GBRb::CPU
224
190
 
225
191
  describe Cp do
226
192
  it "does not modify the accumulator" do
227
- a = Register.new 0x1f
228
- b = Register.new 0x3b
193
+ a = GBRb::CPU::Register.new 0x1f
194
+ b = GBRb::CPU::Register.new 0x3b
229
195
  world = double.as_null_object
230
196
  world.stub(a: a)
231
197
  world.stub(b: b)
@@ -235,49 +201,4 @@ module GBRb::CPU
235
201
  i.call world, nil
236
202
  end
237
203
  end
238
-
239
- describe And do
240
- it "sets the correct flags" do
241
- a = Register.new 0x73
242
- b = Register.new 0xd2
243
- world = double.as_null_object
244
- world.stub(:a).and_return(a)
245
- world.stub(:b).and_return(b)
246
- world.should_receive :clear_add_sub_flag
247
- world.should_receive :set_half_carry_flag
248
- world.should_receive :clear_carry_flag
249
- i = And.new :a, :b
250
- i.call world, nil
251
- end
252
- end
253
-
254
- describe Or do
255
- it "sets the correct flags" do
256
- a = Register.new 0x81
257
- b = Register.new 0xaf
258
- world = double.as_null_object
259
- world.stub(:a).and_return(a)
260
- world.stub(:b).and_return(b)
261
- world.should_receive :clear_add_sub_flag
262
- world.should_receive :clear_half_carry_flag
263
- world.should_receive :clear_carry_flag
264
- i= Or.new :a, :b
265
- i.call world, nil
266
- end
267
- end
268
-
269
- describe Xor do
270
- it "sets the correct flags" do
271
- a = Register.new 0x81
272
- b = Register.new 0xaf
273
- world = double.as_null_object
274
- world.stub(:a).and_return(a)
275
- world.stub(:b).and_return(b)
276
- world.should_receive :clear_add_sub_flag
277
- world.should_receive :clear_half_carry_flag
278
- world.should_receive :clear_carry_flag
279
- i= Xor.new :a, :b
280
- i.call world, nil
281
- end
282
- end
283
204
  end