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/mmu.rb ADDED
@@ -0,0 +1,96 @@
1
+ require_relative '../gbrb'
2
+
3
+ module GBRb
4
+ class MMU
5
+ SIZE = 0x10000
6
+ WORKING_RAM_ADDRESS = 0xc000
7
+ WORKING_RAM_LENGTH = 0x2000
8
+ WORKING_RAM_SHADOW_ADDRESS = 0xe000
9
+ WORKING_RAM_SHADOW_LENGTH = 0x1e00
10
+ ROM_BANK_0_ADDRESS = 0x0000
11
+ ROM_BANK_1_ADDRESS = 0x4000
12
+ GPU_START = 0x8000
13
+ GPU_END = 0x9fff
14
+
15
+ def initialize bios=[], cartridge=:none, gpu=nil
16
+ @storage = Array.new SIZE, 0
17
+ load_cartridge cartridge unless cartridge == :none
18
+ load_bios bios
19
+ @gpu = gpu
20
+ end
21
+
22
+ def read_byte addr
23
+ raise unless (0..SIZE).cover? addr
24
+ @storage[addr]
25
+ end
26
+
27
+ def read_word addr
28
+ (read_byte(addr+1) << 8) + read_byte(addr)
29
+ end
30
+
31
+ def write_byte addr, value
32
+ @storage[addr] = value & 0xff
33
+ if (WORKING_RAM_ADDRESS..WORKING_RAM_ADDRESS + WORKING_RAM_SHADOW_LENGTH).cover? addr
34
+ copy_address = addr + WORKING_RAM_SHADOW_ADDRESS - WORKING_RAM_ADDRESS
35
+ @storage[copy_address] = value & 0xff
36
+ end
37
+ if (GPU_START..GPU_END).cover? addr and @gpu
38
+ @gpu.push [:mmu, addr]
39
+ end
40
+ end
41
+
42
+ def write_word addr, value
43
+ write_byte addr, value
44
+ write_byte addr+1, value >> 8
45
+ end
46
+
47
+ def dump start=0, stop=SIZE
48
+ debug_indicies(start, stop).zip(debug_hex(start, stop), debug_ascii(start, stop))
49
+ .map{|row| row.join(" ")}
50
+ .join("\n")
51
+ end
52
+
53
+ def debug_indicies start, stop
54
+ (start...stop).step(0x10)
55
+ .map{|el| el.to_s 16 }
56
+ .map{|el| el.rjust(4, "0")}
57
+ end
58
+
59
+ def debug_hex start, stop
60
+ @storage[start..stop].map do |cell|
61
+ cell.to_s(16)
62
+ .rjust(2, "0")
63
+ end.each_slice(0x08).map{|el| el.join(" ")}
64
+ .each_slice(0x02).map{|el| el.join(" ")}
65
+ end
66
+
67
+ def debug_ascii start, stop
68
+ @storage[start..stop].map{|el| (0x20..0x7e).cover?(el) ? el.chr : "."}
69
+ .each_slice(0x10).map{|el| el.join}
70
+ .map{|el| "|" + el + "|"}
71
+ end
72
+
73
+ def reset
74
+ end
75
+
76
+ def unload_bios
77
+ @storage[0..(ROM_BANK_0_ADDRESS + MEMORY_BANK_SIZE)] = if @cartridge
78
+ @cartridge.bank
79
+ else
80
+ Array.new(ROM_BANK_0_ADDRESS + MEMORY_BANK_SIZE){0}
81
+ end
82
+ end
83
+
84
+ private
85
+ def load_cartridge cartridge
86
+ @cartridge = cartridge
87
+ @storage[0...(ROM_BANK_0_ADDRESS + MEMORY_BANK_SIZE)] = cartridge.bank 0
88
+ @storage[ROM_BANK_1_ADDRESS...(ROM_BANK_1_ADDRESS + MEMORY_BANK_SIZE)] = cartridge.bank 1
89
+ end
90
+
91
+ def load_bios commands
92
+ @bios = commands
93
+ @storage[0..commands.length-1] = commands
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,3 @@
1
+ module GBRb
2
+ VERSION = "0.1.0"
3
+ end
data/misc/parse_tiles ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ input = ARGV.first
6
+
7
+ data = File.read input
8
+
9
+ t = data.chomp + "]"
10
+
11
+ t = eval t
12
+
13
+ formatted_tiles = t.map do |tile|
14
+ s = "P6 8 8 3 "
15
+ tile.each do |row|
16
+ row.each do |pixel|
17
+ s << pixel.chr * 3
18
+ end
19
+ end
20
+ s << "\n"
21
+ end
22
+
23
+ out_path = Pathname.new "tile_set"
24
+ out_path.mkpath
25
+ formatted_tiles.each_with_index do |tile, i|
26
+ (out_path + "#{i}.ppm").open("w:utf-8") {|f| f.write tile }
27
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../lib/gbrb/cpu/z80.rb'
2
+ require 'benchmark'
3
+
4
+ module GBRb::CPU
5
+ describe Z80 do
6
+ xit "full speed" do
7
+ n = 1_000_000
8
+ class Z80
9
+ def fetch
10
+ 0x3c
11
+ end
12
+ end
13
+
14
+
15
+ @cpu = Z80.new
16
+ elapsed_time = Benchmark.realtime do
17
+ n.times { @cpu.run }
18
+ end
19
+
20
+ elapsed_time.should be < 1.0
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ require_relative '../../lib/gbrb/cartridge.rb'
4
+
5
+ module GBRb
6
+ describe Cartridge do
7
+ it "is initialized with an IO object" do
8
+ c = Cartridge.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 = Cartridge.new raw_data
14
+ c.bank(0).each {|b| b.should eq 0x01}
15
+ c.bank(1).each {|b| b.should eq 0x02}
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,36 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/gbrb/cpu/concatenated_register'
3
+ require_relative '../../../lib/gbrb/cpu/register'
4
+
5
+ module GBRb::CPU
6
+ describe ConcatenatedRegister do
7
+ let(:b) { Register.new }
8
+ let(:c) { Register.new }
9
+ let(:bc) { ConcatenatedRegister.new b, c}
10
+
11
+ it "#store" do
12
+ bc.store 0x3456
13
+ b.read.should eq 0x34
14
+ c.read.should eq 0x56
15
+ end
16
+
17
+ it "#read" do
18
+ b.store 0xab
19
+ c.store 0x81
20
+ bc.read.should eq 0xab81
21
+ end
22
+
23
+ it "#zero?" do
24
+ bc.zero?.should be_true
25
+ b.store 0x01
26
+ bc.zero?.should be_false
27
+ end
28
+
29
+ it "#==" do
30
+ a = Register.new 0x5839, 16
31
+ bc.should_not eq a
32
+ bc.store 0x5839
33
+ bc.should eq a
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,91 @@
1
+ require_relative '../../spec_helper.rb'
2
+ require_relative '../../../lib/gbrb/cpu/flags_register.rb'
3
+
4
+ module GBRb::CPU
5
+ describe FlagsRegister do
6
+ let(:f) { FlagsRegister.new }
7
+ context "zero flag" do
8
+ it "has a zero flag" do
9
+ f.zero_flag?.should be_false
10
+ end
11
+
12
+ it "can set the zero flag" do
13
+ f.set_zero_flag
14
+ end
15
+
16
+ it "correctly sets the zero flag" do
17
+ f.set_zero_flag
18
+ f.zero_flag?.should be_true
19
+ end
20
+
21
+ it "clears the zero flag" do
22
+ f.set_zero_flag
23
+ f.clear_zero_flag
24
+ f.zero_flag?.should be_false
25
+ end
26
+ end
27
+
28
+ context "add/sub flag" do
29
+ it "has a add_sub flag" do
30
+ f.add_sub_flag?.should be_false
31
+ end
32
+
33
+ it "can set the add_sub flag" do
34
+ f.set_add_sub_flag
35
+ end
36
+
37
+ it "correctly sets the add_sub_flag" do
38
+ f.set_add_sub_flag
39
+ f.add_sub_flag?.should be_true
40
+ end
41
+
42
+ it "clears the add_sub flag" do
43
+ f.set_add_sub_flag
44
+ f.clear_add_sub_flag
45
+ f.add_sub_flag?.should be_false
46
+ end
47
+ end
48
+
49
+ context "half carry flag" do
50
+ it "has a half_carry flag" do
51
+ f.half_carry_flag?.should be_false
52
+ end
53
+
54
+ it "can set the half_carry flag" do
55
+ f.set_half_carry_flag
56
+ end
57
+
58
+ it "correctly sets the half_carry flag" do
59
+ f.set_half_carry_flag
60
+ f.half_carry_flag?.should be_true
61
+ end
62
+
63
+ it "clears the half_carry flag" do
64
+ f.set_half_carry_flag
65
+ f.clear_half_carry_flag
66
+ f.half_carry_flag?.should be_false
67
+ end
68
+ end
69
+
70
+ context "carry flag" do
71
+ it "has a carry flag" do
72
+ f.carry_flag?.should be_false
73
+ end
74
+
75
+ it "can set the carry flag" do
76
+ f.set_carry_flag
77
+ end
78
+
79
+ it "correctly sets the carry flag" do
80
+ f.set_carry_flag
81
+ f.carry_flag?.should be_true
82
+ end
83
+
84
+ it "clears the carry flag" do
85
+ f.set_carry_flag
86
+ f.clear_carry_flag
87
+ f.carry_flag?.should be_false
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,283 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/gbrb/cpu/instruction.rb'
3
+ require_relative '../../../lib/gbrb/cpu/register.rb'
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
+
24
+ describe Inc do
25
+ it "clears the add sub flag" do
26
+ fake_register = double.as_null_object
27
+ fake_register.stub(:store)
28
+ fake_register.stub(:read).and_return(1)
29
+ world = double.as_null_object
30
+ world.should_receive(:clear_add_sub_flag)
31
+ world.should_receive(:a).at_least(1).times.and_return(fake_register)
32
+ i = Inc.new :a
33
+ i.call world, nil
34
+ end
35
+
36
+ it "sets the zero flag when zero" do
37
+ a = Register.new 0xff
38
+ world = double.as_null_object
39
+ world.should_receive(:a).at_least(1).times.and_return(a)
40
+ world.should_receive(:set_zero_flag)
41
+ i = Inc.new :a
42
+ i.call world, nil
43
+ end
44
+
45
+ it "does not set the carry flag when value overflows register" do
46
+ a = Register.new 0xff
47
+ world = double.as_null_object
48
+ world.should_receive(:a).at_least(1).times.and_return(a)
49
+ world.should_not_receive(:set_carry_flag)
50
+ i = Inc.new :a
51
+ i.call world, nil
52
+ end
53
+
54
+ it "sets the half carry flag when low nibble overflows" do
55
+ a = Register.new 0x0f
56
+ world = double.as_null_object
57
+ world.should_receive(:a).at_least(1).times.and_return(a)
58
+ world.should_receive(:set_half_carry_flag)
59
+ world.should_not_receive(:set_carry_flag)
60
+ i = Inc.new :a
61
+ i.call world, nil
62
+ end
63
+
64
+ it "does not touch the flags if initialized with false" do
65
+ a = Register.new 0xff
66
+ world = double.as_null_object
67
+ world.should_receive(:a).at_least(1).times.and_return(a)
68
+ world.should_not_receive(:set_carry_flag)
69
+ world.should_not_receive(:set_half_carry_flag)
70
+ world.should_not_receive(:set_zero_flag)
71
+ i = Inc.new :a, 1, 4, false
72
+ i.call world, nil
73
+ end
74
+ end
75
+
76
+ describe Dec do
77
+ it "sets the add sub flag" do
78
+ fake_register = double.as_null_object
79
+ fake_register.stub(:store)
80
+ fake_register.stub(:read).and_return(1)
81
+ world = double.as_null_object
82
+ world.should_receive(:set_add_sub_flag)
83
+ world.should_receive(:a).at_least(1).times.and_return(fake_register)
84
+ i = Dec.new :a
85
+ i.call world, nil
86
+ end
87
+
88
+ it "sets the zero flag when zero" do
89
+ a = Register.new 0x01
90
+ world = double.as_null_object
91
+ world.should_receive(:a).at_least(1).times.and_return(a)
92
+ world.should_not_receive(:set_half_carry_flag)
93
+ world.should_receive(:set_zero_flag)
94
+ i = Dec.new :a
95
+ i.call world, nil
96
+ end
97
+
98
+ it "does not set the carry flag when value underflows register" do
99
+ a = Register.new 0x00
100
+ world = double.as_null_object
101
+ world.should_receive(:a).at_least(1).times.and_return(a)
102
+ world.should_not_receive(:set_carry_flag)
103
+ i = Dec.new :a
104
+ i.call world, nil
105
+ end
106
+
107
+ it "sets the half carry flag when borrowing from high nibble" do
108
+ a = Register.new 0x10
109
+ world = double.as_null_object
110
+ world.should_receive(:a).at_least(1).times.and_return(a)
111
+ world.should_receive(:set_half_carry_flag)
112
+ world.should_not_receive(:set_carry_flag)
113
+ i = Dec.new :a
114
+ i.call world, nil
115
+ end
116
+
117
+ it "does not touch the flags if initialized with false" do
118
+ a = Register.new 0xff
119
+ world = double.as_null_object
120
+ world.should_receive(:a).at_least(1).times.and_return(a)
121
+ world.should_not_receive(:set_half_carry_flag)
122
+ world.should_not_receive(:set_zero_flag)
123
+ i = Dec.new :a, 1, 4, false
124
+ i.call world, nil
125
+ end
126
+ end
127
+
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
+ describe Add do
145
+ it "clears the add sub flag" do
146
+ a = Register.new 0x34
147
+ world = double.as_null_object
148
+ world.should_receive(:clear_add_sub_flag)
149
+ world.should_receive(:a).at_least(1).times.and_return(a)
150
+ i = Add.new :a
151
+ i.call world, nil
152
+ end
153
+
154
+ it "sets the zero flag when zero" do
155
+ a = Register.new 0x80
156
+ world = double.as_null_object
157
+ world.should_receive(:a).at_least(1).times.and_return(a)
158
+ world.should_receive(:set_zero_flag)
159
+ i = Add.new :a
160
+ i.call world, nil
161
+ i.call world, nil
162
+ end
163
+
164
+ it "sets the carry flag when value overflows register" do
165
+ a = Register.new 0xff
166
+ world = double.as_null_object
167
+ world.should_receive(:a).at_least(1).times.and_return(a)
168
+ i = Add.new :a
169
+ i.call world, nil
170
+ i.call world, nil
171
+ end
172
+
173
+ it "sets the half carry flag when low nibble overflows" do
174
+ a = Register.new 0x0d
175
+ world = double.as_null_object
176
+ world.should_receive(:a).at_least(1).times.and_return(a)
177
+ world.should_receive(:set_half_carry_flag)
178
+ world.should_not_receive(:set_carry_flag)
179
+ i = Add.new :a
180
+ i.call world, nil
181
+ end
182
+ end
183
+
184
+ describe Sub do
185
+ it "sets the add sub flag" do
186
+ a = Register.new 0x34
187
+ world = double.as_null_object
188
+ world.should_receive(:set_add_sub_flag)
189
+ world.should_receive(:a).at_least(1).times.and_return(a)
190
+ i = Sub.new :a
191
+ i.call world, nil
192
+ end
193
+
194
+ it "sets the zero flag when zero" do
195
+ a = Register.new 0x80
196
+ world = double.as_null_object
197
+ world.should_receive(:a).at_least(1).times.and_return(a)
198
+ world.should_receive(:set_zero_flag)
199
+ i = Sub.new :a
200
+ i.call world, nil
201
+ i.call world, nil
202
+ end
203
+
204
+ it "sets the carry flag when value underflows register" do
205
+ a = Register.new 0x00
206
+ world = double.as_null_object
207
+ world.should_receive(:a).at_least(1).times.and_return(a)
208
+ i = Sub.new :a
209
+ i.call world, nil
210
+ end
211
+
212
+ it "sets the half carry flag when borrowing from high nibble" do
213
+ a = Register.new 0x10
214
+ b = Register.new 0x01
215
+ world = double.as_null_object
216
+ world.should_receive(:a).at_least(1).times.and_return(a)
217
+ world.should_receive(:b).at_least(1).times.and_return(b)
218
+ world.should_receive(:set_half_carry_flag)
219
+ world.should_not_receive(:set_carry_flag)
220
+ i = Sub.new :b
221
+ i.call world, nil
222
+ end
223
+ end
224
+
225
+ describe Cp do
226
+ it "does not modify the accumulator" do
227
+ a = Register.new 0x1f
228
+ b = Register.new 0x3b
229
+ world = double.as_null_object
230
+ world.stub(a: a)
231
+ world.stub(b: b)
232
+ a.should_not_receive(:store)
233
+ b.should_not_receive(:store)
234
+ i = Cp.new :b
235
+ i.call world, nil
236
+ end
237
+ 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
+ end