 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Mon Jun 30, 2008 12:36 am Post subject: [PSX -> PSP / GTE -> VFPU] need some hints |
|
|
I'm coding GTE instructions into functions using VFPU.
But I run into some troubles with the MACx computations which are not clear. So can someone give me some hints about it ?
To illustrate it, I'm taking the AVSZ4 instruction as an example :
| Code: |
FLAG = 0;
MAC0 = F( ( (s64)(s16)ZSF4 * SZ0 ) + ( (s16)ZSF4 * SZ1 ) + ( (s16)ZSF4 * SZ2 ) + ( (s16)ZSF4 * SZ3 ) );
OTZ = Lm_D( (s32)MAC0 >> 12 );
|
what I don't understand is the output MAC0 is said to be in 1:32:0 format (sign:integer:fraction) whereas ZSF4 is 1:3:12, so i would expect MAC0 to be :
x[1:19:12] = ZSF4[1:3:12] * SZ0[0:16:0]
y[1:19:12] = ZSF4[1:3:12] * SZ1[0:16:0]
z[1:19:12] = ZSF4[1:3:12] * SZ2[0:16:0]
w[1:19:12] = ZSF4[1:3:12] * SZ3[0:16:0]
MAC0[1:21:12] = x+y+z+w
which explains why we get OTZ[0:16:0] = clamp(0, MAC0[1:21:12]>>12, 65535).
But the description says the output MAC0 is [0:31:0], so I'm totally lost.
My code :
| Code: | /*
Name Cycles Command Description
AVSZ4 6 cop2 0x168002E Average of four Z values
Fields:
in: SZ1,SZ2,SZ3,SZ4 Z-Values [0,16,0]
ZSF4 Divider [1,3,12]
out: OTZ Average. [0,16,0]
MAC0 Average. [1,31,0]
Calculation:
[1,31,0] MAC0=F[ZSF4*SZ0 + ZSF4*SZ1 + ZSF4*SZ2 + ZSF4*SZ3] [1,31,12]
[0,16,0] OTZ=Lm_D[MAC0] [1,31,0]
*/
.set OTZ, $s013.s // [0:16:0] but stored as float internally
.set SZ0SZ1SZ2SZ3, $c100.q // [0:16:0] but stored as float internally
.set MAC0, $s120.s // [1:31:0] or [1:27:4] or [1:19:12]
.set ZSF4, $s332.s // [1:3:12] but stored as float internally
.set FLAG, $s333.s // bit set but stored as float internally
.set DQBZSF3ZSF4FLAG, $c330.q
.extern host_gte_avsz4
.ent host_gte_avsz4
//
// OTZ = MAC0 = ZSF4*SZ0 + ZSF4*SZ1 + ZSF4*SZ2 + ZSF4*SZ3
vdot.q $s700.s, DQBZSF3ZSF4FLAG[z,z,z,z], SZ0SZ1SZ2SZ3 // 7 cycles latency so we shouldn't use $s700.s before 6 instructions to make it run as an 1 cycle instruction.
// constants for limits (and conveniently no stall because of vdot.q)
lui $at, %hi(0x4F000000) // 2147483648.0
vfim.s $s413.s, 65536.0
mtv $at, $s423.s // 2147483648.0
vzero.s $s433.s // 0.0
mtv $at, $s403.s // 2147483648.0
viim.s $s432.s, 65535.0
// (Fn,Dz) = (MAC0 < -2^31, OTZ < 0) <==> (Fn,Dz) = (2^31 < -MAC0, 0 < -OTZ)
vslt.p $c602.p, $r423.p, $c700.p[-x,-x]
// (Fp,Dp) = (MAC0 >= 2^31, OTZ >= 2^16)
vsge.p $c600.p, $c700.p[x,x], $r403.p
// constants for FLAG
vmov.s $s400.s, $s413.s # F pos
viim.s $s401.s, 32778 # F neg
viim.s $s402.s, 8192 # D
// save MAC0 in fixed point (1:31:0) !?
vf2in.s MAC0, $s700.s, 0
// clamp OTZ between 0 and 65535 (part I)
vmin.s $s700.s, $s700.s, $s432.s
// FLAG = Fp*(1<<(31-16)) + Dp*(1<<(31-18)) + Fn*(1<<(31-15)) + Dz*(1<<(31-18))
vdot.q FLAG, $c400.q[x,z,y,z], $c600.q
// clamp OTZ between 0 and 65535 (part II) and save it
jr $ra
vmax.s OTZ, $s700.s, $s433.s
.end host_gte_avsz4
|
NOTE:
FLAG contains various bits to indicate overflows. But I'm not handling FLAG as in a real PSX FLAG in order to reduce bit computations at each GTE instructions. Instead I defer those extra computations when reading FLAG register into a GP register. To retrieve the exact FLAG :
| Code: |
host_gte_mfc2_flag:
vf2in.s $s400.s, FLAG, 0 // convert float into integer to get reversed bits (only 20 bits are used in FLAG so it is okay to have them stored as a 32-bit float)
lui $at, %hi(OVERFLOW_BITS_WHICH_SET_BIT31)
ori $at, %lo(OVERFLOW_BITS_WHICH_SET_BIT31)
mfv $v0, $s400.s // put bits into a GP register
and $at, $at, $v0
sltu $at, $0, $at // check if one of overflow bits is set
or $v0, $v0, $at // "bit 32" is set if one of overflow bit is set
jr $ra
bitrev $v0, $v0 // reverse the bit order to reflect a true PSX FLAG
|
|
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Mon Jun 30, 2008 5:19 am Post subject: |
|
|
Well, looking at the Padua gte.txt doc, it looks that some operations are carried out at with temporaries of 1:31:12, so if a source were 1:3:12, it would be promoted to 1:31:12 for the duration of the calculation, then truncated to 1:31:0 when stored to MACn.
For example, look how they describe the RTPS operation:
| Code: | RTPS 15 Perspective transformation
Fields: none
Opcode: cop2 $0180001
In: V0 Vector to transform. [1,15,0]
R Rotation matrix [1,3,12]
TR Translation vector [1,31,0]
H View plane distance [0,16,0]
DQA Depth que interpolation values. [1,7,8]
DQB [1,7,8]
OFX Screen offset values. [1,15,16]
OFY [1,15,16]
Out: SXY fifo Screen XY coordinates.(short) [1,15,0]
SZ fifo Screen Z coordinate.(short) [0,16,0]
IR0 Interpolation value for depth queing. [1,3,12]
IR1 Screen X (short) [1,15,0]
IR2 Screen Y (short) [1,15,0]
IR3 Screen Z (short) [1,15,0]
MAC1 Screen X (long) [1,31,0]
MAC2 Screen Y (long) [1,31,0]
MAC3 Screen Z (long) [1,31,0]
Calculation:
[1,31,0] MAC1=A1[TRX + R11*VX0 + R12*VY0 + R13*VZ0] [1,31,12]
[1,31,0] MAC2=A2[TRY + R21*VX0 + R22*VY0 + R23*VZ0] [1,31,12]
[1,31,0] MAC3=A3[TRZ + R31*VX0 + R32*VY0 + R33*VZ0] [1,31,12]
[1,15,0] IR1= Lm_B1[MAC1] [1,31,0]
[1,15,0] IR2= Lm_B2[MAC2] [1,31,0]
[1,15,0] IR3= Lm_B3[MAC3] [1,31,0]
SZ0<-SZ1<-SZ2<-SZ3
[0,16,0] SZ3= Lm_D(MAC3) [1,31,0]
SX0<-SX1<-SX2, SY0<-SY1<-SY2
[1,15,0] SX2= Lm_G1[F[OFX + IR1*(H/SZ)]] [1,27,16]
[1,15,0] SY2= Lm_G2[F[OFY + IR2*(H/SZ)]] [1,27,16]
[1,31,0] MAC0= F[DQB + DQA * (H/SZ)] [1,19,24]
[1,15,0] IR0= Lm_H[MAC0] [1,31,0]
Notes:
Z values are limited downwards at 0.5 * H. For smaller z values you'll have
write your own routine. |
Note that the first three MAC calculations are done at [1,31,12], then stored as [1,31,0]. Similarly, you see SX and SY later done as [1,27,16] before being stored as [1,15,0]. So the GTE seems to do most calculations as fixed point, signed, 44 bit numbers that are later shifted and truncated to store in various registers. |
|
| Back to top |
|
 |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Mon Jun 30, 2008 6:29 am Post subject: |
