mirror of
https://github.com/mattrberry/crab.git
synced 2024-11-16 19:49:30 +01:00
abstract bg reg reads/writes, clear scanline before render, decouple scanline row
This commit is contained in:
parent
8e221d221e
commit
f1f59c3ce6
2 changed files with 240 additions and 261 deletions
361
src/crab/ppu.cr
361
src/crab/ppu.cr
|
@ -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
|
||||
|
|
140
src/crab/reg.cr
140
src/crab/reg.cr
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue