2025-04-27 07:49:33 -04:00

1648 lines
60 KiB
NASM

;-----------------------------------------------------------------------;
; This delay is necessitated by a bug in the ATI Ultra when running in
; VGA mode.
SLOW_OUT macro
push ecx
pop ecx
out dx,ax
endm
.386
ifndef DOS_PLATFORM
.model small,c
else
ifdef STD_CALL
.model small,c
else
.model small,pascal
endif; STD_CALL
endif; DOS_PLATFORM
assume ds:FLAT,es:FLAT,ss:FLAT
assume fs:nothing,gs:nothing
.xlist
include stdcall.inc ;calling convention cmacros
include i386\egavga.inc
include i386\strucs.inc
include i386\ropdefs.inc
include i386\display.inc ; Display specific structures
.list
;-----------------------------------------------------------------------;
.data
;
; We share some tables with vgablts.asm
;
extrn jALUFuncTable :byte
extrn jLeftMask :byte
extrn jRightMask :byte
extrn jForceOnTable :byte
extrn jNotTable :byte
extrn jInvertDest :byte
extrn jForceOffTable :byte
extrn vTrgBlt@20 :dword
;-----------------------------------------------------------------------;
; Table of routines to be called to draw edges, according to which edges are
; partial and which edges are whole bytes.
align 4
public pfnEdgeDrawing
pfnEdgeDrawing label dword
dd edge_byte_setup
dd edge_byte_setup
dd check_next_bank
dd edge_byte_setup
;-----------------------------------------------------------------------;
; Table of pointers to wide whole byte drawing loops.
align 4
public pfnWideWholeRep
pfnWideWholeRep label dword
dd draw_wide_00_loop
dd draw_wide_01_loop
dd draw_wide_10_loop
dd draw_wide_11_loop
;-----------------------------------------------------------------------;
; Table of pointers to replace whole byte drawing loops.
; Note: The breakpoint where one should switch from special-casing to
; REP STOS is purely a guess on my part. 8 seemed reasonable.
; Start address MOD 2 is 0.
align 4
public pfnWholeBytesMod0Entries
pfnWholeBytesMod0Entries label dword
dd 0 ;we never get a 0-wide case
dd draw_1_wide_even_loop
dd draw_2_wide_even_loop
dd draw_3_wide_even_loop
dd draw_4_wide_even_loop
dd draw_5_wide_even_loop
dd draw_6_wide_even_loop
dd draw_7_wide_even_loop
dd draw_8_wide_even_loop
MAX_REPLACE_SPECIAL equ ($-pfnWholeBytesMod0Entries)/4
; Start address MOD 2 is 1.
align 4
public pfnWholeBytesMod1Entries
pfnWholeBytesMod1Entries label dword
dd 0 ;we never get a 0-wide case
dd draw_1_wide_odd_loop
dd draw_2_wide_odd_loop
dd draw_3_wide_odd_loop
dd draw_4_wide_odd_loop
dd draw_5_wide_odd_loop
dd draw_6_wide_odd_loop
dd draw_7_wide_odd_loop
dd draw_8_wide_odd_loop
;-----------------------------------------------------------------------;
; Table of pointers to non-replace whole byte drawing loops.
; Note: The breakpoint where one should switch from special-casing to
; REP MOVSB is purely a guess on my part. 5 seemed reasonable.
align 4
pfnWholeBytesNonReplace label dword
dd 0 ;we never get a 0-wide case
dd draw_1_wide_rop_loop
dd draw_2_wide_rop_loop
dd draw_3_wide_rop_loop
dd draw_4_wide_rop_loop
MAX_NON_REPLACE_SPECIAL equ ($-pfnWholeBytesNonReplace)/4
; Master MOD 2 alignment look-up table for entry tables for two possible
; alignments for narrow, special-cased replace whole byte drawing loops.
align 4
public pfnWholeBytesSpecial
pfnWholeBytesSpecial label dword
dd pfnWholeBytesMod0Entries
dd pfnWholeBytesMod1Entries
.code
;=============================================================================
_TEXT$01 SEGMENT DWORD USE32 PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
cProc vMonoPatBlt,24,< \
uses esi edi ebx, \
pdsurf: ptr DEVSURF, \
culRcl: dword, \
prcl: ptr RECTL, \
ulMix: dword, \
pBrush: ptr oem_brush_def, \
pBrushOrg: ptr POINTL >
local ulRowOffset :dword ;Offset from start of scan line
; first byte to fill
local ulWholeBytes :dword ;# of whole bytes to fill
local ulWholeWords :dword ;# of whole words to fill excluding
;leading and/or trailing bytes
local pfnWholeFn :dword ;pointer to routine used to draw
; whole bytes
local ulScanWidth :dword ;offset from start of one scan to start
; of next
local ulNextScan :dword ;offset from end of one scan line's
; fill to start of next
local ulCurrentTopScan :dword ;top scan line to fill in current bank
local ulMasks :dword ;low byte = right mask, high byte =
; left mask
local ulBottomScan :dword ;bottom scan line of fill rectangle
local jALUFunc :dword ;VGA ALU logical operation (SET, AND,
; OR, or XOR)
local pfnStartDrawing :dword ;pointer to function to call to start
; drawing
local pfnContinueDrawing :dword ;pointer to function to call to
; continue drawing after doing whole
; bytes
local ulLeftEdgeAdjust :dword ;used to bump the whole bytes start
; address past the left edge when the
; left edge is partial
local pfnWholeBytes :dword ;pointer to whole byte filling loop
local ulSpecialBytes ;If we are doing a special case wide
; fill, this will be the width of the
; fill. We need this so we can properly
; increment to the next line.
local ulVbNextScan :dword ;Offset from the end of the current
; wide fill drawing operation to the
; top of the next venetian blind line
local fdInvertDestFirst :dword;1 if the rop requires a pass to invert
; the destination before the normal
; pass
local ulPatternOrgY: dword ;Local copy of the pattern offset Y
local ulVbBlindCount :dword ;Temp Height of pattern.
local ulVbTopScan :dword ;slats in our blinds
local ulVbStartScan :dword ;Current to slat
local pUlVbPattern:dword ;inner loop pattern pointer
local pUlPattern:dword ;current pattern with proper Y offset
local ulVbMask ;Inversion mask for partial edges
local ulVbYRound ;
local ulVbYShift ;
local RotatedPat[32]:byte ;Aligned pattern buffer
local ulFgClr:dword ;Local copy of the foreground color
local ulBkClr:dword ;Local copy of the background color
local pfnWesTrick:dword ;Pointer to the desired inner loop
; wes trick code. While we are doing
; a ROP to full bytes, this will point
; to do_wide_wes_trick otherwise it
; will point to do_edge_wes_trick for
; the edge cases
cld
;-----------------------------------------------------------------------;
; Make sure there's something to draw; clip enumerations can be empty.
;-----------------------------------------------------------------------;
cmp culRcl,0 ;any rects to fill?
jz vMonoPatBlts_done ;no, we're done
mov esi,pBrush ;point to the brush
xor eax,eax
mov al,[esi + oem_brush_fg]
mov ulFgClr,eax ;Make local copy of the fg color
mov al,[esi + oem_brush_bg]
mov ulBkClr,eax ;Make local copy of the bk color
;-----------------------------------------------------------------------;
; Set up for the desired raster op.
;-----------------------------------------------------------------------;
sub ebx,ebx ;ignore any background mix; we're only
mov bl,byte ptr ulMix ; concerned with the foreground in this
; module
cmp ebx,R2_NOP ;is this NOP?
jz vMonoPatBlts_done ;yes, we're done
sub eax,eax ;we want a dword
mov al,jInvertDest[ebx] ;remember whether we need to invert the
mov fdInvertDestFirst,eax ; destination before finishing the rop
mov eax,ulFgClr
and al,jForceOffTable[ebx] ;force color to 0 if necessary
; (R2_BLACK)
or al,jForceOnTable[ebx] ;force color to 0ffh if necessary
; (R2_WHITE, R2_NOT)
xor al,jNotTable[ebx] ;invert color if necessary (any Pn mix)
;at this point, CH has the color we
; want to draw with; set up the VGA
; hardware to draw with that color
mov ulFgClr,eax
mov eax,ulBkClr
and al,jForceOffTable[ebx] ;force color to 0 if necessary
; (R2_BLACK)
or al,jForceOnTable[ebx] ;force color to 0ffh if necessary
; (R2_WHITE, R2_NOT)
xor al,jNotTable[ebx] ;invert color if necessary (any Pn mix)
;at this point, CH has the color we
; want to draw with; set up the VGA
; hardware to draw with that color
mov ulBkClr,eax
mov ah,jALUFuncTable[ebx] ;get the ALU logical function
and ah,ah ;is the logical function DR_SET?
.errnz DR_SET
jz short skip_ALU_set ;yes, don't have to set because that's
; the VGA's default state
mov edx,VGA_BASE + GRAF_ADDR
mov al,GRAF_DATA_ROT
SLOW_OUT ;set the ALU logical function
skip_ALU_set:
mov byte ptr jALUFunc,ah ;remember the ALU logical function
;-----------------------------------------------------------------------;
; Set up variables that are constant for the entire time we're in this
; module.
;-----------------------------------------------------------------------;
mov edx,pBrushOrg ;point to the brush origin
mov ecx,[edx].ptl_x
and ecx,15 ;eax mod 16
mov eax,[edx].ptl_y
mov ulPatternOrgY,eax
;We are now going to make a copy of our rotated copy of our pattern.
;The reason that we do this is because we may be called with several
;rectangles and we don't really want to rotate the pattern data for
;each rectangle. We copy this rectangle to be double high so that
;we can incorperate our Y offest later without having to worry
;about running off the end of the pattern.
lea edi,RotatedPat ;Pattern Dest
mov esi,[esi + oem_brush_pmono] ;Pattern Src
or cl,cl
jnz rotate_and_expand
INDEX=0
rept 4 ;patterns are 16x8
mov eax,[esi+INDEX]
mov [edi+INDEX],eax
mov [edi+16+INDEX],eax
INDEX=INDEX+4
endm ;-----------------
jmp fill_rect_loop
rotate_and_expand:
INDEX=0
rept 8 ;patterns are 16x8
mov ah,[esi+INDEX] ;load bytes for shift
mov al,[esi+1+INDEX] ;convert from little to big endian
ror ax,cl ;shift into position
mov [edi+INDEX],ah ;save result
mov [edi+1+INDEX],al
mov [edi+16+INDEX],ah ;save result to second copy
mov [edi+17+INDEX],al
INDEX=INDEX+2
endm ;-----------------
fill_rect_loop:
;-----------------------------------------------------------------------;
; Set up masks and widths.
;-----------------------------------------------------------------------;
mov edi,prcl ;point to rectangle to fill
sub eax,eax
mov ulLeftEdgeAdjust,eax ;initalize variable
mov ulSpecialBytes,eax ;initalize variable
mov eax,[edi].yBottom
mov ulBottomScan,eax ;remember the bottom scan line of fill
mov ebx,[edi].xRight ;right edge of fill (non-inclusive)
mov ecx,ebx
and ecx,0111b ;intrabyte address of right edge
mov ah,jRightMask[ecx] ;right edge mask
mov esi,[edi].xLeft ;left edge of fill (inclusive)
mov ecx,esi
shr ecx,3 ;/8 for start offset from left edge
; of scan line
mov ulRowOffset,ecx ;remember offset from start of scan
; line
sub ebx,esi ;width in pixels of fill
and esi,0111b ;intrabyte address of left edge
mov al,jLeftMask[esi] ;left edge mask
dec ebx ;make inclusive on right
add ebx,esi ;inclusive width, starting counting at
; the beginning of the left edge byte
shr ebx,3 ;width of fill in bytes touched - 1
jnz short more_than_1_byte ;more than 1 byte is involved
; Only one byte will be affected. Combine first/last masks.
and al,ah ;we'll use first byte mask only
xor ah,ah ;want last byte mask to be 0
inc ebx ;so there's one count to subtract below
; if this isn't a whole edge byte
more_than_1_byte:
; If all pixels in the left edge are altered, combine the first byte into the
; whole byte count and clear the first byte mask, because we can handle solid
; edge bytes faster as part of the whole bytes. Ditto for the right edge.
sub ecx,ecx ;edge whole-status accumulator
cmp al,-1 ;is left edge a whole byte or partial?
adc ecx,ecx ;ECX=1 if left edge partial, 0 if whole
sub ebx,ecx ;if left edge partial, deduct it from
; the whole bytes count
mov ulLeftEdgeAdjust,ecx ;for skipping over the left edge if
; it's partial when pointing to the
; whole bytes
and ah,ah ;is right edge mask 0, meaning this
; fill is only 1 byte wide?
jz short save_masks ;yes, no need to do anything
cmp ah,-1 ;is right edge a whole byte or partial?
jnz short save_masks ;partial
mov ah,0 ;
add ecx,2 ;bit 1 of ECX=0 if right edge partial,
; 1 if whole;
;bit 1=0 if left edge partial, 1 whole
inc ebx ;if right edge whole, include it in the
; whole bytes count
save_masks:
mov ulMasks,eax ;save left and right clip masks
mov ulWholeBytes,ebx ;save # of whole bytes
mov ecx,pfnEdgeDrawing[ecx*4] ;set address of routine to draw
mov pfnContinueDrawing,ecx ; all partial (non-whole) edges
and ebx,ebx ;any whole bytes?
jz start_vec_set ;no
;yes, so draw the whole bytes before
; the edge bytes
; The whole bytes loop depends on the type of operation being done. If the
; operation is one which uses DR_SET, then we can use a STOS-type operation,
; else we have to use a MOVSB-type operation (to load the latches with the
; existing contents of display memory to allow the ALUs to work).
cmp byte ptr jALUFunc,DR_SET ;is it a replace-type rop?
jz short is_replace_type ;yes
;no, set up for non-replace whole bytes
mov ecx,offset non_replace_wide
cmp ebx,MAX_NON_REPLACE_SPECIAL ;too wide to special case?
jb short non_replace_spec ;nope
mov pfnWholeBytes,offset draw_wide_rop_loop
;assume too wide to special-case
jmp short start_vec_set
non_replace_spec:
mov eax,pfnWholeBytesNonReplace[ebx*4] ;no, point to entry
mov pfnWholeBytes,eax ; table for width
mov ulSpecialBytes,ebx
;narrow enough to special case. Look up
; the entry table for the special case
; base on the start alignment
jmp short start_vec_set
is_replace_type: ;set up for replace-type rop
cmp ebx,MAX_REPLACE_SPECIAL ;too wide to special case?
jnb short is_wide_replace ;yes
mov ulSpecialBytes,ebx
;narrow enough to special case. Look up
; the entry table for the special case
; base on the start alignment
mov ecx,ulRowOffset
add ecx,ulLeftEdgeAdjust ;left edge whole bytes start offset
and ecx,01b ;left edge whole bytes start alignment
; MOD 2
mov ecx,pfnWholeBytesSpecial[ecx*4] ;look up table of entry
; tables for alignment
mov ecx,[ecx+ebx*4] ;look up entry table for width
mov pfnWholeBytes,ecx ; table for width
mov ecx,offset whole_bytes_rep_wide
jmp short start_vec_set
is_wide_replace: ;set up for wide replace-type op
;Note: assumes there is at least one
; full word involved!
mov ecx,ulRowOffset
add ecx,ulLeftEdgeAdjust ;left edge whole bytes start offset
neg ecx
and ecx,01b
mov edx,ebx
sub edx,ecx ;ignore odd leading bytes
mov eax,edx
shr edx,1 ;# of whole words across (not counting
; odd leading & trailing bytes)
mov ulWholeWords,edx
and eax,01b ;# of odd (fractional) trailing bytes
add ecx,ecx
or ecx,eax ;build a look-up index from the number
; of leading and trailing bytes
mov ecx,pfnWideWholeRep[ecx*4] ;proper drawing handler for front/
mov pfnWholeBytes,ecx ; back alignment
mov ecx,offset whole_bytes_rep_wide
;set up to call routine to perform wide
; whole bytes fill
start_vec_set:
mov pfnStartDrawing,ecx ; all partial (non-whole) edges
mov ecx,pdsurf
mov eax,[ecx].dsurf_lNextScan
mov ulScanWidth,eax ;local copy of scan line width
sub eax,ebx ;EAX = delta to next scan
mov ulNextScan,eax
mov esi,pBrush
mov eax,[esi+oem_brush_height]
dec eax
mov ulVbYRound,eax
mov al,[esi + oem_brush_yshft] ; blind to the next.
mov ulVbYShift,eax
mov cl,al
mov eax,UlScanWidth
shl eax,cl ;ulNextScan * 8
mov ulVbNextScan,eax ;
cmp fdInvertDestFirst,1 ;is this an invert-dest-plus-something-
; else rop that requires two passes?
jnz short do_single_pass
lea eax,vTrgBlt@20
ptrCall <eax>,<pdsurf, culRcl, prcl, R2_NOT, -1>
mov ah,byte ptr jALUFunc ;reset the ALU logical function
mov edx,VGA_BASE + GRAF_ADDR
mov al,GRAF_DATA_ROT
SLOW_OUT ;set the ALU logical function
do_single_pass:
call draw_banks
;-----------------------------------------------------------------------;
; See if there are any more rectangles to fill.
;-----------------------------------------------------------------------;
add prcl,(size RECTL) ;point to the next rectangle, if there is one
dec culRcl ;count down the rectangles to fill
jnz fill_rect_loop
;-----------------------------------------------------------------------;
; We have filled all rectangles. Restore the VGA to its default state.
;-----------------------------------------------------------------------;
mov edx,VGA_BASE + GRAF_ADDR
mov eax,0000h + GRAF_ENAB_SR ;disable set/reset
out dx,ax
mov eax,GRAF_MODE + ((M_PROC_WRITE + M_DATA_READ) SHL 8)
out dx,ax ;restore read mode 0 and write mode 0
mov eax,(DR_SET shl 8) + GRAF_DATA_ROT ;set the logical function to
out dx,ax ; SET
vMonoPatBlts_done:
cRet vMonoPatBlt
;-----------------------------------------------------------------------;
; Fills all banks in the current fill rectangle. Called once per fill
; rectangle, except for destination-inversion-plus-something-else rops.
;-----------------------------------------------------------------------;
draw_banks:
;-----------------------------------------------------------------------;
; Map in the bank containing the top scan to fill, if it's not mapped in
; already.
;-----------------------------------------------------------------------;
mov edi,prcl ;point to rectangle to fill
mov ecx,pdsurf ;point to surface
mov eax,[edi].yTop ;top scan line of fill
mov ulCurrentTopScan,eax ;this will be the fill top in 1st bank
cmp eax,[ecx].dsurf_rcl1WindowClip.yTop ;is fill top less than
; current bank?
jl short map_init_bank ;yes, map in proper bank
cmp eax,[ecx].dsurf_rcl1WindowClip.yBottom ;fill top greater than
; current bank?
jl short init_bank_mapped ;no, proper bank already mapped
map_init_bank:
; Map in the bank containing the top scan line of the fill.
ptrCall <dword ptr [ecx].dsurf_pfnBankControl>,<ecx,eax,JustifyTop>
init_bank_mapped:
;-----------------------------------------------------------------------;
; Main loop for processing fill in each bank.
;-----------------------------------------------------------------------;
; Compute the starting address and scan line count for the initial bank.
mov eax,pdsurf ;EAX->target surface
mov ebx,ulBottomScan ;bottom of destination rectangle
cmp ebx,[eax].dsurf_rcl1WindowClip.yBottom
;which comes first, the bottom of the
; dest rect or the bottom of the
; current bank?
jl short BottomScanSet ;fill bottom comes first, so draw to
; that; this is the last bank in fill
mov ebx,[eax].dsurf_rcl1WindowClip.yBottom
;bank bottom comes first; draw to
; bottom of bank
BottomScanSet:
mov edi,ulCurrentTopScan ;top scan line to fill in current bank
sub ebx,edi ;# of scans to fill in bank
imul edi,ulScanWidth ;offset of starting scan line
; Note that the start of the bitmap will change each time through the
; bank loop, because the start of the bitmap is varied to map the
; desired scan line to the banking window.
add edi,[eax].dsurf_pvBitmapStart ;start of scan in bitmap
add edi,ulRowOffset ;EDI = start offset of fill in bitmap
; We have computed the starting address and scan count. Time to start drawing
; in the initial bank.
mov esi,pBrush ;edx = min(PatternHeight,BltHeight)
mov ecx,[esi + oem_brush_height]
sub ecx,ebx
sbb edx,edx
and edx,ecx
add edx,ebx
mov ulVbBlindCount,edx
; Brush alignment. We need to look at pptlBrush
mov eax,ulCurrentTopScan ;top scan line to fill in current bank
sub eax,ulPatternOrgY ;
jns short pos_y_offset ;
neg eax ;
and eax,7 ;-eax mod 8
neg eax ;
add eax,8 ;
jmp short save_pat_pointer
pos_y_offset:
and eax,7 ;eax mod 8
save_pat_pointer:
add eax,eax ;Y Offset * PatternWidth (2 bytes)
lea edx,RotatedPat ;Pattern Dest
add eax,edx
mov pulPattern,eax ;Drawing code uses this as the
;source for the pattern
jmp pfnStartDrawing
;-----------------------------------------------------------------------;
; Whole byte fills.
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
; Handles non-replace whole byte fills wider than the maximum special
; case width.
;
; The destination is not involved, so a STOS (or equivalent) can be used
; (no read needed before write).
;-----------------------------------------------------------------------;
public whole_bytes_rep_wide
whole_bytes_rep_wide::
push ebx ;save scan count
push edi ;save starting address
add edi,ulLeftEdgeAdjust ;point to first whole byte to fill
mov edx,VGA_BASE + GRAF_ADDR
mov eax,GRAF_MODE + ((M_COLOR_WRITE + M_COLOR_READ) SHL 8)
out dx,ax ;write mode 2
mov eax,ulBkClr ;Set the write mode to write mode
mov [edi],al ; three after we load the latches
mov al,[edi] ; with our background color
mov al,GRAF_SET_RESET ;Set the foreground color
out dx,al ; into set/reset
inc edx
in al,dx
and eax,0f0h
or eax,ulFgClr
out dx,al
dec edx
mov eax,GRAF_MODE + ((M_AND_WRITE + M_COLOR_READ) SHL 8)
out dx,ax ;write mode 3 so we can do the masking
; without OUTs, read mode 1 so we can
; read 0xFF from memory always, for
; ANDing (because Color Don't Care is
; all zeros)
mov esi,pulPattern ; pointer to pattern bits
mov ax,[esi] ; into place
add esi,2
mov pulVbPattern,esi
mov ulVbTopScan,ebx ;our pattern is 8 high so we don't
add ebx,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift
shr ebx,cl ;only need to go through the code
; count/8 times. We will handle any
; extra lines at the bottom
; (ulVbTopScan mod 8) in our loops.
push ulVbBlindCount
public wide_bytes_loop
wide_bytes_loop::
mov esi,ulWholeWords ;number of aligned word writes
mov edx,ulVbNextScan ;offset from end of one scan line to
; start of next the same scan line
; in the next pattern.
sub edx,ulWholeBytes
add edx,ulSpecialBytes
; eax = rotated pattern
; ebx = count
; ecx = routine address
; edx = ulVbNextScan
; esi = ulFvWholeWords
; edi = pDest
;
push edi ;save out dest pointer
call pfnWholeBytes ;draw the wide whole bytes
pop edi ;restore out dest pointer
add edi,ulScanWidth ;advance to next scan line
dec ulVbBlindCount
jz short wide_bytes_end
mov eax,ulVbTopScan ;restore scan count
dec eax ;Subtract off completed top line
mov ulVbTopScan,eax
add eax,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift
shr eax,cl ;for this venetian blind pass
mov ebx,eax ;including any partial patterns
; at the bottom
mov esi,pulVbPattern ;Pattern data
mov ax,[esi] ;get pattern word
add esi,2
mov pulVbPattern,esi ;save pattern pointer for later
jmp short wide_bytes_loop
wide_bytes_end:
pop ulVbBlindCount
pop edi ;restore screen pointer
pop ebx ;restore fill scan count
mov edx,VGA_BASE + GRAF_ADDR ;restore proper read/write modes
mov eax,GRAF_MODE + ((M_PROC_WRITE + M_DATA_READ) SHL 8)
out dx,ax
jmp pfnContinueDrawing ;either keep drawing or we're done
;-----------------------------------------------------------------------;
; Handle case where both edges are partial (non-whole) bytes.
;-----------------------------------------------------------------------;
public non_replace_wide
non_replace_wide::
push ebx ;Save line count
push edi ;Save Dest Addr
add edi,ulLeftEdgeAdjust ;point to first whole byte to fill
mov pfnWesTrick,offset do_wide_wes_trick
mov ecx,ulFgClr
xor ecx,ulBkClr ;mask = ulBkClr ^ ulFgClr
mov ah,cl ;sre = !mask
not ah ;Set/Reset Enable
mov edx,EGA_BASE+GRAF_ADDR
mov al,GRAF_ENAB_SR
out dx,ax ;Set Set/Reset Enable bits
mov ah,byte ptr ulBkClr ;Set/Reset = background color
mov al,GRAF_SET_RESET
out dx,ax
mov eax,GRAF_MODE + ((M_PROC_WRITE + M_COLOR_READ) SHL 8)
out dx,ax ; Set Read Mode 0
;save the width count and pfn here
call wes_trick
mov edx,EGA_BASE+SEQ_DATA
mov eax,0fh
out dx,al
mov edx,EGA_BASE+GRAF_ADDR
mov eax,GRAF_MODE + ((M_PROC_WRITE + M_DATA_READ) SHL 8)
out dx,ax
mov eax,GRAF_ENAB_SR
out dx,ax ;Reset Set/Reset Enable bits
pop edi
pop ebx
jmp pfnContinueDrawing ;either keep drawing or we're done
;-----------------------------------------------------------------------;
; Process any left/right columns that that have to be done.
;
; Currently:
; EBX = height to fill, in scans
; EDI --> first byte of left edge
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
; Handle case where both edges are partial (non-whole) bytes.
;-----------------------------------------------------------------------;
public edge_byte_setup
edge_byte_setup::
mov pfnWesTrick,offset do_edge_wes_trick
mov ecx,ulFgClr
xor ecx,ulBkClr ;mask = ulBkClr ^ ulFgClr
mov ah,cl ;sre = !mask
not ah ;Set/Reset Enable
mov edx,EGA_BASE+GRAF_ADDR
mov al,GRAF_ENAB_SR
out dx,ax ;Set Set/Reset Enable bits
mov ah,byte ptr ulBkClr ;Set/Reset = foreground color
mov al,GRAF_SET_RESET
out dx,ax
mov eax,ulLeftEdgeAdjust
or eax,eax
jz short do_right_edge
mov eax,ulMasks ;Get Left/Right edge Masks
mov ah,al
mov al,GRAF_BIT_MASK
mov edx,EGA_BASE+GRAF_ADDR
out dx,ax
inc pulPattern ;Adjust Pattern rotation
push ebx ;Save line count
push edi ;Save Dest Addr
call wes_trick
pop edi
pop ebx
mov eax,ulMasks ;restore Left/Right edge Masks
dec pulPattern ;Adjust Pattern rotation
do_right_edge:
mov eax,ulMasks ;Get Left/Right edge Masks
and ah,0ffh
jz edge_done
mov al,GRAF_BIT_MASK
mov edx,EGA_BASE+GRAF_ADDR
out dx,ax
add edi,ulLeftEdgeAdjust ;point to first whole byte to fill
add edi,ulWholeBytes ;point to right edge byte to fill
call wes_trick
edge_done:
mov edx,EGA_BASE+SEQ_DATA
mov eax,0fh
out dx,al
mov edx,EGA_BASE+GRAF_ADDR
mov eax,GRAF_BIT_MASK+0ff00h
out dx,ax
mov eax,GRAF_ENAB_SR
out dx,ax ;Reset Set/Reset Enable bits
;-----------------------------------------------------------------------;
; See if there are any more banks to process.
;-----------------------------------------------------------------------;
public check_next_bank
check_next_bank::
mov edi,pdsurf
mov eax,[edi].dsurf_rcl1WindowClip.yBottom ;is the fill bottom in
cmp ulBottomScan,eax ; the current bank?
jle short banks_done ;yes, so we're done
;no, map in the next bank and fill it
mov ulCurrentTopScan,eax ;remember where the top of the bank
; we're about to map in is (same as
; bottom of bank we just did)
ptrCall <dword ptr [edi].dsurf_pfnBankControl>,<edi,eax,JustifyTop>
;map in the bank
; Compute the starting address and scan line count in this bank.
mov eax,pdsurf ;EAX->target surface
mov ebx,ulBottomScan ;bottom of destination rectangle
cmp ebx,[eax].dsurf_rcl1WindowClip.yBottom
;which comes first, the bottom of the
; dest rect or the bottom of the
; current bank?
jl short BottomScanSet2 ;fill bottom comes first, so draw to
; that; this is the last bank in fill
mov ebx,[eax].dsurf_rcl1WindowClip.yBottom
;bank bottom comes first; draw to
; bottom of bank
BottomScanSet2:
mov edi,ulCurrentTopScan ;top scan line to fill in current bank
sub ebx,edi ;# of scans to fill in bank
imul edi,ulScanWidth ;offset of starting scan line
; Note that the start of the bitmap will change each time through the
; bank loop, because the start of the bitmap is varied to map the
; desired scan line to the banking window.
add edi,[eax].dsurf_pvBitmapStart ;start of scan in bitmap
add edi,ulRowOffset ;EDI = start offset of fill in bitmap
; We have computed the starting address and scan count. Time to start drawing
; in the initial bank.
mov esi,pBrush ;edx = min(PatternHeight,BltHeight)
mov ecx,[esi + oem_brush_height]
sub ecx,ebx
sbb edx,edx
and edx,ecx
add edx,ebx
mov ulVbBlindCount,edx
; Brush alignment. We need to look at pptlBrush
mov eax,ulCurrentTopScan ;top scan line to fill in current bank
sub eax,ulPatternOrgY ;
jns short pos_y_offset1 ;
neg eax ;
and eax,7 ;-eax mod 8
neg eax ;
add eax,8 ;
jmp short save_pat_pointer1
pos_y_offset1:
and eax,7 ;eax mod 8
save_pat_pointer1:
add eax,eax ;Y Offset * PatternWidth (2 bytes)
lea edx,RotatedPat ;Pattern Dest
add eax,edx
mov pulPattern,eax ;Drawing code uses this as the
;source for the pattern
; Draw in the new bank.
jmp pfnStartDrawing
;-----------------------------------------------------------------------;
; Done with all banks in this fill.
public banks_done
banks_done::
PLAIN_RET
;----------------------------------------------------------------------------
; Wes Trick Setup code. This code decides if this is a one or a two pass
; operation.
;----------------------------------------------------------------------------
public wes_trick
wes_trick::
mov ecx,ulFgClr ;
mov eax,ecx ;
xor ecx,ulBkClr ;mask = ulBkClr ^ ulFgClr
mov edx,EGA_BASE+SEQ_DATA ;Index should be pointing to the
; plane mask (2)
mov ch,cl
not ch ;Set/Reset Enable bits
and cl,al ;ulFgdColor & mask
or cl,al
jz short check_bk_bits ;if zero - one background pass
mov ulVbMask,0 ;We do not want to invert the
;foreground pass
or ch,cl
mov al,ch
out dx,al ;Enable Planes for First Pass
push ecx
push edi ;Save our Dest pointer
push ebx ;Save our count
call pfnWesTrick ;Draw the foreground pass
pop ebx ;restore the line count
pop edi ;restore the dest pointer
pop eax ;Restore bk mask
check_bk_bits:
not al
and al,MM_ALL
jnz short @f
PLAIN_RET
@@:
mov ulVbMask,-1 ;We do not want to invert the
mov edx,EGA_BASE+SEQ_DATA ;Index should be pointing to the
; plane mask (2)
out dx,al
jmp pfnWesTrick
;--------------------------------------------------------------------------
; Do the edges here.
;--------------------------------------------------------------------------
public do_edge_wes_trick
do_edge_wes_trick::
; ebx = line count
; edi = dest
mov ulVbTopScan,ebx ;Mod 8 our count for the venetian blind
add ebx,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift
shr ebx,cl
mov esi,pulPattern
mov ax,[esi] ;get pattern into place
add esi,2 ;patterns stored as words
xor eax,ulVbMask ;Invert the pattern if we are doing
; a background pass
push ulVbBlindCount
; Set up variables for entering loop.
wes_trick_loop:
mov ecx,ulVbNextScan ;offset from one scan to next
push edi ;save dest pointer
call draw_1_wide_loop ;jump into the loop to draw
pop edi ;restore dest pointer
add edi,ulScanWidth ;move to next scan line
dec ulVbBlindCount
jz short wes_trick_loop_done ;jz if we are finished
mov eax,ulVbTopScan ;restore scan count
dec eax ;Subtract off completed top line
mov ulVbTopScan,eax ;save for next loop
add eax,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift ;for this venetian blind pass
shr eax,cl ;including any partial patterns
mov ebx,eax ;at the bottom
mov ax,[esi]
add esi,2 ;point to the next pattern line
xor eax,ulVbMask ;Invert the pattern if we are doing
; a background pass
jmp short wes_trick_loop
wes_trick_loop_done:
pop ulVbBlindCount
PLAIN_RET
;--------------------------------------------------------------------------
; Do the middle bytes here for blts with rops.
;--------------------------------------------------------------------------
public do_wide_wes_trick
do_wide_wes_trick::
; ebx = line count
; edi = dest
mov ulVbTopScan,ebx ;Mod 8 our count for the venetian blind
add ebx,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift
shr ebx,cl
mov esi,pulPattern
mov al,[esi] ;get pattern into place
add esi,2 ;patterns stored as words
mov pulVbPattern,esi
xor eax,ulVbMask ;Invert the pattern if we are doing
; a background pass
push ulVbBlindCount
; Set up variables for entering loop.
wide_wes_trick_loop:
mov esi,ulWholeBytes
mov edx,ulVbNextScan ;offset from one scan to next
sub edx,esi
add edx,ulSpecialBytes
push edi ;save dest pointer
call pfnWholeBytes ;draw
pop edi ;restore dest pointer
add edi,ulScanWidth ;move to next scan line
dec ulVbBlindCount
jz short wide_wes_trick_loop_done ;jz if we are finished
mov eax,ulVbTopScan ;restore scan count
dec eax ;Subtract off completed top line
mov ulVbTopScan,eax ;save for next loop
add eax,ulVbYRound ;Calc the number of lines to do
mov ecx,ulVbyShift ;for this venetian blind pass
shr eax,cl ;including any partial patterns
mov ebx,eax ;at the bottom
mov esi,pulVbPattern
mov al,[esi] ;get pattern word
add esi,2 ;point to the next pattern line
mov pulVbPattern,esi
xor eax,ulVbMask ;Invert the pattern if we are doing
; a background pass
jmp short wide_wes_trick_loop
wide_wes_trick_loop_done:
pop ulVbBlindCount
PLAIN_RET
endProc vMonoPatBlt
;-----------------------------------------------------------------------;
; Drawing loops.
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
; N-wide write-only, 0 leading bytes, 0 trailing bytes.
; EAX = Pattern Byte
; EBX = count of scans to fill ((total scans/ pattern height) + partial)
; EDX = offset from end of one scan's fill to start of next similar line
; ESI = pattern data
; EDI = target address to fill
draw_wide_00_loop proc near
mov ecx,esi ;# of whole words
rep stosw ;fill all whole bytes as dwords
add edi,edx ;point to the next scan line
dec ebx
jnz draw_wide_00_loop
ret
draw_wide_00_loop endp
;-----------------------------------------------------------------------;
; N-wide write-only, 0 leading bytes, 0 trailing bytes.
; EAX = Pattern Byte
; EBX = count of scans to fill ((total scans/ pattern height) + partial)
; EDX = offset from end of one scan's fill to start of next similar line
; ESI = pattern data
; EDI = target address to fill
draw_wide_01_loop proc near
mov ecx,esi ;# of whole words
rep stosw ;fill all whole bytes as dwords
mov [edi],al ;trailing byte
inc edi
add edi,edx ;point to the next scan line
dec ebx
jnz draw_wide_01_loop
ret
draw_wide_01_loop endp
;-----------------------------------------------------------------------;
; N-wide write-only, 0 leading bytes, 0 trailing bytes.
; EAX = Pattern Byte
; EBX = count of scans to fill ((total scans/ pattern height) + partial)
; EDX = offset from end of one scan's fill to start of next similar line
; ESI = pattern data
; EDI = target address to fill
draw_wide_10_loop proc near
mov [edi],ah ;do leading byte
inc edi ;advance poitner
mov ecx,esi ;# of whole words
rep stosw ;fill all whole bytes as dwords
add edi,edx ;point to the next scan line
dec ebx
jnz draw_wide_10_loop
ret
draw_wide_10_loop endp
;-----------------------------------------------------------------------;
; N-wide write-only, 0 leading bytes, 0 trailing bytes.
; EAX = Pattern Byte
; EBX = count of scans to fill ((total scans/ pattern height) + partial)
; EDX = offset from end of one scan's fill to start of next similar line
; ESI = pattern data
; EDI = target address to fill
draw_wide_11_loop proc near
mov [edi],ah ;do leading byte
inc edi ;advance poitner
mov ecx,esi ;# of whole words
rep stosw ;fill all whole bytes as dwords
mov [edi],al ;trailing byte
inc edi
add edi,edx ;point to the next scan line
dec ebx
jnz draw_wide_11_loop
ret
draw_wide_11_loop endp
;-----------------------------------------------------------------------;
; Drawing stuff for cases where read before write is NOT required.
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
; 1-, 2-, 3-, and 4-wide write-only edge-drawing loops.
;
; Entry:
; AL/AX/EAX = pixel mask (if AX or EAX, then 0xFFFF or 0xFFFFFFFF)
; EBX = loop count
; ECX = scan line width in bytes
; EDI = start offset
;
; EBX, EDI modified. All other registers preserved.
;-----------------------------------------------------------------------;
; 1-wide write-only.
draw_1_wide_even_loop proc near
mov [edi],al ;we always read 0xFF, so AL is written
; as-is; because we're in write mode 3,
; AL becomes the Bit Mask
add edi,edx ;point to the next scan line
dec ebx
jnz draw_1_wide_even_loop
ret
draw_1_wide_even_loop endp
; 1-wide write-only.
draw_1_wide_odd_loop proc near
mov [edi],ah ;we always read 0xFF, so AL is written
; as-is; because we're in write mode 3,
; AL becomes the Bit Mask
add edi,edx ;point to the next scan line
dec ebx
jnz draw_1_wide_odd_loop
ret
draw_1_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 2-wide write-only.
draw_2_wide_even_loop proc near
mov [edi],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_2_wide_even_loop
ret
draw_2_wide_even_loop endp
; 2-wide write-only.
draw_2_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_2_wide_odd_loop
ret
draw_2_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 3-wide write-only, starting at an even address.
draw_3_wide_even_loop proc near
mov [edi],ax
mov [edi+2],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_3_wide_even_loop
ret
draw_3_wide_even_loop endp
;-----------------------------------------------------------------------;
; 3-wide write-only, starting at an odd address.
draw_3_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_3_wide_odd_loop
ret
draw_3_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 4-wide write-only, starting at an even address.
draw_4_wide_even_loop proc near
mov [edi],ax
mov [edi+2],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_4_wide_even_loop
ret
draw_4_wide_even_loop endp
;-----------------------------------------------------------------------;
; 4-wide write-only, starting at an odd address.
draw_4_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
mov [edi+3],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_4_wide_odd_loop
ret
draw_4_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 5-wide write-only, starting at an even address.
draw_5_wide_even_loop proc near
mov [edi],ax
mov [edi+2],ax
mov [edi+4],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_5_wide_even_loop
ret
draw_5_wide_even_loop endp
;-----------------------------------------------------------------------;
; 5-wide write-only, starting at an odd address.
draw_5_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
mov [edi+3],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_5_wide_odd_loop
ret
draw_5_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 6-wide write-only, starting at an even address.
draw_6_wide_even_loop proc near
mov [edi],ax
mov [edi+2],ax
mov [edi+4],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_6_wide_even_loop
ret
draw_6_wide_even_loop endp
;-----------------------------------------------------------------------;
; 6-wide write-only, starting at an odd address.
draw_6_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
mov [edi+3],ax
mov [edi+5],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_6_wide_odd_loop
ret
draw_6_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 7-wide write-only, starting at an even address.
draw_7_wide_even_loop proc near
mov [edi],ax
mov [edi+2],ax
mov [edi+4],ax
mov [edi+6],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_7_wide_even_loop
ret
draw_7_wide_even_loop endp
;-----------------------------------------------------------------------;
; 7-wide write-only, starting at an odd address.
draw_7_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
mov [edi+3],ax
mov [edi+5],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_7_wide_odd_loop
ret
draw_7_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 8-wide write-only, starting at an even address.
draw_8_wide_even_loop proc near
mov [edi],ax
mov [edi+2],ax
mov [edi+4],ax
mov [edi+6],ax
add edi,edx ;point to the next scan line
dec ebx
jnz draw_8_wide_even_loop
ret
draw_8_wide_even_loop endp
;-----------------------------------------------------------------------;
; 8-wide write-only, starting at an odd address.
draw_8_wide_odd_loop proc near
mov [edi],ah
mov [edi+1],ax
mov [edi+3],ax
mov [edi+5],ax
mov [edi+7],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_8_wide_odd_loop
ret
draw_8_wide_odd_loop endp
;-----------------------------------------------------------------------;
; 1-wide read before write drawing loop; variant for Wes trick.
;
; Entry:
; AL = pixel mask
; EBX = loop count
; ECX = scan line width in bytes
; EDI = start offset
;
; EBX, EDI modified. All other registers preserved.
;-----------------------------------------------------------------------;
; 1-wide read/write.
draw_1_wide_loop proc near
mov dh,[edi] ;load latches w/o destroying our data
mov [edi],al ;write out our byte
add edi,ecx ;move to the next blind
dec ebx
jnz draw_1_wide_loop
ret
draw_1_wide_loop endp
;-----------------------------------------------------------------------;
; 1-, 2-, 3-, and 4-wide read before write drawing loops.
;
; Entry:
; AL = pixel mask
; EBX = loop count
; ECX = scan line width in bytes
; EDI = start offset
;
; EBX, EDI modified. All other registers preserved.
;-----------------------------------------------------------------------;
; 1-wide read/write.
draw_1_wide_rop_loop proc near
mov ah,[edi]
mov [edi],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_1_wide_rop_loop
ret
draw_1_wide_rop_loop endp
;-----------------------------------------------------------------------;
; 2-wide read/write.
draw_2_wide_rop_loop proc near
mov ah,[edi]
mov [edi],al
mov ah,[edi+1]
mov [edi+1],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_2_wide_rop_loop
ret
draw_2_wide_rop_loop endp
;-----------------------------------------------------------------------;
; 3-wide read/write.
draw_3_wide_rop_loop proc near
mov ah,[edi]
mov [edi],al
mov ah,[edi+1]
mov [edi+1],al
mov ah,[edi+2]
mov [edi+2],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_3_wide_rop_loop
ret
draw_3_wide_rop_loop endp
;-----------------------------------------------------------------------;
; 4-wide read/write.
draw_4_wide_rop_loop proc near
mov ah,[edi]
mov [edi],al
mov ah,[edi+1]
mov [edi+1],al
mov ah,[edi+2]
mov [edi+2],al
mov ah,[edi+3]
mov [edi+3],al
add edi,edx ;point to the next scan line
dec ebx
jnz draw_4_wide_rop_loop
ret
draw_4_wide_rop_loop endp
;-----------------------------------------------------------------------;
; 5-or-wider read before write loop.
;
; Entry:
; EAX = # of bytes to fill across scan line (needed only by 5-or-wider
; handler)
; EBX = loop count
; EDX = offset from end of one scan line to the start of the next next
; EDI = start offset
;
; EBX, ECX, ESI, EDI modified. All other registers preserved.
;-----------------------------------------------------------------------;
; 5-or-wider read/write.
draw_wide_rop_loop proc near
mov ecx,esi
@@: mov ah,[edi]
mov [edi],al
inc edi
dec ecx
jnz @b
add edi,edx
dec ebx
jnz draw_wide_rop_loop
ret
draw_wide_rop_loop endp
_TEXT$01 ends
end