; Routines en virgule fixe. ; Testé avec TASM 4.0. USE386 equ 1 ;1 pour les opcodes spécifiques au 386, 0 pour ;les opcodes du 8088 MUL_ROUNDING_ON equ 1 ;1 pour arrondir les multiplications, ;0 pour ne pas arrondir. Ne pas arrondir est ;plus rapide, arrondir est plus précis et ;c'est en général une bonne idée DIV_ROUNDING_ON equ 0 ;1 pour arrondir les divisions, ;0 pour ne pas arrondir. Ne pas arrondir ;est plus rapide, arrondir est plus précis, ;mais comme la division est faite seulement ;pour projeter sur l'écran, arrondir les ;quotients n'est donc pas nécessaire ALIGNMENT equ 2 .model small .386 .code ;===================================================================== ; Multiplie deux valeurs en virgule fixe. ; Appelable en C near comme suit: ; Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2); FMparms struc dw 2 dup(?) ;adresse de retour & BP empilé M1 dd ? M2 dd ? FMparms ends align ALIGNMENT public _FixedMul _FixedMul proc near push bp mov bp,sp if USE386 mov eax,[bp+M1] imul dword ptr [bp+M2] ;multiplie if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat est dans DX endif ;MUL_ROUNDING_ON shr eax,16 ;met la partie fractionnaire dans AX else ;!USE386 ;fait quatre produits partiels et les ;additionne, en accumulant le ;résultat dans CX:BX push si ;préserve les variables registre C push di ;calcule les signes, nous pouvons donc ;employer des multiplications unsigned sub cx,cx ;suppose les deux opérandes positifs mov ax,word ptr [bp+M1+2] mov si,word ptr [bp+M1] and ax,ax ;premier opérande négatif? jns short CheckSecondOperand ;non neg ax ;oui, donc le premier opérande devient négatif neg si sbb ax,0 inc cx ;rend ce premier opérande négatif CheckSecondOperand: mov bx,word ptr [bp+M2+2] mov di,word ptr [bp+M2] and bx,bx ;second opérande négatif? jns short SaveSignStatus ;non neg bx ;oui, donc le second opérande devient négatif neg di sbb bx,0 xor cx,1 ;rend le second opérande négatif SaveSignStatus: push cx ;mémorise le signe du résultat; 1 si le résultat ;négatif, 0 si le résultat n'est pas négatif push ax ;mémorise le mot high de M1 mul bx ;mot high de M1 fois mot high de M2 mov cx,ax ;accumule le résultat dans CX:BX (BX n'est pas ;utilisé jusqu'à la prochaine opération) ;suppose aucun débordement dans DX mov ax,si ;mot low de M1 fois mot high de M2 mul bx mov bx,ax add cx,dx ;accumule le résultat dans CX:BX pop ax ;retrouve le mot high de M1 mul di ;mot high de M1 fois mot low M2 add bx,ax adc cx,dx ;accumule le résultat dans CX:BX mov ax,si ;mot low de M1 fois mot low de M2 mul di if MUL_ROUNDING_ON add ax,8000h ;arrondit en ajoutant 2^(-17) adc bx,dx else ;!MUL_ROUNDING_ON add bx,dx ;n'arrondit pas endif ;MUL_ROUNDING_ON adc cx,0 ;accumule le résultat dans CX:BX mov dx,cx mov ax,bx pop cx and cx,cx ;le résultat est-il négatif? jz short FixedMulDone ;non neg dx ;oui, donc DX:AX devient négatif neg ax sbb dx,0 FixedMulDone: pop di ;restaure les variables registre C pop si endif ;USE386 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 & BP empilé Dividend dd ? Divisor dd ? FDparms ends align ALIGNMENT public _FixedDiv _FixedDiv proc near push bp mov bp,sp if USE386 if DIV_ROUNDING_ON sub cx,cx ;suppose le résultat positif mov eax,[bp+Dividend] and eax,eax ;dividende positif? jns short FDP1 ;oui inc cx ;note 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 ;gauche de 16 bits afin que le résultat soit ;dans EAX rol eax,16 ;met la partie fractionnaire du dividende dans ;le mot high de EAX mov dx,ax ;met toute la partie du dividende dans DX sub ax,ax ;vide le mot low du EAX mov ebx,dword ptr [bp+Divisor] and ebx,ebx ; diviseur positif? jns short FDP2 ;oui dec cx ;note que c'est un diviseur négatif neg ebx ;rend le diviseur positif FDP2: div ebx ;divise shr ebx,1 ;diviseur/2, moins 1 si le diviseur est adc ebx,0 ; pair dec ebx cmp ebx,edx ;paramètre Carry 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 short FDP3 ;non neg eax ;oui, le rend négatif FDP3: else ;!DIV_ROUNDING_ON mov edx,[bp+Dividend] sub eax,eax shrd eax,edx,16 ;position pour que le résultat soit sar edx,16 ; dans EAX idiv dword ptr [bp+Divisor] endif ;DIV_ROUNDING_ON shld edx,eax,16 ;toute la partie du résultat dans DX; ; partie fractionnaire est déjà dans AX else ;!USE386 ;NOTE!!! La division non 386 utilise un dividende sur 32 bits mais seuls les 16 ; bits supérieurs du diviseur; c'est-à-dire, seule la partie entière du diviseur ; est utilisé. Ce qui fait que la division peut être faite avec deux divisions ; matérielles rapides à la place d'une implémentation logicielle lente, et est ;(à mon avis) acceptable car la division est utilisée seulement pour projeter ; les points à l'écran (normalement, le diviseur est une coordonnée Z), ainsi ; il n'y a pas d'erreur cumulative dans le placement du pixel ; (la magnitude de l'erreur est inférieure à la plus grande distance entre les ; objets et le plan Z=0 plane). Ce n'est *pas* une division d'intérêt général ; si le diviseur est inférieur à 1,par exemple, une erreur divide-by-zero ; en sera le résultat! Pour cette raison, une projection non-386 ; ne peut pas être faite pour des points plus proches du point de vue que Z=1. ;calcule les signes, nous pouvons donc ; utiliser des divisions unsigned sub cx,cx ;suppose les deux opérandes positifs mov ax,word ptr [bp+Dividend+2] and ax,ax ;premier opérande négatif? jns short CheckSecondOperandD ;non neg ax ;oui, le premier opérande devient négatif neg word ptr [bp+Dividend] sbb ax,0 inc cx ;note que le premier opérande est négatif CheckSecondOperandD: mov bx,word ptr [bp+Divisor+2] and bx,bx ;second opérande négatif? jns short SaveSignStatusD ;non neg bx ;oui, le second opérande devient négatif neg word ptr [bp+Divisor] sbb bx,0 xor cx,1 ;note que le second opérande est négatif SaveSignStatusD: push cx ;mémorise le signe du résultat; 1 si le résultat ;est négatif, 0 s'il n'est pas négatif sub dx,dx ;met Dividend+2 (partie entière) dans DX:AX div bx ;première moitié de la division 32/16 division, ;partie entière divisée par la partie entière mov cx,ax ;met de coté la partie entière du résultat mov ax,word ptr [bp+Dividend] ;concatène la partie fractionnaire du ; dividende au reste du résultat (partie ;fractionnaire) de la division de la partie ; entière du dividende div bx ;seconde moitié de la division 32/16 if DIV_ROUNDING_ON EQ 0 shr bx,1 ;diviseur/2, moins 1 si le diviseur est adc bx,0 ; pair dec bx cmp bx,dx ;paramètre Carry si le reste est au moins adc ax,0 ; de moitié aussi grand que le diviseur, puis adc cx,0 ;l'utilise pour arrondir si nécessaire endif ;DIV_ROUNDING_ON mov dx,cx ;valeur absolue du résultat dans DX:AX pop cx and cx,cx ;le résultat est-il négatif? jz short FixedDivDone ;non, we're all set neg dx ;oui, DX:AX devient alors négatif neg ax sbb dx,0 FixedDivDone: endif ;USE386 pop bp ret _FixedDiv endp ;===================================================================== ; Retourne le sinus et le cosinus 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 & BP empilé Angle dw ? ;angle pour calculer le sinus & cosinus Cos dw ? ;pointeur sur la destination du cosinus Sin dw ? ;pointer 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 if USE386 mov bx,[bp].Angle and bx,bx ;s'assure que l'angle est entre 0 et 2*pi jns short CheckInRange MakePos: ;inférieur à 0, devient donc positif add bx,360*10 js short MakePos 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 short MakeInRange cmp bx,180*10 ;calcule quel est le quadrant ja short BottomHalf ;quadrant 2 ou 3 cmp bx,90*10 ;quadrant 0 ou 1 ja short Quadrant1 ;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 Quadrant1: 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 short 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[bx] ;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,[bp].Cos mov [bx],eax mov bx,[bp].Sin mov [bx],edx else ;!USE386 mov bx,[bp].Angle and bx,bx ;s'assure que l'angle est entre 0 et 2*pi jns short CheckInRange MakePos: ;inférieur à 0, devient alors positif add bx,360*10 js short MakePos 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 short MakeInRange cmp bx,180*10 ;calcule quel est quadrant ja short BottomHalf ;quadrant 2 ou 3 cmp bx,90*10 ;quadrant 0 ou 1 ja short Quadrant1 ;quadrant 0 shl bx,2 mov ax,word ptr CosTable[bx] ;pré-calcule le sinus mov dx,word ptr CosTable[bx+2] neg bx ;sin(Angle) = cos(90-Angle) mov cx,word ptr CosTable[bx+90*10*4+2] ;pré-calcule le cosinus mov bx,word ptr CosTable[bx+90*10*4] jmp CSDone align ALIGNMENT Quadrant1: neg bx add bx,180*10 ;convertit en angle entre 0 et 90 shl bx,2 mov ax,word ptr CosTable[bx] ;pré-calcule le cosinus mov dx,word ptr CosTable[bx+2] neg dx ;négatif dans ce quadrant neg ax sbb dx,0 neg bx ;sin(Angle) = cos(90-Angle) mov cx,word ptr CosTable[bx+90*10*4+2] ;pré-calcule le cosinus mov bx,word ptr CosTable[bx+90*10*4] 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 short Quadrant2 ;quadrant 3 shl bx,2 mov ax,word ptr CosTable[bx] ; pré-calcule le cosinus mov dx,word ptr CosTable[bx+2] neg bx ;sin(Angle) = cos(90-Angle) mov cx,word ptr CosTable[90*10*4+bx+2] ;pré-calcule le sinus mov bx,word ptr CosTable[90*10*4+bx] neg cx ;négatif dans ce quadrant neg bx sbb cx,0 jmp short CSDone align ALIGNMENT Quadrant2: neg bx add bx,180*10 ;convertit en angle entre 0 et 90 shl bx,2 mov ax,word ptr CosTable[bx] ;pré-calcule le cosinus mov dx,word ptr CosTable[bx+2] neg dx ;négatif dans ce quadrant neg ax sbb dx,0 neg bx ;sin(Angle) = cos(90-Angle) mov cx,word ptr CosTable[90*10*4+bx+2] ;pré-calcule le sinus mov bx,word ptr CosTable[90*10*4+bx] neg cx ;négatif dans ce quadrant neg bx sbb cx,0 CSDone: push bx mov bx,[bp].Cos mov [bx],ax mov [bx+2],dx mov bx,[bp].Sin pop ax mov [bx],ax mov [bx+2],cx endif ;USE386 pop bp ;restaure la stack frame ret _CosSin endp ;===================================================================== ; La matrice multiplie Xform par SourceVec, et stocke le résultat dans ; DestVec. Multiplie une matrice 4x4 fois une matrice 4x1; le résultat ; est une 4x1. Triche en supposant que la coordonnée W est à 1 et que la rangée ; du bas de la matrice est 0 0 0 1, ne s'embarrasse pas à paramétrer ; la coordonnée W de la destination. ; Appelable en C near comme suit: ; void XformVec(Xform WorkingXform, Fixedpoint *SourceVec, ; Fixedpoint *DestVec); ; ; Ce code Assembleur est équivalent à ce code C: ; int i; ; ; for (i=0; i<3; i++) ; DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) + ; FixedMul(WorkingXform[i][1], SourceVec[1]) + ; FixedMul(WorkingXform[i][2], SourceVec[2]) + ; WorkingXform[i][3]; /* pas besoin de multiplier par W = 1 */ XVparms struc dw 2 dup(?) ;adresse de retour & BP empilé WorkingXform dw ? ;pointeur sur la matrice de transformation SourceVec dw ? ;pointeur sur le vecteur source DestVec dw ? ;pointeur sur le vecteur destination XVparms ends ; Macro pour multiplier un non-386. AX, BX, CX, DX sont supprimés. FIXED_MUL MACRO M1,M2 local CheckSecondOperand,SaveSignStatus,FixedMulDone ;fait quatre produits partiels et les ; additionne, en accumulant le ; résultat dans CX:BX ;calcule les signes, nous pouvons donc ;utiliser des multiplications unsigned sub cx,cx ;suppose que les opérandes sont positifs mov bx,word ptr [&M1&+2] and bx,bx ;premier opérande négatif? jns short CheckSecondOperand ;non neg bx ;oui, le premier opérande devient négatif neg word ptr [&M1&] sbb bx,0 mov word ptr [&M1&+2],bx inc cx ;note que le premier opérande est négatif CheckSecondOperand: mov bx,word ptr [&M2&+2] and bx,bx ;second opérande négatif? jns short SaveSignStatus ;non neg bx ;oui, le second opérande devient négatif neg word ptr [&M2&] sbb bx,0 mov word ptr [&M2&+2],bx xor cx,1 ;note que le second opérande est négatif SaveSignStatus: push cx ;mémorise le signe du résultat; 1 si le résultat ; est négatif, 0 si le résultat n'est pas négatif mov ax,word ptr [&M1&+2] ;mot high fois mot high mul word ptr [&M2&+2] mov cx,ax ; ;suppose qu'il n'y a pas de débordement dans DX mov ax,word ptr [&M1&+2] ;mot high fois mot low mul word ptr [&M2&] mov bx,ax add cx,dx mov ax,word ptr [&M1&] ;mot low fois mot high mul word ptr [&M2&+2] add bx,ax adc cx,dx mov ax,word ptr [&M1&] ;mot low fois mot low mul word ptr [&M2&] if MUL_ROUNDING_ON add ax,8000h ;arrondit en ajoutant 2^(-17) adc bx,dx else ;!MUL_ROUNDING_ON add bx,dx ;n'arrondit pas endif ;MUL_ROUNDING_ON adc cx,0 mov dx,cx mov ax,bx pop cx and cx,cx ;le résultat est-il négatif? jz short FixedMulDone ;non, we're all set neg dx ;oui, DX:AX devient donc négatif neg ax sbb dx,0 FixedMulDone: ENDM 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 if USE386 mov si,[bp].WorkingXform ;SI pointe sur la matrice xform mov bx,[bp].SourceVec ;BX pointe sur le vecteur source mov di,[bp].DestVec ;DI pointe sur le vecteur destination soff=0 doff=0 REPT 3 ;le fait une fois pour chaque destination ;X, Y, et 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 X if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous la forme 16.16 mov ecx,eax ;paramètre le total running 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 MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous la forme 16.16 add ecx,eax ; total running de cette rangée mov eax,[si+soff+8] ;entrée de la colonne 2 dans cette rangée imul dword ptr [bx+8] ;entrée xform fois entrée source Z if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous la forme 16.16 add ecx,eax ;total running de cette rangée add ecx,[si+soff+12] ;ajoute dans la translation mov [di+doff],ecx ;sauve le résultat dans le vecteur de destination soff=soff+16 doff=doff+4 ENDM else ;!USE386 mov si,[bp].WorkingXform ;SI pointe sur la matrice xform mov di,[bp].SourceVec ;DI pointe sur le vecteur source mov bx,[bp].DestVec ;BX pointe sur le vecteur destination push bp ;préserve le pointeur de la stack frame soff=0 doff=0 REPT 3 ;le fait en une fois pour chaque destination ;X, Y, et Z push bx ;mémorise le pointeur du vecteur destination push word ptr [si+soff+2] push word ptr [si+soff] push word ptr [di+2] push word ptr [di] call _FixedMul ;entrée xform fois entrée source X add sp,8 ;vide les paramètres de la stack mov cx,ax ;paramètre total running mov bp,dx push cx ;préserve le mot low du total running push word ptr [si+soff+4+2] push word ptr [si+soff+4] push word ptr [di+4+2] push word ptr [di+4] call _FixedMul ;entrée xform fois entrée source Y add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx push cx ;préserve le mot low du total running push word ptr [si+soff+8+2] push word ptr [si+soff+8] push word ptr [di+8+2] push word ptr [di+8] call _FixedMul ;entrée xform fois entrée source Z add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx add cx,[si+soff+12] ;ajoute dans la translation adc bp,[si+soff+12+2] pop bx ;restaure le pointeur du vecteur destination mov [bx+doff],cx ;sauve le résultat dans le vecteur destination mov [bx+doff+2],bp soff=soff+16 doff=doff+4 ENDM pop bp ;restaure le pointeur de la stack frame endif ;USE386 pop di ;restaure les variables registre pop si pop bp ;restaure la stack frame ret _XformVec endp ;===================================================================== ; La matrice multiplie SourceXform1 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 0 0 0 1, ne s'embarrasse pas à paramétrer la rangée du bas ; de la destination. ; Appelable en C near comme suit: ; void ConcatXforms(Xform SourceXform1, Xform SourceXform2, ; Xform DestXform) ; ; Ce code Assembleur est équivalent à ce code C: ; int i, j; ; ; for (i=0; i<3; i++) { ; for (j=0; j<3; j++) ; DestXform[i][j] = ; FixedMul(SourceXform1[i][0], SourceXform2[0][j]) + ; FixedMul(SourceXform1[i][1], SourceXform2[1][j]) + ; FixedMul(SourceXform1[i][2], SourceXform2[2][j]); ; DestXform[i][3] = ; FixedMul(SourceXform1[i][0], SourceXform2[0][3]) + ; FixedMul(SourceXform1[i][1], SourceXform2[1][3]) + ; FixedMul(SourceXform1[i][2], SourceXform2[2][3]) + ; SourceXform1[i][3]; ; } CXparms struc dw 2 dup(?) ;adresse de retour & BP empilé SourceXform1 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 if USE386 mov bx,[bp].SourceXform2 ;BX pointe sur la matrice xform2 mov si,[bp].SourceXform1 ;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 ;une fois pour chacune des 3 colonnes, ; en supposant 0 comme entrée du bas (pas de ; 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 la colonne if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous forme 16.16 mov ecx,eax ;paramètre le total running 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 ;la colonne if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous forme 16.16 add ecx,eax ; total running 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 ;la colonne if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous forme 16.16 add ecx,eax ; total running mov [di+coff+roff],ecx ;sauve le résultat dans la matrice destination coff=coff+4 ;pointe sur la prochaine colonne dans xform2 ;& destination ENDM ;maintenant traite la quatrième colonne, ;supposant 1 comme entrée du bas, ; provoquant la translation à faire 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 la colonne if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous la forme 16.16 mov ecx,eax ;paramètre le total running 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 ;la colonne If MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;décale le résultat sous la forme 16.16 add ecx,eax ;total running 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 ;la colonne if MUL_ROUNDING_ON add eax,8000h ;arrondit en ajoutant 2^(-17) adc edx,0 ;toute la partie du résultat dans DX endif ;MUL_ROUNDING_ON shrd eax,edx,16 ;redécale le résultat sous la forme 16.16 add ecx,eax ;total running 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 else ;!USE386 mov di,[bp].SourceXform2 ;DI pointe sur la matrice xform2 mov si,[bp].SourceXform1 ;SI pointe sur la matrice xform1 mov bx,[bp].DestXform ;BX pointe sur la matrice destination xform push bp ;préserve le pointeur de la stack frame roff=0 ;offset de rangée REPT 3 ;une fois pour chaque rangée coff=0 ;offset de colonne REPT 3 ;une fois pour chacune des 3 premières colonnes, ;en supposant 0 comme entrée de bas (pas de ; translation) push bx ;mémorise le pointeur du vecteur destination push word ptr [si+roff+2] push word ptr [si+roff] push word ptr [di+coff+2] push word ptr [di+coff] call _FixedMul ;entrée de la colonne 0 dans cette rangée ; fois l'entrée de la rangée 0 dans la colonne add sp,8 ;vide les paramètres de la stack mov cx,ax ;paramètre total running mov bp,dx push cx ;préserve le mot low du total running push word ptr [si+roff+4+2] push word ptr [si+roff+4] push word ptr [di+coff+16+2] push word ptr [di+coff+16] call _FixedMul ;entrée de la colonne 1 dans cette rangée fois ;l'entrée de la rangée 0 dans la colonne add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx push cx ;préserve le mot low du total running push word ptr [si+roff+8+2] push word ptr [si+roff+8] push word ptr [di+coff+32+2] push word ptr [di+coff+32] call _FixedMul ;entrée de la colonne 1 dans cette rangée fois ;l'entrée de la rangée 1 dans la colonne add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx pop bx ;restaure le pointeur DestXForm mov [bx+coff+roff],cx ;sauve le résultat dans la matrice destination mov [bx+coff+roff+2],bp coff=coff+4 ;pointe sur la prochaine colonne dans xform2 ;& destination ENDM ;traite maintenant la quatrième colonne, ;en supposant 1 comme entrée du bas, ;provoquant la translation à faire push bx ;mémorise le pointeur du vecteur destination push word ptr [si+roff+2] push word ptr [si+roff] push word ptr [di+coff+2] push word ptr [di+coff] call _FixedMul ;entrée de la colonne 0 dans cette rangée fois ;l'entrée de la rangée 0 dans la colonne add sp,8 ;vide les paramètres de la stack mov cx,ax ;paramètre le total running mov bp,dx push cx ;préserve le mot low du total running push word ptr [si+roff+4+2] push word ptr [si+roff+4] push word ptr [di+coff+16+2] push word ptr [di+coff+16] call _FixedMul ;entrée de la colonne 1 dans cette rangée 1 fois ;l'entrée de la rangée 1 dans la colonne add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx push cx ;préserve le mot low du total running push word ptr [si+roff+8+2] push word ptr [si+roff+8] push word ptr [di+coff+32+2] push word ptr [di+coff+32] call _FixedMul ;entrée de la colonne 1 dans cette rangée fois ;l'entrée de la rangée 1 dans cette colonne add sp,8 ;vide les paramètres de la stack pop cx ;restaure le mot low du total running add cx,ax ;total running de cette rangée adc bp,dx add cx,[si+roff+12] ;ajoute dans la translation add bp,[si+roff+12+2] pop bx ;restaure le pointeur DestXForm mov [bx+coff+roff],cx ;sauve le résultat dans la matrice destination mov [bx+coff+roff+2],bp coff=coff+4 ;pointe sur la prochaine colonne dans xform2 ;& destination roff=roff+16 ;pointe sur la prochaine colonne dans xform2 ;& destination ENDM pop bp ;restaure le pointeur de la stack frame endif ;USE386 pop di ;restaure les variables registre pop si pop bp ;restaure la stack frame ret _ConcatXforms endp end