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 else # Operand 2 is a register
rotate_register bits(instr, 0..11), pointerof(barrel_shifter_carry_out) rotate_register bits(instr, 0..11), pointerof(barrel_shifter_carry_out)
end 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 case opcode
when 0b0000 # AND when 0b0000 # AND
set_reg(rd, @r[rn] & operand_2) set_reg(rd, @r[rn] & operand_2)
@ -84,5 +78,12 @@ module ARM
else raise "Unimplemented execution of data processing opcode: #{hex_str opcode}" else raise "Unimplemented execution of data processing opcode: #{hex_str opcode}"
end end
@r[15] &-= 4 if pc_reads_12_ahead @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
end end

View file

@ -1,21 +1,28 @@
module ARM module ARM
def arm_psr_transfer(instr : Word) : Nil def arm_psr_transfer(instr : Word) : Nil
spsr = bit?(instr, 22) 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 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 |= 0xFF000000 if bit?(instr, 19) # f (aka _flg)
mask |= 0x00FF0000 if bit?(instr, 18) # s mask |= 0x00FF0000 if bit?(instr, 18) # s
mask |= 0x0000FF00 if bit?(instr, 17) # x mask |= 0x0000FF00 if bit?(instr, 17) # x
mask |= 0x000000FF if bit?(instr, 16) # c (aka _ctl) 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 barrel_shifter_carry_out = false # unused, doesn't matter
value = immediate_offset bits(instr, 0..11), pointerof(barrel_shifter_carry_out) value = immediate_offset bits(instr, 0..11), pointerof(barrel_shifter_carry_out)
else # register value else # register value
value = @r[bits(instr, 0..3)] value = @r[bits(instr, 0..3)]
end end
value &= mask value &= mask
if spsr if spsr
@spsr.value = (@spsr.value & ~mask) | value if has_spsr
@spsr.value = (@spsr.value & ~mask) | value
end
else else
thumb = @cpsr.thumb thumb = @cpsr.thumb
switch_mode CPU::Mode.from_value value & 0x1F if mask & 0xFF > 0 switch_mode CPU::Mode.from_value value & 0x1F if mask & 0xFF > 0
@ -24,7 +31,7 @@ module ARM
end end
else # MRS else # MRS
rd = bits(instr, 12..15) rd = bits(instr, 12..15)
if spsr if spsr && has_spsr
set_reg(rd, @spsr.value) set_reg(rd, @spsr.value)
else else
set_reg(rd, @cpsr.value) set_reg(rd, @cpsr.value)

View file

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

View file

@ -50,7 +50,7 @@ class MMIO
@waitcnt.value = (@waitcnt.value & mask) | value.to_u16 << shift @waitcnt.value = (@waitcnt.value & mask) | value.to_u16 << shift
elsif not_used? io_addr elsif not_used? io_addr
else 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
end end