forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[PSX -> PSP / GTE -> VFPU] need some hints

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
hlide



Joined: 10 Sep 2006
Posts: 750

PostPosted: Mon Jun 30, 2008 12:36 am    Post subject: [PSX -> PSP / GTE -> VFPU] need some hints Reply with quote

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
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Mon Jun 30, 2008 5:19 am    Post subject: Reply with quote

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
View user's profile Send private message AIM Address
hlide



Joined: 10 Sep 2006
Posts: 750

PostPosted: Mon Jun 30, 2008 6:29 am    Post subject: Reply with quote

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
View user's profile Send private message
J.F.



Joined: 22 Feb 2004
Posts: 2906

PostPosted: Mon Jun 30, 2008 7:52 am    Post subject: Reply with quote

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
View user's profile Send private message AIM Address
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
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