abstract bg reg reads/writes, clear scanline before render, decouple scanline row

This commit is contained in:
Matthew Berry 2020-12-31 00:07:12 -08:00
parent 8e221d221e
commit f1f59c3ce6
2 changed files with 240 additions and 261 deletions

View file

@ -1,147 +1,26 @@
class PPU
class DISPCNT < BitField(UInt16)
bool obj_window_display
bool window_1_display
bool window_0_display
bool screen_display_obj
bool screen_display_bg3
bool screen_display_bg2
bool screen_display_bg1
bool screen_display_bg0
bool forced_blank # (1=Allow access to VRAM,Palette,OAM)
bool obj_character_vram_mapping # (0=Two dimensional, 1=One dimensional)
bool hblank_interval_free # (1=Allow access to OAM during H-Blank)
bool display_frame_select # (0-1=Frame 0-1) (for BG Modes 4,5 only)
bool reserved_for_bios, lock: true
num bg_mode, 3 # (0-5=Video Mode 0-5, 6-7=Prohibited)
end
class DISPSTAT < BitField(UInt16)
num vcount_setting, 8
num not_used, 2
bool vcounter_irq_enable
bool hblank_irq_enable
bool vblank_irq_enable
bool vcounter, lock: true
bool hblank, lock: true
bool vblank, lock: true
end
class BGCNT < BitField(UInt16)
num screen_size, 2
bool affine_wrap
num screen_base_block, 5
bool color_mode
bool mosaic
num not_used, 2, lock: true
num character_base_block, 2
num priority, 2
end
class BGOFS < BitField(UInt16)
num not_used, 7, lock: true
num offset, 9
end
class WINH < BitField(UInt16)
num x1, 8
num x2, 8
end
class WINV < BitField(UInt16)
num y1, 8
num y2, 8
end
class WININ < BitField(UInt16)
num not_used_1, 2, lock: true
bool window_1_color_special_effect
bool window_1_obj_enable
num window_1_enable_bits, 4
num not_used_0, 2, lock: true
bool window_0_color_special_effect
bool window_0_obj_enable
num window_0_enable_bits, 4
end
class WINOUT < BitField(UInt16)
num not_used_obj, 2, lock: true
bool obj_window_color_special_effect
bool obj_window_obj_enable
num obj_window_enable_bits, 4
num not_used_outside, 2, lock: true
bool outside_color_special_effect
bool outside_obj_enable
num outside_enable_bits, 4
end
class MOSAIC < BitField(UInt16)
num obj_mosiac_v_size, 4
num obj_mosiac_h_size, 4
num bg_mosiac_v_size, 4
num bg_mosiac_h_size, 4
end
class BLDCNT < BitField(UInt16)
num not_used, 2, lock: true
bool bd_2nd_target_pixel
bool obj_2nd_target_pixel
bool bg3_2nd_target_pixel
bool bg2_2nd_target_pixel
bool bg1_2nd_target_pixel
bool bg0_2nd_target_pixel
num color_special_effect, 2
bool bd_1st_target_pixel
bool obj_1st_target_pixel
bool bg3_1st_target_pixel
bool bg2_1st_target_pixel
bool bg1_1st_target_pixel
bool bg0_1st_target_pixel
end
class BLDALPHA < BitField(UInt16)
num not_used_13_15, 3, lock: true
num evb_coefficient, 5
num not_used_5_7, 3, lock: true
num eva_coefficient, 5
end
class BLDY < BitField(UInt16)
num not_used, 11, lock: true
num evy_coefficient, 5
end
@framebuffer : Slice(UInt16) = Slice(UInt16).new 0x9600 # framebuffer as 16-bit xBBBBBGGGGGRRRRR
getter pram = Bytes.new 0x400
getter vram = Bytes.new 0x18000
getter oam = Bytes.new 0x400
getter dispcnt : DISPCNT = DISPCNT.new 0
getter dispstat : DISPSTAT = DISPSTAT.new 0
getter dispcnt = Reg::DISPCNT.new 0
getter dispstat = Reg::DISPSTAT.new 0
getter vcount : UInt16 = 0x0000_u16
getter bg0cnt : BGCNT = BGCNT.new 0
getter bg1cnt : BGCNT = BGCNT.new 0
getter bg2cnt : BGCNT = BGCNT.new 0
getter bg3cnt : BGCNT = BGCNT.new 0
getter bg0hofs : BGOFS = BGOFS.new 0
getter bg0vofs : BGOFS = BGOFS.new 0
getter bg1hofs : BGOFS = BGOFS.new 0
getter bg1vofs : BGOFS = BGOFS.new 0
getter bg2hofs : BGOFS = BGOFS.new 0
getter bg2vofs : BGOFS = BGOFS.new 0
getter bg3hofs : BGOFS = BGOFS.new 0
getter bg3vofs : BGOFS = BGOFS.new 0
getter win0h : WINH = WINH.new 0
getter win1h : WINH = WINH.new 0
getter win0V : WINV = WINV.new 0
getter win1V : WINV = WINV.new 0
getter winin : WININ = WININ.new 0
getter winout : WINOUT = WINOUT.new 0
getter mosaic : MOSAIC = MOSAIC.new 0
getter bldcnt : BLDCNT = BLDCNT.new 0
getter bldalpha : BLDALPHA = BLDALPHA.new 0
getter bldy : BLDY = BLDY.new 0
getter bgcnt = Array(Reg::BGCNT).new 4 { Reg::BGCNT.new 0 }
getter bghofs = Array(Reg::BGOFS).new 4 { Reg::BGOFS.new 0 }
getter bgvofs = Array(Reg::BGOFS).new 4 { Reg::BGOFS.new 0 }
getter win0h = Reg::WINH.new 0
getter win1h = Reg::WINH.new 0
getter win0V = Reg::WINV.new 0
getter win1V = Reg::WINV.new 0
getter winin = Reg::WININ.new 0
getter winout = Reg::WINOUT.new 0
getter mosaic = Reg::MOSAIC.new 0
getter bldcnt = Reg::BLDCNT.new 0
getter bldalpha = Reg::BLDALPHA.new 0
getter bldy = Reg::BLDY.new 0
def initialize(@gba : GBA)
start_line
@ -186,34 +65,36 @@ class PPU
def se_index(tx : Int, ty : Int, screen_size : Int) : Int
n = tx + ty * 32
n += 0x03E0 if tx >= 32
n += 0x0400 if ty >= 32 && @bg0cnt.screen_size == 0b11
n += 0x0400 if ty >= 32 && screen_size == 0b11
n
end
def scanline : Nil
row = @vcount
row = @vcount.to_u32
row_base = 240 * row
scanline = @framebuffer + row_base
scanline.to_unsafe.clear(240)
case @dispcnt.bg_mode
when 0
# todo handle all bg layers
tw, th = case @bg0cnt.screen_size
tw, th = case @bgcnt[0].screen_size
when 0b00 then {32, 32} # 32x32
when 0b01 then {64, 32} # 64x32
when 0b10 then {32, 64} # 32x64
when 0b11 then {64, 64} # 64x64
else raise "Impossible bgcnt screen size: #{@bg0cnt.screen_size}"
else raise "Impossible bgcnt screen size: #{@bgcnt[0].screen_size}"
end
# todo actually handle different sizes
screen_base = 0x800_u32 * @bg0cnt.screen_base_block
character_base = @bg0cnt.character_base_block * 0x4000
effective_row = (row + @bg0vofs.value) % (th << 3)
screen_base = 0x800_u32 * @bgcnt[0].screen_base_block
character_base = @bgcnt[0].character_base_block * 0x4000
effective_row = (row + @bgvofs[0].value) % (th << 3)
ty = effective_row >> 3
240.times do |col|
effective_col = (col + @bg0hofs.value) % (tw << 3)
effective_col = (col + @bghofs[0].value) % (tw << 3)
tx = effective_col >> 3
se_idx = se_index(tx, ty, @bg0cnt.screen_size)
se_idx = se_index(tx, ty, @bgcnt[0].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)
@ -221,42 +102,40 @@ class PPU
y = (effective_row & 7) ^ (7 * (screen_entry >> 11 & 1))
x = (effective_col & 7) ^ (7 * (screen_entry >> 10 & 1))
if @bg0cnt.color_mode # 8bpp
if @bgcnt[0].color_mode # 8bpp
abort "todo 8bpp"
else # 4bpp
palettes = @vram[character_base + tile_id * 0x20 + y * 4 + (x >> 1)]
pal_idx = (palette_bank << 4) + ((palettes >> ((x & 1) * 4)) & 0xF)
end
idx = row_base + col
@framebuffer[idx] = @pram.to_unsafe.as(UInt16*)[pal_idx]
scanline[col] = @pram.to_unsafe.as(UInt16*)[pal_idx]
end
when 1, 2
puts "Unsupported background mode: #{@dispcnt.bg_mode}"
when 3
240.times do |col|
idx = row_base + col
@framebuffer[idx] = @vram.to_unsafe.as(UInt16*)[idx]
scanline[col] = @vram.to_unsafe.as(UInt16*)[idx]
end
when 4
base = @dispcnt.display_frame_select ? 0xA000 : 0
240.times do |col|
idx = row_base + col
pal_idx = @vram[base + idx]
@framebuffer[idx] = @pram.to_unsafe.as(UInt16*)[pal_idx]
pal_idx = @vram[base + row_base + col]
scanline[col] = @pram.to_unsafe.as(UInt16*)[pal_idx]
end
when 5
base = @dispcnt.display_frame_select ? 0xA000 : 0
background_color = @pram.to_unsafe.as(UInt16*)[0]
if @vcount < 128
160.times do |col|
@framebuffer[row_base + col] = (@vram + base).to_unsafe.as(UInt16*)[row * 160 + col]
scanline[col] = (@vram + base).to_unsafe.as(UInt16*)[row * 160 + col]
end
160.to 239 do |col|
@framebuffer[row_base + col] = background_color
scanline[col] = background_color
end
else
240.times do |col|
@framebuffer[row_base + col] = background_color
scanline[col] = background_color
end
end
else abort "Invalid background mode: #{@dispcnt.bg_mode}"
@ -265,117 +144,77 @@ class PPU
def read_io(io_addr : Int) : Byte
case io_addr
when 0x000 then 0xFF_u8 & @dispcnt.value
when 0x001 then 0xFF_u8 & @dispcnt.value >> 8
when 0x002 then 0xFF_u8 # todo green swap
when 0x003 then 0xFF_u8 # todo green swap
when 0x004 then 0xFF_u8 & @dispstat.value
when 0x005 then 0xFF_u8 & @dispstat.value >> 8
when 0x006 then 0xFF_u8 & @vcount
when 0x007 then 0xFF_u8 & @vcount >> 8
when 0x008 then 0xFF_u8 & @bg0cnt.value
when 0x009 then 0xFF_u8 & @bg0cnt.value >> 8
when 0x00A then 0xFF_u8 & @bg1cnt.value
when 0x00B then 0xFF_u8 & @bg1cnt.value >> 8
when 0x00C then 0xFF_u8 & @bg2cnt.value
when 0x00D then 0xFF_u8 & @bg2cnt.value >> 8
when 0x00E then 0xFF_u8 & @bg3cnt.value
when 0x00F then 0xFF_u8 & @bg3cnt.value >> 8
when 0x010 then 0xFF_u8 & @bg0hofs.value
when 0x011 then 0xFF_u8 & @bg0hofs.value >> 8
when 0x012 then 0xFF_u8 & @bg0vofs.value
when 0x013 then 0xFF_u8 & @bg0vofs.value >> 8
when 0x014 then 0xFF_u8 & @bg1hofs.value
when 0x015 then 0xFF_u8 & @bg1hofs.value >> 8
when 0x016 then 0xFF_u8 & @bg1vofs.value
when 0x017 then 0xFF_u8 & @bg1vofs.value >> 8
when 0x018 then 0xFF_u8 & @bg2hofs.value
when 0x019 then 0xFF_u8 & @bg2hofs.value >> 8
when 0x01A then 0xFF_u8 & @bg2vofs.value
when 0x01B then 0xFF_u8 & @bg2vofs.value >> 8
when 0x01C then 0xFF_u8 & @bg3hofs.value
when 0x01D then 0xFF_u8 & @bg3hofs.value >> 8
when 0x01E then 0xFF_u8 & @bg3vofs.value
when 0x01F then 0xFF_u8 & @bg3vofs.value >> 8
when 0x040 then 0xFF_u8 & @win0h.value
when 0x041 then 0xFF_u8 & @win0h.value >> 8
when 0x042 then 0xFF_u8 & @win1h.value
when 0x043 then 0xFF_u8 & @win1h.value >> 8
when 0x044 then 0xFF_u8 & @win0V.value
when 0x045 then 0xFF_u8 & @win0V.value >> 8
when 0x046 then 0xFF_u8 & @win1V.value
when 0x047 then 0xFF_u8 & @win1V.value >> 8
when 0x048 then 0xFF_u8 & @winin.value
when 0x049 then 0xFF_u8 & @winin.value >> 8
when 0x04A then 0xFF_u8 & @winout.value
when 0x04B then 0xFF_u8 & @winout.value >> 8
when 0x04C then 0xFF_u8 & @mosaic.value
when 0x04D then 0xFF_u8 & @mosaic.value >> 8
when 0x050 then 0xFF_u8 & @bldcnt.value
when 0x051 then 0xFF_u8 & @bldcnt.value >> 8
when 0x052 then 0xFF_u8 & @bldalpha.value
when 0x053 then 0xFF_u8 & @bldalpha.value >> 8
when 0x054 then 0xFF_u8 & @bldy.value
when 0x055 then 0xFF_u8 & @bldy.value >> 8
else abort "Unmapped PPU read ~ addr:#{hex_str io_addr.to_u8}"
when 0x000..0x001 then @dispcnt.read_byte(io_addr & 1)
when 0x002..0x003 then 0_u8 # todo green swap
when 0x004..0x005 then @dispstat.read_byte(io_addr & 1)
when 0x006..0x007 then (@vcount >> (8 * (io_addr & 1))).to_u8!
when 0x008..0x00F then @bgcnt[(io_addr - 0x008) >> 1].read_byte(io_addr & 1)
when 0x010..0x01F
bg_num = (io_addr - 0x010) >> 2
if bit?(io_addr, 1)
@bgvofs[bg_num].read_byte(io_addr & 1)
else
@bghofs[bg_num].read_byte(io_addr & 1)
end
when 0x040 then 0xFF_u8 & @win0h.value
when 0x041 then 0xFF_u8 & @win0h.value >> 8
when 0x042 then 0xFF_u8 & @win1h.value
when 0x043 then 0xFF_u8 & @win1h.value >> 8
when 0x044 then 0xFF_u8 & @win0V.value
when 0x045 then 0xFF_u8 & @win0V.value >> 8
when 0x046 then 0xFF_u8 & @win1V.value
when 0x047 then 0xFF_u8 & @win1V.value >> 8
when 0x048 then 0xFF_u8 & @winin.value
when 0x049 then 0xFF_u8 & @winin.value >> 8
when 0x04A then 0xFF_u8 & @winout.value
when 0x04B then 0xFF_u8 & @winout.value >> 8
when 0x04C then 0xFF_u8 & @mosaic.value
when 0x04D then 0xFF_u8 & @mosaic.value >> 8
when 0x050 then 0xFF_u8 & @bldcnt.value
when 0x051 then 0xFF_u8 & @bldcnt.value >> 8
when 0x052 then 0xFF_u8 & @bldalpha.value
when 0x053 then 0xFF_u8 & @bldalpha.value >> 8
when 0x054 then 0xFF_u8 & @bldy.value
when 0x055 then 0xFF_u8 & @bldy.value >> 8
else abort "Unmapped PPU read ~ addr:#{hex_str io_addr.to_u8}"
end
end
def write_io(io_addr : Int, value : Byte) : Nil
case io_addr
when 0x000 then @dispcnt.value = (@dispcnt.value & 0xFF00) | value
when 0x001 then @dispcnt.value = (@dispcnt.value & 0x00FF) | value.to_u16 << 8
when 0x002 # undocumented - green swap
when 0x003 # undocumented - green swap
when 0x004 then @dispstat.value = (@dispstat.value & 0xFF00) | value
when 0x005 then @dispstat.value = (@dispstat.value & 0x00FF) | value.to_u16 << 8
when 0x006 # vcount
when 0x007 # vcount
when 0x008 then @bg0cnt.value = (@bg0cnt.value & 0xFF00) | value
when 0x009 then @bg0cnt.value = (@bg0cnt.value & 0x00FF) | value.to_u16 << 8
when 0x00A then @bg1cnt.value = (@bg1cnt.value & 0xFF00) | value
when 0x00B then @bg1cnt.value = (@bg1cnt.value & 0x00FF) | value.to_u16 << 8
when 0x00C then @bg2cnt.value = (@bg2cnt.value & 0xFF00) | value
when 0x00D then @bg2cnt.value = (@bg2cnt.value & 0x00FF) | value.to_u16 << 8
when 0x00E then @bg3cnt.value = (@bg3cnt.value & 0xFF00) | value
when 0x00F then @bg3cnt.value = (@bg3cnt.value & 0x00FF) | value.to_u16 << 8
when 0x010 then @bg0hofs.value = (@bg0hofs.value & 0xFF00) | value
when 0x011 then @bg0hofs.value = (@bg0hofs.value & 0x00FF) | value.to_u16 << 8
when 0x012 then @bg0vofs.value = (@bg0vofs.value & 0xFF00) | value
when 0x013 then @bg0vofs.value = (@bg0vofs.value & 0x00FF) | value.to_u16 << 8
when 0x014 then @bg1hofs.value = (@bg1hofs.value & 0xFF00) | value
when 0x015 then @bg1hofs.value = (@bg1hofs.value & 0x00FF) | value.to_u16 << 8
when 0x016 then @bg1vofs.value = (@bg1vofs.value & 0xFF00) | value
when 0x017 then @bg1vofs.value = (@bg1vofs.value & 0x00FF) | value.to_u16 << 8
when 0x018 then @bg2hofs.value = (@bg2hofs.value & 0xFF00) | value
when 0x019 then @bg2hofs.value = (@bg2hofs.value & 0x00FF) | value.to_u16 << 8
when 0x01A then @bg2vofs.value = (@bg2vofs.value & 0xFF00) | value
when 0x01B then @bg2vofs.value = (@bg2vofs.value & 0x00FF) | value.to_u16 << 8
when 0x01C then @bg3hofs.value = (@bg3hofs.value & 0xFF00) | value
when 0x01D then @bg3hofs.value = (@bg3hofs.value & 0x00FF) | value.to_u16 << 8
when 0x01E then @bg3vofs.value = (@bg3vofs.value & 0xFF00) | value
when 0x01F then @bg3vofs.value = (@bg3vofs.value & 0x00FF) | value.to_u16 << 8
when 0x040 then @win0h.value = (@win0h.value & 0xFF00) | value
when 0x041 then @win0h.value = (@win0h.value & 0x00FF) | value.to_u16 << 8
when 0x042 then @win1h.value = (@win1h.value & 0xFF00) | value
when 0x043 then @win1h.value = (@win1h.value & 0x00FF) | value.to_u16 << 8
when 0x044 then @win0V.value = (@win0V.value & 0xFF00) | value
when 0x045 then @win0V.value = (@win0V.value & 0x00FF) | value.to_u16 << 8
when 0x046 then @win1V.value = (@win1V.value & 0xFF00) | value
when 0x047 then @win1V.value = (@win1V.value & 0x00FF) | value.to_u16 << 8
when 0x048 then @winin.value = (@winin.value & 0xFF00) | value
when 0x049 then @winin.value = (@winin.value & 0x00FF) | value.to_u16 << 8
when 0x04A then @winout.value = (@winout.value & 0xFF00) | value
when 0x04B then @winout.value = (@winout.value & 0x00FF) | value.to_u16 << 8
when 0x04C then @mosaic.value = (@mosaic.value & 0xFF00) | value
when 0x04D then @mosaic.value = (@mosaic.value & 0x00FF) | value.to_u16 << 8
when 0x050 then @bldcnt.value = (@bldcnt.value & 0xFF00) | value
when 0x051 then @bldcnt.value = (@bldcnt.value & 0x00FF) | value.to_u16 << 8
when 0x052 then @bldalpha.value = (@bldalpha.value & 0xFF00) | value
when 0x053 then @bldalpha.value = (@bldalpha.value & 0x00FF) | value.to_u16 << 8
when 0x054 then @bldy.value = (@bldy.value & 0xFF00) | value
when 0x055 then @bldy.value = (@bldy.value & 0x00FF) | value.to_u16 << 8
else puts "Unmapped PPU write ~ addr:#{hex_str io_addr.to_u8}, val:#{value}".colorize(:yellow)
when 0x000..0x001 then @dispcnt.write_byte(io_addr & 1, value)
when 0x002..0x003 # undocumented - green swap
when 0x004..0x005 then @dispstat.write_byte(io_addr & 1, value)
when 0x006..0x007 # vcount
when 0x008..0x00F then @bgcnt[(io_addr - 0x008) >> 1].write_byte(io_addr & 1, value)
when 0x010..0x01F
bg_num = (io_addr - 0x010) >> 2
if bit?(io_addr, 1)
@bgvofs[bg_num].write_byte(io_addr & 1, value)
else
@bghofs[bg_num].write_byte(io_addr & 1, value)
end
when 0x040 then @win0h.value = (@win0h.value & 0xFF00) | value
when 0x041 then @win0h.value = (@win0h.value & 0x00FF) | value.to_u16 << 8
when 0x042 then @win1h.value = (@win1h.value & 0xFF00) | value
when 0x043 then @win1h.value = (@win1h.value & 0x00FF) | value.to_u16 << 8
when 0x044 then @win0V.value = (@win0V.value & 0xFF00) | value
when 0x045 then @win0V.value = (@win0V.value & 0x00FF) | value.to_u16 << 8
when 0x046 then @win1V.value = (@win1V.value & 0xFF00) | value
when 0x047 then @win1V.value = (@win1V.value & 0x00FF) | value.to_u16 << 8
when 0x048 then @winin.value = (@winin.value & 0xFF00) | value
when 0x049 then @winin.value = (@winin.value & 0x00FF) | value.to_u16 << 8
when 0x04A then @winout.value = (@winout.value & 0xFF00) | value
when 0x04B then @winout.value = (@winout.value & 0x00FF) | value.to_u16 << 8
when 0x04C then @mosaic.value = (@mosaic.value & 0xFF00) | value
when 0x04D then @mosaic.value = (@mosaic.value & 0x00FF) | value.to_u16 << 8
when 0x050 then @bldcnt.value = (@bldcnt.value & 0xFF00) | value
when 0x051 then @bldcnt.value = (@bldcnt.value & 0x00FF) | value.to_u16 << 8
when 0x052 then @bldalpha.value = (@bldalpha.value & 0xFF00) | value
when 0x053 then @bldalpha.value = (@bldalpha.value & 0x00FF) | value.to_u16 << 8
when 0x054 then @bldy.value = (@bldy.value & 0xFF00) | value
when 0x055 then @bldy.value = (@bldy.value & 0x00FF) | value.to_u16 << 8
else puts "Unmapped PPU write ~ addr:#{hex_str io_addr.to_u8}, val:#{value}".colorize(:yellow)
end
end
end

