running PokemonEmerald causes a crash in the Scheduler's event.proc.call

This commit is contained in:
Matthew Berry 2021-02-05 19:41:02 -08:00
parent bc35157dca
commit ee80ede79f
22 changed files with 265 additions and 177 deletions

View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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