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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/README.md +9 -1
- data/bin/gbrb +3 -3
- data/doc/images/blargg_cpu.png +0 -0
- data/doc/images/cpu_01.png +0 -0
- data/doc/images/cpu_03.png +0 -0
- data/doc/images/cpu_04.png +0 -0
- data/doc/images/cpu_05.png +0 -0
- data/doc/images/cpu_06.png +0 -0
- data/doc/images/cpu_07.png +0 -0
- data/doc/images/cpu_08.png +0 -0
- data/doc/images/cpu_09.png +0 -0
- data/doc/images/cpu_10.png +0 -0
- data/doc/images/cpu_11.png +0 -0
- data/doc/images/nintendo_logo.png +0 -0
- data/doc/images/opus5.png +0 -0
- data/doc/images/test.gb.png +0 -0
- data/doc/images/ttt.png +0 -0
- data/lib/gbrb.rb +7 -0
- data/lib/gbrb/cartridge.rb +21 -8
- data/lib/gbrb/cartridge/cartridge.rb +187 -0
- data/lib/gbrb/cpu/concatenated_register.rb +1 -1
- data/lib/gbrb/cpu/z80.rb +575 -498
- data/lib/gbrb/gb.rb +102 -32
- data/lib/gbrb/graphics.rb +1 -1
- data/lib/gbrb/graphics/gpu.rb +38 -30
- data/lib/gbrb/graphics/mode_clock.rb +31 -30
- data/lib/gbrb/graphics/screen_client.rb +3 -2
- data/lib/gbrb/instruction_set.rb +16 -0
- data/lib/gbrb/instruction_set/arithmetic.rb +238 -0
- data/lib/gbrb/instruction_set/bitwise.rb +64 -0
- data/lib/gbrb/instruction_set/boolean.rb +61 -0
- data/lib/gbrb/instruction_set/call.rb +40 -0
- data/lib/gbrb/instruction_set/carry.rb +23 -0
- data/lib/gbrb/instruction_set/cpl.rb +12 -0
- data/lib/gbrb/instruction_set/daa.rb +33 -0
- data/lib/gbrb/instruction_set/instruction.rb +23 -0
- data/lib/gbrb/instruction_set/jump.rb +47 -0
- data/lib/gbrb/instruction_set/ld.rb +241 -0
- data/lib/gbrb/instruction_set/return.rb +43 -0
- data/lib/gbrb/instruction_set/rotate.rb +178 -0
- data/lib/gbrb/instruction_set/rst.rb +16 -0
- data/lib/gbrb/instruction_set/special.rb +37 -0
- data/lib/gbrb/instruction_set/stack.rb +34 -0
- data/lib/gbrb/instruction_set/swap.rb +32 -0
- data/lib/gbrb/mmu.rb +60 -35
- data/lib/gbrb/options.rb +54 -0
- data/lib/gbrb/timer.rb +114 -0
- data/lib/gbrb/version.rb +1 -1
- data/misc/dump_diff +133 -0
- data/perf/cpu_perf_spec.rb +2 -2
- data/spec/gbrb/cartridge/cartridge_spec.rb +19 -0
- data/spec/gbrb/cartridge/mbc1_spec.rb +83 -0
- data/spec/gbrb/cpu/z80_spec.rb +92 -2
- data/spec/gbrb/{cpu/instruction_spec.rb → instruction_set/arithmetic_spec.rb} +21 -100
- data/spec/gbrb/instruction_set/boolean_spec.rb +50 -0
- data/spec/gbrb/instruction_set/instruction_spec.rb +22 -0
- data/spec/gbrb/instruction_set/ld_spec.rb +21 -0
- data/spec/gbrb/instruction_set/special_spec.rb +22 -0
- data/spec/gbrb/mmu_spec.rb +1 -1
- data/spec/gbrb/timer_spec.rb +26 -0
- metadata +53 -9
- data/lib/gbrb/cpu/instruction.rb +0 -648
- data/spec/gbrb/cartridge_spec.rb +0 -19
- data/spec/gbrb/graphics/mode_clock_spec.rb +0 -82
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'instruction_set/instruction'
|
2
|
+
require_relative 'instruction_set/ld'
|
3
|
+
require_relative 'instruction_set/bitwise'
|
4
|
+
require_relative 'instruction_set/rotate'
|
5
|
+
require_relative 'instruction_set/stack'
|
6
|
+
require_relative 'instruction_set/boolean'
|
7
|
+
require_relative 'instruction_set/arithmetic'
|
8
|
+
require_relative 'instruction_set/special'
|
9
|
+
require_relative 'instruction_set/carry'
|
10
|
+
require_relative 'instruction_set/call'
|
11
|
+
require_relative 'instruction_set/return'
|
12
|
+
require_relative 'instruction_set/jump'
|
13
|
+
require_relative 'instruction_set/swap'
|
14
|
+
require_relative 'instruction_set/cpl'
|
15
|
+
require_relative 'instruction_set/rst'
|
16
|
+
require_relative 'instruction_set/daa'
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require_relative 'instruction'
|
2
|
+
|
3
|
+
module GBRb::InstructionSet
|
4
|
+
class Arithmetic < Instruction
|
5
|
+
def initialize target, m, t, indirect, immediates=0
|
6
|
+
super m, t, immediates
|
7
|
+
@target = target
|
8
|
+
@indirect = indirect
|
9
|
+
end
|
10
|
+
|
11
|
+
def carry? left, right, mask
|
12
|
+
(mask-1 & left).public_send(@op, mask-1 & right) & mask == mask
|
13
|
+
end
|
14
|
+
|
15
|
+
def call r, mem, right_value
|
16
|
+
right_value ||= r.public_send(@target.to_sym).read
|
17
|
+
right_value = mem.read_byte(right_value) if @indirect
|
18
|
+
a_value = r.a.read
|
19
|
+
if carry? a_value, right_value, r.a.half_mask
|
20
|
+
r.set_half_carry_flag
|
21
|
+
else
|
22
|
+
r.clear_half_carry_flag
|
23
|
+
end
|
24
|
+
if carry? a_value, right_value, r.a.mask
|
25
|
+
r.set_carry_flag
|
26
|
+
else
|
27
|
+
r.clear_carry_flag
|
28
|
+
end
|
29
|
+
a_value = a_value.public_send(@op, right_value) & 0xff
|
30
|
+
r.a.store a_value unless @skip_store
|
31
|
+
a_value == 0x00 ? r.set_zero_flag : r.clear_zero_flag
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Add < Arithmetic
|
36
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
37
|
+
super
|
38
|
+
@op = :+
|
39
|
+
end
|
40
|
+
|
41
|
+
def call r, mem, right_value=nil
|
42
|
+
super
|
43
|
+
r.clear_add_sub_flag
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class AddHl < Instruction
|
48
|
+
def initialize target, m=2, t=8
|
49
|
+
super m, t
|
50
|
+
@target = target.to_sym
|
51
|
+
@op = :+
|
52
|
+
end
|
53
|
+
|
54
|
+
def call r, mem
|
55
|
+
left = r.hl.read
|
56
|
+
right = r.public_send(@target).read
|
57
|
+
r.hl.store left + right
|
58
|
+
if r.hl.read < left + right
|
59
|
+
r.set_carry_flag
|
60
|
+
else
|
61
|
+
r.clear_carry_flag
|
62
|
+
end
|
63
|
+
if carry? left, right, r.hl.half_mask
|
64
|
+
r.set_half_carry_flag
|
65
|
+
else
|
66
|
+
r.clear_half_carry_flag
|
67
|
+
end
|
68
|
+
r.clear_add_sub_flag
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class AddSpR8 < Instruction
|
73
|
+
def initialize m=4, t=16, immediates=1
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def call r, mem, offset
|
78
|
+
v = r.sp.read
|
79
|
+
if offset > 127
|
80
|
+
v -= ((offset ^ 0xff) + 1) & 0xff
|
81
|
+
else
|
82
|
+
v += offset
|
83
|
+
end
|
84
|
+
r.clear_zero_flag
|
85
|
+
r.clear_add_sub_flag
|
86
|
+
check = r.sp.read ^ offset ^ ((r.sp.read + offset)& 0xffff)
|
87
|
+
r.sp.store v
|
88
|
+
if check & 0x100 == 0x100
|
89
|
+
r.set_carry_flag
|
90
|
+
else
|
91
|
+
r.clear_carry_flag
|
92
|
+
end
|
93
|
+
if check & 0x10 == 0x10
|
94
|
+
r.set_half_carry_flag
|
95
|
+
else
|
96
|
+
r.clear_half_carry_flag
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Sub < Arithmetic
|
102
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
103
|
+
super
|
104
|
+
@op = :-
|
105
|
+
end
|
106
|
+
|
107
|
+
def call r, mem, right_value=nil
|
108
|
+
super
|
109
|
+
r.set_add_sub_flag
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Cp < Sub
|
114
|
+
def initialize *args
|
115
|
+
super
|
116
|
+
@skip_store = true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Adc < Add
|
121
|
+
def call r, mem, right_value=nil
|
122
|
+
carry = r.carry_flag? ? 1 : 0
|
123
|
+
if right_value
|
124
|
+
left_value = r.a.read
|
125
|
+
super r, mem, right_value + carry
|
126
|
+
((left_value & 0xf) + (right_value & 0xf) + carry) > 0xf ? r.set_half_carry_flag : r.clear_half_carry_flag
|
127
|
+
((left_value & 0xff) + (right_value & 0xff) + carry) > 0xff ? r.set_carry_flag : r.clear_carry_flag
|
128
|
+
else
|
129
|
+
right_value = r.public_send(@target.to_sym).read
|
130
|
+
right_value = mem.read_byte(right_value) if @indirect
|
131
|
+
left_value = r.a.read
|
132
|
+
r.clear_add_sub_flag
|
133
|
+
((left_value & 0xf) + (right_value & 0xf) + carry) > 0xf ? r.set_half_carry_flag : r.clear_half_carry_flag
|
134
|
+
((left_value & 0xff) + (right_value & 0xff) + carry) > 0xff ? r.set_carry_flag : r.clear_carry_flag
|
135
|
+
r.a.store left_value + right_value + carry
|
136
|
+
r.a.zero? ? r.set_zero_flag : r.clear_zero_flag
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class Sbc < Sub
|
142
|
+
def call r, mem, right_value=nil
|
143
|
+
carry = r.carry_flag? ? 1 : 0
|
144
|
+
right_value = r.public_send(@target.to_sym).read unless right_value
|
145
|
+
right_value = mem.read_byte(right_value) if @indirect
|
146
|
+
un = right_value & 0xff
|
147
|
+
tmpa = r.a.read
|
148
|
+
ua = r.a.read
|
149
|
+
ua -= un
|
150
|
+
ua -= 1 if r.carry_flag?
|
151
|
+
r.set_add_sub_flag
|
152
|
+
ua < 0 ? r.set_carry_flag : r.clear_carry_flag
|
153
|
+
(ua ^ un ^ tmpa) & 0x10 == 0x10 ? r.set_half_carry_flag : r.clear_half_carry_flag
|
154
|
+
r.a.store ua
|
155
|
+
r.a.zero? ? r.set_zero_flag : r.clear_zero_flag
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Inc < Instruction
|
160
|
+
def initialize register, m=1, t=4, flags=true, indirect=false
|
161
|
+
super m, t
|
162
|
+
|
163
|
+
@register = register
|
164
|
+
@flags = flags
|
165
|
+
@op = :+
|
166
|
+
@indirect = indirect
|
167
|
+
end
|
168
|
+
|
169
|
+
def call r, mem
|
170
|
+
reg = r.public_send(@register.to_sym)
|
171
|
+
tmp = reg.read
|
172
|
+
if @indirect
|
173
|
+
v = mem.read_byte(tmp) + 1
|
174
|
+
mem.write_byte(tmp, v)
|
175
|
+
tmp = v
|
176
|
+
reg = r.h
|
177
|
+
else
|
178
|
+
reg.store tmp + 1
|
179
|
+
end
|
180
|
+
if @flags
|
181
|
+
r.clear_add_sub_flag
|
182
|
+
if @indirect
|
183
|
+
(tmp & 0xff == 0x00) ? r.set_zero_flag : r.clear_zero_flag
|
184
|
+
if carry? tmp - 1, 0x01, r.h.half_mask
|
185
|
+
r.set_half_carry_flag
|
186
|
+
else
|
187
|
+
r.clear_half_carry_flag
|
188
|
+
end
|
189
|
+
else
|
190
|
+
reg.zero? ? r.set_zero_flag : r.clear_zero_flag
|
191
|
+
if carry? tmp, 0x01, reg.half_mask
|
192
|
+
r.set_half_carry_flag
|
193
|
+
else
|
194
|
+
r.clear_half_carry_flag
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Dec < Instruction
|
202
|
+
def initialize register, m=1, t=4, flags=true, indirect=false
|
203
|
+
super m, t
|
204
|
+
|
205
|
+
@register = register
|
206
|
+
@flags = flags
|
207
|
+
@op = :-
|
208
|
+
@indirect = indirect
|
209
|
+
end
|
210
|
+
|
211
|
+
def call r, mem
|
212
|
+
reg = r.public_send(@register.to_sym)
|
213
|
+
tmp = reg.read
|
214
|
+
if @indirect
|
215
|
+
v = mem.read_byte(tmp)
|
216
|
+
mem.write_byte(tmp, v - 1)
|
217
|
+
tmp = v
|
218
|
+
half_mask = r.a.half_mask
|
219
|
+
else
|
220
|
+
reg.store tmp - 0x01
|
221
|
+
half_mask = reg.half_mask
|
222
|
+
end
|
223
|
+
if @flags
|
224
|
+
r.set_add_sub_flag
|
225
|
+
if (tmp - 0x01) == 0x00
|
226
|
+
r.set_zero_flag
|
227
|
+
else
|
228
|
+
r.clear_zero_flag
|
229
|
+
end
|
230
|
+
if carry? tmp, 0x01, half_mask
|
231
|
+
r.set_half_carry_flag
|
232
|
+
else
|
233
|
+
r.clear_half_carry_flag
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
require_relative 'instruction'
|
3
|
+
|
4
|
+
module GBRb::InstructionSet
|
5
|
+
class Res < Instruction
|
6
|
+
def initialize bit, target, indirect=false, m=2, t=16
|
7
|
+
super m, t
|
8
|
+
@bit = bit
|
9
|
+
@mask = 0xff - 0b10 ** bit
|
10
|
+
@target = target.to_sym
|
11
|
+
@indirect = indirect
|
12
|
+
end
|
13
|
+
|
14
|
+
def call r, mem
|
15
|
+
if @indirect
|
16
|
+
addr = r.public_send(@target).read
|
17
|
+
mem.write_byte(addr, mem.read_byte(addr) & @mask)
|
18
|
+
else
|
19
|
+
r.public_send(@target).store r.public_send(@target).read & @mask
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Set < Instruction
|
25
|
+
def initialize bit, target, indirect=false, m=2, t=16
|
26
|
+
super m, t
|
27
|
+
@bit = bit
|
28
|
+
@mask = 0b10 ** bit
|
29
|
+
@target = target.to_sym
|
30
|
+
@indirect = indirect
|
31
|
+
end
|
32
|
+
|
33
|
+
def call r, mem
|
34
|
+
if @indirect
|
35
|
+
addr = r.public_send(@target).read
|
36
|
+
mem.write_byte(addr, mem.read_byte(addr) | @mask)
|
37
|
+
else
|
38
|
+
r.public_send(@target).store r.public_send(@target).read | @mask
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Bit < Instruction
|
44
|
+
def initialize bit, target, indirect=false, m=2, t=8
|
45
|
+
super m,t
|
46
|
+
@target = target
|
47
|
+
@bit = bit
|
48
|
+
@indirect = indirect
|
49
|
+
@mask = 1 << @bit
|
50
|
+
end
|
51
|
+
|
52
|
+
def call r, mem
|
53
|
+
if @indirect
|
54
|
+
v = mem.read_byte r.public_send(@target).read
|
55
|
+
else
|
56
|
+
v = r.public_send(@target).read
|
57
|
+
end
|
58
|
+
|
59
|
+
v & @mask != @mask ? r.set_zero_flag : r.clear_zero_flag
|
60
|
+
r.set_half_carry_flag
|
61
|
+
r.clear_add_sub_flag
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
require_relative 'instruction'
|
3
|
+
|
4
|
+
module GBRb::InstructionSet
|
5
|
+
class Boolean < Instruction
|
6
|
+
def initialize target, m, t, indirect, immediates=0
|
7
|
+
@target = target
|
8
|
+
@indirect = indirect
|
9
|
+
super m, t, immediates
|
10
|
+
end
|
11
|
+
|
12
|
+
def call r, mem, v=nil
|
13
|
+
if v
|
14
|
+
value = v
|
15
|
+
else
|
16
|
+
value = r.public_send(@target.to_sym).read
|
17
|
+
value = mem.read_byte value if @indirect
|
18
|
+
end
|
19
|
+
r.a.store r.a.read.public_send(@op, value)
|
20
|
+
r.a.zero? ? r.set_zero_flag : r.clear_zero_flag
|
21
|
+
r.clear_carry_flag
|
22
|
+
r.clear_add_sub_flag
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class And < Boolean
|
27
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
28
|
+
@op = :&
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def call r, mem, v=nil
|
33
|
+
super
|
34
|
+
r.set_half_carry_flag
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Or < Boolean
|
39
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
40
|
+
@op = :|
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def call r, mem, right_value=nil
|
45
|
+
super
|
46
|
+
r.clear_half_carry_flag
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Xor < Boolean
|
51
|
+
def initialize target, m=1, t=4, indirect=false, immediates=0
|
52
|
+
@op = :^
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def call r, mem, right_value=nil
|
57
|
+
super
|
58
|
+
r.clear_half_carry_flag
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'instruction'
|
2
|
+
|
3
|
+
module GBRb::InstructionSet
|
4
|
+
class Call < Instruction
|
5
|
+
def initialize condition, m=3, t_high=24, t_low=12, immediates=2
|
6
|
+
@condition = condition
|
7
|
+
@t_high = t_high
|
8
|
+
@t_low = t_low
|
9
|
+
@m_low = m
|
10
|
+
super m, t_low, immediates
|
11
|
+
end
|
12
|
+
|
13
|
+
def call r, mem, addr
|
14
|
+
condition_met = case @condition
|
15
|
+
when :c
|
16
|
+
r.carry_flag?
|
17
|
+
when :nc
|
18
|
+
not r.carry_flag?
|
19
|
+
when :nz
|
20
|
+
not r.zero_flag?
|
21
|
+
when :z
|
22
|
+
r.zero_flag?
|
23
|
+
when :none
|
24
|
+
true
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
if condition_met
|
29
|
+
r.sp.store r.sp.read - 2
|
30
|
+
mem.write_word(r.sp.read, r.pc.read)
|
31
|
+
r.pc.store addr
|
32
|
+
@t = @t_high
|
33
|
+
@m = @t_high / 4
|
34
|
+
else
|
35
|
+
@t = @t_low
|
36
|
+
@m = @m_low
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'instruction'
|
2
|
+
|
3
|
+
module GBRb::InstructionSet
|
4
|
+
class Ccf < Instruction
|
5
|
+
def call r, mem
|
6
|
+
if r.carry_flag?
|
7
|
+
r.clear_carry_flag
|
8
|
+
else
|
9
|
+
r.set_carry_flag
|
10
|
+
end
|
11
|
+
r.clear_add_sub_flag
|
12
|
+
r.clear_half_carry_flag
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Scf < Instruction
|
17
|
+
def call r, mem
|
18
|
+
r.clear_half_carry_flag
|
19
|
+
r.clear_add_sub_flag
|
20
|
+
r.set_carry_flag
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|