mirror of
https://github.com/mattrberry/crab.git
synced 2025-02-09 08:46:02 +01:00
support additional instructions for waitloop skipping
Adds support for MultipleLoadStore, LoadStoreImmediateOffset, and MoveShiftedRegister. This allows waitloop skipping to work in Spiderman the Movie, bringing in-game fps from 280 to 375 and title screen fps from 450 to 700. This change also disallows instructions that branch from participating in waitloop skipping. I don't expect this to affect anything, but I figure it's better to be overly cautious.
This commit is contained in:
parent
f6d88f82fa
commit
51a9de443a
1 changed files with 106 additions and 5 deletions
|
@ -10,10 +10,10 @@ module GBA
|
||||||
@branch_dest = 0_u32
|
@branch_dest = 0_u32
|
||||||
|
|
||||||
# Collection of branch destinations identified as waitloops.
|
# Collection of branch destinations identified as waitloops.
|
||||||
@identified_waitloops = Array(UInt32).new
|
@identified_waitloops = Array(UInt32).new(10)
|
||||||
|
|
||||||
# Collection of branch destinations identified as non-waitloops.
|
# Collection of branch destinations identified as non-waitloops.
|
||||||
@identified_non_waitloops = Array(UInt32).new
|
@identified_non_waitloops = Array(UInt32).new(10)
|
||||||
|
|
||||||
# Flags when a waitloop is detected. Used by the CPU to fast-forward.
|
# Flags when a waitloop is detected. Used by the CPU to fast-forward.
|
||||||
@entered_waitloop = false
|
@entered_waitloop = false
|
||||||
|
@ -21,6 +21,9 @@ module GBA
|
||||||
# Table to quickly look up an instruction's class.
|
# Table to quickly look up an instruction's class.
|
||||||
getter waitloop_instr_lut : Slice(Instruction.class) { build_lut }
|
getter waitloop_instr_lut : Slice(Instruction.class) { build_lut }
|
||||||
|
|
||||||
|
# Instructions that waitloop detection fails to parse. Used temporarily for debugging until all instructions are implemented.
|
||||||
|
@waitloop_parse_failures = Set(Instruction.class).new
|
||||||
|
|
||||||
# Attempt to detect a waitloop. Assumes thumb instructions.
|
# Attempt to detect a waitloop. Assumes thumb instructions.
|
||||||
def analyze_loop(start_addr : UInt32, end_addr : UInt32) : Nil
|
def analyze_loop(start_addr : UInt32, end_addr : UInt32) : Nil
|
||||||
return unless @attempt_waitloop_detection
|
return unless @attempt_waitloop_detection
|
||||||
|
@ -37,9 +40,14 @@ module GBA
|
||||||
written_bits = never_write = 0_u16
|
written_bits = never_write = 0_u16
|
||||||
(start_addr...end_addr).step(2) do |addr|
|
(start_addr...end_addr).step(2) do |addr|
|
||||||
instr = @gba.bus.read_half_internal(addr)
|
instr = @gba.bus.read_half_internal(addr)
|
||||||
parsed_instr = waitloop_instr_lut[instr >> 8].parse?(instr)
|
instr_struct = waitloop_instr_lut[instr >> 8]
|
||||||
|
parsed_instr = instr_struct.parse?(instr)
|
||||||
|
|
||||||
unless parsed_instr && parsed_instr.read_only?
|
unless parsed_instr && parsed_instr.read_only?
|
||||||
|
if parsed_instr == nil && !@waitloop_parse_failures.includes?(instr_struct)
|
||||||
|
puts "Failed to parse a #{instr_struct} while checking for a waitloop"
|
||||||
|
@waitloop_parse_failures.add(instr_struct)
|
||||||
|
end
|
||||||
@identified_non_waitloops.push(start_addr) if @cache_waitloop_results
|
@identified_non_waitloops.push(start_addr) if @cache_waitloop_results
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -50,6 +58,11 @@ module GBA
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if parsed_instr.write_bits & 1_u16 << 15 > 0 # instruction might branch, which could indicate an impure loop.
|
||||||
|
@identified_non_waitloops.push(start_addr) if @cache_waitloop_results
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
written_bits |= parsed_instr.write_bits
|
written_bits |= parsed_instr.write_bits
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -125,11 +138,11 @@ module GBA
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_bits : UInt16
|
def read_bits : UInt16
|
||||||
0_u16
|
1_u16 << 15
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_bits : UInt16
|
def write_bits : UInt16
|
||||||
0_u16
|
1_u16 << 15
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse?(instr : UInt16) : ConditionalBranch
|
def self.parse?(instr : UInt16) : ConditionalBranch
|
||||||
|
@ -140,6 +153,43 @@ module GBA
|
||||||
end
|
end
|
||||||
|
|
||||||
struct MultipleLoadStore < Instruction
|
struct MultipleLoadStore < Instruction
|
||||||
|
def initialize(@load : Bool, @rb : UInt16, @list : UInt16)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_only? : Bool
|
||||||
|
@load
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_bits : UInt16
|
||||||
|
res = 1_u16 << @rb # always read
|
||||||
|
unless @load
|
||||||
|
if @list == 0
|
||||||
|
res |= 1_u16 << 15
|
||||||
|
else
|
||||||
|
res |= @list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_bits : UInt16
|
||||||
|
res = 1_u16 << @rb # always written to
|
||||||
|
if @load
|
||||||
|
if @list == 0
|
||||||
|
res |= 1_u16 << 15
|
||||||
|
else
|
||||||
|
res |= @list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse?(instr : UInt16) : MultipleLoadStore
|
||||||
|
load = bit?(instr, 11)
|
||||||
|
rb = bits(instr, 8..10)
|
||||||
|
list = bits(instr, 0..7)
|
||||||
|
new(load, rb, list)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct PushPopRegisters < Instruction
|
struct PushPopRegisters < Instruction
|
||||||
|
@ -186,6 +236,35 @@ module GBA
|
||||||
end
|
end
|
||||||
|
|
||||||
struct LoadStoreImmediateOffset < Instruction
|
struct LoadStoreImmediateOffset < Instruction
|
||||||
|
def initialize(@byte_quantity : Bool, @load : Bool, @offset : UInt16, @rb : UInt16, @rd : UInt16)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_only? : Bool
|
||||||
|
@load
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_bits : UInt16
|
||||||
|
res = 1_u16 << @rb
|
||||||
|
res |= 1_u16 << @rd unless @load
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_bits : UInt16
|
||||||
|
if @load
|
||||||
|
1_u16 << @rd
|
||||||
|
else
|
||||||
|
0_u16
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse?(instr : UInt16) : LoadStoreImmediateOffset
|
||||||
|
byte_quantity = bit?(instr, 12)
|
||||||
|
load = bit?(instr, 11)
|
||||||
|
offset = bits(instr, 6..10)
|
||||||
|
rb = bits(instr, 3..5)
|
||||||
|
rd = bits(instr, 0..2)
|
||||||
|
new(byte_quantity, load, offset, rb, rd)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct LoadStoreSignExtended < Instruction
|
struct LoadStoreSignExtended < Instruction
|
||||||
|
@ -280,6 +359,28 @@ module GBA
|
||||||
end
|
end
|
||||||
|
|
||||||
struct MoveShiftedRegister < Instruction
|
struct MoveShiftedRegister < Instruction
|
||||||
|
def initialize(op : UInt16, @offset : UInt16, @rs : UInt16, @rd : UInt16)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_only? : Bool
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_bits : UInt16
|
||||||
|
1_u16 << @rs
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_bits : UInt16
|
||||||
|
1_u16 << @rd
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse?(instr : UInt16) : MoveShiftedRegister
|
||||||
|
op = bits(instr, 11..12)
|
||||||
|
offset = bits(instr, 6..10)
|
||||||
|
rs = bits(instr, 3..5)
|
||||||
|
rd = bits(instr, 0..2)
|
||||||
|
new(op, offset, rs, rd)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct Unimplemented < Instruction
|
struct Unimplemented < Instruction
|
||||||
|
|
Loading…
Add table
Reference in a new issue