Class: PSX::GPU
- Inherits:
-
Object
- Object
- PSX::GPU
- Defined in:
- lib/psx/gpu.rb
Overview
Graphics Processing Unit Handles 2D/3D rendering to VRAM
Constant Summary collapse
- VRAM_WIDTH =
VRAM dimensions
1024- VRAM_HEIGHT =
512- STAT_TEXTURE_PAGE_X =
Status register bits
0x0000_000F- STAT_TEXTURE_PAGE_Y =
Texture page X base (N*64)
0x0000_0010- STAT_SEMI_TRANSPARENCY =
Texture page Y base (N*256)
0x0000_0060- STAT_TEXTURE_DEPTH =
Semi-transparency mode
0x0000_0180- STAT_DITHER =
Texture color depth
0x0000_0200- STAT_DRAW_TO_DISPLAY =
Dither enabled
0x0000_0400- STAT_SET_MASK_BIT =
Drawing to display area allowed
0x0000_0800- STAT_DRAW_PIXELS =
Set mask bit when drawing
0x0000_1000- STAT_INTERLACE_FIELD =
Draw pixels (0=always, 1=not to masked)
0x0000_2000- STAT_REVERSE_FLAG =
Interlace field
0x0000_4000- STAT_TEXTURE_DISABLE =
Reverse flag
0x0000_8000- STAT_HORIZONTAL_RES2 =
Texture disable
0x0001_0000- STAT_HORIZONTAL_RES1 =
Horizontal resolution 2
0x0006_0000- STAT_VERTICAL_RES =
Horizontal resolution 1
0x0008_0000- STAT_VIDEO_MODE =
Vertical resolution (0=240, 1=480)
0x0010_0000- STAT_COLOR_DEPTH =
Video mode (0=NTSC, 1=PAL)
0x0020_0000- STAT_VERTICAL_INTERLACE =
Display color depth (0=15bit, 1=24bit)
0x0040_0000- STAT_DISPLAY_ENABLE =
Vertical interlace
0x0080_0000- STAT_IRQ =
Display enable (0=enabled, 1=disabled)
0x0100_0000- STAT_DMA_REQUEST =
IRQ flag
0x0200_0000- STAT_CMD_READY =
DMA request
0x0400_0000- STAT_VRAM_TO_CPU_READY =
Ready for command
0x0800_0000- STAT_DMA_READY =
Ready for VRAM to CPU transfer
0x1000_0000- STAT_DMA_DIRECTION =
Ready for DMA
0x6000_0000- STAT_DRAWING_ODD =
DMA direction
0x8000_0000- CMD_NOP =
GP0 command types
0x00- CMD_CLEAR_CACHE =
0x01- CMD_FILL_RECT =
0x02- CMD_POLY_BASE =
0x20-0x3F polygons
0x20- CMD_LINE_BASE =
0x40-0x5F lines
0x40- CMD_RECT_BASE =
0x60-0x7F rectangles
0x60- CMD_COPY_VRAM_VRAM =
0x80- CMD_COPY_CPU_VRAM =
0xA0- CMD_COPY_VRAM_CPU =
0xC0- CMD_ENV_BASE =
0xE0-0xEF environment commands
0xE0
Instance Attribute Summary collapse
-
#vram ⇒ Object
readonly
Returns the value of attribute vram.
Instance Method Summary collapse
-
#framebuffer ⇒ Object
Get current framebuffer as RGBA packed binary string for display Returns hash with :width, :height, :rgba (binary string) Uses caching to avoid regenerating unchanged frames.
-
#gp0(value) ⇒ Object
GP0 - Rendering commands and VRAM access.
-
#gp1(value) ⇒ Object
GP1 - Display control.
- #gp1_texture_disable(value) ⇒ Object
-
#initialize(interrupts: nil) ⇒ GPU
constructor
A new instance of GPU.
-
#mark_dirty ⇒ Object
Mark framebuffer as needing regeneration (call when VRAM is modified).
- #read_data ⇒ Object
- #status ⇒ Object
-
#vblank ⇒ Object
Called by emulator on VBlank to toggle interlace field.
Constructor Details
#initialize(interrupts: nil) ⇒ GPU
Returns a new instance of GPU.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/psx/gpu.rb', line 52 def initialize(interrupts: nil) @interrupts = interrupts # VRAM: 1024x512 16-bit pixels @vram = Array.new(VRAM_WIDTH * VRAM_HEIGHT, 0) # Status register @status = STAT_CMD_READY | STAT_DMA_READY | STAT_VRAM_TO_CPU_READY # Display settings @display_enabled = false @display_start_x = 0 @display_start_y = 0 @display_h_start = 0x200 @display_h_end = 0xC00 @display_v_start = 0x10 @display_v_end = 0x100 @video_mode = :ntsc @horizontal_res = 320 @vertical_res = 240 @color_depth_24 = false @interlaced = false # Drawing area @draw_area_left = 0 @draw_area_top = 0 @draw_area_right = 0 @draw_area_bottom = 0 @draw_offset_x = 0 @draw_offset_y = 0 # Texture settings @texture_page_x = 0 @texture_page_y = 0 @texture_depth = 0 # 0=4bit, 1=8bit, 2=15bit @semi_transparency = 0 @texture_window_mask_x = 0 @texture_window_mask_y = 0 @texture_window_offset_x = 0 @texture_window_offset_y = 0 @texture_disable_allow = false # Mask settings @set_mask_bit = false @check_mask_bit = false # Command buffer for multi-word commands @cmd_buffer = [] @cmd_remaining = 0 @current_cmd = 0 # VRAM transfer state @vram_transfer_x = 0 @vram_transfer_y = 0 @vram_transfer_start_x = 0 # Starting X for line wrap detection @vram_transfer_width = 0 @vram_transfer_height = 0 @vram_transfer_count = 0 @vram_transfer_mode = nil # :cpu_to_vram or :vram_to_cpu @vram_read_buffer = [] # Framebuffer caching @framebuffer_dirty = true @framebuffer_cache = nil # DMA direction @dma_direction = 0 # Interlace field (toggled by VBlank) @odd_field = false end |
Instance Attribute Details
#vram ⇒ Object (readonly)
Returns the value of attribute vram.
50 51 52 |
# File 'lib/psx/gpu.rb', line 50 def vram @vram end |
Instance Method Details
#framebuffer ⇒ Object
Get current framebuffer as RGBA packed binary string for display Returns hash with :width, :height, :rgba (binary string) Uses caching to avoid regenerating unchanged frames
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/psx/gpu.rb', line 253 def framebuffer # Return cached framebuffer if VRAM hasn't changed if !@framebuffer_dirty && @framebuffer_cache && @framebuffer_cache[:width] == @horizontal_res && @framebuffer_cache[:height] == @vertical_res return @framebuffer_cache end width = @horizontal_res height = @vertical_res num_pixels = width * height # Build RGBA array and pack once (faster than building string) rgba_arr = Array.new(num_pixels * 4) dst_i = 0 height.times do |y| vram_row = (@display_start_y + y) * VRAM_WIDTH + @display_start_x width.times do |x| color16 = @vram[vram_row + x] || 0 # Convert 15-bit to 24-bit RGB rgba_arr[dst_i] = (color16 & 0x001F) << 3 # R rgba_arr[dst_i + 1] = (color16 & 0x03E0) >> 2 # G rgba_arr[dst_i + 2] = (color16 & 0x7C00) >> 7 # B rgba_arr[dst_i + 3] = 255 # A dst_i += 4 end end @framebuffer_dirty = false @framebuffer_cache = { width: width, height: height, rgba: rgba_arr.pack("C*") } end |
#gp0(value) ⇒ Object
GP0 - Rendering commands and VRAM access
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/psx/gpu.rb', line 155 def gp0(value) if @vram_transfer_mode == :cpu_to_vram vram_write_data(value) return end if @cmd_remaining > 0 @cmd_buffer << value @cmd_remaining -= 1 if @cmd_remaining == 0 execute_gp0_command end return end cmd = (value >> 24) & 0xFF @current_cmd = cmd @cmd_buffer = [value] case cmd when CMD_NOP # Do nothing when CMD_CLEAR_CACHE # Clear texture cache - ignore for now when CMD_FILL_RECT @cmd_remaining = 2 when 0x20..0x3F # Polygons @cmd_remaining = polygon_word_count(cmd) - 1 when 0x40..0x5F # Lines @cmd_remaining = line_word_count(cmd) - 1 when 0x60..0x7F # Rectangles @cmd_remaining = rectangle_word_count(cmd) - 1 when CMD_COPY_VRAM_VRAM @cmd_remaining = 3 when CMD_COPY_CPU_VRAM @cmd_remaining = 2 when CMD_COPY_VRAM_CPU @cmd_remaining = 2 when 0xE1 gp0_draw_mode(value) when 0xE2 gp0_texture_window(value) when 0xE3 gp0_draw_area_top_left(value) when 0xE4 gp0_draw_area_bottom_right(value) when 0xE5 gp0_draw_offset(value) when 0xE6 gp0_mask_settings(value) else # Unknown command - ignore end execute_gp0_command if @cmd_remaining == 0 && @cmd_buffer.length > 0 end |
#gp1(value) ⇒ Object
GP1 - Display control
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/psx/gpu.rb', line 217 def gp1(value) cmd = (value >> 24) & 0xFF case cmd when 0x00 gp1_reset when 0x01 gp1_reset_command_buffer when 0x02 gp1_acknowledge_irq when 0x03 gp1_display_enable(value) when 0x04 gp1_dma_direction(value) when 0x05 gp1_display_start(value) when 0x06 gp1_horizontal_range(value) when 0x07 gp1_vertical_range(value) when 0x08 gp1_display_mode(value) when 0x09 gp1_texture_disable(value) when 0x10..0x1F gp1_gpu_info(value) end end |
#gp1_texture_disable(value) ⇒ Object
246 247 248 |
# File 'lib/psx/gpu.rb', line 246 def gp1_texture_disable(value) @texture_disable_allow = (value & 1) != 0 end |
#mark_dirty ⇒ Object
Mark framebuffer as needing regeneration (call when VRAM is modified)
288 289 290 |
# File 'lib/psx/gpu.rb', line 288 def mark_dirty @framebuffer_dirty = true end |
#read_data ⇒ Object
146 147 148 149 150 151 152 |
# File 'lib/psx/gpu.rb', line 146 def read_data if @vram_transfer_mode == :vram_to_cpu && !@vram_read_buffer.empty? @vram_read_buffer.shift else 0 end end |
#status ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/psx/gpu.rb', line 129 def status stat = @status # Update dynamic bits stat &= ~STAT_DISPLAY_ENABLE stat |= STAT_DISPLAY_ENABLE unless @display_enabled stat &= ~STAT_DMA_DIRECTION stat |= (@dma_direction << 29) & STAT_DMA_DIRECTION # Bit 31: Drawing odd lines in interlace mode stat &= ~STAT_DRAWING_ODD stat |= STAT_DRAWING_ODD if @odd_field stat end |
#vblank ⇒ Object
Called by emulator on VBlank to toggle interlace field
125 126 127 |
# File 'lib/psx/gpu.rb', line 125 def vblank @odd_field = !@odd_field end |