ldm/stm rewrite & always write incrementing, passing gba-suite/nes

This commit is contained in:
Matthew Berry 2021-02-20 19:49:50 -08:00
parent 33979013c3
commit 2950d0d0a3

View file

@ -14,55 +14,36 @@ module ARM
switch_mode CPU::Mode::USR
end
step_arm # step in advance since str from r15 is 12 ahead
address = @r[rn]
step = add ? 4 : -4
idxs = add ? 16.times : 15.downto(0)
unless list == 0
if load
idxs.each do |idx|
if bit?(list, idx)
address &+= step if pre_index
set_reg(idx, @gba.bus.read_word(address))
address &+= step unless pre_index
end
bits_set = count_set_bits(list)
if bits_set == 0 # odd behavior on empty list, tested in gba-suite
bits_set = 16
list = 0x8000
end
final_addr = address + bits_set * (add ? 4 : -4)
if add
address += 4 if pre_index
else
address = final_addr
address += 4 unless pre_index
end
first_transfer = false
16.times do |idx| # always transfered to/from incrementing addresses
if bit?(list, idx)
if load
set_reg(idx, @gba.bus.read_word(address))
else
@gba.bus[address] = @r[idx]
end
else
base_addr = nil
idxs.each do |idx|
if bit?(list, idx)
address &+= step if pre_index
@gba.bus[address] = @r[idx]
@gba.bus[address] &+= 4 if idx == 15
base_addr = address if rn == idx
address &+= step unless pre_index
end
end
@gba.bus[base_addr] = address if base_addr && first_set_bit(list) != rn # rn is written after first store
address += 4 # can always do these post since the address was accounted for up front
set_reg(rn, final_addr) if write_back && !first_transfer && !(load && bit?(list, rn))
first_transfer = true # writeback happens on second cycle of the instruction
end
else # https://github.com/jsmolka/gba-suite/blob/master/arm/block_transfer.asm#L214
offset = case {add, pre_index} # todo stop hard coding this, but it'll do for now...
when {true, true} then 0x4
when {true, false} then 0x0
when {false, true} then -0x40
when {false, false} then -0x3C
else abort "Impossible ldm/stm empty list case"
end
if load
set_reg(15, @gba.bus.read_word(address &+ offset))
else
@gba.bus[address &+ offset] = @r[15] &+ 4
end
address &+= 0x10 * step
end
if s_bit
switch_mode CPU::Mode.from_value mode.not_nil!
end
set_reg(rn, address) if write_back && !(load && bit?(list, rn))
step_arm unless load && (bit?(list, 15) || list == 0)
end
end