passing destoer isr test, booting kirby, failing tonc irq_demo

- handle exception returns after alu
- proper spsr banking
- fill pipeline before interrupts
- align r15 before each pipeline fill
This commit is contained in:
Matthew Berry 2020-11-28 00:07:21 -08:00
parent b6346536d5
commit 042b76d910
4 changed files with 34 additions and 28 deletions

View file

@ -16,12 +16,6 @@ module ARM
else # Operand 2 is a register
rotate_register bits(instr, 0..11), pointerof(barrel_shifter_carry_out)
end
if rd == 15 && set_conditions
old_spsr = @spsr.value
switch_mode CPU::Mode.from_value @spsr.mode
@cpsr.value = old_spsr
set_conditions = false
end
case opcode
when 0b0000 # AND
set_reg(rd, @r[rn] & operand_2)
@ -84,5 +78,12 @@ module ARM
else raise "Unimplemented execution of data processing opcode: #{hex_str opcode}"
end
@r[15] &-= 4 if pc_reads_12_ahead
if rd == 15 && set_conditions
old_spsr = @spsr.value
new_mode = CPU::Mode.from_value(@spsr.mode)
switch_mode new_mode
@cpsr.value = old_spsr
@spsr.value = new_mode.bank == 0 ? @cpsr.value : @spsr_banks[new_mode.bank]
end
end
end

View file

@ -1,21 +1,28 @@
module ARM
def arm_psr_transfer(instr : Word) : Nil
spsr = bit?(instr, 22)
mode = CPU::Mode.from_value @cpsr.mode
has_spsr = mode != CPU::Mode::USR && mode != CPU::Mode::SYS
if bit?(instr, 21) # MSR
mask = 0_u32 # described in gbatek, not in arm manual https://problemkaputt.de/gbatek.htm#armopcodespsrtransfermrsmsr
mask = 0_u32
mask |= 0xFF000000 if bit?(instr, 19) # f (aka _flg)
mask |= 0x00FF0000 if bit?(instr, 18) # s
mask |= 0x0000FF00 if bit?(instr, 17) # x
mask |= 0x000000FF if bit?(instr, 16) # c (aka _ctl)
if bit?(instr, 25) # immediate
if bit?(instr, 25) # immediate
barrel_shifter_carry_out = false # unused, doesn't matter
value = immediate_offset bits(instr, 0..11), pointerof(barrel_shifter_carry_out)
else # register value
value = @r[bits(instr, 0..3)]
end
value &= mask
if spsr
@spsr.value = (@spsr.value & ~mask) | value
if has_spsr
@spsr.value = (@spsr.value & ~mask) | value
end
else
thumb = @cpsr.thumb
switch_mode CPU::Mode.from_value value & 0x1F if mask & 0xFF > 0
@ -24,7 +31,7 @@ module ARM
end
else # MRS
rd = bits(instr, 12..15)
if spsr
if spsr && has_spsr
set_reg(rd, @spsr.value)
else
set_reg(rd, @cpsr.value)

View file

@ -40,12 +40,13 @@ class CPU
end
getter r = Slice(Word).new 16
@cpsr : PSR = PSR.new 0x0000001F
@spsr : PSR = PSR.new 0
@cpsr : PSR = PSR.new CPU::Mode::SYS.value
@spsr : PSR = PSR.new CPU::Mode::SYS.value
getter pipeline = Pipeline.new
getter lut : Slice(Proc(Word, Nil)) { fill_lut }
getter thumb_lut : Slice(Proc(Word, Nil)) { fill_thumb_lut }
@reg_banks = Array(Array(Word)).new 6 { Array(Word).new 8, 0 }
@reg_banks = Array(Array(Word)).new 6 { Array(Word).new 7, 0 }
@spsr_banks = Array(Word).new 6, CPU::Mode::SYS.value # logically independent of typical register banks
def initialize(@gba : GBA)
@reg_banks[Mode::USR.bank][5] = @r[13] = 0x03007F00
@ -57,8 +58,6 @@ class CPU
def switch_mode(new_mode : Mode) : Nil
old_mode = Mode.from_value @cpsr.mode
return if new_mode == old_mode
@spsr.value = @cpsr.value
@cpsr.mode = new_mode.value
new_bank = new_mode.bank
old_bank = old_mode.bank
if new_mode == Mode::FIQ || old_mode == Mode::FIQ
@ -70,17 +69,20 @@ class CPU
# store old regs
@reg_banks[old_bank][5] = @r[13]
@reg_banks[old_bank][6] = @r[14]
@spsr_banks[old_bank] = @spsr.value
# load new regs
@r[13] = @reg_banks[new_bank][5]
@r[14] = @reg_banks[new_bank][6]
@spsr.value = @cpsr.value
@cpsr.mode = new_mode.value
end
def irq : Nil
unless @cpsr.irq_disable
fill_pipeline
lr = @r[15] - (@cpsr.thumb ? 0 : 4)
switch_mode CPU::Mode::IRQ
@cpsr.thumb = false
clear_pipeline
@cpsr.irq_disable = true
set_reg(14, lr)
set_reg(15, 0x18)
@ -88,6 +90,11 @@ class CPU
end
def fill_pipeline : Nil
if @cpsr.thumb # moved alignment to interpreter loop to fix exception returning
@r[15] &= ~1
else
@r[15] &= ~3
end
while @pipeline.size < 2
if @cpsr.thumb
log "Fetch pc: #{hex_str @r[15]}, instr: #{hex_str @gba.bus.read_half(@r[15]).to_u16}"
@ -141,17 +148,8 @@ class CPU
@[AlwaysInline]
def set_reg(reg : Int, value : UInt32) : UInt32
case reg
when 15
if @cpsr.thumb
@r[reg] = value & ~1
else
@r[reg] = value & ~3
end
clear_pipeline
else @r[reg] = value
end
value
clear_pipeline if reg == 15
@r[reg] = value
end
@[AlwaysInline]

View file

@ -50,7 +50,7 @@ class MMIO
@waitcnt.value = (@waitcnt.value & mask) | value.to_u16 << shift
elsif not_used? io_addr
else
puts "Unmapped MMIO write ~ addr:#{hex_str io_addr.to_u32}, val:#{value}".colorize(:yellow)
puts "Unmapped MMIO write ~ addr:#{hex_str index.to_u32}, val:#{hex_str value}".colorize(:yellow)
end
end