Class: PSX::GTE
- Inherits:
-
Object
- Object
- PSX::GTE
- Defined in:
- lib/psx/gte.rb
Overview
Geometry Transformation Engine (COP2).
32 data registers, 32 control registers, ~24 commands. Register and flag semantics follow the Nocash PSX spec. Math is implemented straightforwardly (not bit-exact with the hardware UNR divide table), which is enough to get the BIOS boot animation moving.
Constant Summary collapse
- FLAG_IR0_SAT =
FLAG bits
1 << 12
- FLAG_SY2_SAT =
1 << 13
- FLAG_SX2_SAT =
1 << 14
- FLAG_MAC0_NEG =
1 << 15
- FLAG_MAC0_POS =
1 << 16
- FLAG_DIVIDE_OVERFLOW =
1 << 17
- FLAG_SZ3_OTZ_SAT =
1 << 18
- FLAG_COLOR_B_SAT =
1 << 19
- FLAG_COLOR_G_SAT =
1 << 20
- FLAG_COLOR_R_SAT =
1 << 21
- FLAG_IR3_SAT =
1 << 22
- FLAG_IR2_SAT =
1 << 23
- FLAG_IR1_SAT =
1 << 24
- FLAG_MAC3_NEG =
1 << 25
- FLAG_MAC2_NEG =
1 << 26
- FLAG_MAC1_NEG =
1 << 27
- FLAG_MAC3_POS =
1 << 28
- FLAG_MAC2_POS =
1 << 29
- FLAG_MAC1_POS =
1 << 30
- FLAG_ERROR_MASK =
Bit 31: OR of bits 30..23 and 18..13
((0xFF) << 23) | ((0x3F) << 13)
Instance Method Summary collapse
-
#execute(instruction) ⇒ Object
Run a GTE command instruction (the lower 26 bits of the COP2 imm25 op).
-
#initialize ⇒ GTE
constructor
A new instance of GTE.
- #read_control(idx) ⇒ Object
-
#read_data(idx) ⇒ Object
— Public register access ———————————————.
- #reset ⇒ Object
- #write_control(idx, value) ⇒ Object
- #write_data(idx, value) ⇒ Object
Constructor Details
#initialize ⇒ GTE
Returns a new instance of GTE.
34 35 36 |
# File 'lib/psx/gte.rb', line 34 def initialize reset end |
Instance Method Details
#execute(instruction) ⇒ Object
Run a GTE command instruction (the lower 26 bits of the COP2 imm25 op).
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/psx/gte.rb', line 268 def execute(instruction) @flag = 0 # cleared at the start of each command sf = ((instruction >> 19) & 1) != 0 # shift-fraction (12) flag lm = ((instruction >> 10) & 1) != 0 # saturate IR1..3 to [0..7FFF] mx = (instruction >> 17) & 3 mv = (instruction >> 15) & 3 cv = (instruction >> 13) & 3 shift = sf ? 12 : 0 opcode = instruction & 0x3F case opcode when 0x01 then cmd_rtps(0, shift, lm, push_sxy: true) when 0x06 then cmd_nclip when 0x0C then cmd_op(shift, lm) when 0x10 then cmd_dpcs(shift, lm, @rgbc) when 0x11 then cmd_intpl(shift, lm) when 0x12 then cmd_mvmva(shift, lm, mx, mv, cv) when 0x13 then cmd_ncds(0, shift, lm) when 0x14 then cmd_cdp(shift, lm) when 0x16 then cmd_ncdt(shift, lm) when 0x1B then cmd_nccs(0, shift, lm) when 0x1C then cmd_cc(shift, lm) when 0x1E then cmd_ncs(0, shift, lm) when 0x20 then cmd_nct(shift, lm) when 0x28 then cmd_sqr(shift, lm) when 0x29 then cmd_dcpl(shift, lm) when 0x2A then cmd_dpct(shift, lm) when 0x2D then cmd_avsz3 when 0x2E then cmd_avsz4 when 0x30 then cmd_rtpt(shift, lm) when 0x3D then cmd_gpf(shift, lm) when 0x3E then cmd_gpl(shift, lm) when 0x3F then cmd_ncct(shift, lm) else # Unimplemented command: leave state alone; flag stays clear. end # Auto-set bit 31 (error flag) @flag |= (1 << 31) if (@flag & FLAG_ERROR_MASK) != 0 end |
#read_control(idx) ⇒ Object
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 |
# File 'lib/psx/gte.rb', line 170 def read_control(idx) case idx & 0x1F when 0 then pack_xy(@rt[0][0], @rt[0][1]) when 1 then pack_xy(@rt[0][2], @rt[1][0]) when 2 then pack_xy(@rt[1][1], @rt[1][2]) when 3 then pack_xy(@rt[2][0], @rt[2][1]) when 4 then to_u32(sign_extend16(@rt[2][2])) when 5 then to_u32(@tr[0]) when 6 then to_u32(@tr[1]) when 7 then to_u32(@tr[2]) when 8 then pack_xy(@ls[0][0], @ls[0][1]) when 9 then pack_xy(@ls[0][2], @ls[1][0]) when 10 then pack_xy(@ls[1][1], @ls[1][2]) when 11 then pack_xy(@ls[2][0], @ls[2][1]) when 12 then to_u32(sign_extend16(@ls[2][2])) when 13 then to_u32(@bk[0]) when 14 then to_u32(@bk[1]) when 15 then to_u32(@bk[2]) when 16 then pack_xy(@lc[0][0], @lc[0][1]) when 17 then pack_xy(@lc[0][2], @lc[1][0]) when 18 then pack_xy(@lc[1][1], @lc[1][2]) when 19 then pack_xy(@lc[2][0], @lc[2][1]) when 20 then to_u32(sign_extend16(@lc[2][2])) when 21 then to_u32(@fc[0]) when 22 then to_u32(@fc[1]) when 23 then to_u32(@fc[2]) when 24 then to_u32(@ofx) when 25 then to_u32(@ofy) # H is unsigned 16-bit, but hardware sign-extends on read. when 26 then to_u32(sign_extend16(@h)) when 27 then to_u32(sign_extend16(@dqa)) when 28 then to_u32(@dqb) when 29 then to_u32(sign_extend16(@zsf3)) when 30 then to_u32(sign_extend16(@zsf4)) when 31 # Bit 31 = OR of error-flag bits flag = @flag & ~(1 << 31) flag |= (1 << 31) if (flag & FLAG_ERROR_MASK) != 0 flag end end |
#read_data(idx) ⇒ Object
— Public register access ———————————————
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 |
# File 'lib/psx/gte.rb', line 73 def read_data(idx) case idx & 0x1F when 0 then pack_xy(@v[0][0], @v[0][1]) when 1 then to_u32(sign_extend16(@v[0][2])) when 2 then pack_xy(@v[1][0], @v[1][1]) when 3 then to_u32(sign_extend16(@v[1][2])) when 4 then pack_xy(@v[2][0], @v[2][1]) when 5 then to_u32(sign_extend16(@v[2][2])) when 6 then pack_rgbc(@rgbc) when 7 then @otz & 0xFFFF when 8 then to_u32(sign_extend16(@ir0)) when 9 then to_u32(sign_extend16(@ir1)) when 10 then to_u32(sign_extend16(@ir2)) when 11 then to_u32(sign_extend16(@ir3)) when 12 then pack_xy(@sxy[0][0], @sxy[0][1]) when 13 then pack_xy(@sxy[1][0], @sxy[1][1]) when 14 then pack_xy(@sxy[2][0], @sxy[2][1]) when 15 then pack_xy(@sxy[2][0], @sxy[2][1]) # SXYP mirror of SXY2 when 16 then @sz[0] & 0xFFFF when 17 then @sz[1] & 0xFFFF when 18 then @sz[2] & 0xFFFF when 19 then @sz[3] & 0xFFFF when 20 then pack_rgbc(@rgb_fifo[0]) when 21 then pack_rgbc(@rgb_fifo[1]) when 22 then pack_rgbc(@rgb_fifo[2]) when 23 then @res1 & 0xFFFF_FFFF when 24 then to_u32(@mac0) when 25 then to_u32(@mac1) when 26 then to_u32(@mac2) when 27 then to_u32(@mac3) when 28, 29 then pack_irgb when 30 then to_u32(@lzcs) when 31 then @lzcr & 0xFFFF_FFFF end end |
#reset ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/psx/gte.rb', line 38 def reset # Data registers (logical) @v = Array.new(3) { [0, 0, 0] } # V0..V2 (S16 X,Y,Z each) @rgbc = [0, 0, 0, 0] # R, G, B, CODE (U8 each) @otz = 0 # U16 @ir0 = 0 # S16 @ir1 = 0; @ir2 = 0; @ir3 = 0 # S16 each @sxy = Array.new(3) { [0, 0] } # SXY0..SXY2 (S16 X,Y) @sz = [0, 0, 0, 0] # SZ0..SZ3 (U16) @rgb_fifo = Array.new(3) { [0, 0, 0, 0] } # RGB0..RGB2 (R,G,B,CD) @res1 = 0 # prohibited @mac0 = 0 # S32 @mac1 = 0; @mac2 = 0; @mac3 = 0 # S32 each (logically S44 between ops) @lzcs = 0 # S32 @lzcr = 32 # leading sign-bit count of LZCS (1..32) # Control registers (logical) @rt = Array.new(3) { [0, 0, 0] } # Rotation matrix 3x3, S16 @tr = [0, 0, 0] # Translation, S32 each @ls = Array.new(3) { [0, 0, 0] } # Light source matrix, S16 @bk = [0, 0, 0] # Background color, S32 @lc = Array.new(3) { [0, 0, 0] } # Light color matrix, S16 @fc = [0, 0, 0] # Far color, S32 @ofx = 0 # S32 @ofy = 0 # S32 @h = 0 # U16 @dqa = 0 # S16 @dqb = 0 # S32 @zsf3 = 0 # S16 @zsf4 = 0 # S16 @flag = 0 # U32 end |
#write_control(idx, value) ⇒ Object
212 213 214 215 216 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 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/psx/gte.rb', line 212 def write_control(idx, value) v = value & 0xFFFF_FFFF case idx & 0x1F when 0 @rt[0][0] = sign16(v & 0xFFFF); @rt[0][1] = sign16((v >> 16) & 0xFFFF) when 1 @rt[0][2] = sign16(v & 0xFFFF); @rt[1][0] = sign16((v >> 16) & 0xFFFF) when 2 @rt[1][1] = sign16(v & 0xFFFF); @rt[1][2] = sign16((v >> 16) & 0xFFFF) when 3 @rt[2][0] = sign16(v & 0xFFFF); @rt[2][1] = sign16((v >> 16) & 0xFFFF) when 4 @rt[2][2] = sign16(v & 0xFFFF) when 5 then @tr[0] = sign32(v) when 6 then @tr[1] = sign32(v) when 7 then @tr[2] = sign32(v) when 8 @ls[0][0] = sign16(v & 0xFFFF); @ls[0][1] = sign16((v >> 16) & 0xFFFF) when 9 @ls[0][2] = sign16(v & 0xFFFF); @ls[1][0] = sign16((v >> 16) & 0xFFFF) when 10 @ls[1][1] = sign16(v & 0xFFFF); @ls[1][2] = sign16((v >> 16) & 0xFFFF) when 11 @ls[2][0] = sign16(v & 0xFFFF); @ls[2][1] = sign16((v >> 16) & 0xFFFF) when 12 then @ls[2][2] = sign16(v & 0xFFFF) when 13 then @bk[0] = sign32(v) when 14 then @bk[1] = sign32(v) when 15 then @bk[2] = sign32(v) when 16 @lc[0][0] = sign16(v & 0xFFFF); @lc[0][1] = sign16((v >> 16) & 0xFFFF) when 17 @lc[0][2] = sign16(v & 0xFFFF); @lc[1][0] = sign16((v >> 16) & 0xFFFF) when 18 @lc[1][1] = sign16(v & 0xFFFF); @lc[1][2] = sign16((v >> 16) & 0xFFFF) when 19 @lc[2][0] = sign16(v & 0xFFFF); @lc[2][1] = sign16((v >> 16) & 0xFFFF) when 20 then @lc[2][2] = sign16(v & 0xFFFF) when 21 then @fc[0] = sign32(v) when 22 then @fc[1] = sign32(v) when 23 then @fc[2] = sign32(v) when 24 then @ofx = sign32(v) when 25 then @ofy = sign32(v) when 26 then @h = v & 0xFFFF when 27 then @dqa = sign16(v & 0xFFFF) when 28 then @dqb = sign32(v) when 29 then @zsf3 = sign16(v & 0xFFFF) when 30 then @zsf4 = sign16(v & 0xFFFF) when 31 # Bit 31 is read-only (auto-OR). Bits 30..12 writable; lower bits zero. @flag = v & 0x7FFF_F000 end end |
#write_data(idx, value) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/psx/gte.rb', line 109 def write_data(idx, value) v = value & 0xFFFF_FFFF case idx & 0x1F when 0 @v[0][0] = sign16(v & 0xFFFF); @v[0][1] = sign16((v >> 16) & 0xFFFF) when 1 @v[0][2] = sign16(v & 0xFFFF) when 2 @v[1][0] = sign16(v & 0xFFFF); @v[1][1] = sign16((v >> 16) & 0xFFFF) when 3 @v[1][2] = sign16(v & 0xFFFF) when 4 @v[2][0] = sign16(v & 0xFFFF); @v[2][1] = sign16((v >> 16) & 0xFFFF) when 5 @v[2][2] = sign16(v & 0xFFFF) when 6 @rgbc = unpack_rgbc(v) when 7 @otz = v & 0xFFFF when 8 then @ir0 = sign16(v & 0xFFFF) when 9 then @ir1 = sign16(v & 0xFFFF) when 10 then @ir2 = sign16(v & 0xFFFF) when 11 then @ir3 = sign16(v & 0xFFFF) when 12 @sxy[0][0] = sign16(v & 0xFFFF); @sxy[0][1] = sign16((v >> 16) & 0xFFFF) when 13 @sxy[1][0] = sign16(v & 0xFFFF); @sxy[1][1] = sign16((v >> 16) & 0xFFFF) when 14 @sxy[2][0] = sign16(v & 0xFFFF); @sxy[2][1] = sign16((v >> 16) & 0xFFFF) when 15 # SXYP: writing pushes the FIFO (SXY0 <- SXY1, SXY1 <- SXY2, SXY2 <- new) @sxy[0] = @sxy[1] @sxy[1] = @sxy[2] @sxy[2] = [sign16(v & 0xFFFF), sign16((v >> 16) & 0xFFFF)] when 16 then @sz[0] = v & 0xFFFF when 17 then @sz[1] = v & 0xFFFF when 18 then @sz[2] = v & 0xFFFF when 19 then @sz[3] = v & 0xFFFF when 20 then @rgb_fifo[0] = unpack_rgbc(v) when 21 then @rgb_fifo[1] = unpack_rgbc(v) when 22 then @rgb_fifo[2] = unpack_rgbc(v) when 23 then @res1 = v when 24 then @mac0 = sign32(v) when 25 then @mac1 = sign32(v) when 26 then @mac2 = sign32(v) when 27 then @mac3 = sign32(v) when 28 # IRGB write: unpack RGB555, each component << 7 into IR1/2/3 @ir1 = ((v >> 0) & 0x1F) << 7 @ir2 = ((v >> 5) & 0x1F) << 7 @ir3 = ((v >> 10) & 0x1F) << 7 when 29 # ORGB is read-only when 30 @lzcs = sign32(v) @lzcr = leading_sign_bits(@lzcs) when 31 # LZCR is read-only end end |