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
|
||||
|
||||
# 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.
|
||||
@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.
|
||||
@entered_waitloop = false
|
||||
|
@ -21,6 +21,9 @@ module GBA
|
|||
# Table to quickly look up an instruction's class.
|
||||
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.
|
||||
def analyze_loop(start_addr : UInt32, end_addr : UInt32) : Nil
|
||||
return unless @attempt_waitloop_detection
|
||||
|
@ -37,9 +40,14 @@ module GBA
|
|||
written_bits = never_write = 0_u16
|
||||
(start_addr...end_addr).step(2) do |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?
|
||||
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
|
||||
return
|
||||
end
|
||||
|
@ -50,6 +58,11 @@ module GBA
|
|||
return
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -125,11 +138,11 @@ module GBA
|
|||
end
|
||||
|
||||
def read_bits : UInt16
|
||||
0_u16
|
||||
1_u16 << 15
|
||||
end
|
||||
|
||||
def write_bits : UInt16
|
||||
0_u16
|
||||
1_u16 << 15
|
||||
end
|
||||
|
||||
def self.parse?(instr : UInt16) : ConditionalBranch
|
||||
|
@ -140,6 +153,43 @@ module GBA
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
struct PushPopRegisters < Instruction
|
||||
|
@ -186,6 +236,35 @@ module GBA
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
struct LoadStoreSignExtended < Instruction
|
||||
|
@ -280,6 +359,28 @@ module GBA
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
struct Unimplemented < Instruction
|
||||
|
|
Loading…
Add table
Reference in a new issue