prefer Slice to Array in most cases

This change updates most uses of Array to Slice in order to avoid an
extra object on the heap and an extra reference to follow when indexing
into these objects. This amounts to a roughly 5% performance improvement
in the games tested.

This change also updates the GBC palette writing code to not allocate
new arrays when new palettes are written.
This commit is contained in:
Matthew Berry 2022-10-30 13:47:22 -07:00
parent 1523742e12
commit e0ef57d9e6
15 changed files with 77 additions and 83 deletions

View file

@ -1,6 +1,6 @@
module GB module GB
class Channel1 < VolumeEnvelopeChannel class Channel1 < VolumeEnvelopeChannel
WAVE_DUTY = [ WAVE_DUTY = Slice[
[0, 0, 0, 0, 0, 0, 0, 1], # 12.5% [0, 0, 0, 0, 0, 0, 0, 1], # 12.5%
[1, 0, 0, 0, 0, 0, 0, 1], # 25% [1, 0, 0, 0, 0, 0, 0, 1], # 25%
[1, 0, 0, 0, 0, 1, 1, 1], # 50% [1, 0, 0, 0, 0, 1, 1, 1], # 50%

View file

@ -1,6 +1,6 @@
module GB module GB
class Channel2 < VolumeEnvelopeChannel class Channel2 < VolumeEnvelopeChannel
WAVE_DUTY = [ WAVE_DUTY = Slice[
[0, 0, 0, 0, 0, 0, 0, 1], # 12.5% [0, 0, 0, 0, 0, 0, 0, 1], # 12.5%
[1, 0, 0, 0, 0, 0, 0, 1], # 25% [1, 0, 0, 0, 0, 0, 0, 1], # 25%
[1, 0, 0, 0, 0, 1, 1, 1], # 50% [1, 0, 0, 0, 0, 1, 1, 1], # 50%

View file

@ -39,7 +39,7 @@ module GB
SLEEP SLEEP
end end
FETCHER_ORDER = [ FETCHER_ORDER = Slice[
FetchStage::SLEEP, FetchStage::GET_TILE, FetchStage::SLEEP, FetchStage::GET_TILE,
FetchStage::SLEEP, FetchStage::GET_TILE_DATA_LOW, FetchStage::SLEEP, FetchStage::GET_TILE_DATA_LOW,
FetchStage::SLEEP, FetchStage::GET_TILE_DATA_HIGH, FetchStage::SLEEP, FetchStage::GET_TILE_DATA_HIGH,

View file

@ -22,7 +22,7 @@ module GB
@scheduler : Scheduler @scheduler : Scheduler
@cgb_ptr : Pointer(Bool) @cgb_ptr : Pointer(Bool)
@wram = Array(Bytes).new 8 { Bytes.new GB::Memory::WORK_RAM_N.size } @wram = Slice(Bytes).new 8 { Bytes.new GB::Memory::WORK_RAM_N.size }
@wram_bank : UInt8 = 1 @wram_bank : UInt8 = 1
@hram = Bytes.new HRAM.size @hram = Bytes.new HRAM.size
@ff72 : UInt8 = 0x00 @ff72 : UInt8 = 0x00

View file

@ -80,7 +80,7 @@ module GB
end end
end end
POST_BOOT_VRAM = [ POST_BOOT_VRAM = Slice[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xF3, 0x00, 0xF3, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xF3, 0x00, 0xF3, 0x00,
0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00,
@ -116,7 +116,7 @@ module GB
WIDTH = 160 WIDTH = 160
HEIGHT = 144 HEIGHT = 144
DMG_COLORS = [0x6BDF_u16, 0x3ABF_u16, 0x35BD_u16, 0x2CEF_u16] DMG_COLORS = Slice[0x6BDF_u16, 0x3ABF_u16, 0x35BD_u16, 0x2CEF_u16]
getter framebuffer = Slice(UInt16).new WIDTH * HEIGHT getter framebuffer = Slice(UInt16).new WIDTH * HEIGHT
property frame = false property frame = false
@ -129,7 +129,7 @@ module GB
@obj_palette_index : UInt8 = 0 @obj_palette_index : UInt8 = 0
@obj_auto_increment = false @obj_auto_increment = false
@vram = Array(Bytes).new 2 { Bytes.new GB::Memory::VRAM.size } # 0x8000..0x9FFF @vram = Slice(Bytes).new 2 { Bytes.new GB::Memory::VRAM.size } # 0x8000..0x9FFF
@vram_bank : UInt8 = 0 # track which bank is active @vram_bank : UInt8 = 0 # track which bank is active
@sprite_table = Bytes.new Memory::OAM.size # 0xFE00..0xFE9F @sprite_table = Bytes.new Memory::OAM.size # 0xFE00..0xFE9F
@lcd_control : UInt8 = 0x00_u8 # 0xFF40 @lcd_control : UInt8 = 0x00_u8 # 0xFF40
@ -139,9 +139,9 @@ module GB
@ly : UInt8 = 0x00_u8 # 0xFF44 @ly : UInt8 = 0x00_u8 # 0xFF44
@lyc : UInt8 = 0x00_u8 # 0xFF45 @lyc : UInt8 = 0x00_u8 # 0xFF45
@dma : UInt8 = 0x00_u8 # 0xFF46 @dma : UInt8 = 0x00_u8 # 0xFF46
@bgp : Array(UInt8) = Array(UInt8).new 4, 0 # 0xFF47 @bgp : Slice(UInt8) = Slice(UInt8).new 4, 0 # 0xFF47
@obp0 : Array(UInt8) = Array(UInt8).new 4, 0 # 0xFF48 @obp0 : Slice(UInt8) = Slice(UInt8).new 4, 0 # 0xFF48
@obp1 : Array(UInt8) = Array(UInt8).new 4, 0 # 0xFF49 @obp1 : Slice(UInt8) = Slice(UInt8).new 4, 0 # 0xFF49
@wy : UInt8 = 0x00_u8 # 0xFF4A @wy : UInt8 = 0x00_u8 # 0xFF4A
@wx : UInt8 = 0x00_u8 # 0xFF4B @wx : UInt8 = 0x00_u8 # 0xFF4B
@ -247,9 +247,9 @@ module GB
when 0xFF44 then @ly when 0xFF44 then @ly
when 0xFF45 then @lyc when 0xFF45 then @lyc
when 0xFF46 then @dma when 0xFF46 then @dma
when 0xFF47 then palette_from_array @bgp when 0xFF47 then palette_from_enumerable @bgp
when 0xFF48 then palette_from_array @obp0 when 0xFF48 then palette_from_enumerable @obp0
when 0xFF49 then palette_from_array @obp1 when 0xFF49 then palette_from_enumerable @obp1
when 0xFF4A then @wy when 0xFF4A then @wy
when 0xFF4B then @wx when 0xFF4B then @wx
when 0xFF4F then @cgb_ptr.value ? 0xFE_u8 | @vram_bank : 0xFF_u8 when 0xFF4F then @cgb_ptr.value ? 0xFE_u8 | @vram_bank : 0xFF_u8
@ -289,9 +289,9 @@ module GB
@lyc = value @lyc = value
handle_stat_interrupt handle_stat_interrupt
when 0xFF46 then @dma = value when 0xFF46 then @dma = value
when 0xFF47 then @bgp = palette_to_array value when 0xFF47 then update_palette(@bgp, value)
when 0xFF48 then @obp0 = palette_to_array value when 0xFF48 then update_palette(@obp0, value)
when 0xFF49 then @obp1 = palette_to_array value when 0xFF49 then update_palette(@obp1, value)
when 0xFF4A then @wy = value when 0xFF4A then @wy = value
when 0xFF4B then @wx = value when 0xFF4B then @wx = value
when 0xFF4F then @vram_bank = value & 1 if @cgb_ptr.value when 0xFF4F then @vram_bank = value & 1 if @cgb_ptr.value
@ -400,11 +400,14 @@ module GB
# palettes # palettes
def palette_to_array(palette : UInt8) : Array(UInt8) def update_palette(palette : Indexable(UInt8), val : UInt8) : Nil
[palette & 0x3, (palette >> 2) & 0x3, (palette >> 4) & 0x3, (palette >> 6) & 0x3] palette[0] = val & 0x3
palette[1] = (val >> 2) & 0x3
palette[2] = (val >> 4) & 0x3
palette[3] = (val >> 6) & 0x3
end end
def palette_from_array(palette_array : Array(UInt8)) : UInt8 def palette_from_enumerable(palette_array : Enumerable(UInt8)) : UInt8
palette_array.each_with_index.reduce(0x00_u8) do |palette, (color, idx)| palette_array.each_with_index.reduce(0x00_u8) do |palette, (color, idx)|
palette | color << (idx * 2) palette | color << (idx * 2)
end end

View file

@ -22,7 +22,7 @@ module GB
end end
# color idx, BG-to-OAM priority bit # color idx, BG-to-OAM priority bit
@scanline_color_vals = Array(Tuple(UInt8, Bool)).new WIDTH, {0_u8, false} @scanline_color_vals = Slice(Tuple(UInt8, Bool)).new WIDTH, {0_u8, false}
def scanline def scanline
@current_window_line = 0 if @ly == 0 @current_window_line = 0 if @ly == 0

View file

@ -1,6 +1,6 @@
module GBA module GBA
class Channel1 < VolumeEnvelopeChannel class Channel1 < VolumeEnvelopeChannel
WAVE_DUTY = [ WAVE_DUTY = Slice[
[-8, -8, -8, -8, -8, -8, -8, +8], # 12.5% [-8, -8, -8, -8, -8, -8, -8, +8], # 12.5%
[+8, -8, -8, -8, -8, -8, -8, +8], # 25% [+8, -8, -8, -8, -8, -8, -8, +8], # 25%
[+8, -8, -8, -8, -8, +8, +8, +8], # 50% [+8, -8, -8, -8, -8, +8, +8, +8], # 50%

View file

@ -1,6 +1,6 @@
module GBA module GBA
class Channel2 < VolumeEnvelopeChannel class Channel2 < VolumeEnvelopeChannel
WAVE_DUTY = [ WAVE_DUTY = Slice[
[-8, -8, -8, -8, -8, -8, -8, +8], # 12.5% [-8, -8, -8, -8, -8, -8, -8, +8], # 12.5%
[+8, -8, -8, -8, -8, -8, -8, +8], # 25% [+8, -8, -8, -8, -8, -8, -8, +8], # 25%
[+8, -8, -8, -8, -8, +8, +8, +8], # 50% [+8, -8, -8, -8, -8, +8, +8, +8], # 50%

View file

@ -7,7 +7,7 @@ module GBA
other.is_a?(Int) && RANGE.includes?(other) || WAVE_RAM_RANGE.includes?(other) other.is_a?(Int) && RANGE.includes?(other) || WAVE_RAM_RANGE.includes?(other)
end end
@wave_ram = Array(Bytes).new 2, Bytes.new(WAVE_RAM_RANGE.size) { |idx| idx & 1 == 0 ? 0x00_u8 : 0xFF_u8 } @wave_ram = Slice(Bytes).new 2, Bytes.new(WAVE_RAM_RANGE.size) { |idx| idx & 1 == 0 ? 0x00_u8 : 0xFF_u8 }
@wave_ram_position : UInt8 = 0 @wave_ram_position : UInt8 = 0
@wave_ram_sample_buffer : UInt8 = 0x00 @wave_ram_sample_buffer : UInt8 = 0x00

View file

@ -2,18 +2,18 @@ module GBA
class DMAChannels class DMAChannels
RANGE = 0xA0..0xA7 RANGE = 0xA0..0xA7
@fifos = Array(Array(Int8)).new 2 { Array(Int8).new 32, 0 } @fifos = Slice(Slice(Int8)).new 2 { Slice(Int8).new 32, 0 }
@positions = Array(Int32).new 2, 0 @positions = Slice(Int32).new 2, 0
@sizes = Array(Int32).new 2, 0 @sizes = Slice(Int32).new 2, 0
@timers : Array(Proc(UInt16)) @timers : Slice(Proc(UInt16))
@latches = Array(Int16).new 2, 0 @latches = Slice(Int16).new 2, 0
def ===(other) : Bool def ===(other) : Bool
other.is_a?(Int) && RANGE.includes?(other) other.is_a?(Int) && RANGE.includes?(other)
end end
def initialize(@gba : GBA, @control : Reg::SOUNDCNT_H) def initialize(@gba : GBA, @control : Reg::SOUNDCNT_H)
@timers = [ @timers = Slice[
->{ @control.dma_sound_a_timer }, ->{ @control.dma_sound_a_timer },
->{ @control.dma_sound_b_timer }, ->{ @control.dma_sound_b_timer },
] ]

View file

@ -3,7 +3,7 @@ require "./gpio"
module GBA module GBA
class Bus class Bus
# Timings for rom are estimated for game compatibility. # Timings for rom are estimated for game compatibility.
ACCESS_TIMING_TABLE = [ ACCESS_TIMING_TABLE = Slice[
[1, 1, 3, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2], # 8-bit and 16-bit accesses [1, 1, 3, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2], # 8-bit and 16-bit accesses
[1, 1, 6, 1, 1, 2, 2, 1, 4, 4, 4, 4, 4, 4, 4, 4], # 32-bit accesses [1, 1, 6, 1, 1, 2, 2, 1, 4, 4, 4, 4, 4, 4, 4, 4], # 32-bit accesses
] ]
@ -172,7 +172,7 @@ module GBA
when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FE)).as(UInt16*).value = 0x0101_u16 * value when 0x5 then (@gba.ppu.pram.to_unsafe + (address & 0x3FE)).as(UInt16*).value = 0x0101_u16 * value
when 0x6 when 0x6
limit = @gba.ppu.bitmap? ? 0x13FFF : 0x0FFFF # (u8 write only) upper limit depends on display mode limit = @gba.ppu.bitmap? ? 0x13FFF : 0x0FFFF # (u8 write only) upper limit depends on display mode
address = 0x1FFFE_u32 & address # (u8 write only) UInt16-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 address -= 0x8000 if address > 0x17FFF # todo: determine if this happens before or after the limit check
(@gba.ppu.vram.to_unsafe + address).as(UInt16*).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 0x7 # can't write bytes to oam

View file

@ -48,8 +48,8 @@ module GBA
getter pipeline = Pipeline.new getter pipeline = Pipeline.new
getter lut : Slice(Proc(UInt32, Nil)) { fill_lut } getter lut : Slice(Proc(UInt32, Nil)) { fill_lut }
getter thumb_lut : Slice(Proc(UInt32, Nil)) { fill_thumb_lut } getter thumb_lut : Slice(Proc(UInt32, Nil)) { fill_thumb_lut }
@reg_banks = Array(Array(UInt32)).new 6 { Array(UInt32).new 7, 0 } @reg_banks = Slice(Slice(UInt32)).new 6 { Slice(UInt32).new 7, 0 }
@spsr_banks = Array(UInt32).new 6, CPU::Mode::SYS.value # logically independent of typical register banks @spsr_banks = Slice(UInt32).new 6, CPU::Mode::SYS.value # logically independent of typical register banks
property halted = false property halted = false
def initialize(@gba : GBA) def initialize(@gba : GBA)

View file

@ -22,23 +22,24 @@ module GBA
end end
end end
SRC_MASK = [0x07FFFFFF_u32, 0x0FFFFFFF_u32, 0x0FFFFFFF_u32, 0x0FFFFFFF_u32] SRC_MASK = Slice[0x07FFFFFF_u32, 0x0FFFFFFF_u32, 0x0FFFFFFF_u32, 0x0FFFFFFF_u32]
DST_MASK = [0x07FFFFFF_u32, 0x07FFFFFF_u32, 0x07FFFFFF_u32, 0x0FFFFFFF_u32] DST_MASK = Slice[0x07FFFFFF_u32, 0x07FFFFFF_u32, 0x07FFFFFF_u32, 0x0FFFFFFF_u32]
LEN_MASK = [0x3FFF_u16, 0x3FFF_u16, 0x3FFF_u16, 0xFFFF_u16] LEN_MASK = Slice[0x3FFF_u16, 0x3FFF_u16, 0x3FFF_u16, 0xFFFF_u16]
getter dmacnt_l : Array(UInt16) getter dmacnt_l : Slice(UInt16)
@interrupt_flags : Array(Proc(Nil)) @interrupt_flags : Slice(Proc(Nil))
def initialize(@gba : GBA) def initialize(@gba : GBA)
@dmasad = Array(UInt32).new 4, 0 @dmasad = Slice(UInt32).new 4, 0
@dmadad = Array(UInt32).new 4, 0 @dmadad = Slice(UInt32).new 4, 0
@dmacnt_l = Array(UInt16).new 4, 0 @dmacnt_l = Slice(UInt16).new 4, 0
@dmacnt_h = Array(Reg::DMACNT).new 4 { Reg::DMACNT.new 0 } @dmacnt_h = Slice(Reg::DMACNT).new 4 { Reg::DMACNT.new 0 }
@src = Array(UInt32).new 4, 0 @src = Slice(UInt32).new 4, 0
@dst = Array(UInt32).new 4, 0 @dst = Slice(UInt32).new 4, 0
@interrupt_flags = [->{ @gba.interrupts.reg_if.dma0 = true }, ->{ @gba.interrupts.reg_if.dma1 = true }, @interrupt_flags = Slice[
->{ @gba.interrupts.reg_if.dma2 = true }, ->{ @gba.interrupts.reg_if.dma3 = true }] ->{ @gba.interrupts.reg_if.dma0 = true }, ->{ @gba.interrupts.reg_if.dma1 = true },
->{ @gba.interrupts.reg_if.dma2 = true }, ->{ @gba.interrupts.reg_if.dma3 = true }]
end end
def [](io_addr : UInt32) : UInt8 def [](io_addr : UInt32) : UInt8

View file

@ -4,7 +4,7 @@ module GBA
getter framebuffer : Slice(UInt16) = Slice(UInt16).new 0x9600 # framebuffer as 16-bit xBBBBBGGGGGRRRRR getter framebuffer : Slice(UInt16) = Slice(UInt16).new 0x9600 # framebuffer as 16-bit xBBBBBGGGGGRRRRR
property frame = false property frame = false
@layer_palettes : Array(Bytes) = Array.new 4 { Bytes.new 240 } @layer_palettes : Slice(Bytes) = Slice.new 4 { Bytes.new 240 }
@sprite_pixels : Slice(SpritePixel) = Slice(SpritePixel).new 240, SPRITE_PIXEL @sprite_pixels : Slice(SpritePixel) = Slice(SpritePixel).new 240, SPRITE_PIXEL
getter pram = Bytes.new 0x400 getter pram = Bytes.new 0x400
@ -14,12 +14,12 @@ module GBA
@dispcnt = Reg::DISPCNT.new 0 @dispcnt = Reg::DISPCNT.new 0
@dispstat = Reg::DISPSTAT.new 0 @dispstat = Reg::DISPSTAT.new 0
@vcount : UInt16 = 0x0000_u16 @vcount : UInt16 = 0x0000_u16
@bgcnt = Array(Reg::BGCNT).new 4 { GBA::Reg::BGCNT.new 0 } @bgcnt = Slice(Reg::BGCNT).new 4 { GBA::Reg::BGCNT.new 0 }
@bghofs = Array(Reg::BGOFS).new 4 { GBA::Reg::BGOFS.new 0 } @bghofs = Slice(Reg::BGOFS).new 4 { GBA::Reg::BGOFS.new 0 }
@bgvofs = Array(Reg::BGOFS).new 4 { GBA::Reg::BGOFS.new 0 } @bgvofs = Slice(Reg::BGOFS).new 4 { GBA::Reg::BGOFS.new 0 }
@bgaff = Array(Array(Reg::BGAFF)).new 2 { Array(GBA::Reg::BGAFF).new 4 { GBA::Reg::BGAFF.new 0 } } @bgaff = Slice(Slice(Reg::BGAFF)).new 2 { Slice(GBA::Reg::BGAFF).new 4 { GBA::Reg::BGAFF.new 0 } }
@bgref = Array(Array(Reg::BGREF)).new 2 { Array(GBA::Reg::BGREF).new 2 { GBA::Reg::BGREF.new 0 } } @bgref = Slice(Slice(Reg::BGREF)).new 2 { Slice(GBA::Reg::BGREF).new 2 { GBA::Reg::BGREF.new 0 } }
@bgref_int = Array(Array(Int32)).new 2 { Array(Int32).new 2, 0 } @bgref_int = Slice(Slice(Int32)).new 2 { Slice(Int32).new 2, 0 }
@win0h = Reg::WINH.new 0 @win0h = Reg::WINH.new 0
@win1h = Reg::WINH.new 0 @win1h = Reg::WINH.new 0
@win0v = Reg::WINV.new 0 @win0v = Reg::WINV.new 0
@ -454,25 +454,13 @@ module GBA
end end
# SIZES[SHAPE][SIZE] # SIZES[SHAPE][SIZE]
SIZES = [ SIZES = Slice[
[ # square # square
{8, 8}, Slice[{8, 8}, {16, 16}, {32, 32}, {64, 64}],
{16, 16}, # horizontal rectangle
{32, 32}, Slice[{16, 8}, {32, 8}, {32, 16}, {64, 32}],
{64, 64}, # vertical rectangle
], Slice[{8, 16}, {8, 32}, {16, 32}, {32, 64}],
[ # horizontal rectangle
{16, 8},
{32, 8},
{32, 16},
{64, 32},
],
[ # vertical rectangle
{8, 16},
{8, 32},
{16, 32},
{32, 64},
],
] ]
record Sprite, attr0 : UInt16, attr1 : UInt16, attr2 : UInt16, aff_param : Int16 do record Sprite, attr0 : UInt16, attr1 : UInt16, attr2 : UInt16, aff_param : Int16 do

View file

@ -1,19 +1,21 @@
module GBA module GBA
class Timer class Timer
PERIODS = [1, 64, 256, 1024] PERIODS = Slice[1, 64, 256, 1024]
EVENT_TYPES = [Scheduler::EventType::Timer0, Scheduler::EventType::Timer1, EVENT_TYPES = Slice[
Scheduler::EventType::Timer2, Scheduler::EventType::Timer3] Scheduler::EventType::Timer0, Scheduler::EventType::Timer1,
Scheduler::EventType::Timer2, Scheduler::EventType::Timer3]
@interrupt_flags : Array(Proc(Nil)) @interrupt_flags : Slice(Proc(Nil))
def initialize(@gba : GBA) def initialize(@gba : GBA)
@tmcnt = Array(Reg::TMCNT).new 4 { Reg::TMCNT.new 0 } # control registers @tmcnt = Slice(Reg::TMCNT).new 4 { Reg::TMCNT.new 0 } # control registers
@tmd = Array(UInt16).new 4, 0 # reload values @tmd = Slice(UInt16).new 4, 0 # reload values
@tm = Array(UInt16).new 4, 0 # counted values @tm = Slice(UInt16).new 4, 0 # counted values
@cycle_enabled = Array(UInt64).new 4, 0 # cycle that the timer was enabled @cycle_enabled = Slice(UInt64).new 4, 0 # cycle that the timer was enabled
@events = Array(Proc(Nil)).new 4 { |i| overflow i } # overflow closures for each timer @events = Slice(Proc(Nil)).new 4 { |i| overflow i } # overflow closures for each timer
@interrupt_flags = [->{ @gba.interrupts.reg_if.timer0 = true }, ->{ @gba.interrupts.reg_if.timer1 = true }, @interrupt_flags = Slice[
->{ @gba.interrupts.reg_if.timer2 = true }, ->{ @gba.interrupts.reg_if.timer3 = true }] ->{ @gba.interrupts.reg_if.timer0 = true }, ->{ @gba.interrupts.reg_if.timer1 = true },
->{ @gba.interrupts.reg_if.timer2 = true }, ->{ @gba.interrupts.reg_if.timer3 = true }]
end end
def overflow(num : Int) : Proc(Nil) def overflow(num : Int) : Proc(Nil)