diff --git a/src/crab/cpu.cr b/src/crab/cpu.cr index 2c23bc1..b8369a9 100644 --- a/src/crab/cpu.cr +++ b/src/crab/cpu.cr @@ -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 diff --git a/src/crab/thumb.cr b/src/crab/thumb.cr new file mode 100644 index 0000000..a901e10 --- /dev/null +++ b/src/crab/thumb.cr @@ -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 diff --git a/src/crab/thumb/add_offset_to_stack_pointer.cr b/src/crab/thumb/add_offset_to_stack_pointer.cr index 28e39bc..ca3d526 100644 --- a/src/crab/thumb/add_offset_to_stack_pointer.cr +++ b/src/crab/thumb/add_offset_to_stack_pointer.cr @@ -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 diff --git a/src/crab/thumb/add_subtract.cr b/src/crab/thumb/add_subtract.cr index 2b84714..1828b46 100644 --- a/src/crab/thumb/add_subtract.cr +++ b/src/crab/thumb/add_subtract.cr @@ -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 diff --git a/src/crab/thumb/alu_operations.cr b/src/crab/thumb/alu_operations.cr index c65e848..07fc84d 100644 --- a/src/crab/thumb/alu_operations.cr +++ b/src/crab/thumb/alu_operations.cr @@ -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 diff --git a/src/crab/thumb/conditional_branch.cr b/src/crab/thumb/conditional_branch.cr index bc1dd8b..05496d7 100644 --- a/src/crab/thumb/conditional_branch.cr +++ b/src/crab/thumb/conditional_branch.cr @@ -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 diff --git a/src/crab/thumb/hi_reg_branch_exchange.cr b/src/crab/thumb/hi_reg_branch_exchange.cr index 2a5ece0..d8804bb 100644 --- a/src/crab/thumb/hi_reg_branch_exchange.cr +++ b/src/crab/thumb/hi_reg_branch_exchange.cr @@ -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 diff --git a/src/crab/thumb/load_address.cr b/src/crab/thumb/load_address.cr index 1ff9473..bd95d52 100644 --- a/src/crab/thumb/load_address.cr +++ b/src/crab/thumb/load_address.cr @@ -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 diff --git a/src/crab/thumb/load_store_halfword.cr b/src/crab/thumb/load_store_halfword.cr index 830cad8..6f3a096 100644 --- a/src/crab/thumb/load_store_halfword.cr +++ b/src/crab/thumb/load_store_halfword.cr @@ -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 diff --git a/src/crab/thumb/load_store_immediate_offset.cr b/src/crab/thumb/load_store_immediate_offset.cr index 0d2edc8..8cbb631 100644 --- a/src/crab/thumb/load_store_immediate_offset.cr +++ b/src/crab/thumb/load_store_immediate_offset.cr @@ -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 diff --git a/src/crab/thumb/load_store_register_offset.cr b/src/crab/thumb/load_store_register_offset.cr index 2ca9160..4fd5290 100644 --- a/src/crab/thumb/load_store_register_offset.cr +++ b/src/crab/thumb/load_store_register_offset.cr @@ -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 diff --git a/src/crab/thumb/load_store_sign_extended.cr b/src/crab/thumb/load_store_sign_extended.cr index 3293f19..6e1c424 100644 --- a/src/crab/thumb/load_store_sign_extended.cr +++ b/src/crab/thumb/load_store_sign_extended.cr @@ -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 diff --git a/src/crab/thumb/long_branch_link.cr b/src/crab/thumb/long_branch_link.cr index 43c798f..c68f881 100644 --- a/src/crab/thumb/long_branch_link.cr +++ b/src/crab/thumb/long_branch_link.cr @@ -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 diff --git a/src/crab/thumb/move_compare_add_subtract.cr b/src/crab/thumb/move_compare_add_subtract.cr index 723b99e..bb54198 100644 --- a/src/crab/thumb/move_compare_add_subtract.cr +++ b/src/crab/thumb/move_compare_add_subtract.cr @@ -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 diff --git a/src/crab/thumb/move_shifted_register.cr b/src/crab/thumb/move_shifted_register.cr index b41fbe8..26807ee 100644 --- a/src/crab/thumb/move_shifted_register.cr +++ b/src/crab/thumb/move_shifted_register.cr @@ -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 diff --git a/src/crab/thumb/multiple_load_store.cr b/src/crab/thumb/multiple_load_store.cr index cc00a29..bf65aaa 100644 --- a/src/crab/thumb/multiple_load_store.cr +++ b/src/crab/thumb/multiple_load_store.cr @@ -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 diff --git a/src/crab/thumb/pc_relative_load.cr b/src/crab/thumb/pc_relative_load.cr index 75a7d03..96659d4 100644 --- a/src/crab/thumb/pc_relative_load.cr +++ b/src/crab/thumb/pc_relative_load.cr @@ -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 diff --git a/src/crab/thumb/push_pop_registers.cr b/src/crab/thumb/push_pop_registers.cr index d3ba4ae..8ed6405 100644 --- a/src/crab/thumb/push_pop_registers.cr +++ b/src/crab/thumb/push_pop_registers.cr @@ -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 diff --git a/src/crab/thumb/software_interrupt.cr b/src/crab/thumb/software_interrupt.cr index bc21d1b..669e901 100644 --- a/src/crab/thumb/software_interrupt.cr +++ b/src/crab/thumb/software_interrupt.cr @@ -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 diff --git a/src/crab/thumb/sp_relative_load_store.cr b/src/crab/thumb/sp_relative_load_store.cr index 77c0017..50f07bb 100644 --- a/src/crab/thumb/sp_relative_load_store.cr +++ b/src/crab/thumb/sp_relative_load_store.cr @@ -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 diff --git a/src/crab/thumb/thumb.cr b/src/crab/thumb/thumb.cr deleted file mode 100644 index b812016..0000000 --- a/src/crab/thumb/thumb.cr +++ /dev/null @@ -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 diff --git a/src/crab/thumb/unconditional_branch.cr b/src/crab/thumb/unconditional_branch.cr index 7cbde8c..e9ae05d 100644 --- a/src/crab/thumb/unconditional_branch.cr +++ b/src/crab/thumb/unconditional_branch.cr @@ -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