mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-19 10:26:44 +01:00
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:
parent
b6346536d5
commit
042b76d910
4 changed files with 34 additions and 28 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue