Class: Amaterasu::GameBoy::Ppu
- Inherits:
-
Object
- Object
- Amaterasu::GameBoy::Ppu
- Includes:
- Utils::BitOps
- Defined in:
- lib/amaterasu/game_boy/ppu.rb,
lib/amaterasu/game_boy/ppu/modes.rb,
lib/amaterasu/game_boy/ppu/registers.rb,
lib/amaterasu/game_boy/ppu/modes/h_blank.rb,
lib/amaterasu/game_boy/ppu/modes/v_blank.rb,
lib/amaterasu/game_boy/ppu/modes/disabled.rb,
lib/amaterasu/game_boy/ppu/modes/oam_scan.rb,
lib/amaterasu/game_boy/ppu/modes/rendering.rb,
lib/amaterasu/game_boy/ppu/registers/lcd_status.rb,
lib/amaterasu/game_boy/ppu/registers/lcd_control.rb,
lib/amaterasu/game_boy/ppu/modes/rendering/pixel_fifo.rb,
lib/amaterasu/game_boy/ppu/modes/rendering/pixel_emitter.rb,
lib/amaterasu/game_boy/ppu/modes/rendering/bg_win_fetcher.rb,
lib/amaterasu/game_boy/ppu/modes/rendering/sprite_fetcher.rb,
sig/akane/game_boy/ppu.rbs,
sig/akane/game_boy/ppu/modes.rbs,
sig/akane/game_boy/ppu/modes/h_blank.rbs,
sig/akane/game_boy/ppu/modes/v_blank.rbs,
sig/akane/game_boy/ppu/modes/disabled.rbs,
sig/akane/game_boy/ppu/modes/oam_scan.rbs,
sig/akane/game_boy/ppu/modes/rendering.rbs
Overview
This class models the Pixel Processing Unit from the Original Game Boy.
The Ppu outputs a 160x144 pixel framebuffer each frame. This framebuffer will be used by the chosen Renderer to display the graphics. The Ppu should not care about how the pixels are rendered, just output them.
Specifications:
- The frame consists of 154 scanlines, 144 visible + 10 vblank (Cpu can access VRAM).
- Scanlines are drawn from top to bottom, left to right.
- Updating registers mid-frame can cause effects since the pixels are still being drawn.
- Dots per scanline = 456 t-cycles (114 m-cycles) -> Hardware spec.
- Dots per frame = 154 * 456 = 70_224 t-cycles (17_556 m-cycles).
- OAM search takes 80 dots (20 m-cycles) -> 40 sprites, 2 dots each.
Defined Under Namespace
Modules: Modes Classes: Registers
Constant Summary collapse
- PIXELS_PER_SCANLINE =
160- VISIBLE_SCANLINES =
144- TOTAL_SCANLINES =
154- DOTS_PER_SCANLINE =
456- MAX_SPRITES_PER_SCANLINE =
10- MODES =
- DOTS_PER_OAM_SCAN =
- WINDOW_TILE_MAPS =
- BG_TILE_MAPS =
- CONSOLE_CHARS =
Instance Attribute Summary collapse
-
#bgp ⇒ Integer
readonly
Returns the value of attribute bgp.
-
#dma ⇒ Integer
readonly
Returns the value of attribute dma.
-
#dots ⇒ Object
readonly
Returns the value of attribute dots.
-
#framebuffer ⇒ Object
readonly
Returns the value of attribute framebuffer.
-
#lcdc ⇒ Integer
readonly
Returns the value of attribute lcdc.
-
#ly ⇒ Integer
readonly
Returns the value of attribute ly.
-
#lyc ⇒ Integer
readonly
Returns the value of attribute lyc.
-
#obp0 ⇒ Integer
readonly
Returns the value of attribute obp0.
-
#obp1 ⇒ Integer
readonly
Returns the value of attribute obp1.
-
#registers ⇒ Object
readonly
Returns the value of attribute registers.
-
#scx ⇒ Integer
readonly
Returns the value of attribute scx.
-
#scy ⇒ Integer
readonly
Returns the value of attribute scy.
-
#sprite_buffer ⇒ Object
readonly
Returns the value of attribute sprite_buffer.
-
#window_y_count ⇒ Object
Returns the value of attribute window_y_count.
-
#wx ⇒ Integer
readonly
Returns the value of attribute wx.
-
#wy ⇒ Integer
readonly
Returns the value of attribute wy.
-
#wy_eq_ly ⇒ Object
Returns the value of attribute wy_eq_ly.
Instance Method Summary collapse
- #addressing_mode ⇒ Integer
- #bg_tile_map ⇒ TileMap
- #bg_win_tile_data ⇒ TileData
- #bgp ⇒ void readonly
-
#draw_frame ⇒ Object
Delegates the draw to the chosen Renderer.
- #draw_scanline ⇒ void
- #fetch_sprite_at(index) ⇒ Object
- #increment_ly ⇒ Object
-
#initialize(vram, oam, display, interrupts, skip_boot_rom: true, trace_ppu: false) ⇒ Ppu
constructor
A new instance of Ppu.
- #lcd_off? ⇒ Boolean
- #lcd_on? ⇒ Boolean
- #lcdc ⇒ void readonly
- #ly_compare ⇒ Object
- #lyc ⇒ void readonly
- #obj_tile_data ⇒ TileData
- #obp0 ⇒ void readonly
- #obp1 ⇒ void readonly
-
#read_oam(address:) ⇒ Integer
Returns a 8-bit value stored in OAM in a given address.
-
#read_vram(address:) ⇒ Integer
Returns a 8-bit value stored in VRAM in a given address.
- #request_interrupt(interrupt_type) ⇒ Object
- #reset_for_scanline ⇒ Object
-
#reset_states ⇒ Object
Restarts the rendering pipeline state.
- #scx ⇒ void readonly
- #scy ⇒ void readonly
-
#set_mode(mode) ⇒ Object
Sets the current PPU mode to be ticked.
- #shade ⇒ Integer
- #stat ⇒ Integer
- #stat= ⇒ void
-
#tick ⇒ void
Core PPU state machine.
- #trace ⇒ void
- #window_tile_map ⇒ TileMap
-
#write_oam(address:, value:) ⇒ void
Stores a 8-bit value in OAM in a given address.
-
#write_vram(address:, value:) ⇒ void
Stores a 8-bit value in VRAM in a given address.
- #wx ⇒ void readonly
- #wy ⇒ void readonly
Methods included from Utils::BitOps
bit, clear_bit, #self?.bit, #self?.clear_bit, #self?.set_bit, set_bit
Constructor Details
#initialize(vram, oam, display, interrupts, skip_boot_rom: true, trace_ppu: false) ⇒ Ppu
Returns a new instance of Ppu.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 34 def initialize( vram, oam, display, interrupts, skip_boot_rom: true, trace_ppu: false ) @vram = vram @oam = oam @display = display @interrupts = interrupts @trace_ppu = trace_ppu @registers = Registers.new(interrupts, skip_boot_rom:) @framebuffer = Array.new @sprite_buffer = Array.new(MAX_SPRITES_PER_SCANLINE).clear @modes = Modes.build_hash(self) @mode = set_mode(:disabled) @wy_eq_ly = false @window_y_count = 0 @dots = 0 end |
Instance Attribute Details
#bgp ⇒ Integer (readonly)
Returns the value of attribute bgp.
20 21 22 |
# File 'sig/akane/game_boy/ppu.rbs', line 20 def bgp @bgp end |
#dma ⇒ Integer (readonly)
Returns the value of attribute dma.
19 20 21 |
# File 'sig/akane/game_boy/ppu.rbs', line 19 def dma @dma end |
#dots ⇒ Object (readonly)
Returns the value of attribute dots.
29 30 31 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 29 def dots @dots end |
#framebuffer ⇒ Object (readonly)
Returns the value of attribute framebuffer.
29 30 31 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 29 def framebuffer @framebuffer end |
#lcdc ⇒ Integer (readonly)
Returns the value of attribute lcdc.
14 15 16 |
# File 'sig/akane/game_boy/ppu.rbs', line 14 def lcdc @lcdc end |
#ly ⇒ Integer (readonly)
Returns the value of attribute ly.
17 18 19 |
# File 'sig/akane/game_boy/ppu.rbs', line 17 def ly @ly end |
#lyc ⇒ Integer (readonly)
Returns the value of attribute lyc.
18 19 20 |
# File 'sig/akane/game_boy/ppu.rbs', line 18 def lyc @lyc end |
#obp0 ⇒ Integer (readonly)
Returns the value of attribute obp0.
21 22 23 |
# File 'sig/akane/game_boy/ppu.rbs', line 21 def obp0 @obp0 end |
#obp1 ⇒ Integer (readonly)
Returns the value of attribute obp1.
22 23 24 |
# File 'sig/akane/game_boy/ppu.rbs', line 22 def obp1 @obp1 end |
#registers ⇒ Object (readonly)
Returns the value of attribute registers.
29 30 31 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 29 def registers @registers end |
#scx ⇒ Integer (readonly)
Returns the value of attribute scx.
16 17 18 |
# File 'sig/akane/game_boy/ppu.rbs', line 16 def scx @scx end |
#scy ⇒ Integer (readonly)
Returns the value of attribute scy.
15 16 17 |
# File 'sig/akane/game_boy/ppu.rbs', line 15 def scy @scy end |
#sprite_buffer ⇒ Object (readonly)
Returns the value of attribute sprite_buffer.
29 30 31 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 29 def sprite_buffer @sprite_buffer end |
#window_y_count ⇒ Object
Returns the value of attribute window_y_count.
27 28 29 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 27 def window_y_count @window_y_count end |
#wx ⇒ Integer (readonly)
Returns the value of attribute wx.
24 25 26 |
# File 'sig/akane/game_boy/ppu.rbs', line 24 def wx @wx end |
#wy ⇒ Integer (readonly)
Returns the value of attribute wy.
23 24 25 |
# File 'sig/akane/game_boy/ppu.rbs', line 23 def wy @wy end |
#wy_eq_ly ⇒ Object
Returns the value of attribute wy_eq_ly.
27 28 29 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 27 def wy_eq_ly @wy_eq_ly end |
Instance Method Details
#addressing_mode ⇒ Integer
55 |
# File 'sig/akane/game_boy/ppu.rbs', line 55
def addressing_mode: () -> Integer
|
#bg_tile_map ⇒ TileMap
164 165 166 167 168 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 164 def bg_tile_map return @vram.tile_map_high if @registers.lcdc.bg_tile_map_high? @vram.tile_map_low end |
#bg_win_tile_data ⇒ TileData
171 172 173 174 175 176 177 178 179 180 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 171 def bg_win_tile_data @vram.tile_data.addressing_mode = if @registers.lcdc.tile_data_at_0x8000? :unsigned else :signed end @vram.tile_data end |
#bgp= ⇒ void (readonly)
This method returns an undefined value.
35 |
# File 'sig/akane/game_boy/ppu.rbs', line 35
def bgp=: (Integer value) -> void
|
#draw_frame ⇒ Object
Delegates the draw to the chosen Renderer.
98 99 100 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 98 def draw_frame @display&.draw(@framebuffer) end |
#draw_scanline ⇒ void
This method returns an undefined value.
49 |
# File 'sig/akane/game_boy/ppu.rbs', line 49
def draw_scanline: () -> void
|
#fetch_sprite_at(index) ⇒ Object
152 153 154 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 152 def fetch_sprite_at(index) @oam.sprite(index) end |
#increment_ly ⇒ Object
106 107 108 109 110 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 106 def increment_ly @registers.ly += 1 ly_compare end |
#lcd_off? ⇒ Boolean
52 |
# File 'sig/akane/game_boy/ppu.rbs', line 52
def lcd_off?: () -> bool
|
#lcd_on? ⇒ Boolean
51 |
# File 'sig/akane/game_boy/ppu.rbs', line 51
def lcd_on?: () -> bool
|
#lcdc= ⇒ void (readonly)
This method returns an undefined value.
30 |
# File 'sig/akane/game_boy/ppu.rbs', line 30
def lcdc=: (Integer value) -> void
|
#ly_compare ⇒ Object
112 113 114 115 116 117 118 119 120 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 112 def ly_compare if @registers.ly == @registers.lyc @registers.stat.set_lyc_bit else @registers.stat.clear_lyc_bit end # request_interrupt(:lcd_stat) if @registers.stat.rising_edge? end |
#lyc= ⇒ void (readonly)
This method returns an undefined value.
34 |
# File 'sig/akane/game_boy/ppu.rbs', line 34
def lyc=: (Integer value) -> void
|
#obj_tile_data ⇒ TileData
183 184 185 186 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 183 def obj_tile_data @vram.tile_data.addressing_mode = :unsigned @vram.tile_data end |
#obp0= ⇒ void (readonly)
This method returns an undefined value.
36 |
# File 'sig/akane/game_boy/ppu.rbs', line 36
def obp0=: (Integer value) -> void
|
#obp1= ⇒ void (readonly)
This method returns an undefined value.
37 |
# File 'sig/akane/game_boy/ppu.rbs', line 37
def obp1=: (Integer value) -> void
|
#read_oam(address:) ⇒ Integer
Returns a 8-bit value stored in OAM in a given address.
141 142 143 144 145 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 141 def read_oam(address:) return 0xFF if [@modes[:oam_scan], @modes[:rendering]].include?(@mode) @oam.read_byte(address:) end |
#read_vram(address:) ⇒ Integer
Returns a 8-bit value stored in VRAM in a given address.
126 127 128 129 130 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 126 def read_vram(address:) return 0xFF if @mode == @modes[:rendering] @vram.read_byte(address:) end |
#request_interrupt(interrupt_type) ⇒ Object
102 103 104 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 102 def request_interrupt(interrupt_type) @interrupts.request(interrupt_type) end |
#reset_for_scanline ⇒ Object
81 82 83 84 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 81 def reset_for_scanline @dots = 0 @sprite_buffer.clear unless @sprite_buffer.empty? end |
#reset_states ⇒ Object
Restarts the rendering pipeline state.
87 88 89 90 91 92 93 94 95 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 87 def reset_states reset_for_scanline @registers.ly = 0x00 @wy_eq_ly = false # here? @window_y_count = 0 @framebuffer.clear ly_compare end |
#scx= ⇒ void (readonly)
This method returns an undefined value.
33 |
# File 'sig/akane/game_boy/ppu.rbs', line 33
def scx=: (Integer value) -> void
|
#scy= ⇒ void (readonly)
This method returns an undefined value.
32 |
# File 'sig/akane/game_boy/ppu.rbs', line 32
def scy=: (Integer value) -> void
|
#set_mode(mode) ⇒ Object
Sets the current PPU mode to be ticked. Updates STAT Bits 1-0 that reads the current PPU mode.
73 74 75 76 77 78 79 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 73 def set_mode(mode) @mode = @modes[mode] @registers.stat.set_mode_bits(@mode.number) # request_interrupt(:lcd_stat) if @registers.stat.rising_edge? @mode end |
#shade ⇒ Integer
50 |
# File 'sig/akane/game_boy/ppu.rbs', line 50
def shade: (Integer color_index) -> Integer
|
#stat ⇒ Integer
28 |
# File 'sig/akane/game_boy/ppu.rbs', line 28
def stat: () -> Integer
|
#stat= ⇒ void
This method returns an undefined value.
31 |
# File 'sig/akane/game_boy/ppu.rbs', line 31
def stat=: (Integer value) -> void
|
#tick ⇒ void
This method returns an undefined value.
Core PPU state machine.
Each mode is responsible for its own logic and also switching to the next mode.
63 64 65 66 67 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 63 def tick @mode.tick log_state if @trace_ppu @dots += 1 end |
#trace ⇒ void
This method returns an undefined value.
56 |
# File 'sig/akane/game_boy/ppu.rbs', line 56
def trace: () -> void
|
#window_tile_map ⇒ TileMap
157 158 159 160 161 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 157 def window_tile_map return @vram.tile_map_high if @registers.lcdc.window_tile_map_high? @vram.tile_map_low end |
#write_oam(address:, value:) ⇒ void
This method returns an undefined value.
Stores a 8-bit value in OAM in a given address.
148 149 150 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 148 def write_oam(address:, value:) @oam.write_byte(address:, value:) end |
#write_vram(address:, value:) ⇒ void
This method returns an undefined value.
Stores a 8-bit value in VRAM in a given address.
136 137 138 |
# File 'lib/amaterasu/game_boy/ppu.rb', line 136 def write_vram(address:, value:) @vram.write_byte(address:, value:) end |
#wx= ⇒ void (readonly)
This method returns an undefined value.
39 |
# File 'sig/akane/game_boy/ppu.rbs', line 39
def wx=: (Integer value) -> void
|
#wy= ⇒ void (readonly)
This method returns an undefined value.
38 |
# File 'sig/akane/game_boy/ppu.rbs', line 38
def wy=: (Integer value) -> void
|