GBRb 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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}"
|