Class: PSX::GTE

Inherits:
Object
  • Object
show all
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

Constructor Details

#initializeGTE

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

#resetObject



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