; Implémentation de tracé de ligne rapide par traitements segmentés pour le mode ; 0x13, le mode 320x200 en 256 couleurs du VGA. ; Affiche une ligne, dans la couleur Color, entre les extrémités spécifiées . ; Appelable depuis C near: ; void LineDraw(int XStart, int YStart, int XEnd, int YEnd, int Color) ; Testé avec TASM 4.0 et lié à L15-2.C: ; bcc -ms l15-2.c l16-1.asm ; Testé par Jim Mischel 11/30/94 SCREEN_WIDTH equ 320 SCREEN_SEGMENT equ 0a000h .model small .code ; Parametres à appeler. parms struc dw ? ;BP empilé dw ? ;adresse de retour empilée XStart dw ? ;coordonnée X du début de a ligne YStart dw ? ;coordonnée Y du début de la ligne XEnd dw ? ;coordonnée X de la fin de la ligne YEnd dw ? ;coordonnée Y de la fin de la ligne Color db ? ;couleur dans laquelle afficher la ;ligne db ? ;pour l'alignement sur un mots parms ends ; Variables locales. AdjUp equ -2 ;terme d'erreur ajusté à ;chaque avancée AdjDown equ -4 ;terme d'erreur ajusté quand ; il déborde WholeStep equ -6 ;longueur minimale du traitement XAdvance equ -8 ;1 ou -1, pour la direction ;dans laquelle avance X LOCAL_SIZE equ 8 public _LineDraw _LineDraw proc near cld push bp ;préserve la stack frame de ;l'appelant mov bp,sp ;pointe sur notre stack frame sub sp,LOCAL_SIZE ;alloue de l'espace aux ; variables locales push si ;préserve les variables ;registre C push di push ds ;préserve DS de l'appelant ; Nous afficherons de haut en bas, pour réduire le nombre de cas à traiter, ; et afficher les mêmes lignes entre les mêmes extrémités. mov ax,[bp].YStart cmp ax,[bp].YEnd jle LineIsTopToBottom xchg [bp].YEnd,ax ;permute les extrémités mov [bp].YStart,ax mov bx,[bp].XStart xchg [bp].XEnd,bx mov [bp].XStart,bx LineIsTopToBottom: ; DI pointe sur le premier pixel à afficher. mov dx,SCREEN_WIDTH mul dx ;YStart * SCREEN_WIDTH mov si,[bp].XStart mov di,si add di,ax ;DI = YStart * SCREEN_WIDTH + ; XStart ; = offset du pixel initial ; Calcule la distance verticale far que nous parcourons (garantie positive). mov cx,[bp].YEnd sub cx,[bp].YStart ;CX = YDelta ; Calcule si nous allons à gauche ou à droite, et la distance ; horizontale que nous parcourons. Dans le processus, les cas particuliers des ;lignes verticales, pour la vitesse et pour éviter des effets de bord et ; la division par 0. mov dx,[bp].XEnd sub dx,si ;XDelta jnz NotVerticalLine ;XDelta == 0 signifie ligne ;verticale ;c'est une ligne verticale ;oui, cas particulier de ligne ;verticale mov ax,SCREEN_SEGMENT mov ds,ax ;DS:DI pointe sur le premier o ;ctet à afficher mov al,[bp].Color VLoop: mov [di],al add di,SCREEN_WIDTH dec cx jns VLoop jmp Done ; Code du cas particulier de lignes horizontales. align 2 IsHorizontalLine: mov ax,SCREEN_SEGMENT mov es,ax ;ES:DI pointe sur le premier ;octet ; à afficher mov al,[bp].Color mov ah,al ;duplique dans l'octet de ;poids fort pour accès mot and bx,bx ;de gauche à droite? jns DirSet ;oui sub di,dx ;de gauche à droite, pointe sur ; l'extrémité gauche nous pouvons ;donc aller de gauche à droite (ce qui ; nous évite les désagréments de ;REP STOSW de droite à gauche) DirSet: mov cx,dx inc cx ;nombre de pixels à afficher shr cx,1 ;nombre de mots à afficher rep stosw ;traiter le plus de mots ;possible adc cx,cx rep stosb ;traité l'octet impair, s'il ;y en a un jmp Done ; Code du cas particulier de lignes diagonales. align 2 IsDiagonalLine: mov ax,SCREEN_SEGMENT mov ds,ax ;DS:DI pointe sur le premier ;octet à afficher mov al,[bp].Color add bx,SCREEN_WIDTH ;avance la distance d'un pixel ; au prochain DLoop: mov [di],al add di,bx dec cx jns DLoop jmp Done align 2 NotVerticalLine: mov bx,1 ;suppose de gauche à droite, ;aussi XAdvance = 1 ;***laisse les drapeaux tels quels*** jns LeftToRight ;de gauche à droite, neg bx ;de droite à gauche, XAdvance ;= -1 neg dx ;|XDelta| LeftToRight: ; Code du cas particulier des lignes horizontales. and cx,cx ;YDelta == 0? jz IsHorizontalLine ;oui ; cas particulier de lignes diagonales. cmp cx,dx ;YDelta == XDelta? jz IsDiagonalLine ;yes ; Détermine si la ligne X ou Y est l'axe principal, traite selon. cmp dx,cx jae XMajor jmp YMajor ; Ligne X axe principal(ligne plus horizontale que verticale). align 2 XMajor: mov ax,SCREEN_SEGMENT mov es,ax ;ES:DI pointe sur le premier ;octet à afficher and bx,bx ;de gauche à droite? jns DFSet ;oui, CLD est déjà positionné std ;droite à gauche, donc afficher ; en arrière DFSet: mov ax,dx ;XDelta sub dx,dx ;prépare la division div cx ;AX = XDelta/YDelta ;(nombre minimum de pixels dans ; le traitement de cette ligne) ;DX = XDelta % YDelta mov bx,dx ;le terme d'erreur est ajusté ;chaque fois que Y avance de 1; add bx,bx ; utiliser pour signaler quand ; un pixel supplémentaire mov [bp].AdjUp,bx ; devrait être affiché comme ; partie du traitement, pour ; tenir compte des étapes ; fractionnaires le long de ; l'axe X par étape de 1pixel le ;long de Y mov si,cx ;le terme d'erreur est ajusté ;quand il déborde add si,si ;utilisé pour effectuer ; l'avancée de X mov [bp].AdjDown,si ; au même moment ;Terme d'erreur initial; correspond à l'avancée initiale de 0.5 le long ;de l'axe Y. sub dx,si ;(XDelta % YDelta) - (YDelta *2) ;DX = terme d'erreur initial ;Le premier et dernier ;traitement sont segmentés, ;car Y avance seulement de 0.5 ;et non de 1. Divise un ;traitement complet, plus le ;pixel initial, entre le premier ; et dernier traitement. mov si,cx ;SI = YDelta mov cx,ax ;avancée complète (longueur ;minimale du traitement) shr cx,1 inc cx ;compte de pixel initial = ;(avancée complète / 2) + 1; ;(peut être ajustée plus tard). ; ce qui correspond également au ;compte de pixel du ;traitement final push cx ;mémorise le compte de pixel du ;traitement final pour plus tard ;Si la longueur du traitement est paire et qu'il n'y a pas d'avancée fractionnaire ;nous avons un pixel qui pourrait aller soit avec le premier soit avec le dernier ;traitement segmenté que nous allouerons arbitrairement au dernier traitement ;segmenté. Si le nombre de pixels par exécution est impair, nous avons un pixel ;qui ne peut être alloué ni au premier ni au dernier traitement partiel, aussi ;nous ajouterons 0 5 au terme d'erreur ainsi ce pixel sera traité par une boucle ;normale de traitement complet. add dx,si ;suppose une longueur impaire, ; ajoute YDelta au terme ; d'erreur (ajoute 0.5 pixel au ; terme d'erreur) test al,1 ;la longueur du traitement est ;-elle paire? jnz XMajorAdjustDone ;non, travail déjà effectué ;pour le cas impair sub dx,si ;la longueur est paire, effacer ;le travail effectué pour impair and bx,bx ;l'ajustement est-il égal à 0? jnz XMajorAdjustDone ;non (nous n'avons pas besoin ; de vérifier si la longueur est ; impaire, à cause du test ;ci-dessus) dec cx ;les deux conditions sont ;remplies ; réduit le traitement de 1 XMajorAdjustDone: mov [bp].WholeStep,ax ;avancée complète (longueur ; minimale du traitement) mov al,[bp].Color ;AL = couleur à afficher ;Affiche le premier traitement ;segmenté de pixels. rep stosb ;affiche le dernier traitement ; add di,SCREEN_WIDTH ;avance le long de l'axe ;secondaire (Y) ;Affiche toutes les exécutions ;complètes. cmp si,1 ;y a t-il plus de 2 lignes d'a ;ffichage, aussi ;y aurait-il des traitements ;complets ; (SI = # scans - 1) jna XMajorDrawLast ;non, il n'y en a pas dec dx ;ajuste le terme d'erreur par ;-1 aussi nous pouvons utiliser ; le test retenue shr si,1 jnc XMajorFullRunsOddEntry XMajorFullRunsLoop: mov cx,[bp].WholeStep ;le traitement est au moins ; de cette longueur add dx,bx ;avance le terme d'erreur et ;affiche un pixel jnc XMajorNoExtra ; supplémentaire si le terme ; d'erreur l'indique inc cx ;un pixel supplémentaire dans ;le traitement sub dx,[bp].AdjDown ;réinitialise le terme d'erreur XMajorNoExtra: rep stosb ;affiche le traitement de cet ;te ligne d'affichage add di,SCREEN_WIDTH ;avance le long de l'axe ;secondaire (Y) XMajorFullRunsOddEntry: ;entre ici dans la boucle si le ; nombre des traitement complets ; est impair mov cx,[bp].WholeStep ;le traitement est au moins ; de cette longueur add dx,bx ;avance le terme d'erreur et a ;joute un pixel jnc XMajorNoExtra2 ; supplémentaire si le terme d ;'erreur l'indique inc cx ;un pixel supplémentaire dans ;le traitement sub dx,[bp].AdjDown ;reinitialise le terme d'erreur XMajorNoExtra2: rep stosb ;affiche le traitement de cet ;te ligne d'affichage add di,SCREEN_WIDTH ;avance le long de l'axe ;secondaire (Y) dec si jnz XMajorFullRunsLoop ; Affiche la dernière exécution des pixels. XMajorDrawLast: pop cx ;récupère la longueur de pixel ;s du dernier traitement rep stosb ;affiche le dernier traitement cld ;restaure le drapeau de ; direction normale jmp Done ; Ligne principale Y (ligne plus verticale que horizontale). align 2 YMajor: mov [bp].XAdvance,bx ;mémorise le sens de l'avancée ;de X mov ax,SCREEN_SEGMENT mov ds,ax ;DS:DI sur le premier octet ;à afficher mov ax,cx ;YDelta mov cx,dx ;XDelta sub dx,dx ;prépare la division div cx ;AX = YDelta/XDelta ;(nombre minimum de pixels dans ; le traitement de cette ligne) ;DX = YDelta % XDelta mov bx,dx ;le terme d'erreur est ajusté ;chaque fois que X avance de 1; add bx,bx ; utilisé pour signaler quand ;un pixel supplémentaire devrait mov [bp].AdjUp,bx ; être affiché dans une partie ; du traitement, pour compenser ; les avancés fractionnaires ; le long de l'axe Y par ; l'avancée de 1 pixel le long ;de X mov si,cx ;le terme d'erreur est ajusté ;quand il déborde add si,si ; utilisé pour effectuer ;l'avancée Y mov [bp].AdjDown,si ; au même moment ; Terme d'erreur initial; correspond à l'avancée initiale de 0.5 le long de l'axe X. sub dx,si ;(YDelta % XDelta) - (XDelta * ; 2) ;DX = terme d'erreur initial ; Les premier et dernier traitement sont segmentés, car X avance seulement de 0 ; 5 et non de 1. Réparti un traitement complet, plus le pixel initial, ; entre le premier et dernier traitement. mov si,cx ;SI = XDelta mov cx,ax ;avancée complète (longueur ;minimale du traitement) shr cx,1 inc cx ;compte de pixel initial = ; (étape complète / 2) + 1; ; (peut être ajustée plus tard) push cx ;mémorise le compte de pixels ;du traitement final pour ;plus tard ;Si la longueur du traitement est paire et qu'il n'y a pas d'avancée ;fractionnaire nous avons un pixel qui pourrait aller soit avec le premier soit ;avec le dernier traitement segmenté que nous allouerons arbitrairement à la ;dernière exécution. Si le nombre de pixels par exécution est impair, nous avons ;un pixel qui ne peut être alloué ni au premier ni au dernier traitement ;segmenté, aussi nous ajouterons 0,5 au terme d'erreur ainsi ce pixel sera traité ;par une boucle normale de traitement complet. add dx,si ;suppose une longueur impaire, ; ajoute XDelta au terme ;d'erreur test al,1 ;la longueur du traitement est ;-elle paire? jnz YMajorAdjustDone ;non, le travail est déjà ;effectué ; pour le cas impair sub dx,si ;la longueur est paire, effacer ;le travail déjà effectué pour ;le cas impair and bx,bx ;l'ajustement est-il égal à 0? jnz YMajorAdjustDone ;non (nous n'avons pas besoin ; de vérifier si la longueur est ; impaire, à cause du test ci- dec cx ; dessus) les deux conditions ; sont remplies,réduit de 1 ; le traitement initial YMajorAdjustDone: mov [bp].WholeStep,ax ;avancée complète (longueur ;minimale de traitement) mov al,[bp].Color ;AL = couleur à afficher mov bx,[bp].XAdvance ;sens dans lequel X avance ;Affiche la premier ;traitement segmenté de ;pixels. YMajorFirstLoop: mov [di],al ;affiche le pixel add di,SCREEN_WIDTH ;avance le long de l'axe ;principal Y dec cx jnz YMajorFirstLoop add di,bx ;avance le long de l'axe ;secondaire X ;Affiche toutes les traitements ;complets. cmp si,1 ;nombre de traitement complets ;. Y a-t-il plus de 2 ; colonnes, ainsi avons-nous des ;traitements complets? ; (SI = nombre de colonnes - 1) jna YMajorDrawLast ;non, nous n'en avons pas dec dx ;ajusté le terme d'erreur de - ;1 aussi nous pouvons utiliser ; le test retenue shr si,1 ;convertit le compte de colonnes ; en paire de colonnes jnc YMajorFullRunsOddEntry ;si le nombre de colonnes ; est impair, traitons ; la colonne impaire maintenant YMajorFullRunsLoop: mov cx,[bp].WholeStep ;le traitement est au moins ; de cette longueur add dx,[bp].AdjUp ;augmente le terme d'erreur et ; ajoute un pixel jnc YMajorNoExtra ; supplémentaire si le terme ;d'erreur l'indique inc cx ;un pixel supplémentaire dans ;le traitement sub dx,[bp].AdjDown ;réinitialise le terme d'erreur YMajorNoExtra: ;affiche le traitement YMajorRunLoop: mov [di],al ;affiche le pixel add di,SCREEN_WIDTH ;avance le long de l'axe ;principal Y dec cx jnz YMajorRunLoop add di,bx ;avance le long de l'axe ;secondaire X YMajorFullRunsOddEntry: ;entre ici dans la boucle si le ; nombre de traitements ; complets est impair mov cx,[bp].WholeStep ;le traitement est au moins ; de cette longueur add dx,[bp].AdjUp ;avance le terme d'erreur et ;ajoute un pixel jnc YMajorNoExtra2 ; supplémentaire si le terme ;d'erreur l'indique inc cx ;un pixel supplémentaire dans ;l'exécution sub dx,[bp].AdjDown ;réinitialise le terme d'erreur YMajorNoExtra2: ;affiche le traitement YMajorRunLoop2: mov [di],al ;affiche le pixel add di,SCREEN_WIDTH ;avance le long de l'axe ;principal Y dec cx jnz YMajorRunLoop2 add di,bx ;avance le long de l'axe ;secondaire X dec si jnz YMajorFullRunsLoop ;Affiche la dernière exécution ;des pixels. YMajorDrawLast: pop cx ;récupère la longueur de pixels ; du dernier traitement YMajorLastLoop: mov [di],al ;affiche le pixel add di,SCREEN_WIDTH ;avance le long de l'axe ;principal Y dec cx jnz YMajorLastLoop Fini: pop ds ;restaure DS de l'appelant pop di pop si ;restaure les variables ;registre C mov sp,bp ;désalloue les variables ;locales pop bp ;restaure la stack frame de ;l'appelant ret _LineDraw endp end