LISTING 53.1 FIXED.ASM ;Routines de virgules fixes spécifiques au 386. ;Testées avec TASM ROUNDING-ON equ 1 ;1 pour arrondir, 0 pour ne pas arrondir ;ne pas arrondir est plus rapide, arrondir est plus précis ALIGNMENT equ 2 .model small .386 .code ;Multiplie deux valeurs en virgule fixe. ;Appelable en C near comme suit: Fixedpoint FixedMul(Fixedpoint Ml, Fixedpoint M2); Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor); FMparms struc dw 2 dup(?) ;adresse de retour et BP empilés M1 dd ? M2 dd ? FMparms ends Align ALIGNMENT public_FixedMul _FixedMul proc near push bp mov bp,sp mov eax.Ebp+Mll imul dword ptr [bp+M2] ;multiplier if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING_ON shr eax,16 ;met la partie fractionnaire dans AX POP bp ret _FixedMul endp ; Divise une valeur en virgule fixe par une autre. ; Appelable en C near comme suit : Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor); FDparms struc dw 2 dup(?) ;adresse de retour et BP empilés Dividend dd ? Divisor dd ? FDparms ends align ALIGNMENT public_FixedDiv _FixedDiv proc near push bp mov bp,sp if ROUNDING_ON sub cx,cx ;suppose que le résultat est positif mov eax,[bp+Dividend] and eax.eax ;dividende positif? jns FDPL ;yes inc cx ;indique que c'est un dividende négatif neg eax ;rend le dividende positif FDP1: sub edx.edx ;en fait un dividende sur 64 bits, puis décale de 16 bits ;à gauche afin que le résultat soit dans EAX rol eax,16 ;met la partie fractionnaire du dividende dans l'octet ; supérieur du mot de EAX mov dx.ax ;met toute la partie du dividende dans DX sub ax.ax ;efface l'octet inférieur du mot de EAX mov ebx.dword ptr [bp+Divisor] and ebx,ebx ;diviseur positif? jns FDP2 ;oui dec cx ;indique que c'est un diviseur négatif neg ebx ;rend le diviseur positif FDP2: div ebx ;divise shr ebx,l ;diviseur/2. Minoré de 1 si le diviseur est adc ebx,0 ;pair dec ebx cmp ebx,edx ;paramètre la retenue si le reste est au moins adc eax,0 ;aussi grand que la moitié du diviseur, puis ;l'utilise pour arrondir si nécessaire and cx.cx ;le résultat devrait-il être négatif? jz FDP3 ;non neg eax ;oui, il devient négatif FDP3: else !ROUNDING_ON mov edx,[bp+Dividend] sub eax,eax shrd eax,edx,16 ;met en place afin que le résultat sar edx,16 ;entre dans EAX idiv dword ptr [bp+Divisorl endif :ROUNDING_ON shld edx,eax,16 ;toute la partie du résultat est dans DX ;la partie fractionnaire est déjà dans AX; POP bp ret FixedDiv endp ;Retourne le cosinus et le sinus d'un angle. ;Appelable en C near comme suit: ; void CosSin(TAngle Angle, Fixedpoint *Cos, Fixedpoint ; align ALIGNMENT ;CosTable label dword ; include costable.inc SCparms struc dw 2 dup(?) ;adresse de retour et BP empilés Angle dw ? ;angle pour calculer le sinus et le cosinus Cos dw ? ;pointeur sur la destination du cosinus Sin dw ? ;pointeur sur la destination du sinus SCparms ends align ALIGNMENT public -CosSin _CosSin proc near push bp ;préserve la stack frame mov bp,sp ;configure la stack frame locale mov bx,[bp],Angle and bx,bx ;s'assure que l'angle est entre 0 et 2*pi jns CheckInRange MakePos: ;inférieur à 0. Devient donc positif add bx,360*10 js Mak@Pos jmp short CheckInRange align ALIGNMENT MakeInRange: ;s'assure que l'angle n'est pas supérieur à 2*pi sub bx,360*10 CheckInRange: cmp bx,360*10 jg MakeInRange cmp bx.180*10 :détermine le quadrant ja BottomHalf ;quadrant 2 ou 3 cmp bx,90*10 ;quadrant 0 ou 1 ja Quadrantl ;quadrant 0 shl bx,2 mov eax.CosTable[bx] ;pré-calcule le sinus neg bx ;sin(Angle) = cos(90-Angle) mov edx.CosTable[bx+90*10*4] ;pré-calcule le cosinus jmp short CSDone align ALIGNMENT Quadrantl: neg bx add bx,180*10 ;convertit en angle entre 0 et 90 shl bx.2 mov eax,CosTable[bx];pré-calcule le cosinus neg eax ;négatif dans ce quadrant neg bx ;sin(Angle) = cos(90-Angle) mov edx.CosTable[bx+90*10*4] ;pré-calcule le cosinus jmp short CSDone align ALIGNMENT BottomHalf: ;quadrant 2 ou 3 neg bx add bx.360*10 ;convertit en angle entre 0 et 180 cmp bx,90*10 ;quadrant 2 ou 3 ja Quadrant2 ;quadrant 3 shl bx,2 mov eax,CosTable[bx] ;pré-calcule le cosinus neg bx ;sin(Angle) = cos(90-Angle) mov edx,CosTable[90*10*4+bx) ;pré-calcule le sinus neg edx ;négatif dans ce quadrant jmp short CSDone align ALIGNMENT Quadrant2: neg bx add bx,180*10 ;convertit en angle entre 0 et 90 shl bx.2 mov eax,CosTable[bxl ;pré-calcule le cosinus neg eax ;négatif dans ce quadrant neg bx ;sin(Angle) = cos(90-Angle) mov edx,CosTable[90*10*4+bx] ;pré-calcule le sinus neg edx ;négatif dans ce quadrant CSDone: mov bx,Ebp].Cos mov [bx].eax mov bx,[bp].Sin mov [bx].edx pop bp ;restaure la stack frame ret -CosSin endp ;La matrice multiplie Xform par SourceVec, et stocke résultat dans DestVec. ;Multiplie une matrice 4x4 fois une matrice 4xl; le résultat est une matrice ;4xl. Triche en supposant que la coordonnée W est à 1 et que la rangée du bas ;est à 0 0 0 1. Et ne s'embarrasse pas à paramétrer la coordonnée W de destination. ;Appelable en C comme suit: ; void XformVec(Xform WorkingXform, Fixedpoint *SourceVec, Fixedpoint *DestVec); ; ; Ce code en Assembleur est équivalent au code en C: ; int i; ;for (i-0; i<3; i++) DestVec[i] = FixedMul(WorkingXform[i][01, SourceVec[OI) + FixedMul(WorkingXform[i][1], SourceVec[l]) + FixedMul(WorkingXform[i][2]. SourceVec[2]) + WorkingXform[i][3]; /*il n'est pas nécessaire de multiplier parW = 1*/ XVparms struc dw 2 dup(?) ;adresse de retour et BP empilés WorkingXform dw ? ; pointeur sur la matrice de transformation SourceVec dw ? ;pointeur sur le vecteur source DestVec dw ? ; pointeur sur le vecteur destination XVparms ends align ALIGNMENT public _XformVec _XformVec proc near push bp ; préserve la stack frame mov bp,sp ;configure la stack frame locale push si ;préserve les variables registre push di mov si,[Ebp].WorkingXform : SI pointe sur la matrice Xform mov bx,[bp].SourceVec ; BX pointe sur le vecteur source mov di,Lbp].DestVec ; DI pointe sur le vecteur destination soff-0 doff-0 REPT 3 ;traite en une seule fois chacune des destinations X,Y,Z mov eax,[si+soff] ; entrée de la colonne 0 dans cette rangée imul dword ptr [bx] ;entrée Xform fois entrée source if ROUNDING_ON add eax, 8000h ; arrondit en ajoutant 2^(-17) adc edx,0 ; toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx,16 ; décale le résultat sous forme 16.16 mov ecx,eax ;total courant pour cette rangée mov eax,[si+soff+4] ; entrée de la colonne 1 dans cette rangée imul dword ptr [bx+4] ; entrée Xform fois entrée source Y if ROUNDING_ON add eax.8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous forme 16.16 add ecx,eax ;total courant pour cette rangée mov eax.[si+soff+8] ;entrée de la colonne 2 dans cette rangée imul dword ptr [bx+81 ; entrée Xform fois entrée source Z if ROUNDING_ON add eax.8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous forme 16.16 add ecx.eax ; total courant dans cette rangée add ecx,[si+soff+12] ;ajoute dans la translation mov [di+doff],ecx ;sauve le résultat dans le vecteur destination soff=soff+16 doff=doff+4 ENDM pop di :restaure les variables registre pop si pop bp ;restaure la stack frame ret _XformVec endp ;La matrice multiplie SourceXforml par SourceXform2 et stocke le résultat dans ;DestXform. Multiplie une matrice 4x4 fois une matrice 4x4 ; le résultat est une ;Matrice 4x4. Triche en supposant que la rangée du bas de chaque matrice est 0001 et ;ne s'embarrasse pas à paramétrer la rangée du bas de destination. ;Appelable en C near comme suit: ; void ConcatXforms(Xform SourceXforml, Xform SourceXform2, Xform DestXform) ;Ce code en Assembleur est équivalent à ce code en C: ; int i, j; ; ; for (i=0 ;i<3 ; i++) ; for (j=0; j<3; j++) ; DestXform[i][j] ; FixedMul(SourceXforml[i][01, SourceXform2[O][j]) + ; FixedMul(SourceXforml[i][13, SourceXform2[l][j]) + ; FixedMul(SourceXforml[i][2], SourceXform2[21[j]) ; DestXform[i][31 - ; FixedMul(SourceXforml[i][0], SourceXform2[0][3]) + ; FixedMul(SourceXforml[i][11, SourceXform2[l][3]) + ; FixedMul(SourceXforml[i][2], SourceXform2[2][3]) + ; SourceXforml[i][3] ; ; } CXparms struc dw 2 dup(?) ;adresse de retour et BP empilé SourceXforml dw ? ;pointeur sur la première matrice source Xform SourceXform2 dw ? ;pointeur sur la seconde matrice source Xform DestXform dw ? ;pointeur sur la matrice destination Xform CXparms ends align ALIGNMENT public _ConcatXforms _ConcatXforms proc near push- bp ; préserve la stack frame mov bp,sp ;configure la stack frame locale push si préserve les variables registre push di mov bx,[bp],SourceXform2 ;BX pointe sur la matrice Xform2 mov si,[bp],SourceXforml ;SI pointe sur la matrice Xform1 mov di,[bp].DestXform ;DI pointe sur la matrice destination Xform roff=0 ; offset de la rangée REPT 3 ;une fois pour chaque rangée Coff=0 ;offset de la colonne REPT 3 ;en une fois pour chacune des 3 colonnes, en supposant que 0 est ;l'entrée inférieure (aucune translation) mov eax.[si+roff] ; entrée de la colonne 0 dans cette rangée imul dword ptr [bx+coff] ;fois l'entrée de la rangée 0 dans cette colonne if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING-ON shrd eax,edx,16 ;décale le résultat sous la forme 16.16 mov ecx,eax ;total courant mov eax,[si+roff+4] ; entrée de la colonne 1 dans cette rangée imul dword ptr [bx+coff+16] ;fois l'entrée de la rangée 1 dans cette ; colonne if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax.edx,16 ;décale le résultat sous forme 16.16 add ecx.eax ;total courant mov eax.[si+roff+B] ;entrée de la colonne 2 dans cette rangée imul dword ptr [bx+coff+32] ;fois l'entrée de la rangée 2 dans cette ;colonne if ROUNDING_ON add eax,8000h ; arrondit en ajoutant 2^(-17) adc edx,0 ; toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx.16 ; décale le résultat sous la forme 16.16 add ecx,eax ; total courant mov [di+coff+roff],ecx ; sauve le résultat dans la matrice destination coff-coff+4 ; pointe sur la prochaine colonne dans Xform et destination ENDM ; traite maintenant la quatrième colonne, en supposant que 1 est ; l'entrée inférieure, afin que la translation puisse se faire mov eax,[si+roff] ;entrée de la colonne 0 dans cette rangée imul dword ptr Ebx+coffl ;fois l'entrée de la rangée 0 dans la colonne if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ; toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous forme 16.16 mov ecx.eax ;total courant mov eax,[si+roff+4] ;entrée de la colonne 1 dans cette rangée imul dword ptr [bx+coff+16] ; fois l'entrée de la rangée 1 dans cette ;colonne if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif;ROUNDING_ON shrd eax.edx,16 ;décale le résultat sous forme 16.16 add ecx.eax ;total courant mov eax,[si+roff+8] ; entrée de la colonne 2 dans cette rangée imul dword ptr [bx+coff+32] ;fois l'entrée de la rangée 2 dans cette ; colonne if ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous forme 16.16 add ecx,eax ; total courant add ecx,[si+roff+12] ;ajoute dans la translation mov [di+coff+roff],ecx ;sauve le résultat dans la matrice destination coff-coff+4 ;pointe sur la prochaine colonne dans xform2 & destination roff-roff+16 ;pointe sur la prochaine colonne dans xform2 & destination ENDM pop di ;restaure les variables registre pop si pop bp ;restaure la stack frame ret _ConcatXforms endp end