View file

@ -1,4 +1,17 @@
module Reg
module Base16
def read_byte(byte_num : Int) : Byte
(@value >> (8 * byte_num)).to_u8!
end
def write_byte(byte_num : Int, byte : Byte) : Byte
shift = 8 * byte_num
mask = ~(0xFF_u16 << shift)
@value = (@value & mask) | byte.to_u16 << shift
byte
end
end
####################
# APU
@ -38,4 +51,131 @@ module Reg
num bias_level, 9
bool not_used_2
end
####################
# PPU
class DISPCNT < BitField(UInt16)
include Base16
bool obj_window_display
bool window_1_display
bool window_0_display
bool screen_display_obj
bool screen_display_bg3
bool screen_display_bg2
bool screen_display_bg1
bool screen_display_bg0
bool forced_blank # (1=Allow access to VRAM,Palette,OAM)
bool obj_character_vram_mapping # (0=Two dimensional, 1=One dimensional)
bool hblank_interval_free # (1=Allow access to OAM during H-Blank)
bool display_frame_select # (0-1=Frame 0-1) (for BG Modes 4,5 only)
bool reserved_for_bios, lock: true
num bg_mode, 3 # (0-5=Video Mode 0-5, 6-7=Prohibited)
end
class DISPSTAT < BitField(UInt16)
include Base16
num vcount_setting, 8
num not_used, 2
bool vcounter_irq_enable
bool hblank_irq_enable
bool vblank_irq_enable
bool vcounter, lock: true
bool hblank, lock: true
bool vblank, lock: true
end
class BGCNT < BitField(UInt16)
include Base16
num screen_size, 2
bool affine_wrap
num screen_base_block, 5
bool color_mode
bool mosaic
num not_used, 2, lock: true
num character_base_block, 2
num priority, 2
end
class BGOFS < BitField(UInt16)
include Base16
num not_used, 7, lock: true
num offset, 9
end
class WINH < BitField(UInt16)
include Base16
num x1, 8
num x2, 8
end
class WINV < BitField(UInt16)
include Base16
num y1, 8
num y2, 8
end
class WININ < BitField(UInt16)
include Base16
num not_used_1, 2, lock: true
bool window_1_color_special_effect
bool window_1_obj_enable
num window_1_enable_bits, 4
num not_used_0, 2, lock: true
bool window_0_color_special_effect
bool window_0_obj_enable
num window_0_enable_bits, 4
end
class WINOUT < BitField(UInt16)
include Base16
num not_used_obj, 2, lock: true
bool obj_window_color_special_effect
bool obj_window_obj_enable
num obj_window_enable_bits, 4
num not_used_outside, 2, lock: true
bool outside_color_special_effect
bool outside_obj_enable
num outside_enable_bits, 4
end
class MOSAIC < BitField(UInt16)
include Base16
num obj_mosiac_v_size, 4
num obj_mosiac_h_size, 4
num bg_mosiac_v_size, 4
num bg_mosiac_h_size, 4
end
class BLDCNT < BitField(UInt16)
include Base16
num not_used, 2, lock: true
bool bd_2nd_target_pixel
bool obj_2nd_target_pixel
bool bg3_2nd_target_pixel
bool bg2_2nd_target_pixel
bool bg1_2nd_target_pixel
bool bg0_2nd_target_pixel
num color_special_effect, 2
bool bd_1st_target_pixel
bool obj_1st_target_pixel
bool bg3_1st_target_pixel
bool bg2_1st_target_pixel
bool bg1_1st_target_pixel
bool bg0_1st_target_pixel
end
class BLDALPHA < BitField(UInt16)
include Base16
num not_used_13_15, 3, lock: true
num evb_coefficient, 5
num not_used_5_7, 3, lock: true
num eva_coefficient, 5
end
class BLDY < BitField(UInt16)
include Base16
num not_used, 11, lock: true
num evy_coefficient, 5
end
end