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

438 lines
18 KiB
NASM

;---------------------------Module-Header------------------------------;
; Module Name: savescrn.asm
;
; Copyright (c) 1992 Microsoft Corporation
;-----------------------------------------------------------------------;
;-----------------------------------------------------------------------;
; VOID vSaveScreenBitsToMemory(PDEVSURF pdsurf, PRECTL prcl,
; PVOID pjDestBuffer, ULONG ulSaveWidthInBytes,
; ULONG ulSaveHeight, ULONG ulDestScanWidth);
; Input:
; pdsurf - surface from which to copy
; prcl - pointer to rectangle to copy
; pjDestBuffer - pointer to destination memory buffer. Should have the same
; dword alignment as the source rect left edge does in screen
; memory
; ulSaveWidthInBytes - # of bytes to save per scan
; ulSaveHeight - # of scans to save
; ulDestScanWidth - distance from the start of one saved scan in the
; destination buffer to the start of the next saved scan.
; This should be a dword multiple, to maintain dword
; alignment between the source and destination
;
; Copies a rectangle from VGA memory to a memory buffer.
;
;-----------------------------------------------------------------------;
;
; Note: Assumes all rectangles have positive heights and widths. Will not
; work properly if this is not the case.
;
;-----------------------------------------------------------------------;
;
; Note: The rectangle is saved in interleaved-scan format; all four planes
; of one scan are saved together, then all four plane of the next scan,
; and so on. This is done for maximum restoration efficiency. Planes are
; saved in order 3, 2, 1, 0:
;
; Scan n, plane 3
; Scan n, plane 2
; Scan n, plane 1
; Scan n, plane 0
; Scan n+1, plane 3
; Scan n+1, plane 2
; Scan n+1, plane 1
; Scan n+1, plane 0
; :
;
; There may be padding on either edge of the saved bits in the destination
; buffer, so that the destination can be dword aligned with the source.
;
;-----------------------------------------------------------------------;
;Additional optimizations:
;
; Could handle odd bytes to dword align more efficiently, by having separate
; optimizations for various alignment widths, thereby avoiding starting REP
; and getting word accesses for 2 and 3 wide cases. Most VGAs are 8 or 16
; bit devices, and for them, using MOVSB/REP MOVSW/MOVSB might actually be
; faster, especially because it's easier to break out optimizations.
;
; Could end all scan handlers with loop bottom code.
;
;-----------------------------------------------------------------------;
.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
.list
.code
_TEXT$03 SEGMENT DWORD USE32 PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
;-----------------------------------------------------------------------;
cProc vSaveScreenBitsToMemory,24,< \
uses esi edi ebx, \
pdsurf:ptr, \
prcl:ptr, \
pjDestBuffer:ptr, \
ulSaveWidthInBytes:dword, \
ulSaveHeight:dword, \
ulDestScanWidth:dword >
local ulCurrentTopScan :dword ;top scan line to copy from in current
; bank
local ulBottomScan :dword ;bottom scan line of rectangle to copy
local pjSrcStart :dword ;source address
local ulSrcDelta :dword ;distance from end of source scan to
; start of next
local ulDestDelta :dword ;distance from end of dest scan to
; start of next
local ulBlockHeight :dword ;# of scans to copy in block
local ulLeadingBytes :dword ;# of leading bytes to copy per scan
local ulMiddleDwords :dword ;# of dwords to copy per scan
local ulTrailingBytes :dword ;# of trailing bytes to copy per scan
local pfnCopyVector :dword ;pointer to inner loop routine to copy
; from screen to buffer
;-----------------------------------------------------------------------;
; Leave the GC Index pointing to the Read Map for the rest of this routine.
;-----------------------------------------------------------------------;
mov edx,VGA_BASE + GRAF_ADDR
mov al,GRAF_READ_MAP
out dx,al
;-----------------------------------------------------------------------;
; Set up local variables.
;-----------------------------------------------------------------------;
mov edi,prcl ;point to rectangle from which to copy
mov esi,pdsurf ;point to surface from which to copy
mov eax,[edi].yBottom
mov ulBottomScan,eax ;bottom scan line of source rect
mov eax,ulDestScanWidth
shl eax,2
sub eax,ulSaveWidthInBytes
mov ulDestDelta,eax ;distance from end of one scan's saved bits
; to start of next scan's (skips over the three
; other plane's save areas for each scan)
mov eax,[esi].dsurf_lNextScan
sub eax,ulSaveWidthInBytes
mov ulSrcDelta,eax ;distance from end of one scan's bits to save
; to start of next scan's in display memory
;-----------------------------------------------------------------------;
; Set up for copying as much as possible via aligned dwords, and
; select the most efficient loop for doing the copy, based on the copy
; width, and on the necessity for leading and/or trailing bytes to
; dword align.
;-----------------------------------------------------------------------;
mov edx,ulSaveWidthInBytes ;# of bytes to copy per scan
cmp edx,8 ;if it's less than 8 bytes, just do a
; straight byte copy. This means we
; we only have to start 1 REP per line,
; and performing this check guarantees
; that we have at least 1 aligned dword
; to copy in the dword loops
jb short copy_all_as_bytes ;do straight byte copy
mov eax,pjDestBuffer
neg eax
and eax,3 ;# of bytes that have to be done as leading
; bytes to dword align with destination (note
; that for performance, the destination should
; be dword aligned with the source)
jz short copy_no_leading_bytes ;no leading bytes
;leading bytes
mov ulLeadingBytes,eax
mov ebx,offset copy_to_buffer_l ;assume no trailing bytes
sub edx,eax ;# of bytes after leading bytes
mov eax,edx
shr eax,2 ;# of dwords that can be handled as aligned
; dwords
mov ulMiddleDwords,eax
and edx,3 ;# of trailing bytes left after aligned dwords
; copied
jz short set_copy_vector ;no trailing bytes
mov ulTrailingBytes,edx
mov ebx,offset copy_to_buffer_lt ;there are both leading and
; trailing bytes
jmp short set_copy_vector
align 4
copy_no_leading_bytes:
mov ebx,offset copy_to_buffer ;assume no trailing bytes
mov eax,edx
shr eax,2 ;# of dwords that can be handled as aligned
; dwords
mov ulMiddleDwords,eax
and edx,3 ;# of trailing bytes left after aligned dwords
; copied
jz short set_copy_vector ;no trailing bytes
mov ulTrailingBytes,edx
mov ebx,offset copy_to_buffer_t ;there are trailing bytes
jmp short set_copy_vector
; It's so narrow that we'll forget about aligned dwords, and just do a straight
; byte copy, which we'll handle by treating the entire copy as leading bytes.
align 4
copy_all_as_bytes:
mov ulLeadingBytes,edx
mov ebx,offset copy_to_buffer_lonly
set_copy_vector:
mov pfnCopyVector,ebx
;-----------------------------------------------------------------------;
; Map in the bank containing the top scan to copy, if it's not mapped in
; already.
;-----------------------------------------------------------------------;
mov eax,[edi].yTop ;top scan line of copy
mov ulCurrentTopScan,eax ;this will be the copy top in 1st bank
cmp eax,[esi].dsurf_rcl1WindowClip.yTop ;is copy top less than
; current bank?
jl short map_init_bank ;yes, map in proper bank
cmp eax,[esi].dsurf_rcl1WindowClip.yBottom ;copy 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 copy.
ptrCall <dword ptr [esi].dsurf_pfnBankControl>,<esi,eax,JustifyTop>
init_bank_mapped:
;-----------------------------------------------------------------------;
; Calculate the initial start address from which to copy.
;-----------------------------------------------------------------------;
mov eax,ulCurrentTopScan ;top scan line to copy in current bank
imul [esi].dsurf_lNextScan ;offset of starting scan line in bitmap
add eax,[esi].dsurf_pvBitmapStart ;start address of scan
mov ebx,[edi].xLeft
shr ebx,3 ;convert from pixel to byte address
add eax,ebx ;start source address
mov pjSrcStart,eax
;-----------------------------------------------------------------------;
; Loop through and copy all bank blocks spanned by the source rectangle.
;
; Input:
; ESI = pdsurf
;-----------------------------------------------------------------------;
copy_to_buffer_bank_loop:
; Copy this bank block to the buffer.
; Calculate # of scans in this bank.
mov ebx,ulBottomScan ;bottom of source rectangle
cmp ebx,[esi].dsurf_rcl1WindowClip.yBottom
;which comes first, the bottom of the
; source rect or the bottom of the
; current bank?
jl short @F ;source bottom comes first, so copy to
; that; this is the last bank in copy
mov ebx,[esi].dsurf_rcl1WindowClip.yBottom
;bank bottom comes first; copy to
; bottom of bank
@@:
sub ebx,ulCurrentTopScan ;# of scans to copy in this bank
mov ulBlockHeight,ebx
;-----------------------------------------------------------------------;
; Loop through all four planes, copying all scans in this block for each
; plane in turn.
;-----------------------------------------------------------------------;
mov eax,3 ;start by copying plane 3
copy_whole_to_buffer_plane_loop:
mov edx,VGA_BASE + GRAF_DATA
out dx,al ;set Read Map to plane from which we're copying
push eax ;remember plane index
mov esi,pjSrcStart ;point to start of source rect
mov edi,pjDestBuffer ;point to start of dest buffer
mov eax,ulDestScanWidth
add pjDestBuffer,eax ;point to start of next plane's scan in dest
; buffer
mov eax,ulSrcDelta ;offset to next source scan
mov edx,ulDestDelta ;offset to next dest scan
mov ebx,ulBlockHeight
jmp pfnCopyVector ;jump to the appropriate loop to copy plane
;-----------------------------------------------------------------------;
; Copy loops, broken out by leading and trailing bytes needed for dword
; alignment, plus one loop to perform a straight byte copy.
;
; Input:
; EAX = offset from end of one source scan to start of next
; EBX = block height in scans
; EDX = offset from end of one dest scan to start of next
; ESI = initial source copy address
; EDI = initial dest copy address
;-----------------------------------------------------------------------;
; All byte can be copied as aligned dwords (no leading or trailing bytes).
align 4
copy_to_buffer:
copy_to_buffer_scan_loop:
mov ecx,ulMiddleDwords
rep movsd
add esi,eax ;point to next source scan
add edi,edx ;point to next dest scan
dec ebx ;count down scan lines
jnz copy_to_buffer_scan_loop
jmp short copy_to_buffer_scans_done
; Leading odd bytes, but no trailing bytes.
align 4
copy_to_buffer_l:
copy_to_buffer_scan_loop_l:
mov ecx,ulLeadingBytes
rep movsb
mov ecx,ulMiddleDwords
rep movsd
add esi,eax ;point to next source scan
add edi,edx ;point to next dest scan
dec ebx ;count down scan lines
jnz copy_to_buffer_scan_loop_l
jmp short copy_to_buffer_scans_done
; Trailing odd bytes, but no leading bytes.
align 4
copy_to_buffer_t:
copy_to_buffer_scan_loop_t:
mov ecx,ulMiddleDwords
rep movsd
mov ecx,ulTrailingBytes
rep movsb
add esi,eax ;point to next source scan
add edi,edx ;point to next dest scan
dec ebx ;count down scan lines
jnz copy_to_buffer_scan_loop_t
jmp short copy_to_buffer_scans_done
; Only leading bytes (straight byte copy; no aligned dwords).
align 4
copy_to_buffer_lonly:
copy_to_buffer_scan_loop_lonly:
mov ecx,ulLeadingBytes
rep movsb
add esi,eax ;point to next source scan
add edi,edx ;point to next dest scan
dec ebx ;count down scan lines
jnz copy_to_buffer_scan_loop_lonly
jmp short copy_to_buffer_scans_done
; Leading and trailing odd bytes.
align 4
copy_to_buffer_lt:
copy_to_buffer_scan_loop_lt:
mov ecx,ulLeadingBytes
rep movsb
mov ecx,ulMiddleDwords
rep movsd
mov ecx,ulTrailingBytes
rep movsb
add esi,eax ;point to next source scan
add edi,edx ;point to next dest scan
dec ebx ;count down scan lines
jnz copy_to_buffer_scan_loop_lt
copy_to_buffer_scans_done:
pop eax ;get back plane index
dec eax ;count down planes
jns copy_whole_to_buffer_plane_loop
; Remember where we left off, for the next block.
mov pjSrcStart,esi
mov eax,ulDestScanWidth
lea eax,[eax+eax*2] ;ulDestScanWidth*3
sub edi,eax ;adjust back to first plane's save area
; (remember, scans are interleaved by plane, so
; there are four planes per scan; we're
; adjusting back from the fourth to the first)
mov pjDestBuffer,edi
;-----------------------------------------------------------------------;
; See if there are more banks to do
;-----------------------------------------------------------------------;
mov esi,pdsurf
mov eax,[esi].dsurf_rcl1WindowClip.yBottom ;is the copy bottom in
cmp ulBottomScan,eax ; the current bank?
jle short copy_to_buffer_banks_done ;yes, so we're done
;no, map in the next bank and copy 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)
mov edx,[esi].dsurf_pvBitmapStart
sub pjSrcStart,edx ;convert from address to offset within bitmap
ptrCall <dword ptr [esi].dsurf_pfnBankControl>,<esi,eax,JustifyTop>
;map in the bank
; Compute the starting address in this bank.
; 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.
mov eax,[esi].dsurf_pvBitmapStart
add pjSrcStart,eax ;address of next scan to draw
; Copy the new bank.
jmp copy_to_buffer_bank_loop
;-----------------------------------------------------------------------;
; Done with all banks.
;-----------------------------------------------------------------------;
align 4
copy_to_buffer_banks_done:
cRet vSaveScreenBitsToMemory ;done
;-----------------------------------------------------------------------;
endProc vSaveScreenBitsToMemory
_TEXT$03 ends
end