;----------------------------------------------------------------------- ; MODULE XRECT ; ; Rectangle functions all MODE X 256 Color resolutions ; ; Compile with Tasm. ; C callable. ; ; ; ****** XLIB - Mode X graphics library **************** ; ****** **************** ; ****** Written By Themie Gouthas **************** ; ; egg@dstos3.dsto.gov.au ; teg@bart.dsto.gov.au ;----------------------------------------------------------------------- include xlib.inc include xrect.inc .data ; Plane masks for clipping left and right edges of rectangle. LeftClipPlaneMask db 00fh,00eh,00ch,008h RightClipPlaneMask db 00fh,001h,003h,007h .code ;--------------------------------------------------------------------------- ; Mode X (320x240, 256 colors) rectangle solid colour fill routine. ; ; Based on code originally published in DDJ Mag by M. Abrash ; ; with TASM 2. C near-callable as: ; ; void x_rect_fill_clipped(int StartX, int StartY, int EndX, int EndY, ; unsigned int PageBase, unsigne int color); ; ; _x_rect_fill_clipped proc ARG StartX:word,StartY:word,EndX:word,EndY:word,PageBase:word,Color:word push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame push si ;preserve caller's register variables push di mov dx,[_TopClip] ; Compare u.l. Y coord with Top mov cx,[_BottomClip] mov ax,[StartY] mov bx,[EndY] cmp dx,ax jle @@CheckBottomClip cmp dx,bx jg @@NotVisible mov [StartY],dx @@CheckBottomClip: cmp cx,bx jg @@CheckLeftClip cmp cx,ax jl @@NotVisible mov [EndY],cx @@CheckLeftClip: mov dx,[_LeftClip] ; Compare u.l. Y coord with Top mov cx,[_RightClip] mov ax,[StartX] mov bx,[EndX] sal dx,2 sal cx,2 cmp dx,ax jle @@CheckRightClip cmp dx,bx jg @@NotVisible mov [StartX],dx @@CheckRightClip: cmp cx,bx jg RFClipDone cmp cx,ax jl @@NotVisible mov [EndX],cx jmp RFClipDone @@NotVisible: mov ax,1 pop di ; restore registers pop si pop bp ret _x_rect_fill_clipped endp ;--------------------------------------------------------------------------- ; Mode X (320x240, 256 colors) rectangle solid colour fill routine. ; ; Based on code originally published in DDJ Mag by M. Abrash ; ; with TASM 2. C near-callable as: ; ; void x_rect_fill(int StartX, int StartY, int EndX, int EndY, ; unsigned int PageBase, unsigne int color); ; ; _x_rect_fill proc ARG StartX:word,StartY:word,EndX:word,EndY:word,PageBase:word,Color:word push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame push si ;preserve caller's register variables push di RFClipDone: cld mov ax,[_ScrnLogicalByteWidth] mul [StartY] ;offset in page of top rectangle scan line mov di,[StartX] sar di,2 ;X/4 = offset of first rectangle pixel in scan add di,ax ;offset of first rectangle pixel in page add di,[PageBase] ;offset of first rectangle pixel in ; display memory mov ax,SCREEN_SEG ;point ES:DI to the first rectangle mov es,ax ; pixel's address mov dx,SC_INDEX ;set the Sequence Controller Index to mov al,MAP_MASK ; point to the Map Mask register out dx,al inc dx ;point DX to the SC Data register mov si,[StartX] and si,0003h ;look up left edge plane mask mov bh,LeftClipPlaneMask[si] ; to clip & put in BH mov si,[EndX] and si,0003h ;look up right edge plane mov bl,RightClipPlaneMask[si] ; mask to clip & put in BL mov cx,[EndX] ;calculate # of addresses across rect mov si,[StartX] cmp cx,si jle @@FillDone ;skip if 0 or negative width dec cx and si,not 011b sub cx,si sar cx,2 ;# of addresses across rectangle to fill - 1 jnz @@MasksSet ;there's more than one byte to draw and bh,bl ;there's only one byte, so combine the left ; and right edge clip masks @@MasksSet: mov si,[EndY] sub si,[StartY] ;BX = height of rectangle jle @@FillDone ;skip if 0 or negative height mov ah,byte ptr [Color] ;color with which to fill mov bp,[_ScrnLogicalByteWidth] ;stack frame isn't needed any more sub bp,cx ;distance from end of one scan line to start dec bp ; of next @@FillRowsLoop: push cx ;remember width in addresses - 1 mov al,bh ;put left-edge clip mask in AL out dx,al ;set the left-edge plane (clip) mask mov al,ah ;put color in AL stosb ;draw the left edge dec cx ;count off left edge byte js @@FillLoopBottom ;that's the only byte jz @@DoRightEdge ;there are only two bytes mov al,00fh ;middle addresses drawn 4 pixels at a pop out dx,al ;set the middle pixel mask to no clip mov al,ah ;put color in AL rep stosb ;draw middle addresses four pixels apiece @@DoRightEdge: mov al,bl ;put right-edge clip mask in AL out dx,al ;set the right-edge plane (clip) mask mov al,ah ;put color in AL stosb ;draw the right edge @@FillLoopBottom: add di,bp ;point to start of the next scan line of ; the rectangle pop cx ;retrieve width in addresses - 1 dec si ;count down scan lines jnz @@FillRowsLoop @@FillDone: pop di ;restore caller's register variables pop si pop bp ;restore caller's stack frame ret _x_rect_fill endp ;--------------------------------------------------------------------------- ; Mode X (320x240, 256 colors) rectangle 4x4 pattern fill routine. ; Upper left corner of pattern is always aligned to a multiple-of-4 ; row and column. Works on all VGAs. Uses approach of copying the ; pattern to off-screen display memory, then loading the latches with ; the pattern for each scan line and filling each scan line four ; pixels at a time. Fills up to but not including the column at EndX ; and the row at EndY. No clipping is performed. All ASM code tested ; ; ; Based on code originally published in DDJ Mag by M. Abrash ; ; ; C near-callable as: ; ; void x_rect_pattern_clipped(int StartX, int StartY, int EndX, int EndY, ; unsigned int PageBase, char far * Pattern); ; ; _x_rect_pattern_clipped proc ARG StartX:word,StartY:word,EndX:word,EndY:word,PageBase:word,Pattern:dword LOCAL NextScanOffset:word,RectAddrWidth:word,Height:word=LocalStk push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame sub sp,LocalStk ;allocate space for local vars push si ;preserve caller's register variables push di push ds mov dx,[_TopClip] ; Compare u.l. Y coord with Top mov cx,[_BottomClip] mov ax,[StartY] mov bx,[EndY] cmp dx,ax jle @@CheckBottomClip cmp dx,bx jg @@NotVisible mov [StartY],dx @@CheckBottomClip: cmp cx,bx jg @@CheckLeftClip cmp cx,ax jl @@NotVisible mov [EndY],cx @@CheckLeftClip: mov dx,[_LeftClip] ; Compare u.l. Y coord with Top mov cx,[_RightClip] mov ax,[StartX] mov bx,[EndX] sal dx,2 sal cx,2 cmp dx,ax jle @@CheckRightClip cmp dx,bx jg @@NotVisible mov [StartX],dx @@CheckRightClip: cmp cx,bx jg RPClipDone cmp cx,ax jl @@NotVisible mov [EndX],cx jmp RPClipDone @@NotVisible: mov ax,1 pop ds pop di ; restore registers pop si mov sp,bp pop bp ret _x_rect_pattern_clipped endp ;--------------------------------------------------------------------------- ; Mode X (320x240, 256 colors) rectangle 4x4 pattern fill routine. ; Upper left corner of pattern is always aligned to a multiple-of-4 ; row and column. Works on all VGAs. Uses approach of copying the ; pattern to off-screen display memory, then loading the latches with ; the pattern for each scan line and filling each scan line four ; pixels at a time. Fills up to but not including the column at EndX ; and the row at EndY. No clipping is performed. All ASM code tested ; ; ; Based on code originally published in DDJ Mag by M. Abrash ; ; ; C near-callable as: ; ; void x_rect_pattern(int StartX, int StartY, int EndX, int EndY, ; unsigned int PageBase, char far * Pattern); _x_rect_pattern proc ARG StartX:word,StartY:word,EndX:word,EndY:word,PageBase:word,Pattern:dword LOCAL NextScanOffset:word,RectAddrWidth:word,Height:word=LocalStk push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame sub sp,LocalStk ;allocate space for local vars push si ;preserve caller's register variables push di push ds RPClipDone: cld mov ax,SCREEN_SEG ;point ES to display memory mov es,ax ;copy pattern to display memory buffer lds si,dword ptr [Pattern] ;point to pattern to fill with mov di,PATTERN_BUFFER ;point ES:DI to pattern buffer mov dx,SC_INDEX ;point Sequence Controller Index to mov al,MAP_MASK ; Map Mask out dx,al inc dx ;point to SC Data register mov cx,4 ;4 pixel quadruplets in pattern @@DownloadPatternLoop: mov al,1 ; out dx,al ;select plane 0 for writes movsb ;copy over next plane 0 pattern pixel dec di ;stay at same address for next plane mov al,2 ; out dx,al ;select plane 1 for writes movsb ;copy over next plane 1 pattern pixel dec di ;stay at same address for next plane mov al,4 ; out dx,al ;select plane 2 for writes movsb ;copy over next plane 2 pattern pixel dec di ;stay at same address for next plane mov al,8 ; out dx,al ;select plane 3 for writes movsb ;copy over next plane 3 pattern pixel ; and advance address loop @@DownloadPatternLoop pop ds mov dx,GC_INDEX ;set the bit mask to select all bits mov ax,00000h+BIT_MASK ; from the latches and none from out dx,ax ; the CPU, so that we can write the ; latch contents directly to memory mov ax,[StartY] ;top rectangle scan line mov si,ax and si,011b ;top rect scan line modulo 4 add si,PATTERN_BUFFER ;point to pattern scan line that ; maps to top line of rect to draw mov dx,[_ScrnLogicalByteWidth] mul dx ;offset in page of top rect scan line mov di,[StartX] mov bx,di sar di,2 ;X/4 = offset of first rectangle pixel in scan add di,ax ;offset of first rectangle pixel in page add di,[PageBase] ;offset of first rectangle pixel in ; display memory and bx,0003h ;look up left edge plane mask mov ah,LeftClipPlaneMask[bx] ; to clip mov bx,[EndX] and bx,0003h ;look up right edge plane mov al,RightClipPlaneMask[bx] ; mask to clip mov bx,ax ;put the masks in BX mov cx,[EndX] ;calculate # of addresses across rect mov ax,[StartX] cmp cx,ax jle @@FillDone ;skip if 0 or negative width dec cx and ax,not 011b sub cx,ax sar cx,2 ;# of addresses across rectangle to fill - 1 jnz @@MasksSet ;there's more than one pixel to draw and bh,bl ;there's only one pixel, so combine the left ; and right edge clip masks @@MasksSet: mov ax,[EndY] sub ax,[StartY] ;AX = height of rectangle jle @@FillDone ;skip if 0 or negative height mov [Height],ax mov ax,[_ScrnLogicalByteWidth] sub ax,cx ;distance from end of one scan line to start dec ax ; of next mov [NextScanOffset],ax mov [RectAddrWidth],cx ;remember width in addresses - 1 mov dx,SC_INDEX+1 ;point to Sequence Controller Data reg ; (SC Index still points to Map Mask) @@FillRowsLoop: mov cx,[RectAddrWidth] ;width across - 1 mov al,es:[si] ;read display memory to latch this scan ; line's pattern inc si ;point to the next pattern scan line, wrapping jnz short @@NoWrap ; back to the start of the pattern if sub si,4 ; we've run off the end @@NoWrap: mov al,bh ;put left-edge clip mask in AL out dx,al ;set the left-edge plane (clip) mask stosb ;draw the left edge (pixels come from latches; ; value written by CPU doesn't matter) dec cx ;count off left edge address js @@FillLoopBottom ;that's the only address jz @@DoRightEdge ;there are only two addresses mov al,00fh ;middle addresses drawn 4 pixels at a pop out dx,al ;set middle pixel mask to no clip rep stosb ;draw middle addresses four pixels apiece ; (from latches; value written doesn't matter) @@DoRightEdge: mov al,bl ;put right-edge clip mask in AL out dx,al ;set the right-edge plane (clip) mask stosb ;draw the right edge (from latches; value ; written doesn't matter) @@FillLoopBottom: add di,[NextScanOffset] ;point to the start of the next scan ; line of the rectangle dec word ptr [Height] ;count down scan lines jnz @@FillRowsLoop @@FillDone: mov dx,GC_INDEX+1 ;restore the bit mask to its default, mov al,0ffh ; which selects all bits from the CPU out dx,al ; and none from the latches (the GC ; Index still points to Bit Mask) pop di ;restore caller's register variables pop si mov sp,bp ;discard storage for local variables pop bp ;restore caller's stack frame ret _x_rect_pattern endp ;----------------------------------------------------------------------- ; Mode X (320x240, 256 colors) display memory to display memory copy ; routine. Left edge of source rectangle modulo 4 must equal left edge ; of destination rectangle modulo 4. Works on all VGAs. Uses approach ; of reading 4 pixels at a time from the source into the latches, then ; writing the latches to the destination. Copies up to but not ; including the column at SrcEndX and the row at SrcEndY. No ; clipping is performed. Results are not guaranteed if the source and ; destination overlap. ; ; ; Based on code originally published in DDJ Mag by M. Abrash ; ;C near-callable as: ; void x_cp_vid_rect(int SrcStartX, int SrcStartY, ; int SrcEndX, int SrcEndY, int DestStartX, ; int DestStartY, unsigned int SrcPageBase, ; unsigned int DestPageBase, int SrcBitmapWidth, ; int DestBitmapWidth); _x_cp_vid_rect proc ARG SrcStartX:word,SrcStartY:word,SrcEndX:word,SrcEndY:word,DestStartX:word,DestStartY:word,SrcPageBase:word,DestPageBase:word,SrcBitmapW:word,DestBitmapW:word LOCAL SrcNextOffs:word,DestNextOffs:word,RectAddrW:word,Height:word=LocalStk push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame sub sp,LocalStk ;allocate space for local vars push si ;preserve caller's register variables push di push ds cld mov dx,GC_INDEX ;set the bit mask to select all bits mov ax,00000h+BIT_MASK ; from the latches and none from out dx,ax ; the CPU, so that we can write the ; latch contents directly to memory mov ax,SCREEN_SEG ;point ES to display memory mov es,ax mov ax,[DestBitmapW] shr ax,2 ;convert to width in addresses mul [DestStartY] ;top dest rect scan line mov di,[DestStartX] sar di,2 ;X/4 = offset of first dest rect pixel in ; scan line add di,ax ;offset of first dest rect pixel in page add di,[DestPageBase] ;offset of first dest rect pixel ; in display memory mov ax,[SrcBitmapW] sar ax,2 ;convert to width in addresses mul [SrcStartY] ;top source rect scan line mov si,[SrcStartX] mov bx,si sar si,2 ;X/4 = offset of first source rect pixel in ; scan line add si,ax ;offset of first source rect pixel in page add si,[SrcPageBase] ;offset of first source rect ; pixel in display memory and bx,0003h ;look up left edge plane mask mov ah,LeftClipPlaneMask[bx] ; to clip mov bx,[SrcEndX] and bx,0003h ;look up right edge plane mov al,RightClipPlaneMask[bx] ; mask to clip mov bx,ax ;put the masks in BX mov cx,[SrcEndX] ;calculate # of addresses across mov ax,[SrcStartX] ; rect cmp cx,ax jle @@CopyDone ;skip if 0 or negative width dec cx and ax,not 011b sub cx,ax sar cx,2 ;# of addresses across rectangle to copy - 1 jnz @@MasksSet ;there's more than one address to draw and bh,bl ;there's only one address, so combine the left ; and right edge clip masks @@MasksSet: mov ax,[SrcEndY] sub ax,[SrcStartY] ;AX = height of rectangle jle @@CopyDone ;skip if 0 or negative height mov [Height],ax mov ax,[DestBitmapW] sar ax,2 ;convert to width in addresses sub ax,cx ;distance from end of one dest scan line dec ax ; to start of next mov [DestNextOffs],ax mov ax,[SrcBitmapW] sar ax,2 ;convert to width in addresses sub ax,cx ;distance from end of one source scan line dec ax ; to start of next mov [SrcNextOffs],ax mov [RectAddrW],cx ;remember width in addresses - 1 mov dx,SC_INDEX+1 ;point to Sequence Controller Data reg ; (SC Index still points to Map Mask) mov ax,es ;DS=ES=screen segment for MOVS mov ds,ax @@CopyRowsLoop: mov cx,[RectAddrW] ;width across - 1 mov al,bh ;put left-edge clip mask in AL out dx,al ;set the left-edge plane (clip) mask movsb ;copy the left edge (pixels go through ; latches) dec cx ;count off left edge address js @@CopyLoopBottom ;that's the only address jz @@DoRightEdge ;there are only two addresses mov al,00fh ;middle addresses are drawn 4 pix per go out dx,al ;set the middle pixel mask to no clip rep movsb ;draw the middle addresses four pix per go ; (pixels copied through latches) @@DoRightEdge: mov al,bl ;put right-edge clip mask in AL out dx,al ;set the right-edge plane (clip) mask movsb ;draw the right edge (pixels copied through ; latches) @@CopyLoopBottom: add si,[SrcNextOffs] ;point to the start of add di,[DestNextOffs] ; next source & dest lines dec word ptr [Height] ;count down scan lines jnz @@CopyRowsLoop @@CopyDone: mov dx,GC_INDEX+1 ;restore the bit mask to its default, mov al,0ffh ; which selects all bits from the CPU out dx,al ; and none from the latches (the GC ; Index still points to Bit Mask) pop ds pop di ;restore caller's register variables pop si mov sp,bp ;discard storage for local variables pop bp ;restore caller's stack frame ret _x_cp_vid_rect endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Copy a rectangular region of a VGA screen, with x coordinates ; rounded to the nearest byte -- source and destination may overlap. ; ; C near-callable as: ; ; void x_shift_rect (WORD SrcLeft, WORD SrcTop, ; WORD SrcRight, WORD SrcBottom, ; WORD DestLeft, WORD DestTop, WORD ScreenOffs); ; ; SrcRight is rounded up, and the left edges are rounded down, to ensure ; that the pixels pointed to by the arguments are inside the rectangle. ; ; The width of the rectangle in bytes (width in pixels / 4) ; cannot exceed 255. ; ; ax, bx, cx, dx, and es eat hot lead. ; ; This function was written by Matthew MacKenzie ; matm@eng.umd.edu align 2 _x_shift_rect proc ARG SrcLeft,SrcTop,SrcRight,SrcBottom,DestLeft,DestTop,ScreenOffs:word LOCAL width_temp:word=LocalStk push bp mov bp, sp sub sp, LocalStk push si push di push ds ; find values for width & x motion mov si, SrcLeft ; source x in bytes sar si, 2 mov di, DestLeft ; destination x in bytes sar di, 2 mov bx, SrcRight ; right edge of source in bytes, rounded up add bx, 3 sar bx, 2 sub bx, si mov ax, bx ; width - 1 inc bx ; we'll use this as an offset for moving up or down mov width_temp, bx cld ; by default, strings increment cmp si, di jge @@MovingLeft ; we're moving our rectangle right, so we copy it from right to left add si, ax ; source & destination will start on the right edge add di, ax neg bx std ; strings decrement @@MovingLeft: ; find values for height & y motion mov cx, _ScrnLogicalByteWidth ; bytes to move to advance one line mov ax, SrcTop mov dx, DestTop ; default destination y cmp ax, dx jge @@MovingUp ; we're moving our rectangle down, so we copy it from bottom to top mov ax, SrcBottom ; source starts at bottom add dx, ax ; add (height - 1) to destination y sub dx, SrcTop neg cx ; advance up screen rather than down @@MovingUp: push dx ; save destination y during multiply mul _ScrnLogicalByteWidth add si, ax ; add y in bytes to source pop ax ; restore destination y mul _ScrnLogicalByteWidth add di, ax ; add y in bytes to destination sub cx, bx ; final value for moving up or down add si, ScreenOffs ; source & destination are on the same screen add di, ScreenOffs mov dx, SC_INDEX ; set map mask to all four planes mov ax, 00f02h out dx, ax mov dx, GC_INDEX ; set bit mask to take data from latches mov ax, BIT_MASK ; rather than CPU out dx, ax mov ax, SCREEN_SEG ; source and destination are VGA memory mov es, ax mov ds, ax mov ah, byte ptr width_temp ; width in bytes should fit in 8 bits mov bx, SrcBottom ; height - 1 sub bx, SrcTop mov dx, cx ; bytes to add to advance one line xor ch, ch ; ready to rock @@LineLoop: mov cl, ah ; load width in bytes rep movsb ; move 4 pixels at a time using latches (YOW!) add si, dx ; advance source by one line add di, dx ; advance destination by one line dec bx ; line counter jge @@LineLoop ; 0 still means one more to go mov dx, GC_INDEX + 1; set bit mask to take data from CPU (normal setting) mov al, 0ffh out dx, al ; kick pop ds pop di pop si mov sp, bp pop bp ret _x_shift_rect endp end