|
|
; mem.asm: ; ; masm -Mx -Zi -DSEGNAME=????? asm.asm ; TITLE MEM.ASM
;**************************************************************** ;* MEM.ASM - Assembly mem-copy routines * ;* for 80286 and 80386 * ;**************************************************************** ;
?PLM=1 ; PASCAL Calling convention is DEFAULT ?WIN=0 ; Windows calling convention PMODE=1
.xlist include cmacros.inc include windows.inc .list
externA __WinFlags ; in KERNEL externA __AHINCR ; in KERNEL externA __AHSHIFT ; in KERNEL
; The following structure should be used to access high and low ; words of a DWORD. This means that "word ptr foo[2]" -> "foo.hi".
LONG struc lo dw ? hi dw ? LONG ends
FARPOINTER struc off dw ? sel dw ? FARPOINTER ends
; ------------------------------------------------------- ; DATA SEGMENT DECLARATIONS ; -------------------------------------------------------
ifndef SEGNAME SEGNAME equ <_TEXT> endif
createSeg %SEGNAME, CodeSeg, word, public, CODE
sBegin Data sEnd Data
sBegin CodeSeg assumes cs,CodeSeg assumes ds,DATA
;---------------------------Public-Routine------------------------------; ; hmemcpy ; ; copy memory ; ; Entry: ; lpSrc HPSTR to copy from ; lpDst HPSTR to copy to ; cbMem DWORD count of bytes to move ; ; NOTE: overlapped copies will work iff lpSrc.sel == lpDst.sel ; [This is a lie. They will always work.] ; ; Returns: ; destination pointer ; Error Returns: ; None ; Registers Preserved: ; BP,DS,SI,DI ; Registers Destroyed: ; AX,BX,CX,DX,FLAGS ; Calls: ; nothing ; History: ; ; Wed 04-Jan-1990 13:45:58 -by- Todd Laney [ToddLa] ; Created. ; Tue 16-Oct-1990 16:41:00 -by- David Maymudes [DavidMay] ; Modified 286 case to work correctly with cbMem >= 64K. ; Changed name to hmemcpy. ; Changed 386 case to copy by longwords ;-----------------------------------------------------------------------;
cProc MemCopy,<FAR,PASCAL,PUBLIC,NODATA>,<> ; ParmD lpDst ; ParmD lpSrc ; ParmD cbMem cBegin <nogen> mov ax,__WinFlags test ax,WF_CPU286 jz fmemcpy386 jmp FAR PTR fmemcpy286 cEnd <nogen>
cProc fmemcpy386,<FAR,PASCAL,PUBLIC,NODATA>,<ds> ParmD lpDst ParmD lpSrc ParmD cbMem cBegin .386 push edi push esi cld
mov ecx,cbMem jecxz mc386_exit
movzx edi,di movzx esi,si lds si,lpSrc les di,lpDst ; ; calculate differance of pointers in "selector" space ; mov ax,si ; DX:AX = lpSrc mov dx,ds
mov bx,es ; BX = selector of ptr B
mov cx,__AHSHIFT ; number of selector bits per 64K 'segment' shr dx,cl ; linearize ptr A shr bx,cl ; linearize ptr B ; ; DX and BX contain normalized selectors ; mov ecx,cbMem
sub ax,di sbb dx,bx ; do long subtraction. jnc mc_copy_forward
add ax,cx adc dx,cbMem.hi jnc mc_copy_forward ; carry, so >0, thus they do hit.
std add edi,ecx add esi,ecx
sub edi,4 sub esi,4
push ecx shr ecx,2 ; get count in DWORDs rep movs dword ptr es:[edi], dword ptr ds:[esi] db 67H ; Fix strange 386 bug add edi,3 add esi,3 pop ecx and ecx,3 rep movs byte ptr es:[edi], byte ptr ds:[esi] db 67H ; Fix strange 386 bug jmp mc386_exit
mc_copy_forward: push ecx shr ecx,2 ; get count in DWORDs rep movs dword ptr es:[edi], dword ptr ds:[esi] db 67H pop ecx and ecx,3 rep movs byte ptr es:[edi], byte ptr ds:[esi] db 67H nop mc386_exit: cld pop esi pop edi mov dx,lpDst.sel ; return destination address mov ax,lpDst.off .286 cEnd
cProc fmemcpy286,<FAR,PASCAL,PUBLIC,NODATA>,<ds,si,di> ParmD lpDst ParmD lpSrc ParmD cbMem cBegin mov cx,cbMem.lo ; CX holds count or cx,cbMem.hi ; or with high word jnz @f jmp empty_copy @@: lds si,lpSrc ; DS:SI = src les di,lpDst ; ES:DI = dst ; ; calculate differance of pointers in "selector" space ; mov ax,si ; DX:AX = lpSrc mov dx,ds
mov bx,es ; BX = selector of ptr B
mov cx,__AHSHIFT ; number of selector bits per 64K 'segment' shr dx,cl ; linearize ptr A shr bx,cl ; linearize ptr B ; ; DX and BX contain normalized selectors ; mov cx,cbMem.lo
sub ax,di sbb dx,bx ; do long subtraction. jnc forward_copy ; difference is positive, so copy forward
; see if the blocks intersect: is source + count > dest? ; equivalently, is source-dest + count > 0 ? ; sub ax,cx ; sbb dx,0 ; jnc next ; This looks wrong. Recheck!
add ax,cx adc dx,cbMem.hi jc reverse_copy ; carry, so >0, thus they do hit.
forward_copy: jmp next reverse_copy: ; first, we have to set ds:si and es:di to the _ends_ of the blocks
sub cx,1 sbb cbMem.hi,0 ; subtract 2 from (long) count xor ax,ax add si,cx adc ax,cbMem.hi
push cx mov cx,__AHSHIFT shl ax,cl pop cx mov bx,ds add ax,bx ; advance DS mov ds,ax
xor ax,ax add di,cx adc ax,cbMem.hi
push cx mov cx,__AHSHIFT shl ax,cl pop cx mov bx,es add ax,bx ; advance ES mov es,ax
add cx,1 adc cbMem.hi,0 ; restore count ; ; DS:SI += Count ; ES:DI += Count ; While Count != 0 Do ; Num = MIN(Count,SI+1,DI+1) ; Reverse Copy "Num" Bytes from DS:SI to ES:DI ; (SI -= Num, DI -= Num) ; Count -= Num ; If Count == 0 Then ; BREAK ; If SI == 0xFFFF Then ; DS -= __AHINCR ; If DI == 0xFFFF Then ; ES -= __AHINCR ; next_r: mov ax,si sub ax,di sbb bx,bx and ax,bx add ax,di ; AX = MIN(SI, DI) test cbMem.hi,-1 ; is high word not zero? jnz @f ; at least 64k to go
dec cx sub ax,cx sbb bx,bx and ax,bx add ax,cx inc cx @@: xor bx,bx add ax,1 ; AX = Num = MIN(Count-1,SI,DI)+1 adc bx,0 ; bx==1 if exactly 64k
xchg ax,cx sub ax,cx ; Count -= Num sbb cbMem.hi,bx
std shr bx,1 rcr cx,1 ; if bx==1, then cx ends up 0x8000 dec si dec di rep movsw inc si ; realign pointers inc di adc cl,cl rep movsb ; move last byte, if necessary cld
mov cx,ax ; restore cx or ax,cbMem.hi
jz done ; If Count == 0 Then BREAK
cmp si,-1 ; if SI wraps, update DS jne @f mov ax,ds sub ax,__AHINCR mov ds,ax ; update DS if appropriate @@: cmp di,-1 ; if DI wraps, update ES jne next_r mov ax,es sub ax,__AHINCR mov es,ax ; update ES if appropriate jmp next_r
; ; While Count != 0 Do ; If (Count + SI > 65536) OR (Count + DI > 65536) Then ; Num = Min(65536-SI, 65536-DI) ; Else ; Num = Count ; Copy "Num" Bytes from DS:SI to ES:DI (SI += Num, DI += Num) ; Count -= Num ; If Count == 0 Then ; BREAK ; If SI == 0 Then ; DS += __AHINCR ; If DI == 0 Then ; ES += __AHINCR ; next: ;;;;;;;;mov ax,cx ;;;;;;;;dec ax
mov ax,di not ax ; AX = 65535-DI
mov dx,si not dx ; DX = 65535-SI
sub ax,dx sbb bx,bx and ax,bx add ax,dx ; AX = MIN(AX,DX) = MIN(65535-SI,65535-DI)
; problem: ax might have wrapped to zero
test cbMem.hi,-1 jnz plentytogo ; at least 64k still to copy dec cx ; this is ok, since high word is zero sub ax,cx sbb bx,bx and ax,bx add ax,cx ; AX = MIN(AX,CX) inc cx
plentytogo: xor bx,bx add ax,1 ; AX = Num = MIN(count,65536-SI,65536-DI) ; we must check the carry here! adc bx,0 ; BX could be 1 here, if CX==0 indicating ; exactly 64k to copy xchg ax,cx sub ax,cx ; Count -= Num sbb cbMem.hi,bx
shr bx,1 rcr cx,1 ; if bx==1, then cx ends up 0x8000 rep movsw adc cl,cl rep movsb ; move last byte, if necessary
mov cx,ax ; put low word of count back in cx or ax,cbMem.hi
jz done ; If Count == 0 Then BREAK
or si,si ; if SI wraps, update DS jnz @f mov ax,ds add ax,__AHINCR mov ds,ax ; update DS if appropriate @@: or di,di ; if DI wraps, update ES jnz next mov ax,es add ax,__AHINCR mov es,ax ; update ES if appropriate jmp next ; ; Restore registers and return ; done: empty_copy: mov dx,lpDst.sel ; return destination address mov ax,lpDst.off cEnd
sEnd
sEnd CodeSeg end
|