From 042b76d91084e36df902d327f35f1c79187f4251 Mon Sep 17 00:00:00 2001 From: Matthew Berry Date: Sat, 28 Nov 2020 00:07:21 -0800 Subject: [PATCH] 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 --- src/crab/arm/data_processing.cr | 13 +++++++------ src/crab/arm/psr_transfer.cr | 15 +++++++++++---- src/crab/cpu.cr | 32 +++++++++++++++----------------- src/crab/mmio.cr | 2 +- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/crab/arm/data_processing.cr b/src/crab/arm/data_processing.cr index 84009d9..41e152f 100644 --- a/src/crab/arm/data_processing.cr +++ b/src/crab/arm/data_processing.cr @@ -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 diff --git a/src/crab/arm/psr_transfer.cr b/src/crab/arm/psr_transfer.cr index 64fbb7a..afae637 100644 --- a/src/crab/arm/psr_transfer.cr +++ b/src/crab/arm/psr_transfer.cr @@ -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) diff --git a/src/crab/cpu.cr b/src/crab/cpu.cr index 62ddc43..aa46837 100644 --- a/src/crab/cpu.cr +++ b/src/crab/cpu.cr @@ -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] diff --git a/src/crab/mmio.cr b/src/crab/mmio.cr index c67fce3..04044a1 100644 --- a/src/crab/mmio.cr +++ b/src/crab/mmio.cr @@ -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