GBRb 0.1.0 → 0.2.0

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