mirror of
https://github.com/mattrberry/crab.git
synced 2025-02-09 08:46:02 +01:00
tighten address type restrictions from int to uint32
This commit is contained in:
parent
afcd4b667e
commit
1523742e12
57 changed files with 362 additions and 369 deletions
|
@ -136,7 +136,7 @@ module GBA
|
|||
@dma_channels.timer_overflow timer
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : UInt8
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
case io_addr
|
||||
when @channel1 then @channel1[io_addr]
|
||||
when @channel2 then @channel2[io_addr]
|
||||
|
@ -161,7 +161,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : UInt8) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
return unless @sound_enabled || 0x82 <= io_addr <= 0x89 || Channel3::WAVE_RAM_RANGE.includes?(io_addr)
|
||||
case io_addr
|
||||
when @channel1 then @channel1[io_addr] = value
|
||||
|
@ -175,7 +175,7 @@ module GBA
|
|||
when 0x83 then @soundcnt_h.value = (@soundcnt_h.value & 0x00FF) | value.to_u16 << 8
|
||||
when 0x84
|
||||
if value & 0x80 == 0 && @sound_enabled
|
||||
(0x60..0x81).each { |addr| self[addr] = 0x00 }
|
||||
(0x60_u32..0x81_u32).each { |addr| self[addr] = 0x00 }
|
||||
@sound_enabled = false
|
||||
elsif value & 0x80 > 0 && !@sound_enabled
|
||||
@sound_enabled = true
|
||||
|
|
|
@ -42,8 +42,8 @@ module GBA
|
|||
|
||||
abstract def get_amplitude : Int16
|
||||
|
||||
abstract def [](index : Int) : UInt8
|
||||
abstract def []=(index : Int, value : UInt8) : Nil
|
||||
abstract def [](address : UInt32) : UInt8
|
||||
abstract def []=(address : UInt32, value : UInt8) : Nil
|
||||
end
|
||||
|
||||
abstract class VolumeEnvelopeChannel < SoundChannel
|
||||
|
|
|
@ -77,8 +77,8 @@ module GBA
|
|||
calculated
|
||||
end
|
||||
|
||||
def [](index : Int) : UInt8
|
||||
case index
|
||||
def [](address : UInt32) : UInt8
|
||||
case address
|
||||
when 0x60 then @sweep_period << 4 | (@negate ? 0x08 : 0) | @shift
|
||||
when 0x62 then @duty << 6
|
||||
when 0x63 then read_NRx2
|
||||
|
@ -87,8 +87,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : UInt8) : Nil
|
||||
case index
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
case address
|
||||
when 0x60
|
||||
@sweep_period = (value & 0x70) >> 4
|
||||
@negate = value & 0x08 > 0
|
||||
|
@ -139,7 +139,7 @@ module GBA
|
|||
end
|
||||
when 0x66 # not used
|
||||
when 0x67 # not used
|
||||
else raise "Writing to invalid Channel1 register: #{hex_str index.to_u16}"
|
||||
else raise "Writing to invalid Channel1 register: #{hex_str address.to_u16}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,8 +43,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](index : Int) : UInt8
|
||||
case index
|
||||
def [](address : UInt32) : UInt8
|
||||
case address
|
||||
when 0x68 then @duty << 6
|
||||
when 0x69 then read_NRx2
|
||||
when 0x6D then (@length_enable ? 0x40_u8 : 0_u8)
|
||||
|
@ -52,8 +52,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : UInt8) : Nil
|
||||
case index
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
case address
|
||||
when 0x68
|
||||
@duty = (value & 0xC0) >> 6
|
||||
@length_load = value & 0x3F
|
||||
|
@ -91,7 +91,7 @@ module GBA
|
|||
end
|
||||
when 0x6E # not used
|
||||
when 0x6F # not used
|
||||
else raise "Writing to invalid Channel2 register: #{hex_str index.to_u16}"
|
||||
else raise "Writing to invalid Channel2 register: #{hex_str address.to_u16}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,8 +49,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](index : Int) : UInt8
|
||||
case index
|
||||
def [](address : UInt32) : UInt8
|
||||
case address
|
||||
when 0x70 then (@dac_enabled ? 0x80_u8 : 0_u8) | @wave_ram_bank << 6 | (@wave_ram_dimension ? 0x20 : 0)
|
||||
when 0x73 then (@volume_force ? 0x80_u8 : 0_u8) | @volume_code << 5
|
||||
when 0x75 then (@length_enable ? 0x40_u8 : 0_u8)
|
||||
|
@ -58,14 +58,14 @@ module GBA
|
|||
if @enabled
|
||||
@wave_ram[@wave_ram_bank][@wave_ram_position // 2]
|
||||
else
|
||||
@wave_ram[@wave_ram_bank][index - WAVE_RAM_RANGE.begin]
|
||||
@wave_ram[@wave_ram_bank][address - WAVE_RAM_RANGE.begin]
|
||||
end
|
||||
else 0_u8
|
||||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : UInt8) : Nil
|
||||
case index
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
case address
|
||||
when 0x70
|
||||
@dac_enabled = value & 0x80 > 0
|
||||
@enabled = false if !@dac_enabled
|
||||
|
@ -116,9 +116,9 @@ module GBA
|
|||
if @enabled
|
||||
@wave_ram[@wave_ram_bank][@wave_ram_position // 2] = value
|
||||
else
|
||||
@wave_ram[@wave_ram_bank][index - WAVE_RAM_RANGE.begin] = value
|
||||
@wave_ram[@wave_ram_bank][address - WAVE_RAM_RANGE.begin] = value
|
||||
end
|
||||
else raise "Writing to invalid Channel3 register: #{hex_str index.to_u16}"
|
||||
else raise "Writing to invalid Channel3 register: #{hex_str address.to_u16}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,8 +43,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](index : Int) : UInt8
|
||||
case index
|
||||
def [](address : UInt32) : UInt8
|
||||
case address
|
||||
when 0x79 then read_NRx2
|
||||
when 0x7C then @clock_shift << 4 | @width_mode << 3 | @divisor_code
|
||||
when 0x7D then (@length_enable ? 0x40_u8 : 0_u8)
|
||||
|
@ -52,8 +52,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : UInt8) : Nil
|
||||
case index
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
case address
|
||||
when 0x78
|
||||
@length_load = value & 0x3F
|
||||
# Internal values
|
||||
|
@ -93,7 +93,7 @@ module GBA
|
|||
end
|
||||
when 0x7E # not used
|
||||
when 0x7F # not used
|
||||
else raise "Writing to invalid Channel4 register: #{hex_str index.to_u16}"
|
||||
else raise "Writing to invalid Channel4 register: #{hex_str address.to_u16}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,12 +19,12 @@ module GBA
|
|||
]
|
||||
end
|
||||
|
||||
def [](index : Int) : UInt8
|
||||
@gba.bus.read_open_bus_value(index)
|
||||
def [](address : UInt32) : UInt8
|
||||
@gba.bus.read_open_bus_value(address)
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
channel = bit?(index, 2).to_unsafe
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
channel = bit?(address, 2).to_unsafe
|
||||
if @sizes[channel] < 32
|
||||
@fifos[channel][(@positions[channel] + @sizes[channel]) % 32] = value.to_i8!
|
||||
@sizes[channel] += 1
|
||||
|
|
|
@ -5,7 +5,7 @@ module GBA
|
|||
# module.
|
||||
|
||||
module ARM
|
||||
def arm_execute(instr : Word) : Nil
|
||||
def arm_execute(instr : UInt32) : Nil
|
||||
if check_cond bits(instr, 28..31)
|
||||
hash = hash_instr instr
|
||||
lut[hash].call instr
|
||||
|
@ -15,15 +15,15 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
private def hash_instr(instr : Word) : Word
|
||||
private def hash_instr(instr : UInt32) : UInt32
|
||||
(instr >> 16 & 0x0FF0) | (instr >> 4 & 0xF)
|
||||
end
|
||||
|
||||
def fill_lut : Slice(Proc(Word, Nil))
|
||||
lut = Slice(Proc(Word, Nil)).new 4096, ->arm_unimplemented(Word)
|
||||
def fill_lut : Slice(Proc(UInt32, Nil))
|
||||
lut = Slice(Proc(UInt32, Nil)).new 4096, ->arm_unimplemented(UInt32)
|
||||
4096.times do |idx|
|
||||
if idx & 0b111100000000 == 0b111100000000
|
||||
lut[idx] = ->arm_software_interrupt(Word)
|
||||
lut[idx] = ->arm_software_interrupt(UInt32)
|
||||
elsif idx & 0b111100000001 == 0b111000000001
|
||||
# coprocessor register transfer
|
||||
elsif idx & 0b111100000001 == 0b111000000001
|
||||
|
@ -31,45 +31,45 @@ module GBA
|
|||
elsif idx & 0b111000000000 == 0b110000000000
|
||||
# coprocessor data transfer
|
||||
elsif idx & 0b111000000000 == 0b101000000000
|
||||
lut[idx] = ->arm_branch(Word)
|
||||
lut[idx] = ->arm_branch(UInt32)
|
||||
elsif idx & 0b111000000000 == 0b100000000000
|
||||
lut[idx] = ->arm_block_data_transfer(Word)
|
||||
lut[idx] = ->arm_block_data_transfer(UInt32)
|
||||
elsif idx & 0b111000000001 == 0b011000000001
|
||||
# undefined
|
||||
elsif idx & 0b110000000000 == 0b010000000000
|
||||
lut[idx] = ->arm_single_data_transfer(Word)
|
||||
lut[idx] = ->arm_single_data_transfer(UInt32)
|
||||
elsif idx & 0b111111111111 == 0b000100100001
|
||||
lut[idx] = ->arm_branch_exchange(Word)
|
||||
lut[idx] = ->arm_branch_exchange(UInt32)
|
||||
elsif idx & 0b111110111111 == 0b000100001001
|
||||
lut[idx] = ->arm_single_data_swap(Word)
|
||||
lut[idx] = ->arm_single_data_swap(UInt32)
|
||||
elsif idx & 0b111110001111 == 0b000010001001
|
||||
lut[idx] = ->arm_multiply_long(Word)
|
||||
lut[idx] = ->arm_multiply_long(UInt32)
|
||||
elsif idx & 0b111111001111 == 0b000000001001
|
||||
lut[idx] = ->arm_multiply(Word)
|
||||
lut[idx] = ->arm_multiply(UInt32)
|
||||
elsif idx & 0b111001001001 == 0b000001001001
|
||||
lut[idx] = ->arm_halfword_data_transfer_immediate(Word)
|
||||
lut[idx] = ->arm_UInt16_data_transfer_immediate(UInt32)
|
||||
elsif idx & 0b111001001001 == 0b000000001001
|
||||
lut[idx] = ->arm_halfword_data_transfer_register(Word)
|
||||
lut[idx] = ->arm_UInt16_data_transfer_register(UInt32)
|
||||
elsif idx & 0b110110010000 == 0b000100000000
|
||||
lut[idx] = ->arm_psr_transfer(Word)
|
||||
lut[idx] = ->arm_psr_transfer(UInt32)
|
||||
elsif idx & 0b110000000000 == 0b000000000000
|
||||
lut[idx] = ->arm_data_processing(Word)
|
||||
lut[idx] = ->arm_data_processing(UInt32)
|
||||
else
|
||||
lut[idx] = ->arm_unused(Word)
|
||||
lut[idx] = ->arm_unused(UInt32)
|
||||
end
|
||||
end
|
||||
lut
|
||||
end
|
||||
|
||||
def arm_unimplemented(instr : Word) : Nil
|
||||
def arm_unimplemented(instr : UInt32) : Nil
|
||||
abort "Unimplemented instruction: #{hex_str instr}"
|
||||
end
|
||||
|
||||
def arm_unused(instr : Word) : Nil
|
||||
def arm_unused(instr : UInt32) : Nil
|
||||
puts "Unused instruction: #{hex_str instr}"
|
||||
end
|
||||
|
||||
def rotate_register(instr : Word, carry_out : Pointer(Bool), allow_register_shifts : Bool) : Word
|
||||
def rotate_register(instr : UInt32, carry_out : Pointer(Bool), allow_register_shifts : Bool) : UInt32
|
||||
reg = bits(instr, 0..3)
|
||||
shift_type = bits(instr, 5..6)
|
||||
immediate = !(allow_register_shifts && bit?(instr, 4))
|
||||
|
@ -89,11 +89,11 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def immediate_offset(instr : Word, carry_out : Pointer(Bool)) : Word
|
||||
def immediate_offset(instr : UInt32, carry_out : Pointer(Bool)) : UInt32
|
||||
rotate = bits(instr, 8..11)
|
||||
imm = bits(instr, 0..7)
|
||||
# todo putting "false" here causes the gba-suite tests to pass, but _why_
|
||||
ror(imm, 2 * rotate, false, carry_out)
|
||||
ror(imm, rotate << 1, false, carry_out)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_block_data_transfer(instr : Word) : Nil
|
||||
pre_index = bit?(instr, 24)
|
||||
def arm_block_data_transfer(instr : UInt32) : Nil
|
||||
pre_address = bit?(instr, 24)
|
||||
add = bit?(instr, 23)
|
||||
s_bit = bit?(instr, 22)
|
||||
write_back = bit?(instr, 21)
|
||||
|
@ -23,10 +23,10 @@ module GBA
|
|||
end
|
||||
final_addr = address + bits_set * (add ? 4 : -4)
|
||||
if add
|
||||
address += 4 if pre_index
|
||||
address += 4 if pre_address
|
||||
else
|
||||
address = final_addr
|
||||
address += 4 unless pre_index
|
||||
address += 4 unless pre_address
|
||||
end
|
||||
first_transfer = false
|
||||
16.times do |idx| # always transfered to/from incrementing addresses
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_branch(instr : Word) : Nil
|
||||
def arm_branch(instr : UInt32) : Nil
|
||||
link = bit?(instr, 24)
|
||||
offset = (bits(instr, 0..23) << 8).to_i32! >> 6
|
||||
set_reg(14, @r[15] - 4) if link
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_branch_exchange(instr : Word) : Nil
|
||||
def arm_branch_exchange(instr : UInt32) : Nil
|
||||
address = @r[bits(instr, 0..3)]
|
||||
@cpsr.thumb = bit?(address, 0)
|
||||
set_reg(15, address)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_data_processing(instr : Word) : Nil
|
||||
def arm_data_processing(instr : UInt32) : Nil
|
||||
imm_flag = bit?(instr, 25)
|
||||
opcode = bits(instr, 21..24)
|
||||
set_conditions = bit?(instr, 20)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_halfword_data_transfer_immediate(instr : Word) : Nil
|
||||
pre_index = bit?(instr, 24)
|
||||
def arm_UInt16_data_transfer_immediate(instr : UInt32) : Nil
|
||||
pre_address = bit?(instr, 24)
|
||||
add = bit?(instr, 23)
|
||||
write_back = bit?(instr, 21)
|
||||
load = bit?(instr, 20)
|
||||
|
@ -14,7 +14,7 @@ module GBA
|
|||
address = @r[rn]
|
||||
offset = offset_high << 4 | offset_low
|
||||
|
||||
if pre_index
|
||||
if pre_address
|
||||
if add
|
||||
address &+= offset
|
||||
else
|
||||
|
@ -24,7 +24,7 @@ module GBA
|
|||
|
||||
case sh
|
||||
when 0b00 # swp, no docs on this?
|
||||
abort "HalfwordDataTransferReg swp #{hex_str instr}"
|
||||
abort "UInt16DataTransferReg swp #{hex_str instr}"
|
||||
when 0b01 # ldrh/strh
|
||||
if load
|
||||
set_reg(rd, @gba.bus.read_half_rotate address)
|
||||
|
@ -38,20 +38,20 @@ module GBA
|
|||
set_reg(rd, @gba.bus[address].to_i8!.to_u32!)
|
||||
when 0b11 # ldrsh
|
||||
set_reg(rd, @gba.bus.read_half_signed(address))
|
||||
else raise "Invalid halfword data transfer imm op: #{sh}"
|
||||
else raise "Invalid UInt16 data transfer imm op: #{sh}"
|
||||
end
|
||||
|
||||
unless pre_index
|
||||
unless pre_address
|
||||
if add
|
||||
address &+= offset
|
||||
else
|
||||
address &-= offset
|
||||
end
|
||||
end
|
||||
# In the case of post-indexed addressing, the write back bit is redundant and is always set to
|
||||
# In the case of post-addressed addressing, the write back bit is redundant and is always set to
|
||||
# zero, since the old base value can be retained by setting the offset to zero. Therefore
|
||||
# post-indexed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_index) && (rd != rn || !load)
|
||||
# post-addressed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_address) && (rd != rn || !load)
|
||||
|
||||
step_arm unless load && rd == 15
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_halfword_data_transfer_register(instr : Word) : Nil
|
||||
pre_index = bit?(instr, 24)
|
||||
def arm_UInt16_data_transfer_register(instr : UInt32) : Nil
|
||||
pre_address = bit?(instr, 24)
|
||||
add = bit?(instr, 23)
|
||||
write_back = bit?(instr, 21)
|
||||
load = bit?(instr, 20)
|
||||
|
@ -13,7 +13,7 @@ module GBA
|
|||
address = @r[rn]
|
||||
offset = @r[rm]
|
||||
|
||||
if pre_index
|
||||
if pre_address
|
||||
if add
|
||||
address &+= offset
|
||||
else
|
||||
|
@ -23,7 +23,7 @@ module GBA
|
|||
|
||||
case sh
|
||||
when 0b00 # swp, no docs on this?
|
||||
abort "HalfwordDataTransferReg swp #{hex_str instr}"
|
||||
abort "UInt16DataTransferReg swp #{hex_str instr}"
|
||||
when 0b01 # ldrh/strh
|
||||
if load
|
||||
set_reg(rd, @gba.bus.read_half_rotate address)
|
||||
|
@ -37,20 +37,20 @@ module GBA
|
|||
set_reg(rd, @gba.bus[address].to_i8!.to_u32!)
|
||||
when 0b11 # ldrsh
|
||||
set_reg(rd, @gba.bus.read_half_signed(address))
|
||||
else raise "Invalid halfword data transfer imm op: #{sh}"
|
||||
else raise "Invalid UInt16 data transfer imm op: #{sh}"
|
||||
end
|
||||
|
||||
unless pre_index
|
||||
unless pre_address
|
||||
if add
|
||||
address &+= offset
|
||||
else
|
||||
address &-= offset
|
||||
end
|
||||
end
|
||||
# In the case of post-indexed addressing, the write back bit is redundant and is always set to
|
||||
# In the case of post-addressed addressing, the write back bit is redundant and is always set to
|
||||
# zero, since the old base value can be retained by setting the offset to zero. Therefore
|
||||
# post-indexed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_index) && (rd != rn || !load)
|
||||
# post-addressed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_address) && (rd != rn || !load)
|
||||
|
||||
step_arm unless load && rd == 15
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_multiply(instr : Word) : Nil
|
||||
def arm_multiply(instr : UInt32) : Nil
|
||||
accumulate = bit?(instr, 21)
|
||||
set_conditions = bit?(instr, 20)
|
||||
rd = bits(instr, 16..19)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_multiply_long(instr : Word) : Nil
|
||||
def arm_multiply_long(instr : UInt32) : Nil
|
||||
signed = bit?(instr, 22)
|
||||
accumulate = bit?(instr, 21)
|
||||
set_conditions = bit?(instr, 20)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_psr_transfer(instr : Word) : Nil
|
||||
def arm_psr_transfer(instr : UInt32) : Nil
|
||||
spsr = bit?(instr, 22)
|
||||
mode = CPU::Mode.from_value @cpsr.mode
|
||||
has_spsr = mode != CPU::Mode::USR && mode != CPU::Mode::SYS
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_single_data_swap(instr : Word) : Nil
|
||||
def arm_single_data_swap(instr : UInt32) : Nil
|
||||
byte_quantity = bit?(instr, 22)
|
||||
rn = bits(instr, 16..19)
|
||||
rd = bits(instr, 12..15)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_single_data_transfer(instr : Word) : Nil
|
||||
def arm_single_data_transfer(instr : UInt32) : Nil
|
||||
imm_flag = bit?(instr, 25)
|
||||
pre_indexing = bit?(instr, 24)
|
||||
pre_addressing = bit?(instr, 24)
|
||||
add_offset = bit?(instr, 23)
|
||||
byte_quantity = bit?(instr, 22)
|
||||
write_back = bit?(instr, 21)
|
||||
|
@ -19,7 +19,7 @@ module GBA
|
|||
|
||||
address = @r[rn]
|
||||
|
||||
if pre_indexing
|
||||
if pre_addressing
|
||||
if add_offset
|
||||
address &+= offset
|
||||
else
|
||||
|
@ -44,17 +44,17 @@ module GBA
|
|||
@gba.bus[address] &+= 4 if rd == 15
|
||||
end
|
||||
|
||||
unless pre_indexing
|
||||
unless pre_addressing
|
||||
if add_offset
|
||||
address &+= offset
|
||||
else
|
||||
address &-= offset
|
||||
end
|
||||
end
|
||||
# In the case of post-indexed addressing, the write back bit is redundant and is always set to
|
||||
# In the case of post-addressed addressing, the write back bit is redundant and is always set to
|
||||
# zero, since the old base value can be retained by setting the offset to zero. Therefore
|
||||
# post-indexed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_indexing) && (rd != rn || !load)
|
||||
# post-addressed data transfers always write back the modified base.
|
||||
set_reg(rn, address) if (write_back || !pre_addressing) && (rd != rn || !load)
|
||||
|
||||
step_arm unless load && rd == 15
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module ARM
|
||||
def arm_software_interrupt(instr : Word) : Nil
|
||||
def arm_software_interrupt(instr : UInt32) : Nil
|
||||
lr = @r[15] - 4
|
||||
switch_mode CPU::Mode::SVC
|
||||
set_reg(14, lr)
|
||||
|
|
|
@ -20,256 +20,256 @@ module GBA
|
|||
@gpio = GPIO.new(@gba)
|
||||
end
|
||||
|
||||
def [](index : Int) : Byte
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(index)]
|
||||
read_byte_internal(index)
|
||||
def [](address : UInt32) : UInt8
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(address)]
|
||||
read_byte_internal(address)
|
||||
end
|
||||
|
||||
def read_half(index : Int) : HalfWord
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(index)]
|
||||
read_half_internal(index)
|
||||
def read_half(address : UInt32) : UInt16
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(address)]
|
||||
read_half_internal(address)
|
||||
end
|
||||
|
||||
def read_word(index : Int) : Word
|
||||
@cycles += ACCESS_TIMING_TABLE[1][page(index)]
|
||||
read_word_internal(index)
|
||||
def read_word(address : UInt32) : UInt32
|
||||
@cycles += ACCESS_TIMING_TABLE[1][page(address)]
|
||||
read_word_internal(address)
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(index)]
|
||||
write_byte_internal(index, value)
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(address)]
|
||||
write_byte_internal(address, value)
|
||||
end
|
||||
|
||||
def []=(index : Int, value : HalfWord) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(index)]
|
||||
write_half_internal(index, value)
|
||||
def []=(address : UInt32, value : UInt16) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[0][page(address)]
|
||||
write_half_internal(address, value)
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Word) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[1][page(index)]
|
||||
write_word_internal(index, value)
|
||||
def []=(address : UInt32, value : UInt32) : Nil
|
||||
@cycles += ACCESS_TIMING_TABLE[1][page(address)]
|
||||
write_word_internal(address, value)
|
||||
end
|
||||
|
||||
def read_half_rotate(index : Int) : Word
|
||||
half = read_half(index).to_u32!
|
||||
bits = (index & 1) << 3
|
||||
def read_half_rotate(address : UInt32) : UInt32
|
||||
half = read_half(address).to_u32!
|
||||
bits = (address & 1) << 3
|
||||
half >> bits | half << (32 - bits)
|
||||
end
|
||||
|
||||
# On ARM7 aka ARMv4 aka NDS7/GBA:
|
||||
# LDRH Rd,[odd] --> LDRH Rd,[odd-1] ROR 8 ;read to bit0-7 and bit24-31
|
||||
# LDRSH Rd,[odd] --> LDRSB Rd,[odd] ;sign-expand BYTE value
|
||||
def read_half_signed(index : Int) : Word
|
||||
if bit?(index, 0)
|
||||
self[index].to_i8!.to_u32!
|
||||
def read_half_signed(address : UInt32) : UInt32
|
||||
if bit?(address, 0)
|
||||
self[address].to_i8!.to_u32!
|
||||
else
|
||||
read_half(index).to_i16!.to_u32!
|
||||
read_half(address).to_i16!.to_u32!
|
||||
end
|
||||
end
|
||||
|
||||
def read_word_rotate(index : Int) : Word
|
||||
word = read_word index
|
||||
bits = (index & 3) << 3
|
||||
def read_word_rotate(address : UInt32) : UInt32
|
||||
word = read_word address
|
||||
bits = (address & 3) << 3
|
||||
word >> bits | word << (32 - bits)
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def page(index : Int) : Int
|
||||
bits(index, 24..27)
|
||||
private def page(address : UInt32) : Int
|
||||
bits(address, 24..27)
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def read_byte_internal(index : Int) : Byte
|
||||
case bits(index, 24..27)
|
||||
when 0x0 then @bios[index & 0x3FFF]
|
||||
private def read_byte_internal(address : UInt32) : UInt8
|
||||
case bits(address, 24..27)
|
||||
when 0x0 then @bios[address & 0x3FFF]
|
||||
when 0x1 then 0_u8 # todo: open bus
|
||||
when 0x2 then @wram_board[index & 0x3FFFF]
|
||||
when 0x3 then @wram_chip[index & 0x7FFF]
|
||||
when 0x4 then @gba.mmio[index]
|
||||
when 0x5 then @gba.ppu.pram[index & 0x3FF]
|
||||
when 0x2 then @wram_board[address & 0x3FFFF]
|
||||
when 0x3 then @wram_chip[address & 0x7FFF]
|
||||
when 0x4 then @gba.mmio[address]
|
||||
when 0x5 then @gba.ppu.pram[address & 0x3FF]
|
||||
when 0x6
|
||||
address = 0x1FFFF_u32 & index
|
||||
address = 0x1FFFF_u32 & address
|
||||
address -= 0x8000 if address > 0x17FFF
|
||||
@gba.ppu.vram[address]
|
||||
when 0x7 then @gba.ppu.oam[index & 0x3FF]
|
||||
when 0x7 then @gba.ppu.oam[address & 0x3FF]
|
||||
when 0x8, 0x9, 0xA, 0xB, 0xC, 0xD
|
||||
if @gpio.address?(index) && @gpio.allow_reads
|
||||
@gpio[index]
|
||||
elsif @gba.storage.eeprom?(index)
|
||||
@gba.storage[index]
|
||||
if @gpio.address?(address) && @gpio.allow_reads
|
||||
@gpio[address]
|
||||
elsif @gba.storage.eeprom?(address)
|
||||
@gba.storage[address]
|
||||
else
|
||||
@gba.cartridge.rom[index & 0x01FFFFFF]
|
||||
@gba.cartridge.rom[address & 0x01FFFFFF]
|
||||
end
|
||||
when 0xE, 0xF then @gba.storage[index]
|
||||
else abort "Unmapped read: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then @gba.storage[address]
|
||||
else abort "Unmapped read: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def read_half_internal(index : Int) : HalfWord
|
||||
index &= ~1
|
||||
case bits(index, 24..27)
|
||||
when 0x0 then (@bios.to_unsafe + (index & 0x3FFF)).as(HalfWord*).value
|
||||
private def read_half_internal(address : UInt32) : UInt16
|
||||
address &= ~1
|
||||
case bits(address, 24..27)
|
||||
when 0x0 then (@bios.to_unsafe + (address & 0x3FFF)).as(UInt16*).value
|
||||
when 0x1 then 0_u16 # todo: open bus
|
||||
when 0x2 then (@wram_board.to_unsafe + (index & 0x3FFFF)).as(HalfWord*).value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (index & 0x7FFF)).as(HalfWord*).value
|
||||
when 0x4 then read_half_internal_slow(index)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (index & 0x3FF)).as(HalfWord*).value
|
||||
when 0x2 then (@wram_board.to_unsafe + (address & 0x3FFFF)).as(UInt16*).value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (address & 0x7FFF)).as(UInt16*).value
|
||||
when 0x4 then read_half_internal_slow(address)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FF)).as(UInt16*).value
|
||||
when 0x6
|
||||
address = 0x1FFFF_u32 & index
|
||||
address = 0x1FFFF_u32 & address
|
||||
address -= 0x8000 if address > 0x17FFF
|
||||
(@gba.ppu.vram.to_unsafe + address).as(HalfWord*).value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(HalfWord*).value
|
||||
(@gba.ppu.vram.to_unsafe + address).as(UInt16*).value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (address & 0x3FF)).as(UInt16*).value
|
||||
when 0x8, 0x9, 0xA, 0xB, 0xC, 0xD
|
||||
if @gpio.address?(index) && @gpio.allow_reads
|
||||
@gpio[index].to_u16!
|
||||
elsif @gba.storage.eeprom?(index)
|
||||
@gba.storage[index].to_u16!
|
||||
if @gpio.address?(address) && @gpio.allow_reads
|
||||
@gpio[address].to_u16!
|
||||
elsif @gba.storage.eeprom?(address)
|
||||
@gba.storage[address].to_u16!
|
||||
else
|
||||
(@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(HalfWord*).value
|
||||
(@gba.cartridge.rom.to_unsafe + (address & 0x01FFFFFF)).as(UInt16*).value
|
||||
end
|
||||
when 0xE, 0xF then @gba.storage.read_half(index)
|
||||
else abort "Unmapped read: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then @gba.storage.read_half(address)
|
||||
else abort "Unmapped read: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def read_word_internal(index : Int) : Word
|
||||
index &= ~3
|
||||
case bits(index, 24..27)
|
||||
when 0x0 then (@bios.to_unsafe + (index & 0x3FFF)).as(Word*).value
|
||||
private def read_word_internal(address : UInt32) : UInt32
|
||||
address &= ~3
|
||||
case bits(address, 24..27)
|
||||
when 0x0 then (@bios.to_unsafe + (address & 0x3FFF)).as(UInt32*).value
|
||||
when 0x1 then 0_u32 # todo: open bus
|
||||
when 0x2 then (@wram_board.to_unsafe + (index & 0x3FFFF)).as(Word*).value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (index & 0x7FFF)).as(Word*).value
|
||||
when 0x4 then read_word_internal_slow(index)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (index & 0x3FF)).as(Word*).value
|
||||
when 0x2 then (@wram_board.to_unsafe + (address & 0x3FFFF)).as(UInt32*).value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (address & 0x7FFF)).as(UInt32*).value
|
||||
when 0x4 then read_word_internal_slow(address)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FF)).as(UInt32*).value
|
||||
when 0x6
|
||||
address = 0x1FFFF_u32 & index
|
||||
address = 0x1FFFF_u32 & address
|
||||
address -= 0x8000 if address > 0x17FFF
|
||||
(@gba.ppu.vram.to_unsafe + address).as(Word*).value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(Word*).value
|
||||
(@gba.ppu.vram.to_unsafe + address).as(UInt32*).value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (address & 0x3FF)).as(UInt32*).value
|
||||
when 0x8, 0x9, 0xA, 0xB, 0xC, 0xD
|
||||
if @gpio.address?(index) && @gpio.allow_reads
|
||||
@gpio[index].to_u32!
|
||||
elsif @gba.storage.eeprom?(index)
|
||||
@gba.storage[index].to_u32!
|
||||
if @gpio.address?(address) && @gpio.allow_reads
|
||||
@gpio[address].to_u32!
|
||||
elsif @gba.storage.eeprom?(address)
|
||||
@gba.storage[address].to_u32!
|
||||
else
|
||||
(@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(Word*).value
|
||||
(@gba.cartridge.rom.to_unsafe + (address & 0x01FFFFFF)).as(UInt32*).value
|
||||
end
|
||||
when 0xE, 0xF then @gba.storage.read_word(index)
|
||||
else abort "Unmapped read: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then @gba.storage.read_word(address)
|
||||
else abort "Unmapped read: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def write_byte_internal(index : Int, value : Byte) : Nil
|
||||
return if bits(index, 28..31) > 0
|
||||
@gba.cpu.fill_pipeline if index <= @gba.cpu.r[15] && index >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(index, 24..27)
|
||||
when 0x2 then @wram_board[index & 0x3FFFF] = value
|
||||
when 0x3 then @wram_chip[index & 0x7FFF] = value
|
||||
when 0x4 then @gba.mmio[index] = value
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (index & 0x3FE)).as(HalfWord*).value = 0x0101_u16 * value
|
||||
private def write_byte_internal(address : UInt32, value : UInt8) : Nil
|
||||
return if bits(address, 28..31) > 0
|
||||
@gba.cpu.fill_pipeline if address <= @gba.cpu.r[15] && address >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(address, 24..27)
|
||||
when 0x2 then @wram_board[address & 0x3FFFF] = value
|
||||
when 0x3 then @wram_chip[address & 0x7FFF] = value
|
||||
when 0x4 then @gba.mmio[address] = value
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FE)).as(UInt16*).value = 0x0101_u16 * value
|
||||
when 0x6
|
||||
limit = @gba.ppu.bitmap? ? 0x13FFF : 0x0FFFF # (u8 write only) upper limit depends on display mode
|
||||
address = 0x1FFFE_u32 & index # (u8 write only) halfword-aligned
|
||||
address = 0x1FFFE_u32 & address # (u8 write only) UInt16-aligned
|
||||
address -= 0x8000 if address > 0x17FFF # todo: determine if this happens before or after the limit check
|
||||
(@gba.ppu.vram.to_unsafe + address).as(HalfWord*).value = 0x0101_u16 * value if address <= limit
|
||||
(@gba.ppu.vram.to_unsafe + address).as(UInt16*).value = 0x0101_u16 * value if address <= limit
|
||||
when 0x7 # can't write bytes to oam
|
||||
when 0x8, 0xD # all address between aren't writable
|
||||
if @gpio.address? index
|
||||
@gpio[index] = value
|
||||
elsif @gba.storage.eeprom? index
|
||||
@gba.storage[index]
|
||||
if @gpio.address? address
|
||||
@gpio[address] = value
|
||||
elsif @gba.storage.eeprom? address
|
||||
@gba.storage[address]
|
||||
end
|
||||
when 0xE, 0xF then @gba.storage[index] = value
|
||||
else log "Unmapped write: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then @gba.storage[address] = value
|
||||
else log "Unmapped write: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def write_half_internal(index : Int, value : HalfWord) : Nil
|
||||
return if bits(index, 28..31) > 0
|
||||
index &= ~1
|
||||
@gba.cpu.fill_pipeline if index <= @gba.cpu.r[15] && index >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(index, 24..27)
|
||||
when 0x2 then (@wram_board.to_unsafe + (index & 0x3FFFF)).as(HalfWord*).value = value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (index & 0x7FFF)).as(HalfWord*).value = value
|
||||
when 0x4 then write_half_internal_slow(index, value)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (index & 0x3FF)).as(HalfWord*).value = value
|
||||
private def write_half_internal(address : UInt32, value : UInt16) : Nil
|
||||
return if bits(address, 28..31) > 0
|
||||
address &= ~1
|
||||
@gba.cpu.fill_pipeline if address <= @gba.cpu.r[15] && address >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(address, 24..27)
|
||||
when 0x2 then (@wram_board.to_unsafe + (address & 0x3FFFF)).as(UInt16*).value = value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (address & 0x7FFF)).as(UInt16*).value = value
|
||||
when 0x4 then write_half_internal_slow(address, value)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FF)).as(UInt16*).value = value
|
||||
when 0x6
|
||||
address = 0x1FFFF_u32 & index
|
||||
address = 0x1FFFF_u32 & address
|
||||
address -= 0x8000 if address > 0x17FFF
|
||||
(@gba.ppu.vram.to_unsafe + address).as(HalfWord*).value = value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(HalfWord*).value = value
|
||||
(@gba.ppu.vram.to_unsafe + address).as(UInt16*).value = value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (address & 0x3FF)).as(UInt16*).value = value
|
||||
when 0x8, 0xD # all address between aren't writable
|
||||
if @gpio.address? index
|
||||
@gpio[index] = value.to_u8!
|
||||
elsif @gba.storage.eeprom? index
|
||||
@gba.storage[index] = value.to_u8!
|
||||
if @gpio.address? address
|
||||
@gpio[address] = value.to_u8!
|
||||
elsif @gba.storage.eeprom? address
|
||||
@gba.storage[address] = value.to_u8!
|
||||
end
|
||||
when 0xE, 0xF then write_half_internal_slow(index, value)
|
||||
else log "Unmapped write: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then write_half_internal_slow(address, value)
|
||||
else log "Unmapped write: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def write_word_internal(index : Int, value : Word) : Nil
|
||||
return if bits(index, 28..31) > 0
|
||||
index &= ~3
|
||||
@gba.cpu.fill_pipeline if index <= @gba.cpu.r[15] && index >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(index, 24..27)
|
||||
when 0x2 then (@wram_board.to_unsafe + (index & 0x3FFFF)).as(Word*).value = value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (index & 0x7FFF)).as(Word*).value = value
|
||||
when 0x4 then write_word_internal_slow(index, value)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (index & 0x3FF)).as(Word*).value = value
|
||||
private def write_word_internal(address : UInt32, value : UInt32) : Nil
|
||||
return if bits(address, 28..31) > 0
|
||||
address &= ~3
|
||||
@gba.cpu.fill_pipeline if address <= @gba.cpu.r[15] && address >= @gba.cpu.r[15] &- 4 # detect writes near pc
|
||||
case bits(address, 24..27)
|
||||
when 0x2 then (@wram_board.to_unsafe + (address & 0x3FFFF)).as(UInt32*).value = value
|
||||
when 0x3 then (@wram_chip.to_unsafe + (address & 0x7FFF)).as(UInt32*).value = value
|
||||
when 0x4 then write_word_internal_slow(address, value)
|
||||
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FF)).as(UInt32*).value = value
|
||||
when 0x6
|
||||
address = 0x1FFFF_u32 & index
|
||||
address = 0x1FFFF_u32 & address
|
||||
address -= 0x8000 if address > 0x17FFF
|
||||
(@gba.ppu.vram.to_unsafe + address).as(Word*).value = value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(Word*).value = value
|
||||
(@gba.ppu.vram.to_unsafe + address).as(UInt32*).value = value
|
||||
when 0x7 then (@gba.ppu.oam.to_unsafe + (address & 0x3FF)).as(UInt32*).value = value
|
||||
when 0x8, 0xD # all address between aren't writable
|
||||
if @gpio.address? index
|
||||
@gpio[index] = value.to_u8!
|
||||
elsif @gba.storage.eeprom? index
|
||||
@gba.storage[index] = value.to_u8!
|
||||
if @gpio.address? address
|
||||
@gpio[address] = value.to_u8!
|
||||
elsif @gba.storage.eeprom? address
|
||||
@gba.storage[address] = value.to_u8!
|
||||
end
|
||||
when 0xE, 0xF then write_word_internal_slow(index, value)
|
||||
else log "Unmapped write: #{hex_str index.to_u32}"
|
||||
when 0xE, 0xF then write_word_internal_slow(address, value)
|
||||
else log "Unmapped write: #{hex_str address.to_u32}"
|
||||
end
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def read_half_internal_slow(index : Int) : HalfWord
|
||||
read_byte_internal(index).to_u16! |
|
||||
(read_byte_internal(index + 1).to_u16! << 8)
|
||||
private def read_half_internal_slow(address : UInt32) : UInt16
|
||||
read_byte_internal(address).to_u16! |
|
||||
(read_byte_internal(address + 1).to_u16! << 8)
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def read_word_internal_slow(index : Int) : Word
|
||||
read_byte_internal(index).to_u32! |
|
||||
(read_byte_internal(index + 1).to_u32! << 8) |
|
||||
(read_byte_internal(index + 2).to_u32! << 16) |
|
||||
(read_byte_internal(index + 3).to_u32! << 24)
|
||||
private def read_word_internal_slow(address : UInt32) : UInt32
|
||||
read_byte_internal(address).to_u32! |
|
||||
(read_byte_internal(address + 1).to_u32! << 8) |
|
||||
(read_byte_internal(address + 2).to_u32! << 16) |
|
||||
(read_byte_internal(address + 3).to_u32! << 24)
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def write_half_internal_slow(index : Int, value : HalfWord) : Nil
|
||||
write_byte_internal(index, value.to_u8!)
|
||||
write_byte_internal(index + 1, (value >> 8).to_u8!)
|
||||
private def write_half_internal_slow(address : UInt32, value : UInt16) : Nil
|
||||
write_byte_internal(address, value.to_u8!)
|
||||
write_byte_internal(address + 1, (value >> 8).to_u8!)
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
private def write_word_internal_slow(index : Int, value : Word) : Nil
|
||||
write_byte_internal(index, value.to_u8!)
|
||||
write_byte_internal(index + 1, (value >> 8).to_u8!)
|
||||
write_byte_internal(index + 2, (value >> 16).to_u8!)
|
||||
write_byte_internal(index + 3, (value >> 24).to_u8!)
|
||||
private def write_word_internal_slow(address : UInt32, value : UInt32) : Nil
|
||||
write_byte_internal(address, value.to_u8!)
|
||||
write_byte_internal(address + 1, (value >> 8).to_u8!)
|
||||
write_byte_internal(address + 2, (value >> 16).to_u8!)
|
||||
write_byte_internal(address + 3, (value >> 24).to_u8!)
|
||||
end
|
||||
|
||||
def read_open_bus_value(index : Int, _file = __FILE__) : Byte
|
||||
log "Reading open bus at #{hex_str index.to_u32} from #{_file}"
|
||||
shift = (index & 3) * 8
|
||||
def read_open_bus_value(address : UInt32, _file = __FILE__) : UInt8
|
||||
log "Reading open bus at #{hex_str address.to_u32} from #{_file}"
|
||||
shift = (address & 3) * 8
|
||||
if @gba.cpu.cpsr.thumb
|
||||
# todo: special handling for 16-bit vs 32-bit regions
|
||||
# todo: does this need to have both of the previous opcodes?
|
||||
|
|
|
@ -42,14 +42,14 @@ module GBA
|
|||
bool negative
|
||||
end
|
||||
|
||||
getter r = Slice(Word).new 16
|
||||
getter r = Slice(UInt32).new 16
|
||||
getter 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(GBA::Word).new 7, 0 }
|
||||
@spsr_banks = Array(Word).new 6, CPU::Mode::SYS.value # logically independent of typical register banks
|
||||
getter lut : Slice(Proc(UInt32, Nil)) { fill_lut }
|
||||
getter thumb_lut : Slice(Proc(UInt32, Nil)) { fill_thumb_lut }
|
||||
@reg_banks = Array(Array(UInt32)).new 6 { Array(UInt32).new 7, 0 }
|
||||
@spsr_banks = Array(UInt32).new 6, CPU::Mode::SYS.value # logically independent of typical register banks
|
||||
property halted = false
|
||||
|
||||
def initialize(@gba : GBA)
|
||||
|
@ -118,7 +118,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def read_instr : Word
|
||||
def read_instr : UInt32
|
||||
if @pipeline.size == 0
|
||||
if @cpsr.thumb
|
||||
@r[15] &= ~1
|
||||
|
@ -148,7 +148,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def check_cond(cond : Word) : Bool
|
||||
def check_cond(cond : UInt32) : Bool
|
||||
case cond
|
||||
when 0x0 then @cpsr.zero
|
||||
when 0x1 then !@cpsr.zero
|
||||
|
@ -178,20 +178,20 @@ module GBA
|
|||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
def set_reg(reg : Int, value : Int) : UInt32
|
||||
@r[reg] = value.to_u32!
|
||||
def set_reg(reg : Int, value : UInt32) : UInt32
|
||||
@r[reg] = value
|
||||
clear_pipeline if reg == 15
|
||||
value.to_u32!
|
||||
value
|
||||
end
|
||||
|
||||
@[AlwaysInline]
|
||||
def set_neg_and_zero_flags(value : Int) : Nil
|
||||
def set_neg_and_zero_flags(value : UInt32) : Nil
|
||||
@cpsr.negative = bit?(value, 31)
|
||||
@cpsr.zero = value == 0
|
||||
end
|
||||
|
||||
# Logical shift left
|
||||
def lsl(word : Word, bits : Int, carry_out : Pointer(Bool)) : Word
|
||||
def lsl(word : UInt32, bits : Int::Unsigned, carry_out : Pointer(Bool)) : UInt32
|
||||
log "lsl - word:#{hex_str word}, bits:#{bits}"
|
||||
return word if bits == 0
|
||||
carry_out.value = bit?(word, 32 - bits)
|
||||
|
@ -199,7 +199,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Logical shift right
|
||||
def lsr(word : Word, bits : Int, immediate : Bool, carry_out : Pointer(Bool)) : Word
|
||||
def lsr(word : UInt32, bits : Int::Unsigned, immediate : Bool, carry_out : Pointer(Bool)) : UInt32
|
||||
log "lsr - word:#{hex_str word}, bits:#{bits}"
|
||||
if bits == 0
|
||||
return word unless immediate
|
||||
|
@ -210,7 +210,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Arithmetic shift right
|
||||
def asr(word : Word, bits : Int, immediate : Bool, carry_out : Pointer(Bool)) : Word
|
||||
def asr(word : UInt32, bits : Int::Unsigned, immediate : Bool, carry_out : Pointer(Bool)) : UInt32
|
||||
log "asr - word:#{hex_str word}, bits:#{bits}"
|
||||
if bits == 0
|
||||
return word unless immediate
|
||||
|
@ -227,7 +227,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Rotate right
|
||||
def ror(word : Word, bits : Int, immediate : Bool, carry_out : Pointer(Bool)) : Word
|
||||
def ror(word : UInt32, bits : Int::Unsigned, immediate : Bool, carry_out : Pointer(Bool)) : UInt32
|
||||
log "ror - word:#{hex_str word}, bits:#{bits}"
|
||||
if bits == 0 # RRX #1
|
||||
return word unless immediate
|
||||
|
@ -243,7 +243,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Subtract two values
|
||||
def sub(operand_1 : Word, operand_2 : Word, set_conditions : Bool) : Word
|
||||
def sub(operand_1 : UInt32, operand_2 : UInt32, set_conditions : Bool) : UInt32
|
||||
log "sub - operand_1:#{hex_str operand_1}, operand_2:#{hex_str operand_2}"
|
||||
res = operand_1 &- operand_2
|
||||
if set_conditions
|
||||
|
@ -255,7 +255,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Subtract two values with carry
|
||||
def sbc(operand_1 : Word, operand_2 : Word, set_conditions : Bool) : Word
|
||||
def sbc(operand_1 : UInt32, operand_2 : UInt32, set_conditions : Bool) : UInt32
|
||||
log "sbc - operand_1:#{hex_str operand_1}, operand_2:#{hex_str operand_2}"
|
||||
res = operand_1 &- operand_2 &- 1 &+ @cpsr.carry.to_unsafe
|
||||
if set_conditions
|
||||
|
@ -267,7 +267,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Add two values
|
||||
def add(operand_1 : Word, operand_2 : Word, set_conditions : Bool) : Word
|
||||
def add(operand_1 : UInt32, operand_2 : UInt32, set_conditions : Bool) : UInt32
|
||||
log "add - operand_1:#{hex_str operand_1}, operand_2:#{hex_str operand_2}"
|
||||
res = operand_1 &+ operand_2
|
||||
if set_conditions
|
||||
|
@ -279,7 +279,7 @@ module GBA
|
|||
end
|
||||
|
||||
# Add two values with carry
|
||||
def adc(operand_1 : Word, operand_2 : Word, set_conditions : Bool) : Word
|
||||
def adc(operand_1 : UInt32, operand_2 : UInt32, set_conditions : Bool) : UInt32
|
||||
log "adc - operand_1:#{hex_str operand_1}, operand_2:#{hex_str operand_2}"
|
||||
res = operand_1 &+ operand_2 &+ @cpsr.carry.to_unsafe
|
||||
if set_conditions
|
||||
|
@ -290,8 +290,8 @@ module GBA
|
|||
res
|
||||
end
|
||||
|
||||
def print_state(instr : Word? = nil) : Nil
|
||||
@r.each_with_index do |val, reg|
|
||||
def print_state(instr : UInt32? = nil) : Nil
|
||||
@r.each_with_address do |val, reg|
|
||||
print "#{hex_str reg == 15 ? val - (@cpsr.thumb ? 2 : 4) : val, prefix: false} "
|
||||
end
|
||||
instr ||= @pipeline.peek
|
||||
|
|
|
@ -41,7 +41,7 @@ module GBA
|
|||
->{ @gba.interrupts.reg_if.dma2 = true }, ->{ @gba.interrupts.reg_if.dma3 = true }]
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : UInt8
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
channel = (io_addr - 0xB0) // 12
|
||||
reg = (io_addr - 0xB0) % 12
|
||||
case reg
|
||||
|
@ -54,7 +54,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : UInt8) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
channel = (io_addr - 0xB0) // 12
|
||||
reg = (io_addr - 0xB0) % 12
|
||||
case reg
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require "./types"
|
||||
require "./reg"
|
||||
require "./cartridge"
|
||||
require "./storage"
|
||||
|
|
|
@ -10,7 +10,7 @@ module GBA
|
|||
@rtc = RTC.new(@gba) # todo: support other forms of gpio
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : Byte
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
case io_addr & 0xFF
|
||||
when 0xC4 # IO Port Data
|
||||
if @allow_reads
|
||||
|
@ -27,7 +27,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : Byte) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
case io_addr & 0xFF
|
||||
when 0xC4 # IO Port Data
|
||||
@data &= value & 0xF_u8
|
||||
|
|
|
@ -7,7 +7,7 @@ module GBA
|
|||
def initialize(@gba : GBA)
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : Byte
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
case io_addr
|
||||
when 0x200 then 0xFF_u8 & @reg_ie.value
|
||||
when 0x201 then 0xFF_u8 & @reg_ie.value >> 8
|
||||
|
@ -19,7 +19,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : Byte) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
case io_addr
|
||||
when 0x200 then @reg_ie.value = (@reg_ie.value & 0xFF00) | value
|
||||
when 0x201 then @reg_ie.value = (@reg_ie.value & 0x00FF) | value.to_u16 << 8
|
||||
|
|
|
@ -6,7 +6,7 @@ module GBA
|
|||
def initialize(@gba : GBA)
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : Byte
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
case io_addr
|
||||
when 0x130..0x131 then @keyinput.read_byte(io_addr & 1)
|
||||
when 0x132..0x133 then @keycnt.read_byte(io_addr & 1)
|
||||
|
@ -14,7 +14,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : Byte) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
# TODO: If stopping is implemented, implement keycnt to exit stop mode
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ module GBA
|
|||
def initialize(@gba : GBA)
|
||||
end
|
||||
|
||||
def [](index : Int) : Byte
|
||||
io_addr = 0xFFFFFF_u32 & index
|
||||
def [](address : UInt32) : UInt8
|
||||
io_addr = 0xFFFFFF_u32 & address
|
||||
case io_addr
|
||||
when 0x000..0x055 then @gba.ppu[io_addr]
|
||||
when 0x060..0x0A7 then @gba.apu[io_addr]
|
||||
|
@ -29,8 +29,8 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
io_addr = 0xFFFFFF_u32 & index
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
io_addr = 0xFFFFFF_u32 & address
|
||||
case io_addr
|
||||
when 0x000..0x055 then @gba.ppu[io_addr] = value
|
||||
when 0x060..0x0A7 then @gba.apu[io_addr] = value
|
||||
|
|
|
@ -2,25 +2,25 @@ module GBA
|
|||
# A super minimalistic FIFO queue implementation optimized for
|
||||
# use as an ARM instruction pipeline.
|
||||
class Pipeline
|
||||
@buffer = Slice(Word).new 2
|
||||
@buffer = Slice(UInt32).new 2
|
||||
@pos = 0
|
||||
@size = 0
|
||||
|
||||
def push(instr : Word) : Nil
|
||||
def push(instr : UInt32) : Nil
|
||||
raise "Pushing to full pipeline" if @size == 2
|
||||
index = (@pos + @size) & 1
|
||||
@buffer[index] = instr
|
||||
address = (@pos + @size) & 1
|
||||
@buffer[address] = instr
|
||||
@size += 1
|
||||
end
|
||||
|
||||
def shift : Word
|
||||
def shift : UInt32
|
||||
@size -= 1
|
||||
val = @buffer[@pos]
|
||||
@pos = (@pos + 1) & 1
|
||||
val
|
||||
end
|
||||
|
||||
def peek : Word
|
||||
def peek : UInt32
|
||||
@buffer[@pos]
|
||||
end
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ module GBA
|
|||
|
||||
# Get the screen entry offset from the tile x, tile y, and background screen-size param using tonc algo
|
||||
@[AlwaysInline]
|
||||
def se_index(tx : Int, ty : Int, screen_size : Int) : Int
|
||||
def se_address(tx : Int, ty : Int, screen_size : Int) : Int
|
||||
n = tx + ty * 32
|
||||
n += 0x03E0 if tx >= 32
|
||||
n += 0x0400 if ty >= 32 && screen_size == 0b11
|
||||
|
@ -167,7 +167,7 @@ module GBA
|
|||
effective_col = (col + bghofs.offset) & bg_width
|
||||
tile_x = effective_col >> 3
|
||||
|
||||
se_idx = se_index(tile_x, tile_y, bgcnt.screen_size)
|
||||
se_idx = se_address(tile_x, tile_y, bgcnt.screen_size)
|
||||
screen_entry = @vram[screen_base + se_idx * 2 + 1].to_u16 << 8 | @vram[screen_base + se_idx * 2]
|
||||
|
||||
tile_id = bits(screen_entry, 0..9)
|
||||
|
@ -295,7 +295,7 @@ module GBA
|
|||
tile_id += offset
|
||||
palettes = @vram[base + tile_id * 0x20 + tile_y * 4 + (tile_x >> 1)]
|
||||
pal_idx = ((palettes >> ((tile_x & 1) * 4)) & 0xF)
|
||||
pal_idx += (sprite.palette_bank << 4) if pal_idx > 0 # convert palette index to absolute value
|
||||
pal_idx += (sprite.palette_bank << 4) if pal_idx > 0 # convert palette address to absolute value
|
||||
end
|
||||
|
||||
if sprite.obj_mode == 0b10 # object window
|
||||
|
@ -396,7 +396,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : Byte
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
case io_addr
|
||||
when 0x000..0x001 then @dispcnt.read_byte(io_addr & 1)
|
||||
when 0x002..0x003 then 0_u8 # todo green swap
|
||||
|
@ -415,7 +415,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : Byte) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
case io_addr
|
||||
when 0x000..0x001 then @dispcnt.write_byte(io_addr & 1, value)
|
||||
when 0x002..0x003 # undocumented - green swap
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
module GBA
|
||||
module Reg
|
||||
module Base16
|
||||
def read_byte(byte_num : Int) : Byte
|
||||
def read_byte(byte_num : Int) : UInt8
|
||||
(value >> (8 * byte_num)).to_u8!
|
||||
end
|
||||
|
||||
def write_byte(byte_num : Int, byte : Byte) : Byte
|
||||
def write_byte(byte_num : Int, byte : UInt8) : UInt8
|
||||
shift = 8 * byte_num
|
||||
mask = ~(0xFF_u16 << shift)
|
||||
self.value = (@value & mask) | byte.to_u16 << shift
|
||||
|
@ -14,11 +14,11 @@ module GBA
|
|||
end
|
||||
|
||||
module Base32
|
||||
def read_byte(byte_num : Int) : Byte
|
||||
def read_byte(byte_num : Int) : UInt8
|
||||
(value >> (8 * byte_num)).to_u8!
|
||||
end
|
||||
|
||||
def write_byte(byte_num : Int, byte : Byte) : Byte
|
||||
def write_byte(byte_num : Int, byte : UInt8) : UInt8
|
||||
shift = 8 * byte_num
|
||||
mask = ~(0xFF_u32 << shift)
|
||||
self.value = (@value & mask) | byte.to_u32 << shift
|
||||
|
|
|
@ -49,20 +49,20 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
abstract def [](index : Int) : Byte
|
||||
abstract def [](address : UInt32) : UInt8
|
||||
|
||||
def read_half(index : Int) : HalfWord
|
||||
0x0101_u16 * self[index & ~1]
|
||||
def read_half(address : UInt32) : UInt16
|
||||
0x0101_u16 * self[address & ~1]
|
||||
end
|
||||
|
||||
def read_word(index : Int) : Word
|
||||
0x01010101_u32 * self[index & ~3]
|
||||
def read_word(address : UInt32) : UInt32
|
||||
0x01010101_u32 * self[address & ~3]
|
||||
end
|
||||
|
||||
abstract def []=(index : Int, value : Byte) : Nil
|
||||
abstract def []=(address : UInt32, value : UInt8) : Nil
|
||||
|
||||
def eeprom?(index : Int) : Bool
|
||||
self.class == EEPROM && (0x0D000000..0x0DFFFFFF).includes? index
|
||||
def eeprom?(address : UInt32) : Bool
|
||||
self.class == EEPROM && (0x0D000000..0x0DFFFFFF).includes?(address)
|
||||
end
|
||||
|
||||
private def self.find_type(file : File) : Type?
|
||||
|
|
|
@ -69,7 +69,7 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](index : Int) : Byte
|
||||
def [](address : UInt32) : UInt8
|
||||
case @state
|
||||
when .includes? State::READ_IGNORE
|
||||
if (@ignored_reads += 1) == 4
|
||||
|
@ -89,7 +89,7 @@ module GBA
|
|||
1_u8
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
return if @state == State::READ || @state == State::READ_IGNORE
|
||||
value &= 1
|
||||
@buffer.push value
|
||||
|
|
|
@ -11,7 +11,7 @@ module GBA
|
|||
SET_BANK
|
||||
end
|
||||
|
||||
enum Command : Byte
|
||||
enum Command : UInt8
|
||||
ENTER_IDENT = 0x90
|
||||
EXIT_IDENT = 0xF0
|
||||
PREPARE_ERASE = 0x80
|
||||
|
@ -32,37 +32,37 @@ module GBA
|
|||
end
|
||||
end
|
||||
|
||||
def [](index : Int) : Byte
|
||||
index &= 0xFFFF
|
||||
if @state.includes?(State::IDENTIFICATION) && 0 <= index <= 1
|
||||
(@id >> (8 * index) & 0xFF).to_u8!
|
||||
def [](address : UInt32) : UInt8
|
||||
address &= 0xFFFF
|
||||
if @state.includes?(State::IDENTIFICATION) && 0 <= address <= 1
|
||||
(@id >> (8 * address) & 0xFF).to_u8!
|
||||
else
|
||||
@memory[0x10000 * @bank + index]
|
||||
@memory[0x10000 * @bank + address]
|
||||
end
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
index &= 0xFFFF
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
address &= 0xFFFF
|
||||
case @state
|
||||
when .includes? State::PREPARE_WRITE
|
||||
@memory[0x10000 * @bank + index] &= value
|
||||
@memory[0x10000 * @bank + address] &= value
|
||||
@dirty = true
|
||||
@state ^= State::PREPARE_WRITE
|
||||
when .includes? State::SET_BANK
|
||||
@bank = value & 1
|
||||
@state ^= State::SET_BANK
|
||||
when .includes? State::READY
|
||||
if index == 0x5555 && value == 0xAA
|
||||
if address == 0x5555 && value == 0xAA
|
||||
@state ^= State::READY
|
||||
@state |= State::CMD_1
|
||||
end
|
||||
when .includes? State::CMD_1
|
||||
if index == 0x2AAA && value == 0x55
|
||||
if address == 0x2AAA && value == 0x55
|
||||
@state ^= State::CMD_1
|
||||
@state |= State::CMD_2
|
||||
end
|
||||
when .includes? State::CMD_2
|
||||
if index == 0x5555
|
||||
if address == 0x5555
|
||||
case Command.new(value)
|
||||
when Command::ENTER_IDENT then @state |= State::IDENTIFICATION
|
||||
when Command::EXIT_IDENT then @state ^= State::IDENTIFICATION
|
||||
|
@ -77,8 +77,8 @@ module GBA
|
|||
when Command::SET_BANK then @state |= State::SET_BANK if @type == Type::FLASH1M
|
||||
else puts "Unsupported flash command #{hex_str value}"
|
||||
end
|
||||
elsif @state.includes?(State::PREPARE_ERASE) && index & 0x0FFF == 0 && value == Command::ERASE_CHUNK.value
|
||||
0x1000.times { |i| @memory[0x10000 * @bank + index + i] = 0xFF }
|
||||
elsif @state.includes?(State::PREPARE_ERASE) && address & 0x0FFF == 0 && value == Command::ERASE_CHUNK.value
|
||||
0x1000.times { |i| @memory[0x10000 * @bank + address + i] = 0xFF }
|
||||
@dirty = true
|
||||
@state ^= State::PREPARE_ERASE
|
||||
end
|
||||
|
|
|
@ -2,12 +2,12 @@ module GBA
|
|||
class SRAM < Storage
|
||||
@memory = Bytes.new(Type::SRAM.bytes, 0xFF)
|
||||
|
||||
def [](index : Int) : Byte
|
||||
@memory[index & 0x7FFF]
|
||||
def [](address : UInt32) : UInt8
|
||||
@memory[address & 0x7FFF]
|
||||
end
|
||||
|
||||
def []=(index : Int, value : Byte) : Nil
|
||||
@memory[index & 0x7FFF] = value
|
||||
def []=(address : UInt32, value : UInt8) : Nil
|
||||
@memory[address & 0x7FFF] = value
|
||||
@dirty = true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_add_offset_to_stack_pointer(instr : Word) : Nil
|
||||
def thumb_add_offset_to_stack_pointer(instr : UInt32) : Nil
|
||||
sign = bit?(instr, 7)
|
||||
offset = bits(instr, 0..6)
|
||||
if sign # negative
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_add_subtract(instr : Word) : Nil
|
||||
def thumb_add_subtract(instr : UInt32) : Nil
|
||||
imm_flag = bit?(instr, 10)
|
||||
sub = bit?(instr, 9)
|
||||
imm = bits(instr, 6..8)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_alu_operations(instr : Word) : Nil
|
||||
def thumb_alu_operations(instr : UInt32) : Nil
|
||||
op = bits(instr, 6..9)
|
||||
rs = bits(instr, 3..5)
|
||||
rd = bits(instr, 0..2)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_conditional_branch(instr : Word) : Nil
|
||||
def thumb_conditional_branch(instr : UInt32) : Nil
|
||||
cond = bits(instr, 8..11)
|
||||
offset = bits(instr, 0..7).to_i8!.to_i32
|
||||
if check_cond cond
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_high_reg_branch_exchange(instr : Word) : Nil
|
||||
def thumb_high_reg_branch_exchange(instr : UInt32) : Nil
|
||||
op = bits(instr, 8..9)
|
||||
h1 = bit?(instr, 7)
|
||||
h2 = bit?(instr, 6)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_load_address(instr : Word) : Nil
|
||||
def thumb_load_address(instr : UInt32) : Nil
|
||||
source = bit?(instr, 11)
|
||||
rd = bits(instr, 8..10)
|
||||
word = bits(instr, 0..7)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_load_store_halfword(instr : Word) : Nil
|
||||
def thumb_load_store_UInt16(instr : UInt32) : Nil
|
||||
load = bit?(instr, 11)
|
||||
offset = bits(instr, 6..10)
|
||||
rb = bits(instr, 3..5)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_load_store_immediate_offset(instr : Word) : Nil
|
||||
def thumb_load_store_immediate_offset(instr : UInt32) : Nil
|
||||
byte_quantity_and_load = bits(instr, 11..12)
|
||||
offset = bits(instr, 6..10)
|
||||
rb = bits(instr, 3..5)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_load_store_register_offset(instr : Word) : Nil
|
||||
def thumb_load_store_register_offset(instr : UInt32) : Nil
|
||||
load_and_byte_quantity = bits(instr, 10..11)
|
||||
ro = bits(instr, 6..8)
|
||||
rb = bits(instr, 3..5)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_load_store_sign_extended(instr : Word) : Nil
|
||||
def thumb_load_store_sign_extended(instr : UInt32) : Nil
|
||||
hs = bits(instr, 10..11)
|
||||
ro = bits(instr, 6..8)
|
||||
rb = bits(instr, 3..5)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_long_branch_link(instr : Word) : Nil
|
||||
def thumb_long_branch_link(instr : UInt32) : Nil
|
||||
second_instr = bit?(instr, 11)
|
||||
offset = bits(instr, 0..10)
|
||||
if second_instr
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_move_compare_add_subtract(instr : Word) : Nil
|
||||
def thumb_move_compare_add_subtract(instr : UInt32) : Nil
|
||||
op = bits(instr, 11..12)
|
||||
rd = bits(instr, 8..10)
|
||||
offset = bits(instr, 0..7)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_move_shifted_register(instr : Word) : Nil
|
||||
def thumb_move_shifted_register(instr : UInt32) : Nil
|
||||
op = bits(instr, 11..12)
|
||||
offset = bits(instr, 6..10)
|
||||
rs = bits(instr, 3..5)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_multiple_load_store(instr : Word) : Nil
|
||||
def thumb_multiple_load_store(instr : UInt32) : Nil
|
||||
load = bit?(instr, 11)
|
||||
rb = bits(instr, 8..10)
|
||||
list = bits(instr, 0..7)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_pc_relative_load(instr : Word) : Nil
|
||||
def thumb_pc_relative_load(instr : UInt32) : Nil
|
||||
imm = bits(instr, 0..7)
|
||||
rd = bits(instr, 8..10)
|
||||
set_reg(rd, @gba.bus.read_word((@r[15] & ~2) &+ (imm << 2)))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_push_pop_registers(instr : Word) : Nil
|
||||
def thumb_push_pop_registers(instr : UInt32) : Nil
|
||||
pop = bit?(instr, 11)
|
||||
pclr = bit?(instr, 8)
|
||||
list = bits(instr, 0..7)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_software_interrupt(instr : Word) : Nil
|
||||
def thumb_software_interrupt(instr : UInt32) : Nil
|
||||
lr = @r[15] - 2
|
||||
switch_mode CPU::Mode::SVC
|
||||
set_reg(14, lr)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_sp_relative_load_store(instr : Word) : Nil
|
||||
def thumb_sp_relative_load_store(instr : UInt32) : Nil
|
||||
load = bit?(instr, 11)
|
||||
rd = bits(instr, 8..10)
|
||||
word = bits(instr, 0..7)
|
||||
|
|
|
@ -1,56 +1,56 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_execute(instr : Word) : Nil
|
||||
def thumb_execute(instr : UInt32) : Nil
|
||||
thumb_lut[instr >> 8].call instr
|
||||
end
|
||||
|
||||
def fill_thumb_lut
|
||||
lut = Slice(Proc(Word, Nil)).new 256, ->thumb_unimplemented(Word)
|
||||
lut = Slice(Proc(UInt32, Nil)).new 256, ->thumb_unimplemented(UInt32)
|
||||
256.times do |idx|
|
||||
if idx & 0b11110000 == 0b11110000
|
||||
lut[idx] = ->thumb_long_branch_link(Word)
|
||||
lut[idx] = ->thumb_long_branch_link(UInt32)
|
||||
elsif idx & 0b11111000 == 0b11100000
|
||||
lut[idx] = ->thumb_unconditional_branch(Word)
|
||||
lut[idx] = ->thumb_unconditional_branch(UInt32)
|
||||
elsif idx & 0b11111111 == 0b11011111
|
||||
lut[idx] = ->thumb_software_interrupt(Word)
|
||||
lut[idx] = ->thumb_software_interrupt(UInt32)
|
||||
elsif idx & 0b11110000 == 0b11010000
|
||||
lut[idx] = ->thumb_conditional_branch(Word)
|
||||
lut[idx] = ->thumb_conditional_branch(UInt32)
|
||||
elsif idx & 0b11110000 == 0b11000000
|
||||
lut[idx] = ->thumb_multiple_load_store(Word)
|
||||
lut[idx] = ->thumb_multiple_load_store(UInt32)
|
||||
elsif idx & 0b11110110 == 0b10110100
|
||||
lut[idx] = ->thumb_push_pop_registers(Word)
|
||||
lut[idx] = ->thumb_push_pop_registers(UInt32)
|
||||
elsif idx & 0b11111111 == 0b10110000
|
||||
lut[idx] = ->thumb_add_offset_to_stack_pointer(Word)
|
||||
lut[idx] = ->thumb_add_offset_to_stack_pointer(UInt32)
|
||||
elsif idx & 0b11110000 == 0b10100000
|
||||
lut[idx] = ->thumb_load_address(Word)
|
||||
lut[idx] = ->thumb_load_address(UInt32)
|
||||
elsif idx & 0b11110000 == 0b10010000
|
||||
lut[idx] = ->thumb_sp_relative_load_store(Word)
|
||||
lut[idx] = ->thumb_sp_relative_load_store(UInt32)
|
||||
elsif idx & 0b11110000 == 0b10000000
|
||||
lut[idx] = ->thumb_load_store_halfword(Word)
|
||||
lut[idx] = ->thumb_load_store_UInt16(UInt32)
|
||||
elsif idx & 0b11100000 == 0b01100000
|
||||
lut[idx] = ->thumb_load_store_immediate_offset(Word)
|
||||
lut[idx] = ->thumb_load_store_immediate_offset(UInt32)
|
||||
elsif idx & 0b11110010 == 0b01010010
|
||||
lut[idx] = ->thumb_load_store_sign_extended(Word)
|
||||
lut[idx] = ->thumb_load_store_sign_extended(UInt32)
|
||||
elsif idx & 0b11110010 == 0b01010000
|
||||
lut[idx] = ->thumb_load_store_register_offset(Word)
|
||||
lut[idx] = ->thumb_load_store_register_offset(UInt32)
|
||||
elsif idx & 0b11111000 == 0b01001000
|
||||
lut[idx] = ->thumb_pc_relative_load(Word)
|
||||
lut[idx] = ->thumb_pc_relative_load(UInt32)
|
||||
elsif idx & 0b11111100 == 0b01000100
|
||||
lut[idx] = ->thumb_high_reg_branch_exchange(Word)
|
||||
lut[idx] = ->thumb_high_reg_branch_exchange(UInt32)
|
||||
elsif idx & 0b11111100 == 0b01000000
|
||||
lut[idx] = ->thumb_alu_operations(Word)
|
||||
lut[idx] = ->thumb_alu_operations(UInt32)
|
||||
elsif idx & 0b11100000 == 0b00100000
|
||||
lut[idx] = ->thumb_move_compare_add_subtract(Word)
|
||||
lut[idx] = ->thumb_move_compare_add_subtract(UInt32)
|
||||
elsif idx & 0b11111000 == 0b00011000
|
||||
lut[idx] = ->thumb_add_subtract(Word)
|
||||
lut[idx] = ->thumb_add_subtract(UInt32)
|
||||
elsif idx & 0b11100000 == 0b00000000
|
||||
lut[idx] = ->thumb_move_shifted_register(Word)
|
||||
lut[idx] = ->thumb_move_shifted_register(UInt32)
|
||||
end
|
||||
end
|
||||
lut
|
||||
end
|
||||
|
||||
def thumb_unimplemented(instr : Word) : Nil
|
||||
def thumb_unimplemented(instr : UInt32) : Nil
|
||||
abort "Unimplemented instruction: #{hex_str instr.to_u16}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module GBA
|
||||
module THUMB
|
||||
def thumb_unconditional_branch(instr : Word) : Nil
|
||||
def thumb_unconditional_branch(instr : UInt32) : Nil
|
||||
offset = bits(instr, 0..10)
|
||||
offset = (offset << 5).to_i16! >> 4
|
||||
set_reg(15, @r[15] &+ offset)
|
||||
|
|
|
@ -51,7 +51,7 @@ module GBA
|
|||
@cycle_enabled[num] = @gba.scheduler.cycles
|
||||
end
|
||||
|
||||
def [](io_addr : Int) : UInt8
|
||||
def [](io_addr : UInt32) : UInt8
|
||||
num = (io_addr & 0xF) // 4
|
||||
value = if bit?(io_addr, 1)
|
||||
@tmcnt[num].value
|
||||
|
@ -62,7 +62,7 @@ module GBA
|
|||
value.to_u8!
|
||||
end
|
||||
|
||||
def []=(io_addr : Int, value : UInt8) : Nil
|
||||
def []=(io_addr : UInt32, value : UInt8) : Nil
|
||||
num = (io_addr & 0xF) // 4
|
||||
high = bit?(io_addr, 0)
|
||||
mask = 0xFF00_u16
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
module GBA
|
||||
alias Byte = UInt8
|
||||
alias HalfWord = UInt16
|
||||
alias Word = UInt32
|
||||
alias Words = Slice(UInt32)
|
||||
end
|
Loading…
Add table
Reference in a new issue