tighten address type restrictions from int to uint32

This commit is contained in:
Matthew Berry 2022-10-30 11:30:36 -07:00
parent afcd4b667e
commit 1523742e12
57 changed files with 362 additions and 369 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,3 @@
require "./types"
require "./reg"
require "./cartridge"
require "./storage"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +0,0 @@
module GBA
alias Byte = UInt8
alias HalfWord = UInt16
alias Word = UInt32
alias Words = Slice(UInt32)
end