|
|
Thanks J.P., your explanation makes sense. The only problem I see is :
| Quote: | FLAG = 0;
MAC0 = F( ( (s64)(s16)ZSF4 * SZ0 ) + ( (s16)ZSF4 * SZ1 ) + ( (s16)ZSF4 * SZ2 ) + ( (s16)ZSF4 * SZ3 ) );
OTZ = Lm_D( (s32)MAC0 >> 12 );
|
as you can see, the last result stored in MAC0 is still [1:19:12] at the end and not [1:31:0]. So I guess this GTE emulation code I found on several PSX emulators is not totally accurate about the results left on MAC0. Sure, if this result is never used, it should be probably okay.
But I like your explanation and I'll apply it this way, as it just costs 1 cycle and allow me to avoid some stalls anyway.
According to your explanation, the accurate code should be :
| Quote: | FLAG = 0;
s64 tmp = ( ( (s64)(s16)ZSF4 * SZ0 ) + ( (s16)ZSF4 * SZ1 ) + ( (s16)ZSF4 * SZ2 ) + ( (s16)ZSF4 * SZ3 ) ) >> 12;
MAC0 = F( tmp );
OTZ = Lm_D( (s32)tmp );
|
|
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Mon Jun 30, 2008 7:52 am Post subject: |
|
|
| Well, it's more padua's explanation, but I imagine that some emulators used to take shortcuts when they could to save time. Many of these PSX emulators were written when doing a PSX emu was pushing the hardware to the limit, and not converting MAC0 from [1,19,12] to [1,31,0] when it wasn't needed by any games (that they knew of) was probably considered a valid speedup. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|