arm / thumb software interrupts, register banking

This commit is contained in:
Matthew Berry 2020-10-19 00:00:05 -07:00
parent 49f6d39e84
commit b401524ea5
5 changed files with 66 additions and 2 deletions

View file

@ -21,7 +21,7 @@ module ARM
lut = Slice(Proc(Word, Nil)).new 4096, ->arm_unimplemented(Word) lut = Slice(Proc(Word, Nil)).new 4096, ->arm_unimplemented(Word)
4096.times do |idx| 4096.times do |idx|
if idx & 0b111100000000 == 0b111100000000 if idx & 0b111100000000 == 0b111100000000
# software interrupt lut[idx] = ->arm_software_interrupt(Word)
elsif idx & 0b111100000001 == 0b111000000001 elsif idx & 0b111100000001 == 0b111000000001
# coprocessor register transfer # coprocessor register transfer
elsif idx & 0b111100000001 == 0b111000000001 elsif idx & 0b111100000001 == 0b111000000001

View file

@ -0,0 +1,10 @@
module ARM
def arm_software_interrupt(instr : Word) : Nil
lr = @r[15] - 4
switch_mode CPU::Mode::SVC
@r[14] = lr
@cpsr.irq_disable = true
@r[15] = 0x08
clear_pipeline
end
end

View file

@ -6,6 +6,27 @@ class CPU
include ARM include ARM
include THUMB include THUMB
enum Mode
USR = 0b10000
FIQ = 0b10001
IRQ = 0b10010
SVC = 0b10011
ABT = 0b10111
UND = 0b11011
SYS = 0b11111
def bank : Int
case self
in Mode::USR, Mode::SYS then 0 # todo maybe some cpsr bits can't be changed in user mode?
in Mode::FIQ then 1
in Mode::IRQ then 2
in Mode::SVC then 3
in Mode::ABT then 4
in Mode::UND then 5
end
end
end
class PSR < BitField(UInt32) class PSR < BitField(UInt32)
bool negative bool negative
bool zero bool zero
@ -23,6 +44,7 @@ class CPU
@pipeline = Pipeline.new @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 }
def initialize(@gba : GBA) def initialize(@gba : GBA)
@r[0] = 0x08000000 @r[0] = 0x08000000
@ -32,6 +54,27 @@ class CPU
@cpsr = PSR.new 0x6000001F @cpsr = PSR.new 0x6000001F
end end
def switch_mode(new_mode : Mode) : Nil
old_mode = Mode.from_value @cpsr.mode
return if new_mode == old_mode
new_bank = new_mode.bank
old_bank = old_mode.bank
if new_mode == Mode::FIQ || old_mode == Mode::FIQ
5.times do |idx|
@reg_banks[old_bank][idx] = @r[8 + idx]
@r[8 + idx] = @reg_banks[new_bank][idx]
end
end
# store old regs
@reg_banks[old_bank][5] = @r[13]
@reg_banks[old_bank][6] = @r[14]
@reg_banks[old_bank][7] = @cpsr.value
# load new regs
@r[13] = @reg_banks[new_bank][5]
@r[14] = @reg_banks[new_bank][6]
@cpsr.value = @reg_banks[new_bank][7]
end
def fill_pipeline : Nil def fill_pipeline : Nil
while @pipeline.size < 2 while @pipeline.size < 2
if @cpsr.thumb if @cpsr.thumb

View file

@ -0,0 +1,11 @@
module THUMB
def thumb_software_interrupt(instr : Word) : Nil
lr = @r[15] - 4
switch_mode CPU::Mode::SVC
@r[14] = lr
@cpsr.irq_disable = true
@cpsr.thumb = false
@r[15] = 0x08
clear_pipeline
end
end

View file

@ -11,7 +11,7 @@ module THUMB
elsif idx & 0b11111000 == 0b11100000 elsif idx & 0b11111000 == 0b11100000
lut[idx] = ->thumb_unconditional_branch(Word) lut[idx] = ->thumb_unconditional_branch(Word)
elsif idx & 0b11111111 == 0b11011111 elsif idx & 0b11111111 == 0b11011111
# software interrupt lut[idx] = ->thumb_software_interrupt(Word)
elsif idx & 0b11110000 == 0b11010000 elsif idx & 0b11110000 == 0b11010000
lut[idx] = ->thumb_conditional_branch(Word) lut[idx] = ->thumb_conditional_branch(Word)
elsif idx & 0b11110000 == 0b11000000 elsif idx & 0b11110000 == 0b11000000