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'
|
2
|
+
|
3
|
+
module GBRb::InstructionSet
|
4
|
+
class Rst < Instruction
|
5
|
+
def initialize offset
|
6
|
+
@offset = offset
|
7
|
+
super 1, 16
|
8
|
+
end
|
9
|
+
|
10
|
+
def call r, mem
|
11
|
+
r.sp.store r.sp.read - 2
|
12
|
+
mem.write_word r.sp.read, r.pc.read
|
13
|
+
r.pc.store 0x0000 + @offset
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'instruction'
|
2
|
+
require_relative '../../gbrb'
|
3
|
+
|
4
|
+
module GBRb::InstructionSet
|
5
|
+
class Stop < Instruction; end
|
6
|
+
|
7
|
+
class EnableInterrupts < Instruction
|
8
|
+
def initialize
|
9
|
+
super 1, 4, -1
|
10
|
+
end
|
11
|
+
|
12
|
+
def call cpu
|
13
|
+
cpu.interrupts = :enabled
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class DisableInterrupts < Instruction
|
18
|
+
def initialize
|
19
|
+
super 1, 4, -1
|
20
|
+
end
|
21
|
+
|
22
|
+
def call cpu
|
23
|
+
cpu.interrupts = :disabled
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Halt < Instruction
|
28
|
+
def initialize
|
29
|
+
super 1, 4, -1
|
30
|
+
end
|
31
|
+
|
32
|
+
def call cpu
|
33
|
+
cpu.interrupt_before_halt = cpu.mmu.read_byte GBRb::INTERRUPT_FLAG_ADDR
|
34
|
+
cpu.halted = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
require_relative 'instruction'
|
3
|
+
|
4
|
+
module GBRb::InstructionSet
|
5
|
+
class Pop < Instruction
|
6
|
+
def initialize target, m=3, t=12
|
7
|
+
super m, t
|
8
|
+
@targets = target.to_s.chars.map{|a| a.to_sym}.reverse
|
9
|
+
end
|
10
|
+
|
11
|
+
def call r, mem
|
12
|
+
@targets.each do |target|
|
13
|
+
r.public_send(target).store mem.read_byte(r.sp.read)
|
14
|
+
r.sp.store r.sp.read + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
r.f.store r.f.read & 0xf0 if @targets.include? :f
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Push < Instruction
|
22
|
+
def initialize target, m=4, t=16
|
23
|
+
super m, t
|
24
|
+
@targets = target.to_s.chars.map{|a| a.to_sym}
|
25
|
+
end
|
26
|
+
|
27
|
+
def call r, mem
|
28
|
+
@targets.each do |target|
|
29
|
+
r.sp.store r.sp.read - 1
|
30
|
+
mem.write_byte(r.sp.read, r.public_send(target).read)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative '../cpu'
|
2
|
+
require_relative 'instruction'
|
3
|
+
|
4
|
+
module GBRb::InstructionSet
|
5
|
+
class Swap < Instruction
|
6
|
+
def initialize target, m=2, t=8, indirect=false
|
7
|
+
@target = target
|
8
|
+
@indirect = indirect
|
9
|
+
super m, t
|
10
|
+
end
|
11
|
+
|
12
|
+
def call r, mem
|
13
|
+
initial = r.public_send(@target).read
|
14
|
+
initial = mem.read_byte(initial) if @indirect
|
15
|
+
high = initial >> 4
|
16
|
+
low = initial & ((1 << 4) - 1)
|
17
|
+
|
18
|
+
result = (low << 4) + high
|
19
|
+
|
20
|
+
if @indirect
|
21
|
+
mem.write_byte r.public_send(@target).read, result
|
22
|
+
mem.read_byte(r.public_send(@target).read) == 0 ? r.set_zero_flag : r.clear_zero_flag
|
23
|
+
else
|
24
|
+
r.public_send(@target).store result
|
25
|
+
r.public_send(@target).read == 0 ? r.set_zero_flag : r.clear_zero_flag
|
26
|
+
end
|
27
|
+
r.clear_add_sub_flag
|
28
|
+
r.clear_half_carry_flag
|
29
|
+
r.clear_carry_flag
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/gbrb/mmu.rb
CHANGED
@@ -9,19 +9,23 @@ module GBRb
|
|
9
9
|
WORKING_RAM_SHADOW_LENGTH = 0x1e00
|
10
10
|
ROM_BANK_0_ADDRESS = 0x0000
|
11
11
|
ROM_BANK_1_ADDRESS = 0x4000
|
12
|
-
|
13
|
-
GPU_END = 0x9fff
|
12
|
+
BIOS_STATUS_ADDRESS = 0xff50
|
14
13
|
|
15
|
-
def initialize bios=[]
|
16
|
-
@storage
|
17
|
-
|
14
|
+
def initialize bios=[]
|
15
|
+
@storage = Array.new SIZE, 0
|
16
|
+
@peripherals = Array.new SIZE, nil
|
18
17
|
load_bios bios
|
19
|
-
@gpu = gpu
|
20
18
|
end
|
21
19
|
|
22
20
|
def read_byte addr
|
23
21
|
raise unless (0..SIZE).cover? addr
|
24
|
-
@
|
22
|
+
return @bios[addr] if addr < @bios.length and @in_bios
|
23
|
+
|
24
|
+
if (p = @peripherals[addr])
|
25
|
+
p.read_byte addr
|
26
|
+
else
|
27
|
+
@storage[addr]
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
def read_word addr
|
@@ -29,13 +33,19 @@ module GBRb
|
|
29
33
|
end
|
30
34
|
|
31
35
|
def write_byte addr, value
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@storage[copy_address] = value & 0xff
|
36
|
+
if BIOS_STATUS_ADDRESS == addr
|
37
|
+
value == 0x00 ? load_bios(@bios) : unload_bios
|
38
|
+
return
|
36
39
|
end
|
37
|
-
|
38
|
-
|
40
|
+
|
41
|
+
if (p = @peripherals[addr])
|
42
|
+
p.write_byte addr, value
|
43
|
+
else
|
44
|
+
@storage[addr] = value & 0xff
|
45
|
+
if (WORKING_RAM_ADDRESS..WORKING_RAM_ADDRESS + WORKING_RAM_SHADOW_LENGTH).cover? addr
|
46
|
+
copy_address = addr + WORKING_RAM_SHADOW_ADDRESS - WORKING_RAM_ADDRESS
|
47
|
+
@storage[copy_address] = value & 0xff
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
41
51
|
|
@@ -44,12 +54,49 @@ module GBRb
|
|
44
54
|
write_byte addr+1, value >> 8
|
45
55
|
end
|
46
56
|
|
57
|
+
def connect peripheral, *locations
|
58
|
+
locations.map! do |location|
|
59
|
+
if location.class == Range
|
60
|
+
STDOUT.puts "Connecting #{peripheral.class.to_s.split('::').last} at [#{location.first.to_s 16}, #{location.last.to_s 16}]..."
|
61
|
+
location.to_a
|
62
|
+
else
|
63
|
+
STDOUT.puts "Connecting #{peripheral.class.to_s.split('::').last} at #{Array(location).map{|l| l.to_s 16}.join ", "}..."
|
64
|
+
Array(location).map &:to_i
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
locations.flatten.each do |l|
|
69
|
+
@peripherals[l.to_i] = peripheral
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
47
73
|
def dump start=0, stop=SIZE
|
48
74
|
debug_indicies(start, stop).zip(debug_hex(start, stop), debug_ascii(start, stop))
|
49
75
|
.map{|row| row.join(" ")}
|
50
76
|
.join("\n")
|
51
77
|
end
|
52
78
|
|
79
|
+
def reset
|
80
|
+
end
|
81
|
+
|
82
|
+
def unload_bios
|
83
|
+
@in_bios = false
|
84
|
+
end
|
85
|
+
|
86
|
+
def irq i
|
87
|
+
old_value = read_byte INTERRUPT_FLAG_ADDR
|
88
|
+
case i
|
89
|
+
when TIMER_OVERFLOW_IRQ
|
90
|
+
write_byte INTERRUPT_FLAG_ADDR, old_value | TIMER_OVERFLOW_IRQ
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def load_bios commands
|
96
|
+
@bios = commands
|
97
|
+
@in_bios = commands.length > 0
|
98
|
+
end
|
99
|
+
|
53
100
|
def debug_indicies start, stop
|
54
101
|
(start...stop).step(0x10)
|
55
102
|
.map{|el| el.to_s 16 }
|
@@ -70,27 +117,5 @@ module GBRb
|
|
70
117
|
.map{|el| "|" + el + "|"}
|
71
118
|
end
|
72
119
|
|
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
120
|
end
|
96
121
|
end
|
data/lib/gbrb/options.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module GBRb
|
4
|
+
class Options
|
5
|
+
def initialize argv
|
6
|
+
if argv.length < 1
|
7
|
+
STDERR.puts "No game cartridge specified. See `gbrb -h` for usage."
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
@config = { skip_boot: false,
|
12
|
+
debug: false,
|
13
|
+
cartridge_path: argv.pop
|
14
|
+
}
|
15
|
+
|
16
|
+
if @config[:cartridge_path] =~ /^-/
|
17
|
+
argv.push @config[:cartridge_path]
|
18
|
+
@config[:cartridge_path] = ''
|
19
|
+
end
|
20
|
+
|
21
|
+
parse argv
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing m, *a, &b
|
25
|
+
@config.fetch(m) { super }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def parse argv
|
30
|
+
OptionParser.new do |opts|
|
31
|
+
opts.on("-d", "--debug", "Enable interactive debugger") do
|
32
|
+
@config[:debug] = :interactive
|
33
|
+
end
|
34
|
+
opts.on("-D", "--dump", "Dump the state of the registers after each instruction.") do
|
35
|
+
@config[:debug] = :dump
|
36
|
+
end
|
37
|
+
opts.on("-h", "--help", "Show this help screen") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
opts.on("-s", "--skip-boot", "Skip the Nintendo logo scroll") do
|
42
|
+
@config[:skip_boot] = true
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
opts.parse! argv
|
47
|
+
rescue OptionParser::ParseError => e
|
48
|
+
STDERR.puts e.message, "\n", opts
|
49
|
+
exit 2
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/gbrb/timer.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative '../gbrb'
|
2
|
+
|
3
|
+
module GBRb
|
4
|
+
class Timer
|
5
|
+
DIV_ADDRESS = 0xff04
|
6
|
+
TIMA_ADDRESS = 0xff05
|
7
|
+
TMA_ADDRESS = 0xff06
|
8
|
+
TAC_ADDRESS = 0xff07
|
9
|
+
ADDRESSES = [DIV_ADDRESS, TIMA_ADDRESS, TMA_ADDRESS, TAC_ADDRESS]
|
10
|
+
|
11
|
+
Frequency = Struct.new :string, :cycles
|
12
|
+
FREQUENCIES = { freq4096: Frequency.new('4096 Hz', 256),
|
13
|
+
freq16384: Frequency.new('16384 Hz', 64),
|
14
|
+
freq65536: Frequency.new('65536 Hz', 16),
|
15
|
+
freq262144: Frequency.new('262144 Hz', 4)
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_reader :div, :tima, :tma, :tac
|
19
|
+
attr_writer :irq_handler
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@div = Counter.new 'DIV', :freq16384
|
23
|
+
@tima = Counter.new 'TIMA', :freq4096
|
24
|
+
@tac = 0x00
|
25
|
+
@tma = 0x00
|
26
|
+
end
|
27
|
+
|
28
|
+
def step cycles
|
29
|
+
if @tac & 0x04 == 0x04 and @tima.step cycles
|
30
|
+
@irq_handler.irq TIMER_OVERFLOW_IRQ
|
31
|
+
@tima.value = @tma
|
32
|
+
end
|
33
|
+
@div.step cycles
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_byte addr
|
37
|
+
case addr
|
38
|
+
when DIV_ADDRESS
|
39
|
+
@div.value
|
40
|
+
when TIMA_ADDRESS
|
41
|
+
@tima.value
|
42
|
+
when TMA_ADDRESS
|
43
|
+
@tma
|
44
|
+
when TAC_ADDRESS
|
45
|
+
@tac
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def write_byte addr, value
|
50
|
+
case addr
|
51
|
+
when DIV_ADDRESS
|
52
|
+
@div.value = 0
|
53
|
+
when TIMA_ADDRESS
|
54
|
+
@tima.value = value
|
55
|
+
when TMA_ADDRESS
|
56
|
+
@tma = value
|
57
|
+
when TAC_ADDRESS
|
58
|
+
old_freq = get_frequency(@tac & 0x03)
|
59
|
+
new_freq = get_frequency(value & 0x03)
|
60
|
+
@tima.set_frequency new_freq unless old_freq == new_freq
|
61
|
+
@tac = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_frequency id
|
66
|
+
case id
|
67
|
+
when 0
|
68
|
+
:freq4096
|
69
|
+
when 1
|
70
|
+
:freq262144
|
71
|
+
when 2
|
72
|
+
:freq65536
|
73
|
+
when 3
|
74
|
+
:freq16384
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
class Counter
|
80
|
+
attr_accessor :value
|
81
|
+
|
82
|
+
def initialize name, initial_frequency
|
83
|
+
reset
|
84
|
+
@name = name
|
85
|
+
set_frequency initial_frequency
|
86
|
+
end
|
87
|
+
|
88
|
+
def reset
|
89
|
+
@frequency = FREQUENCIES[:freq4096]
|
90
|
+
@value = 0
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_frequency frequency
|
94
|
+
@frequency = FREQUENCIES[frequency] if FREQUENCIES.has_key? frequency
|
95
|
+
@clock_counter = @frequency.cycles
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
"#{@name}: Frequency: #{@frequency.string} (#{@frequency.cycles} cycles) | Current Counter: #{@clock_counter} | Value: #{@value}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def step cycles
|
103
|
+
@clock_counter -= cycles
|
104
|
+
|
105
|
+
while @clock_counter <= 0
|
106
|
+
@value = (@value + 1) & 0xff
|
107
|
+
@clock_counter += @frequency.cycles
|
108
|
+
return true if @value == 0
|
109
|
+
end
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/gbrb/version.rb
CHANGED
data/misc/dump_diff
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
REGISTERS = %i[pc sp a b c d e h l f opcode]
|
4
|
+
|
5
|
+
class Cpu
|
6
|
+
attr_accessor :registers
|
7
|
+
|
8
|
+
def initialize values=nil
|
9
|
+
if values
|
10
|
+
@registers = values
|
11
|
+
else
|
12
|
+
@registers = {}
|
13
|
+
REGISTERS.each {|r| @registers[r.to_sym] = 0}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@registers.inspect
|
19
|
+
end
|
20
|
+
|
21
|
+
def == other
|
22
|
+
@registers == other.registers
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Parser
|
27
|
+
include Enumerable
|
28
|
+
|
29
|
+
def initialize input, separator="\n"
|
30
|
+
@input = input
|
31
|
+
@separator = separator
|
32
|
+
skip_preamble
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip_preamble
|
36
|
+
raise StandardError
|
37
|
+
end
|
38
|
+
|
39
|
+
def succ
|
40
|
+
raise StopIteration if @input.eof?
|
41
|
+
@current_raw_value = @input.gets(@separator).chomp(@separator)
|
42
|
+
end
|
43
|
+
alias :next :succ
|
44
|
+
|
45
|
+
def rewind
|
46
|
+
@input.rewind
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy
|
50
|
+
@input.close
|
51
|
+
end
|
52
|
+
|
53
|
+
def each &block
|
54
|
+
return self.dup unless block
|
55
|
+
loop { block.call succ }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class GBRbParser < Parser
|
60
|
+
def current
|
61
|
+
Cpu.new Hash[
|
62
|
+
REGISTERS.zip( @current_raw_value.split('|')
|
63
|
+
.tap{|el| el.delete_at(10)} # Remove CB prefix
|
64
|
+
.map{|el| el.to_i(16)}
|
65
|
+
)
|
66
|
+
]
|
67
|
+
end
|
68
|
+
|
69
|
+
def skip_preamble
|
70
|
+
separator_count = 0
|
71
|
+
loop do
|
72
|
+
c = @input.getc
|
73
|
+
if /\*/.match c
|
74
|
+
separator_count += 1
|
75
|
+
c = @input.getc until c == "\n"
|
76
|
+
end
|
77
|
+
if separator_count == 2
|
78
|
+
self.next
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class GomeboyParser < Parser
|
86
|
+
def current
|
87
|
+
tmp = @current_raw_value.split(' ')
|
88
|
+
.map{|el| el.split(': ') }
|
89
|
+
.take(11)
|
90
|
+
.map{|el| el.length == 2 ? el : el.first.split(' ').first}
|
91
|
+
|
92
|
+
tmp[-1] = ["OPCODE", tmp[-1]]
|
93
|
+
tmp[-2] = ["F", "0x" + tmp[-2].chars.each_with_index.map{|el, i| el == '-' ? 0 : 0b1 << (7-i)}.inject(0, &:+).to_s(16)]
|
94
|
+
Cpu.new Hash[ tmp.map{|el| [el.first.downcase.to_sym, el.last.to_i(16)]} ]
|
95
|
+
end
|
96
|
+
|
97
|
+
def skip_preamble
|
98
|
+
self.next
|
99
|
+
separator_count = 0
|
100
|
+
loop do
|
101
|
+
c = @input.getc
|
102
|
+
if /\-/.match c
|
103
|
+
separator_count += 1
|
104
|
+
c = @input.getc until c == "\n"
|
105
|
+
end
|
106
|
+
break if separator_count == 2
|
107
|
+
end
|
108
|
+
self.next
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if ARGV.length != 2
|
113
|
+
puts "Two dumps required"
|
114
|
+
exit 1
|
115
|
+
end
|
116
|
+
|
117
|
+
gbrb = GBRbParser.new File.open ARGV[0]
|
118
|
+
gomeboy = GomeboyParser.new File.open ARGV[1]
|
119
|
+
|
120
|
+
steps_matched = 0
|
121
|
+
different = false
|
122
|
+
while !different do
|
123
|
+
gomeboy.next
|
124
|
+
gbrb.next
|
125
|
+
if gomeboy.current != gbrb.current
|
126
|
+
puts "Gomeboy: #{gomeboy.current}"
|
127
|
+
puts "GBRb: #{gbrb.current}"
|
128
|
+
different = true
|
129
|
+
end
|
130
|
+
steps_matched += 1
|
131
|
+
end
|
132
|
+
|
133
|
+
puts "Steps Matched: #{steps_matched}"
|