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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/display +9 -0
- data/bin/gbrb +12 -0
- data/gbrb.gemspec +26 -0
- data/lib/gbrb.rb +9 -0
- data/lib/gbrb/bios +256 -0
- data/lib/gbrb/cartridge.rb +15 -0
- data/lib/gbrb/cpu.rb +7 -0
- data/lib/gbrb/cpu/concatenated_register.rb +44 -0
- data/lib/gbrb/cpu/flags_register.rb +42 -0
- data/lib/gbrb/cpu/instruction.rb +648 -0
- data/lib/gbrb/cpu/register.rb +44 -0
- data/lib/gbrb/cpu/register_ensemble.rb +59 -0
- data/lib/gbrb/cpu/z80.rb +584 -0
- data/lib/gbrb/gb.rb +125 -0
- data/lib/gbrb/graphics.rb +14 -0
- data/lib/gbrb/graphics/gpu.rb +196 -0
- data/lib/gbrb/graphics/mode_clock.rb +51 -0
- data/lib/gbrb/graphics/screen_client.rb +31 -0
- data/lib/gbrb/graphics/screen_server.rb +90 -0
- data/lib/gbrb/mmu.rb +96 -0
- data/lib/gbrb/version.rb +3 -0
- data/misc/parse_tiles +27 -0
- data/perf/cpu_perf_spec.rb +23 -0
- data/spec/gbrb/cartridge_spec.rb +19 -0
- data/spec/gbrb/cpu/concatenated_register_spec.rb +36 -0
- data/spec/gbrb/cpu/flags_register_spec.rb +91 -0
- data/spec/gbrb/cpu/instruction_spec.rb +283 -0
- data/spec/gbrb/cpu/register_ensemble_spec.rb +84 -0
- data/spec/gbrb/cpu/register_spec.rb +86 -0
- data/spec/gbrb/cpu/z80_spec.rb +2534 -0
- data/spec/gbrb/graphics/mode_clock_spec.rb +82 -0
- data/spec/gbrb/mmu_spec.rb +61 -0
- data/spec/gbrb/version_spec.rb +10 -0
- data/spec/spec_helper.rb +4 -0
- metadata +154 -0
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
|
data/lib/gbrb/version.rb
ADDED
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
|