mirror of
https://github.com/mattrberry/crab.git
synced 2024-12-26 09:58:25 +01:00
running PokemonEmerald causes a crash in the Scheduler's event.proc.call
This commit is contained in:
parent
bc35157dca
commit
ee80ede79f
22 changed files with 265 additions and 177 deletions
|
@ -1,5 +1,6 @@
|
|||
require "./arm/*"
|
||||
require "./thumb/*"
|
||||
require "./thumb.cr"
|
||||
require "./pipeline"
|
||||
|
||||
class CPU
|
||||
|
@ -41,12 +42,12 @@ class CPU
|
|||
num mode, 5
|
||||
end
|
||||
|
||||
getter r = Slice(Word).new 16
|
||||
@cpsr : PSR = PSR.new CPU::Mode::SYS.value
|
||||
@spsr : PSR = PSR.new CPU::Mode::SYS.value
|
||||
getter r : Slice(Word) = Slice(Word).new 16
|
||||
getter cpsr : PSR = PSR.new CPU::Mode::SYS.value
|
||||
getter 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 }
|
||||
# getter thumb_lut : Slice(Proc(Word, Nil)) { fill_thumb_lut }
|
||||
@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
|
||||
property halted = false
|
||||
|
@ -126,7 +127,7 @@ class CPU
|
|||
instr = @pipeline.shift
|
||||
{% if flag? :trace %} print_state instr {% end %}
|
||||
if @cpsr.thumb
|
||||
thumb_execute instr
|
||||
thumb_execute @gba, instr
|
||||
else
|
||||
arm_execute instr
|
||||
end
|
||||
|
|
105
src/crab/thumb.cr
Normal file
105
src/crab/thumb.cr
Normal file
|
@ -0,0 +1,105 @@
|
|||
module THUMB
|
||||
def thumb_execute(gba : GBA, instr : Word) : Nil
|
||||
THUMB_LUT[instr >> 8].call gba, instr
|
||||
end
|
||||
|
||||
macro meme
|
||||
THUMB_LUT = Array(Proc(GBA, Word, Nil)).new 256, thumb_unimplemented
|
||||
{% for idx in (0...256) %}
|
||||
{% if idx & 0b11110000 == 0b11110000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_long_branch_link
|
||||
{% elsif idx & 0b11111000 == 0b11100000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_unconditional_branch
|
||||
{% elsif idx & 0b11111111 == 0b11011111 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_software_interrupt
|
||||
{% elsif idx & 0b11110000 == 0b11010000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_conditional_branch
|
||||
{% elsif idx & 0b11110000 == 0b11000000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_multiple_load_store
|
||||
{% elsif idx & 0b11110110 == 0b10110100 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_push_pop_registers
|
||||
{% elsif idx & 0b11111111 == 0b10110000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_add_offset_to_stack_pointer
|
||||
{% elsif idx & 0b11110000 == 0b10100000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_load_address
|
||||
{% elsif idx & 0b11110000 == 0b10010000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_sp_relative_load_store
|
||||
{% elsif idx & 0b11110000 == 0b10000000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_load_store_halfword
|
||||
{% elsif idx & 0b11100000 == 0b01100000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_load_store_immediate_offset
|
||||
{% elsif idx & 0b11110010 == 0b01010010 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_load_store_sign_extended
|
||||
{% elsif idx & 0b11110010 == 0b01010000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_load_store_register_offset
|
||||
{% elsif idx & 0b11111000 == 0b01001000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_pc_relative_load
|
||||
{% elsif idx & 0b11111100 == 0b01000100 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_high_reg_branch_exchange
|
||||
{% elsif idx & 0b11111100 == 0b01000000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_alu_operations
|
||||
{% elsif idx & 0b11100000 == 0b00100000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_move_compare_add_subtract
|
||||
{% elsif idx & 0b11111000 == 0b00011000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_add_subtract
|
||||
{% elsif idx & 0b11100000 == 0b00000000 %}
|
||||
THUMB_LUT[{{idx}}] = thumb_move_shifted_register
|
||||
{% end %}
|
||||
{% end %}
|
||||
end
|
||||
|
||||
meme
|
||||
|
||||
def fill_thumb_lut
|
||||
lut = Slice(Proc(Word, Nil)).new 256, ->thumb_unimplemented(Word)
|
||||
256.times do |idx|
|
||||
if idx & 0b11110000 == 0b11110000
|
||||
lut[idx] = ->thumb_long_branch_link(Word)
|
||||
elsif idx & 0b11111000 == 0b11100000
|
||||
lut[idx] = ->thumb_unconditional_branch(Word)
|
||||
elsif idx & 0b11111111 == 0b11011111
|
||||
lut[idx] = ->thumb_software_interrupt(Word)
|
||||
elsif idx & 0b11110000 == 0b11010000
|
||||
lut[idx] = ->thumb_conditional_branch(Word)
|
||||
elsif idx & 0b11110000 == 0b11000000
|
||||
lut[idx] = ->thumb_multiple_load_store(Word)
|
||||
elsif idx & 0b11110110 == 0b10110100
|
||||
lut[idx] = ->thumb_push_pop_registers(Word)
|
||||
elsif idx & 0b11111111 == 0b10110000
|
||||
lut[idx] = ->thumb_add_offset_to_stack_pointer(Word)
|
||||
elsif idx & 0b11110000 == 0b10100000
|
||||
lut[idx] = ->thumb_load_address(Word)
|
||||
elsif idx & 0b11110000 == 0b10010000
|
||||
lut[idx] = ->thumb_sp_relative_load_store(Word)
|
||||
elsif idx & 0b11110000 == 0b10000000
|
||||
lut[idx] = ->thumb_load_store_halfword(Word)
|
||||
elsif idx & 0b11100000 == 0b01100000
|
||||
lut[idx] = ->thumb_load_store_immediate_offset(Word)
|
||||
elsif idx & 0b11110010 == 0b01010010
|
||||
lut[idx] = ->thumb_load_store_sign_extended(Word)
|
||||
elsif idx & 0b11110010 == 0b01010000
|
||||
lut[idx] = ->thumb_load_store_register_offset(Word)
|
||||
elsif idx & 0b11111000 == 0b01001000
|
||||
lut[idx] = ->thumb_pc_relative_load(Word)
|
||||
elsif idx & 0b11111100 == 0b01000100
|
||||
lut[idx] = ->thumb_high_reg_branch_exchange(Word)
|
||||
elsif idx & 0b11111100 == 0b01000000
|
||||
lut[idx] = ->thumb_alu_operations(Word)
|
||||
elsif idx & 0b11100000 == 0b00100000
|
||||
lut[idx] = ->thumb_move_compare_add_subtract(Word)
|
||||
elsif idx & 0b11111000 == 0b00011000
|
||||
lut[idx] = ->thumb_add_subtract(Word)
|
||||
elsif idx & 0b11100000 == 0b00000000
|
||||
lut[idx] = ->thumb_move_shifted_register(Word)
|
||||
end
|
||||
end
|
||||
lut
|
||||
end
|
||||
|
||||
macro thumb_unimplemented
|
||||
->(gba : GBA, instr : Word) {
|
||||
puts "Unimplemented instruction: #{hex_str instr.to_u16}"
|
||||
exit 1
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,11 +1,13 @@
|
|||
module THUMB
|
||||
def thumb_add_offset_to_stack_pointer(instr : Word) : Nil
|
||||
macro thumb_add_offset_to_stack_pointer
|
||||
->(gba : GBA, instr : Word) {
|
||||
sign = bit?(instr, 7)
|
||||
offset = bits(instr, 0..6)
|
||||
if sign # negative
|
||||
set_reg(13, @r[13] &- (offset << 2))
|
||||
gba.cpu.set_reg(13, gba.cpu.r[13] &- (offset << 2))
|
||||
else # positive
|
||||
set_reg(13, @r[13] &+ (offset << 2))
|
||||
gba.cpu.set_reg(13, gba.cpu.r[13] &+ (offset << 2))
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module THUMB
|
||||
def thumb_add_subtract(instr : Word) : Nil
|
||||
macro thumb_add_subtract
|
||||
->(gba : GBA, instr : Word) {
|
||||
imm_flag = bit?(instr, 10)
|
||||
sub = bit?(instr, 9)
|
||||
imm = bits(instr, 6..8)
|
||||
|
@ -8,13 +9,14 @@ module THUMB
|
|||
operand = if imm_flag
|
||||
imm
|
||||
else
|
||||
@r[imm]
|
||||
gba.cpu.r[imm]
|
||||
end
|
||||
if sub
|
||||
set_reg(rd, sub(@r[rs], operand, true))
|
||||
gba.cpu.set_reg(rd, gba.cpu.sub(gba.cpu.r[rs], operand, true))
|
||||
else
|
||||
set_reg(rd, add(@r[rs], operand, true))
|
||||
gba.cpu.set_reg(rd, gba.cpu.add(gba.cpu.r[rs], operand, true))
|
||||
end
|
||||
set_neg_and_zero_flags(@r[rd])
|
||||
gba.cpu.set_neg_and_zero_flags(gba.cpu.r[rd])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
module THUMB
|
||||
def thumb_alu_operations(instr : Word) : Nil
|
||||
macro thumb_alu_operations
|
||||
->(gba : GBA, instr : Word) {
|
||||
op = bits(instr, 6..9)
|
||||
rs = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
barrel_shifter_carry_out = @cpsr.carry
|
||||
barrel_shifter_carry_out = gba.cpu.cpsr.carry
|
||||
case op
|
||||
when 0b0000 then res = set_reg(rd, @r[rd] & @r[rs])
|
||||
when 0b0001 then res = set_reg(rd, @r[rd] ^ @r[rs])
|
||||
when 0b0000 then res = gba.cpu.set_reg(rd, gba.cpu.r[rd] & gba.cpu.r[rs])
|
||||
when 0b0001 then res = gba.cpu.set_reg(rd, gba.cpu.r[rd] ^ gba.cpu.r[rs])
|
||||
when 0b0010
|
||||
res = set_reg(rd, lsl(@r[rd], @r[rs], pointerof(barrel_shifter_carry_out)))
|
||||
@cpsr.carry = barrel_shifter_carry_out
|
||||
res = gba.cpu.set_reg(rd, gba.cpu.lsl(gba.cpu.r[rd], gba.cpu.r[rs], pointerof(barrel_shifter_carry_out)))
|
||||
gba.cpu.cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b0011
|
||||
res = set_reg(rd, lsr(@r[rd], @r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
@cpsr.carry = barrel_shifter_carry_out
|
||||
res = gba.cpu.set_reg(rd, gba.cpu.lsr(gba.cpu.r[rd], gba.cpu.r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
gba.cpu.cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b0100
|
||||
res = set_reg(rd, asr(@r[rd], @r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
@cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b0101 then res = set_reg(rd, adc(@r[rd], @r[rs], set_conditions: true))
|
||||
when 0b0110 then res = set_reg(rd, sbc(@r[rd], @r[rs], set_conditions: true))
|
||||
res = gba.cpu.set_reg(rd, gba.cpu.asr(gba.cpu.r[rd], gba.cpu.r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
gba.cpu.cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b0101 then res = gba.cpu.set_reg(rd, gba.cpu.adc(gba.cpu.r[rd], gba.cpu.r[rs], set_conditions: true))
|
||||
when 0b0110 then res = gba.cpu.set_reg(rd, gba.cpu.sbc(gba.cpu.r[rd], gba.cpu.r[rs], set_conditions: true))
|
||||
when 0b0111
|
||||
res = set_reg(rd, ror(@r[rd], @r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
@cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b1000 then res = @r[rd] & @r[rs]
|
||||
when 0b1001 then res = set_reg(rd, sub(0, @r[rs], set_conditions: true))
|
||||
when 0b1010 then res = sub(@r[rd], @r[rs], set_conditions: true)
|
||||
when 0b1011 then res = add(@r[rd], @r[rs], set_conditions: true)
|
||||
when 0b1100 then res = set_reg(rd, @r[rd] | @r[rs])
|
||||
when 0b1101 then res = set_reg(rd, @r[rs] &* @r[rd])
|
||||
when 0b1110 then res = set_reg(rd, @r[rd] & ~@r[rs])
|
||||
when 0b1111 then res = set_reg(rd, ~@r[rs])
|
||||
res = gba.cpu.set_reg(rd, gba.cpu.ror(gba.cpu.r[rd], gba.cpu.r[rs], false, pointerof(barrel_shifter_carry_out)))
|
||||
gba.cpu.cpsr.carry = barrel_shifter_carry_out
|
||||
when 0b1000 then res = gba.cpu.r[rd] & gba.cpu.r[rs]
|
||||
when 0b1001 then res = gba.cpu.set_reg(rd, gba.cpu.sub(0, gba.cpu.r[rs], set_conditions: true))
|
||||
when 0b1010 then res = gba.cpu.sub(gba.cpu.r[rd], gba.cpu.r[rs], set_conditions: true)
|
||||
when 0b1011 then res = gba.cpu.add(gba.cpu.r[rd], gba.cpu.r[rs], set_conditions: true)
|
||||
when 0b1100 then res = gba.cpu.set_reg(rd, gba.cpu.r[rd] | gba.cpu.r[rs])
|
||||
when 0b1101 then res = gba.cpu.set_reg(rd, gba.cpu.r[rs] &* gba.cpu.r[rd])
|
||||
when 0b1110 then res = gba.cpu.set_reg(rd, gba.cpu.r[rd] & ~gba.cpu.r[rs])
|
||||
when 0b1111 then res = gba.cpu.set_reg(rd, ~gba.cpu.r[rs])
|
||||
else raise "Invalid alu op: #{op}"
|
||||
end
|
||||
set_neg_and_zero_flags(res)
|
||||
gba.cpu.set_neg_and_zero_flags(res)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
module THUMB
|
||||
def thumb_conditional_branch(instr : Word) : Nil
|
||||
macro thumb_conditional_branch
|
||||
->(gba : GBA, instr : Word) {
|
||||
cond = bits(instr, 8..11)
|
||||
offset = bits(instr, 0..7).to_i8!.to_i32
|
||||
if check_cond cond
|
||||
set_reg(15, @r[15] &+ (offset * 2))
|
||||
if gba.cpu.check_cond cond
|
||||
gba.cpu.set_reg(15, gba.cpu.r[15] &+ (offset * 2))
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module THUMB
|
||||
def thumb_high_reg_branch_exchange(instr : Word) : Nil
|
||||
macro thumb_high_reg_branch_exchange
|
||||
->(gba : GBA, instr : Word) {
|
||||
op = bits(instr, 8..9)
|
||||
h1 = bit?(instr, 7)
|
||||
h2 = bit?(instr, 6)
|
||||
|
@ -11,16 +12,17 @@ module THUMB
|
|||
|
||||
# In this group only CMP (Op = 01) sets the CPSR condition codes.
|
||||
case op
|
||||
when 0b00 then set_reg(rd, add(@r[rd], @r[rs], false))
|
||||
when 0b01 then sub(@r[rd], @r[rs], true)
|
||||
when 0b10 then set_reg(rd, @r[rs])
|
||||
when 0b00 then gba.cpu.set_reg(rd, gba.cpu.add(gba.cpu.r[rd], gba.cpu.r[rs], false))
|
||||
when 0b01 then gba.cpu.sub(gba.cpu.r[rd], gba.cpu.r[rs], true)
|
||||
when 0b10 then gba.cpu.set_reg(rd, gba.cpu.r[rs])
|
||||
when 0b11
|
||||
if bit?(@r[rs], 0)
|
||||
set_reg(15, @r[rs])
|
||||
if bit?(gba.cpu.r[rs], 0)
|
||||
gba.cpu.set_reg(15, gba.cpu.r[rs])
|
||||
else
|
||||
@cpsr.thumb = false
|
||||
set_reg(15, @r[rs])
|
||||
gba.cpu.cpsr.thumb = false
|
||||
gba.cpu.set_reg(15, gba.cpu.r[rs])
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
module THUMB
|
||||
def thumb_load_address(instr : Word) : Nil
|
||||
macro thumb_load_address
|
||||
->(gba : GBA, instr : Word) {
|
||||
source = bit?(instr, 11)
|
||||
rd = bits(instr, 8..10)
|
||||
word = bits(instr, 0..7)
|
||||
imm = word << 2
|
||||
# Where the PC is used as the source register (SP = 0), bit 1 of the PC is always read as 0.
|
||||
set_reg(rd, (source ? @r[13] : @r[15] & ~2) &+ imm)
|
||||
gba.cpu.set_reg(rd, (source ? gba.cpu.r[13] : gba.cpu.r[15] & ~2) &+ imm)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
module THUMB
|
||||
def thumb_load_store_halfword(instr : Word) : Nil
|
||||
macro thumb_load_store_halfword
|
||||
->(gba : GBA, instr : Word) {
|
||||
load = bit?(instr, 11)
|
||||
offset = bits(instr, 6..10)
|
||||
rb = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
address = @r[rb] + (offset << 1)
|
||||
address = gba.cpu.r[rb] + (offset << 1)
|
||||
if load
|
||||
set_reg(rd, @gba.bus.read_half_rotate(address))
|
||||
gba.cpu.set_reg(rd, gba.bus.read_half_rotate(address))
|
||||
else
|
||||
@gba.bus[address] = @r[rd].to_u16!
|
||||
gba.bus[address] = gba.cpu.r[rd].to_u16!
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
module THUMB
|
||||
def thumb_load_store_immediate_offset(instr : Word) : Nil
|
||||
macro thumb_load_store_immediate_offset
|
||||
->(gba : GBA, instr : Word) {
|
||||
byte_quantity_and_load = bits(instr, 11..12)
|
||||
offset = bits(instr, 6..10)
|
||||
rb = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
base_address = @r[rb]
|
||||
base_address = gba.cpu.r[rb]
|
||||
case byte_quantity_and_load
|
||||
when 0b00 then @gba.bus[base_address &+ (offset << 2)] = @r[rd] # str
|
||||
when 0b01 then set_reg(rd, @gba.bus.read_word_rotate(base_address &+ (offset << 2))) # ldr
|
||||
when 0b10 then @gba.bus[base_address &+ offset] = @r[rd].to_u8! # strb
|
||||
when 0b11 then set_reg(rd, @gba.bus[base_address &+ offset].to_u32) # ldrb
|
||||
when 0b00 then gba.bus[base_address &+ (offset << 2)] = gba.cpu.r[rd] # str
|
||||
when 0b01 then gba.cpu.set_reg(rd, gba.bus.read_word_rotate(base_address &+ (offset << 2))) # ldr
|
||||
when 0b10 then gba.bus[base_address &+ offset] = gba.cpu.r[rd].to_u8! # strb
|
||||
when 0b11 then gba.cpu.set_reg(rd, gba.bus[base_address &+ offset].to_u32) # ldrb
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
module THUMB
|
||||
def thumb_load_store_register_offset(instr : Word) : Nil
|
||||
macro thumb_load_store_register_offset
|
||||
->(gba : GBA, instr : Word) {
|
||||
load_and_byte_quantity = bits(instr, 10..11)
|
||||
ro = bits(instr, 6..8)
|
||||
rb = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
address = @r[rb] &+ @r[ro]
|
||||
address = gba.cpu.r[rb] &+ gba.cpu.r[ro]
|
||||
case load_and_byte_quantity
|
||||
when 0b00 then @gba.bus[address] = @r[rd] # str
|
||||
when 0b01 then @gba.bus[address] = @r[rd].to_u8! # strb
|
||||
when 0b10 then set_reg(rd, @gba.bus.read_word_rotate(address)) # ldr
|
||||
when 0b11 then set_reg(rd, @gba.bus[address].to_u32!) # ldrb
|
||||
when 0b00 then gba.bus[address] = gba.cpu.r[rd] # str
|
||||
when 0b01 then gba.bus[address] = gba.cpu.r[rd].to_u8! # strb
|
||||
when 0b10 then gba.cpu.set_reg(rd, gba.bus.read_word_rotate(address)) # ldr
|
||||
when 0b11 then gba.cpu.set_reg(rd, gba.bus[address].to_u32!) # ldrb
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
module THUMB
|
||||
def thumb_load_store_sign_extended(instr : Word) : Nil
|
||||
macro thumb_load_store_sign_extended
|
||||
->(gba : GBA, instr : Word) {
|
||||
hs = bits(instr, 10..11)
|
||||
ro = bits(instr, 6..8)
|
||||
rb = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
address = @r[rb] &+ @r[ro]
|
||||
address = gba.cpu.r[rb] &+ gba.cpu.r[ro]
|
||||
case hs
|
||||
when 0b00 then @gba.bus[address] = @r[rd].to_u16! # strh
|
||||
when 0b01 then set_reg(rd, @gba.bus[address].to_i8!.to_u32!) # ldsb
|
||||
when 0b10 then set_reg(rd, @gba.bus.read_half_rotate(address)) # ldrh
|
||||
when 0b11 then set_reg(rd, @gba.bus.read_half_signed(address)) # ldsh
|
||||
when 0b00 then gba.bus[address] = gba.cpu.r[rd].to_u16! # strh
|
||||
when 0b01 then gba.cpu.set_reg(rd, gba.bus[address].to_i8!.to_u32!) # ldsb
|
||||
when 0b10 then gba.cpu.set_reg(rd, gba.bus.read_half_rotate(address)) # ldrh
|
||||
when 0b11 then gba.cpu.set_reg(rd, gba.bus.read_half_signed(address)) # ldsh
|
||||
else raise "Invalid load/store signed extended: #{hs}"
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
module THUMB
|
||||
def thumb_long_branch_link(instr : Word) : Nil
|
||||
macro thumb_long_branch_link
|
||||
->(gba : GBA, instr : Word) {
|
||||
second_instr = bit?(instr, 11)
|
||||
offset = bits(instr, 0..10)
|
||||
if second_instr
|
||||
temp = @r[15] &- 2
|
||||
set_reg(15, @r[14] &+ (offset << 1))
|
||||
set_reg(14, temp | 1)
|
||||
temp = gba.cpu.r[15] &- 2
|
||||
gba.cpu.set_reg(15, gba.cpu.r[14] &+ (offset << 1))
|
||||
gba.cpu.set_reg(14, temp | 1)
|
||||
else
|
||||
offset = (offset << 5).to_i16! >> 5
|
||||
set_reg(14, @r[15] &+ (offset.to_u32! << 12))
|
||||
gba.cpu.set_reg(14, gba.cpu.r[15] &+ (offset.to_u32! << 12))
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
module THUMB
|
||||
def thumb_move_compare_add_subtract(instr : Word) : Nil
|
||||
macro thumb_move_compare_add_subtract
|
||||
->(gba : GBA, instr : Word) {
|
||||
op = bits(instr, 11..12)
|
||||
rd = bits(instr, 8..10)
|
||||
offset = bits(instr, 0..7)
|
||||
case op
|
||||
when 0b00
|
||||
set_reg(rd, offset)
|
||||
set_neg_and_zero_flags(@r[rd])
|
||||
when 0b01 then sub(@r[rd], offset, true)
|
||||
when 0b10 then set_reg(rd, add(@r[rd], offset, true))
|
||||
when 0b11 then set_reg(rd, sub(@r[rd], offset, true))
|
||||
else raise "Invalid move/compare/add/subtract op: #{op}"
|
||||
gba.cpu.set_reg(rd, offset)
|
||||
gba.cpu.set_neg_and_zero_flags(gba.cpu.r[rd])
|
||||
when 0b01 then gba.cpu.sub(gba.cpu.r[rd], offset, true)
|
||||
when 0b10 then gba.cpu.set_reg(rd, gba.cpu.add(gba.cpu.r[rd], offset, true))
|
||||
when 0b11 then gba.cpu.set_reg(rd, gba.cpu.sub(gba.cpu.r[rd], offset, true))
|
||||
else raise "Invalid move/compare/gba.cpu.add/gba.cpu.subtract op: #{op}"
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
module THUMB
|
||||
def thumb_move_shifted_register(instr : Word) : Nil
|
||||
macro thumb_move_shifted_register
|
||||
->(gba : GBA, instr : Word) {
|
||||
op = bits(instr, 11..12)
|
||||
offset = bits(instr, 6..10)
|
||||
rs = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
carry_out = @cpsr.carry
|
||||
carry_out = gba.cpu.cpsr.carry
|
||||
case op
|
||||
when 0b00 then set_reg(rd, lsl(@r[rs], offset, pointerof(carry_out)))
|
||||
when 0b01 then set_reg(rd, lsr(@r[rs], offset, true, pointerof(carry_out)))
|
||||
when 0b10 then set_reg(rd, asr(@r[rs], offset, true, pointerof(carry_out)))
|
||||
when 0b00 then gba.cpu.set_reg(rd, gba.cpu.lsl(gba.cpu.r[rs], offset, pointerof(carry_out)))
|
||||
when 0b01 then gba.cpu.set_reg(rd, gba.cpu.lsr(gba.cpu.r[rs], offset, true, pointerof(carry_out)))
|
||||
when 0b10 then gba.cpu.set_reg(rd, gba.cpu.asr(gba.cpu.r[rs], offset, true, pointerof(carry_out)))
|
||||
else raise "Invalid shifted register op: #{op}"
|
||||
end
|
||||
set_neg_and_zero_flags(@r[rd])
|
||||
@cpsr.carry = carry_out
|
||||
gba.cpu.set_neg_and_zero_flags(gba.cpu.r[rd])
|
||||
gba.cpu.cpsr.carry = carry_out
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
module THUMB
|
||||
def thumb_multiple_load_store(instr : Word) : Nil
|
||||
macro thumb_multiple_load_store
|
||||
->(gba : GBA, instr : Word) {
|
||||
load = bit?(instr, 11)
|
||||
rb = bits(instr, 8..10)
|
||||
list = bits(instr, 0..7)
|
||||
address = @r[rb]
|
||||
address = gba.cpu.r[rb]
|
||||
unless list == 0
|
||||
if load # ldmia
|
||||
8.times do |idx|
|
||||
if bit?(list, idx)
|
||||
set_reg(idx, @gba.bus.read_word(address))
|
||||
gba.cpu.set_reg(idx, gba.bus.read_word(address))
|
||||
address &+= 4
|
||||
end
|
||||
end
|
||||
|
@ -16,21 +17,22 @@ module THUMB
|
|||
base_addr = nil
|
||||
8.times do |idx|
|
||||
if bit?(list, idx)
|
||||
@gba.bus[address] = @r[idx]
|
||||
gba.bus[address] = gba.cpu.r[idx]
|
||||
base_addr = address if rb == idx
|
||||
address &+= 4
|
||||
end
|
||||
end
|
||||
@gba.bus[base_addr] = address if base_addr && first_set_bit(list) != rb # rb is written after first store
|
||||
gba.bus[base_addr] = address if base_addr && first_set_bit(list) != rb # rb is written after first store
|
||||
end
|
||||
set_reg(rb, address)
|
||||
gba.cpu.set_reg(rb, address)
|
||||
else # https://github.com/jsmolka/gba-suite/blob/0e32e15c6241e6dc20851563ba88f4656ac50936/thumb/memory.asm#L459
|
||||
if load
|
||||
set_reg(15, @gba.bus.read_word(address))
|
||||
gba.cpu.set_reg(15, gba.bus.read_word(address))
|
||||
else
|
||||
@gba.bus[address] = @r[15] &+ 2
|
||||
gba.bus[address] = gba.cpu.r[15] &+ 2
|
||||
end
|
||||
set_reg(rb, address &+ 0x40)
|
||||
gba.cpu.set_reg(rb, address &+ 0x40)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
module THUMB
|
||||
def thumb_pc_relative_load(instr : Word) : Nil
|
||||
macro thumb_pc_relative_load
|
||||
->(gba : GBA, instr : Word) {
|
||||
imm = bits(instr, 0..7)
|
||||
rd = bits(instr, 8..10)
|
||||
set_reg(rd, @gba.bus.read_word((@r[15] & ~2) &+ (imm << 2)))
|
||||
gba.cpu.set_reg(rd, gba.bus.read_word((gba.cpu.r[15] & ~2) &+ (imm << 2)))
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
module THUMB
|
||||
def thumb_push_pop_registers(instr : Word) : Nil
|
||||
macro thumb_push_pop_registers
|
||||
->(gba : GBA, instr : Word) {
|
||||
pop = bit?(instr, 11)
|
||||
pclr = bit?(instr, 8)
|
||||
list = bits(instr, 0..7)
|
||||
address = @r[13]
|
||||
address = gba.cpu.r[13]
|
||||
if pop
|
||||
8.times do |idx|
|
||||
if bit?(list, idx)
|
||||
set_reg(idx, @gba.bus.read_word(address))
|
||||
gba.cpu.set_reg(idx, gba.bus.read_word(address))
|
||||
address &+= 4
|
||||
end
|
||||
end
|
||||
if pclr
|
||||
set_reg(15, @gba.bus.read_word(address))
|
||||
gba.cpu.set_reg(15, gba.bus.read_word(address))
|
||||
address &+= 4
|
||||
end
|
||||
else
|
||||
if pclr
|
||||
address &-= 4
|
||||
@gba.bus[address] = @r[14]
|
||||
gba.bus[address] = gba.cpu.r[14]
|
||||
end
|
||||
7.downto(0).each do |idx|
|
||||
if bit?(list, idx)
|
||||
address &-= 4
|
||||
@gba.bus[address] = @r[idx]
|
||||
gba.bus[address] = gba.cpu.r[idx]
|
||||
end
|
||||
end
|
||||
end
|
||||
set_reg(13, address)
|
||||
gba.cpu.set_reg(13, address)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
module THUMB
|
||||
def thumb_software_interrupt(instr : Word) : Nil
|
||||
lr = @r[15] - 2
|
||||
switch_mode CPU::Mode::SVC
|
||||
set_reg(14, lr)
|
||||
@cpsr.irq_disable = true
|
||||
@cpsr.thumb = false
|
||||
set_reg(15, 0x08)
|
||||
macro thumb_software_interrupt
|
||||
->(gba : GBA, instr : Word) {
|
||||
lr = gba.cpu.r[15] - 2
|
||||
gba.cpu.switch_mode CPU::Mode::SVC
|
||||
gba.cpu.set_reg(14, lr)
|
||||
gba.cpu.cpsr.irq_disable = true
|
||||
gba.cpu.cpsr.thumb = false
|
||||
gba.cpu.set_reg(15, 0x08)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
module THUMB
|
||||
def thumb_sp_relative_load_store(instr : Word) : Nil
|
||||
macro thumb_sp_relative_load_store
|
||||
->(gba : GBA, instr : Word) {
|
||||
load = bit?(instr, 11)
|
||||
rd = bits(instr, 8..10)
|
||||
word = bits(instr, 0..7)
|
||||
address = @r[13] &+ (word << 2)
|
||||
address = gba.cpu.r[13] &+ (word << 2)
|
||||
if load
|
||||
set_reg(rd, @gba.bus.read_word_rotate(address))
|
||||
gba.cpu.set_reg(rd, gba.bus.read_word_rotate(address))
|
||||
else
|
||||
@gba.bus[address] = @r[rd]
|
||||
gba.bus[address] = gba.cpu.r[rd]
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
module THUMB
|
||||
def thumb_execute(instr : Word) : Nil
|
||||
thumb_lut[instr >> 8].call instr
|
||||
end
|
||||
|
||||
def fill_thumb_lut
|
||||
lut = Slice(Proc(Word, Nil)).new 256, ->thumb_unimplemented(Word)
|
||||
256.times do |idx|
|
||||
if idx & 0b11110000 == 0b11110000
|
||||
lut[idx] = ->thumb_long_branch_link(Word)
|
||||
elsif idx & 0b11111000 == 0b11100000
|
||||
lut[idx] = ->thumb_unconditional_branch(Word)
|
||||
elsif idx & 0b11111111 == 0b11011111
|
||||
lut[idx] = ->thumb_software_interrupt(Word)
|
||||
elsif idx & 0b11110000 == 0b11010000
|
||||
lut[idx] = ->thumb_conditional_branch(Word)
|
||||
elsif idx & 0b11110000 == 0b11000000
|
||||
lut[idx] = ->thumb_multiple_load_store(Word)
|
||||
elsif idx & 0b11110110 == 0b10110100
|
||||
lut[idx] = ->thumb_push_pop_registers(Word)
|
||||
elsif idx & 0b11111111 == 0b10110000
|
||||
lut[idx] = ->thumb_add_offset_to_stack_pointer(Word)
|
||||
elsif idx & 0b11110000 == 0b10100000
|
||||
lut[idx] = ->thumb_load_address(Word)
|
||||
elsif idx & 0b11110000 == 0b10010000
|
||||
lut[idx] = ->thumb_sp_relative_load_store(Word)
|
||||
elsif idx & 0b11110000 == 0b10000000
|
||||
lut[idx] = ->thumb_load_store_halfword(Word)
|
||||
elsif idx & 0b11100000 == 0b01100000
|
||||
lut[idx] = ->thumb_load_store_immediate_offset(Word)
|
||||
elsif idx & 0b11110010 == 0b01010010
|
||||
lut[idx] = ->thumb_load_store_sign_extended(Word)
|
||||
elsif idx & 0b11110010 == 0b01010000
|
||||
lut[idx] = ->thumb_load_store_register_offset(Word)
|
||||
elsif idx & 0b11111000 == 0b01001000
|
||||
lut[idx] = ->thumb_pc_relative_load(Word)
|
||||
elsif idx & 0b11111100 == 0b01000100
|
||||
lut[idx] = ->thumb_high_reg_branch_exchange(Word)
|
||||
elsif idx & 0b11111100 == 0b01000000
|
||||
lut[idx] = ->thumb_alu_operations(Word)
|
||||
elsif idx & 0b11100000 == 0b00100000
|
||||
lut[idx] = ->thumb_move_compare_add_subtract(Word)
|
||||
elsif idx & 0b11111000 == 0b00011000
|
||||
lut[idx] = ->thumb_add_subtract(Word)
|
||||
elsif idx & 0b11100000 == 0b00000000
|
||||
lut[idx] = ->thumb_move_shifted_register(Word)
|
||||
end
|
||||
end
|
||||
lut
|
||||
end
|
||||
|
||||
def thumb_unimplemented(instr : Word) : Nil
|
||||
puts "Unimplemented instruction: #{hex_str instr.to_u16}"
|
||||
exit 1
|
||||
end
|
||||
end
|
|
@ -1,7 +1,9 @@
|
|||
module THUMB
|
||||
def thumb_unconditional_branch(instr : Word) : Nil
|
||||
macro thumb_unconditional_branch
|
||||
->(gba : GBA, instr : Word) {
|
||||
offset = bits(instr, 0..10)
|
||||
offset = (offset << 5).to_i16! >> 4
|
||||
set_reg(15, @r[15] &+ offset)
|
||||
gba.cpu.set_reg(15, gba.cpu.r[15] &+ offset)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue