$NOLIST  DEBUG

;Os.Bits.asm -- bit boundary block operations
  NAME Bits

; 2/9/82 - JMT: Reverse copy failed if boundaries
;   and lengths caused toFullWords to be different
;   from fromFullWords. These values are important
;   as they must be added to DI and SI respectively
;   to determine where blt starts.

; 7/26/82 - RS: GROUP is CommonCode
;               SEGMENT is Bits

Sysdep_CGROUP GROUP Sysdep_CODE

PUBLIC BitInvertHLine, BitEraseHLine, CpCopyRectangle
PUBLIC StartPosition


Sysdep_CODE SEGMENT PUBLIC 'CODE'

ASSUME CS:Sysdep_CGROUP

GRiDmask        EQU  7FFFH
PCmask          EQU  7FFFH
GRiDSpecMask 	 EQU  0FFFEH
PCSpecMask      EQU  0FFFCH

pelsPerWordBase EQU  16

PCFormat         EQU   0
GRIDFormat       EQU   1
MaskFormatSource EQU   1		; used when formats are packed
MaskFormatDest   EQU   2		; in one byte

ScreenSeg       EQU   0B800H

forward         EQU   0
reverse         EQU   1
pelsPerWord     EQU   16
bytesInLine    EQU   80
allOnes EQU 0FFFFH

;------------------
%*DEFINE(DivAxBy16)
 (SAR  AX,1
  SAR  AX,1
  SAR  AX,1
  SAR  AX,1)


;----------------------
StartPosition PROC FAR
;----------------------
;computes word address & shift count, given x & y
;INPUT: 
;   AX = y
;   BX = x
;   DX = bytesPerLine
;   DI = odd/evenline offset
;   CX = screenFormat
;
;OUTPUT:
;   DI = offset to word
;   CX = shift count
;   DX = bytesPerLine
;   AX = 0 for an even line , 1 for an oddline
;
  PUSH DX
  CLC
  CMP  CX,PCformat
  JNZ  S0
  SHR  AX, 1           ; alternating scan lines
S0:
  PUSHF
  JC   S1
  MOV  DI, 0

S1:
  IMUL DX
  ADD  DI, AX          ;offset to beginning of line
  MOV  AX, BX		   ; get x
  SAR  AX, 1           ; account for bits/pel
  SAR  AX, 1           
  SAR  AX, 1           
  SAR  AX, 1           ; x/16 pels/word
;  CMP  CX,PCformat
;  MOV  CX,7
;  JZ   S2
;  SAR  AX, 1           
  MOV  CX,0FH

S2:
  SHL  AX, 1           ; make it a byte count
  ADD  DI,AX

  AND  CX,BX          ;  pels per word
  XOR  AX,AX
  POPF
  ADC  AX,0
  POP  DX
  RET
StartPosition ENDP

$EJ

;-----------------------------------------
;Parameters for InvertHLine and EraseHLine
;-----------------------------------------
params STRUC
;-----------
oldBp  DW  ?
oldDs  DW  ?
returnIP DW  ?
returnCS DW  ?
;-----------
numLines     DW  ?
w            DW  ?   ;width
x            DW  ?
y            DW  ?
windowHeight DW  ?
bytesPerLine DW  ?
screen       DW  ?
params ENDS

localBytes EQU 0
loc EQU [BP-localBytes]
paramBytes EQU 14

; ES:DI points to the screen
; SI is offset to start of current line
; BX = last word mask
; DX = # words in middle of line
; DS = first word mask

;----------------------------------------------
;This macro is the body for the HLine functions
;----------------------------------------------
%*DEFINE(DoHLine(OP,notMask))
 LOCAL doFirst middle middleLoop1 lastWord epilogue TopOfLoop doLastWord UsingScreen BottomofLoop GoToEven EndLoop
(
  PUSH DS
  PUSH BP
  MOV  BP,SP
  MOV  AX, loc.screen
  MOV  ES,AX

  CMP  AX, ScreenSeg	 ; see if we are on screen
  MOV  AX, 2000h
  JZ   %UsingScreen
  MOV  AX, loc.windowHeight
  SHR  AX, 1
  ADC  AX, 0
  IMUL loc.bytesPerLine

%UsingScreen:
  MOV  loc.windowHeight, AX
  MOV  DI, AX
  MOV  CX, PcFormat

  MOV  AX,loc.y
  MOV  BX,loc.x
  MOV  DX, loc.bytesPerLine
  CALL StartPosition    ; calculate where to start
  MOV  loc.y, AX	    ; now use loc.y as odd/even line flag

;first word:
  MOV  AX,CX		    ; example CX = 4
  MOV  BX,pelsPerWord
  SUB  BX,AX		    ; # pels to end of word
  MOV  AX,0FFFFH
  SHR  AX,CL		    ; 0000 1111 1111 1111
  MOV  CX,BX
  SUB  CX,loc.w
  JLE  SHORT %doFirst
;fits in one word:
  SHR  AX,CL
  SHL  AX,CL
  %IF (%EQS(%notMask,true)) THEN (NOT  AX) FI
  XCHG AL, AH			    ; bytes are backwards on PC
  MOV  DS, AX                ; first mask
  XOR  DX, DX                ; middle count := 0
  XOR  BX, BX                ; last mask := 0
  JMP  SHORT %epilogue
%doFirst:
  %IF (%EQS(%notMask,true)) THEN (NOT  AX) FI
  XCHG AL, AH			    ; bytes are backwards on PC
  MOV  DS, AX                ; first mask

$EJ

%middle:
  MOV  AX,loc.w
  SUB  AX,BX           ;bits already done
  MOV  BX,AX           ;bit count
  %DivAxBy16
  MOV  DX,AX           ;word count for middle

  AND  BX,0FH		   ; example BX = 6
  MOV  CX,pelsPerWord
  SUB  CX,BX		   ; CX = 10
  MOV  AX,0FFFFH
  SHL  AX,CL		   ; 1111 1100 0000 0000
  %IF (%EQS(%notMask,true)) THEN (NOT  AX) FI
  MOV  BX,AX           ; last
  XCHG BL, BH		   ; bytes are backwards on Pc

%epilogue:

  CLD                       ; clear direction
; **  PUSH BP                   ; this will be a register
  MOV  CX, loc.numLines ; get # of lines
  MOV  SI, DI               ; SI is offset to start of line

%TopOfLoop:
  PUSH CX
; **  MOV BP, CX                ; save current count in BP

  MOV DI, SI                ; DI := offset of next line
  MOV AX, DS                ; AX := first mask

; first
  %OP ES:[DI], AX
  INC DI
  INC DI

; middle
  MOV CX, DX                ; CX := # words in middle
  JCXZ %DoLastWord

  %IF (%EQS(%notMask,true)) THEN
      (XOR AX,AX           
       REP STOSW)        
  ELSE 
      (MOV AX, -1
       MiddleLoop1:
       %OP ES:[DI], AX   
       INC DI
       INC DI
       LOOP MiddleLoop1) 
  FI

%DoLastWord:
  AND BX, BX                ; is last word (BX) = 0
  JZ %BottomOfLoop

  %OP ES:[DI], BX

%BottomOfLoop:
  MOV AX, loc.y			  ; get odd/even flag
  OR  AX, AX				  ; IF AX = 0 (even line) THEN
  JNE %GoToEven
  ADD SI, loc.windowHeight	  ; go to odd buffer
  INC loc.y				  ; indicate next line is odd
  JMP SHORT %EndLoop

%GoToEven:
  SUB SI, loc.windowHeight	  ; go to even buffer
  ADD SI, loc.bytesPerLine      ; make SI be offset to next line
  DEC loc.y				  ; indicate next line is even

%EndLoop:
; **  MOV CX, BP                ; get current count
  POP CX
  LOOP %TopOfLoop            ; do next one

; **  POP BP
  MOV  SP,BP
  POP  BP
  POP  DS
  RET  paramBytes
)

$EJ

;----------------------------------------
BitInvertHLine PROC FAR
;PROCEDURE BitInvertHLine (screen,
;                          bytesPerLine,
;                          windowHeight
;                          y, x, w, numLines: Integer);
;----------------------------------------
;Invert a horizontal line on the display in line
;y, starting at position x for width w

 %DoHLine(XOR,false)

BitInvertHLine ENDP


;----------------------------------------
BitEraseHLine PROC FAR
;PROCEDURE BitEraseHLine (screen, 
;                         bytesPerLine,
;                         windowHeight,
;                         y, x, w, numLines: Integer);
;----------------------------------------
;Erase a horizontal line on the display

 %DoHLine(AND,true)

BitEraseHLine ENDP

  PURGE params
;  PURGE lastBits
  PURGE oldBP
  PURGE returnIP
  PURGE returnCS
  PURGE w
  PURGE x
  PURGE y
  PURGE screen
  PURGE localBytes
  PURGE loc
  PURGE paramBytes
  PURGE bytesPerLine
  PURGE windowHeight
  PURGE numLines
  PURGE OldDS

$EJ

;---------------------------------
;parameters & locals for MoveHLine
;---------------------------------
params STRUC
firstWord  DW ?
fromFullWords  DB ?
toFullWords DB ?
toLastBits DB ?
fromLastBits DB ?
startSI    DW ?
startDI    DW ?

fromAlign  DB ?
toAlign    DB ?
lineAltSource DB ?
lineAltDest   DB ?
bitsLeft   DW ?
directionFlag DB ?
dummy      DB ?
;---------------
oldBP      DW ?
oldDS      DW ?
returnIP   DW ?
returnCS   DW ?
;---------------
mode       DW ?
lineCount  DW ?
bitCount   DW ?
toY        DW ?
toX        DW ?
fromY      DW ?
fromX      DW ?
destFormat DW ?
destHeight DW ?
dest_BPL   DW ?
dest       DW ?
sourceFormat DW ?
sourceHeight DW ?
source_BPL  DW ?
source     DW ?
params ENDS

localBytes EQU 18
loc EQU [BP-localBytes]
paramBytes EQU 30

$EJ
;--------------------------------------------
CpCopyRectangle PROC FAR
;PROCEDURE CpCopyRectangle(source,
;                        sourceBytesPerLine, sourceHeight,
;                        sourceFormat, dest, destBytesPerLine,
;                        destHeight, destFormat,
;                        fromX,fromY, toX,toY,
;                       bitCount,lineCount, mode:Integer);
;--------------------------------------------
;Moves horizontal lines of bits from one place on
;the screen to another

  PUSH DS
  PUSH BP
  MOV  BP,SP
  SUB  SP,localBytes


  MOV  AX,loc.source
  MOV  DS,AX					         ; Set	source segment in DS
  CMP  AX,screenSeg
  MOV  AX, 2000H					    ; screen has odd buffer 
  JZ   useScreenSource				    ; 2000h lower than even
  MOV  AX, loc.sourceHeight
  SHR  AX,1
  ADC  AX,0
  IMUL loc.source_BPL
useScreenSource:					    ; store numBytes of even buffer
  MOV  loc.sourceHeight,AX			    ; in loc.sourceHeight


  MOV  AX,loc.dest
  MOV  ES,AX						    ; set dest segment in ES
  CMP  AX,screenSeg
  MOV  AX, 2000H					    ; screen has odd buffer 
  JZ   useScreendest				    ; 2000h lower than even
  MOV  AX, loc.destHeight
  SHR  AX,1
  ADC  AX,0
  IMUL loc.dest_BPL

useScreendest:						    ; store numBytes of even buffer
     MOV  loc.destHeight,AX			    ; in loc.destHeight
;
;
;
;

screenToScreen:
  MOV  AX, DS
  MOV  BX, ES
  CMP  AX, BX
  JNE  calcStartPc           ; if we are not in same window, do nothing

  MOV  AX,loc.fromY
  CMP  AX,loc.toY
  JGE SHORT calcStartpc
;
; do lines in reverse order.
; When the from rect is above the to Rect then copy the rect
; in reverse order from bottom to top
;
  ADD  AX,loc.lineCount
  DEC  AX
  MOV  loc.fromY,AX
  MOV  AX,loc.toY
  ADD  AX,loc.lineCount
  DEC  AX
  MOV  loc.toY,AX

calcStartpc:
; Calculate the word in buffer of start source rect
; along with the alignment and whether odd or even buffer
;
  MOV  AX,loc.fromY
  MOV  BX,loc.fromX
  MOV  DX,loc.Source_Bpl
  MOV  DI,loc.sourceHeight
  MOV  CX,loc.sourceFormat

  CALL StartPosition
  MOV  SI,DI
  MOV  loc.fromAlign,CL
  MOV  loc.lineAltSource,AL

; do the same for the destination
  MOV  AX,loc.toY
  MOV  BX,loc.toX
  MOV  DX,loc.Dest_Bpl
  MOV  DI,loc.DestHeight
  MOV  CX,loc.DestFormat
  CALL StartPosition
  MOV  loc.toAlign,CL
  MOV  loc.LineAltDest,AL

  MOV  AX,loc.bitCount         ; number of bits to move in a line
  NEG  CX                      ;returned by StartPosition
  ADD  CX,pelsPerWord          ;CX := 16 - toAlign 
                               ;= # bits to be written over in this word
  SUB  AX,CX                   ;AX := bitCount - (16-toAlign)
                               ;= # bits to be written over in remaining words
  JG SHORT calcWords
; bits to move spans only 1 word
  ADD  AX,pelsPerWord          ;bitCount + toAlign
  MOV  loc.toFullWords,0
  MOV  loc.toLastBits,AL		 ; toLastBits = toAlign + bitCount
  JMP SHORT yLoop
calcWords:
; calculate the number of full words in rect and remaining bits
  MOV  BL,pelsPerWord
  DIV  BL
  MOV  loc.toFullWords,AL
  MOV  loc.toLastBits,AH

  CMP  AL, 0               ; forward copy if no full words
  JE   SHORT yLoop

  MOV  AX,loc.fromY
  CMP  AX,loc.toY
  JNE  yLoop
; If from rect is on same line as to rect then check if
; fromX is less than toX.  If so,  then the rect has
; to be copied in reverse x order (but still top down)
  MOV  AX,loc.fromX
  CMP  AX,loc.toX
  JGE  yLoop

  MOV  AX, DS
  CMP  AX, loc.dest
  JNE  yLoop             ; forward copy if different windows

  JMP  reverseCopy

$EJ

yLoop:
  CMP  loc.lineCount,0		  ; have we done all the lines?
  JNE SHORT doFirstWord
  JMP  finished
doFirstWord:
  DEC  loc.lineCount
  MOV  loc.startSI,SI		  ; store the start position in source line
  MOV  loc.startDI,DI		  ; store the start position in dest line
  CLD
  LODSW					   ; get the first word
  CMP loc.sourceFormat, PcFormat ; is it screenFormat?
  JNZ NoSwap 
  XCHG AL,AH                     ;bassackwards screen

NoSwap:
  MOV  loc.firstWord,AX
  MOV  CL,loc.toAlign
  SUB  CL,loc.fromAlign	  ; CL = toAlign - fromAlign
  JL SHORT toAlignLess1
  JG SHORT toAlignGreater
  JMP sameAlignment
  
; In the following code: 
; DL := amount to shift to the left the first source word 
;       to align it to the dest position
; DH := (16 - DL); amount to shift the the right the next source
;       word to align it to the dest position

toAlignGreater:
; Case: toAlignment > fromAlign
; example:  toAlign   = 0000 0001 1111 1111 (7)
;           fromAlign = 0000 1111 1111 1111 (4)
;
  MOV  DH,CL            ; DH := toAlign-fromAlign
  SHR  AX,CL		    ; position the bits to the to position
  NEG  CL
  ADD  CL,pelsPerWord   ; CL:= 16 - CL
  MOV  DL,CL		    ; DL := 16 - (toAlign - fromAlign)
  JMP SHORT doFirst

toAlignLess1:
; Case: toAlignment < fromAlign
; example:  toAlign   = 0000 1111 1111 1111 (4)
;           fromAlign = 0000 0001 1111 1111 (7)
;
; In this case two source words are needed to fill in one dest
; word.
;
  NEG  CL               ;fromAlign-toAlign
  MOV  DL,CL		    ; DL := fromAlign - toAlign
  SHL  AX,CL		    ; position the bits to the to position
				    ; ex: 0000 1111 1111 1000
  MOV  BX,AX		    ; save data word in BX
  LODSW			    ; get next data word
  CMP loc.sourceFormat, PcFormat ; is it screenFormat?
  JNZ NoSwap1
  XCHG AL,AH            ;backwards screen

noSwap1:
  MOV  loc.firstWord,AX ; store for the loop
  NEG  CL
  ADD  CL,pelsPerWord   ;CL:=16-CL
  MOV  DH,CL		    ; DH := 16 - (fromAlign - toAlign)
  SHR  AX,CL            ;position data word to "to" alignment
				    ; ex: 0000 0000 0000 0111
  OR   AX,BX		    ; OR it to come up with 1st word
				    ; ex: 0000 1111 1111 1111


doFirst:
  MOV  BX,allOnes	    ; 1111 1111 1111 1111
  MOV  CL,loc.toAlign
  SHR  BX,CL		    ; ex: 0000 1111 1111 1111
  AND  AX,BX		    ; get rid of bits to left of rect
  NOT  BX			    ; ex: 1111 0000 0000 0000

  CMP loc.destFormat, PcFormat ; is it screenFormat?
  JNZ NoSwap2
  XCHG BL,BH            ; screen is low byte first
  AND  BX,ES:[DI]	    ; get dest bits to left of rect
  XCHG BL,BH		    ; Screen format
  JMP  SHORT NoSwap3

NoSwap2:
  AND  BX,ES:[DI]	    ; get dest bits to left of rect

NoSwap3:
  OR   AX,BX		    ; merger the two together
  MOV  CH,0
  ADD  CX,loc.bitCount  ; toAlign + bitCount
  CMP  CX,pelsPerWord   ; Does rect cross word boundary
  JLE SHORT lastWord

  CMP loc.destFormat, PcFormat ; is it screenFormat?
  JNZ NoSwap4
  XCHG AL,AH            ;reset back to original

NoSwap4:
  STOSW			    ; store the first word

$EJ

;set up for word loop:
; Put into CL both dest and source formats
  MOV  CX, loc.sourceFormat ; get source format
  AND  CX, MaskFormatSource
  MOV  AX, loc.destFormat   ; get dest format
  SHL  AX, 1			   ; move it over 1 bit
  AND  AX, MaskFormatDest
  OR   CX, AX

  MOV  CH,loc.toFullWords
  PUSH BP
  MOV  BP,loc.firstWord	 ; BP := previous Word
  INC  CH                 ;include once through for partial word
middleLoop:
  MOV  BX,BP
  XCHG  CL,DL      ; swap amount to shift previous word and formats
  SHL  BX,CL      ; take least significant bits and shift them up
  XCHG  CL,DL      ; swap amount to shift previous word and formats
  LODSW	        ; get next word
  TEST  CL, MaskFormatSource
  JNZ  NoSwap5
  XCHG AL,AH	   ; exchange if pc format

NoSwap5:
  MOV  BP,AX	   ; this word is next words previous word
  XCHG  CL,DH	   ; swap formats with align byte
                  ; move high order bits to right to align
  SHR  AX,CL	   ; with dest
  OR   AX,BX	   ; merge prev with this word
  XCHG  CL,DH	   ; swap formats with align byte
  DEC  CH		   ; toFullWords = toFullWords - 1
  JE SHORT endLoop

  TEST  CL, MaskFormatDest  ; is dest screen format?
  JNZ  NoSwap6
  XCHG AL,AH	   ; exchange if pc format

NoSwap6:
  STOSW		   ; store the word
  JMP SHORT middleLoop
endLoop:
  POP  BP		   ; restore so loc is valid

lastWord:
  MOV  CH,loc.toLastBits  ; get the # bits on left of word to store
  CMP  CH,0
  JNE   SHORT continue
  JMP   endYLoop

continue:
  MOV  BX,allOnes		 ; 1111 1111 1111 1111
  MOV  CL,loc.toLastBits
  SHR  BX,CL              ; 0000 1111 1111 1111
  NOT  BX                 ; 1111 0000 0000 0000
  AND  AX,BX
  NOT  BX                 ; 0000 1111 1111 1111

  CMP loc.destFormat, PcFormat ; is it screenFormat?
  JNZ NoSwap7
  XCHG BL,BH              ; reverse the bytes if screen format
  AND  BX,ES:[DI]         ; get dest bits to right of rect
  XCHG AL,AH              ; reverse the bytes if screen format
  JMP  SHORT NoSwap8

NoSwap7:
  AND  BX,ES:[DI]		 ; get dest bits to right of rect

NoSwap8:
  OR   AX,BX			 ; merge the bits together
  STOSW
  JMP  endYLoop

$EJ

;
; This is the case where toAlign = fromAlign
; example:  toAlign   = 0000 0001 1111 1111 (7)
;           fromAlign = 0000 0001 1111 1111 (7)
;

sameAlignment:
  MOV  DX, loc.destFormat  ; DX := destFormat
  CMP  loc.toAlign,0
  JNE  SHORT sameFirstWord
  CMP  loc.toFullWords, 0
  JE   SHORT sameFirstWord1
  JMP  MiddleWords

sameFirstWord:
  MOV  BX,allOnes		  ; 1111 1111 1111 1111
  MOV  CL,loc.toAlign
  SHR  BX,CL			  ; 0000 0001 1111 1111
  AND  AX,BX			  ; mask out bits to left of rect
  NOT  BX				  ; 1111 1110 0000 0000
  CMP  DL, PcFormat        ; is it screenFormat?
  JNZ NoSwap9
  XCHG BL,BH               ; reverse the bytes in screen format
  AND  BX,ES:[DI]		  ; get bits to left of rect
  XCHG BL,BH               ; reverse the bytes in screen format
  JMP  SHORT NoSwap10

NoSwap9:
  AND  BX,ES:[DI]		  ; get bits to left of rect

NoSwap10:
  OR   AX,BX			  ; merge the bits together

SameFirstWord1:
  XOR  CH,CH
  ADD  CX,loc.bitCount
  CMP  CX,pelsPerWord	  ; are there any full words to do?
  JLE  SHORT noMiddle

middleWords:
  CMP  DL, PcFormat        ; is dest screenFormat?
  JNZ NoSwap11
  XCHG AL,AH               ; reverse the bytes in screen format

NoSwap11:
  STOSW				  ; store the dest word
  CMP  loc.toFullWords,0
  JE   partialLastWord
  XOR  CX,CX			  ; CX := 0
  MOV  CL,loc.toFullWords
  MOV  BX, loc.sourceFormat
  SUB  BX, loc.destFormat
  JZ   MoveTheBytes
TopALoop:
  LODSW				 ; get the word
  XCHG AL, AH			 ; swap the bytes
  STOSW
  LOOP TopALoop
  JMP SHORT PartialLastWord

MoveTheBytes:
  REP  MOVSW			  ; straight move

partialLastWord:
  CMP  loc.toLastBits,0
  JE   SHORT endYLoop
  LODSW
  CMP  loc.sourceFormat, PcFormat        ; is source screenFormat?
  JNZ NoSwap12
  XCHG AL,AH			  ; reverse bytes in screen format

noSwap12:
noMiddle:
  MOV  BX,allOnes		  ; 1111 1111 1111 1111
  MOV  CL,loc.toLastBits
  SHR  BX,CL               ; 0000 1111 1111 1111
  NOT  BX                  ; 1111 0000 0000 0000
  AND  AX,BX			  ; get last bits of rect
  NOT  BX                  ; 0000 1111 1111 1111
  CMP  DL, PcFormat        ; is dest screenFormat?
  JNZ NoSwap13
  XCHG BL,BH			  ; reverse bytes in screen mode
  AND  BX,ES:[DI]		  ; get bits to right of rect
  XCHG BL,BH			  ; reverse bytes in screen mode
  OR   AX,BX			  ; merge the bits
  XCHG AL,AH			  ; reverse the dest word
  JMP  NoSwap14

NoSwap13:
  AND  BX,ES:[DI]		  ; get bits to right of rect
  OR   AX,BX			  ; merge the bits

NoSwap14:
  STOSW

$EJ

endYLoop:
  MOV  SI,loc.startSI
  MOV  DI,loc.startDI

  MOV  AX, DS
  MOV  BX, ES
  CMP  AX, BX                    ; are we in the same window?
  JNE  yForward                  ; if so, forward copy

  MOV  AX,loc.fromY
  CMP  AX,loc.toY		              ; determine the direction of copy
  JGE  SHORT yForward

yBackward:
  CMP  loc.sourceFormat, PcFormat
  JE   screenFmt
  SUB  SI,loc.source_BPL        ; Subtract bytesInLine
  JMP  SHORT yBackAdjustDestPtr

screenFmt:
  XOR   loc.lineAltSource,1
  Jz    yBackNextsourceEven

  SUB  SI,loc.source_BPL        ;yes sub bytesInLine
  ADD  SI,loc.sourceHeight
  JMP  yBackAdjustDestPtr

yBackNextSourceEven:
  SUB  SI,loc.sourceHeight

yBackAdjustDestPtr:
  CMP  loc.destFormat, PcFormat
  JE   screenFmtDest
  SUB  DI,loc.dest_BPL        ; Subtract bytesInLine
  JMP  yLoop

screenFmtDest:
  XOR   loc.lineAltDest,1
  JZ    yBackNextDestEven

  SUB  DI,loc.dest_BPL        ;yes sub bytesInLine
  ADD  DI,loc.destHeight
  JMP  yLoop

yBackNextDestEven:
  SUB  DI,loc.destHeight
  JMP  yLoop



yForward:
  CMP  loc.sourceFormat, PcFormat
  JE   screenFmt1
  ADD  SI,loc.source_BPL        ; add bytesInLine
  JMP  SHORT yForAdjustDestPtr

screenFmt1:
  XOR   loc.lineAltSource,1
  JZ    yForNextsourceEven

  ADD  SI,loc.sourceHeight
  JMP  SHORT yForAdjustDestPtr

yForNextSourceEven:
  SUB  SI,loc.sourceHeight
  ADD  SI,loc.source_BPL        ;yes sub bytesInLine

yForAdjustDestPtr:
  CMP  loc.destFormat, PcFormat
  JE   screenFmtDest1
  ADD  DI,loc.dest_BPL        ; add bytesInLine
  JMP  yLoop

screenFmtDest1:
  XOR   loc.lineAltDest,1
  Jz    yForNextDestEven

  ADD  DI,loc.destHeight
  JMP  yLoop

yForNextDestEven:
  SUB  DI,loc.destHeight
  ADD  DI,loc.dest_BPL        ;yes sub bytesInLine
  JMP  yLoop



finished:
  MOV  SP,BP
  POP  BP
  POP  DS
  RET  paramBytes

$EJECT

; This code is for the special case where the to and
; from Y values are the same and the from X value is
; less than the to X value. The copying must be 
; done in reverse order to prevent overwriting. This
; case is important for horizontal scrolling in the
; text editor.

reverseCopy:

; compute fromLastBits

  MOV  AX,loc.bitCount
  MOV  CL,loc.fromAlign
  XOR  CH,CH		          ; CH := 0
  NEG  CX					; CX := -fromAlign
  ADD  CX,pelsPerWord         ; CX := 16 - fromAlign
						; = # bits to be written over in this word
  SUB  AX,CX                  ; AX := bitCount - (16-fromAlign)
						; = # bits to be written over in remaining words
  JG   SHORT revCalcWords	; 
; bits to move spans only one word
  ADD  AX,pelsPerWord         ; AX := bitCount + fromAlign
  MOV  loc.fromFullWords,0
  MOV  loc.fromLastBits,AL	; put in fromLastBits
  JMP  SHORT revYLoop
revCalcWords:
  MOV  BL,pelsPerWord
  DIV  BL
  MOV  loc.fromFullWords,AL	; # full words in source rect
  MOV  loc.fromLastBits,AH	; # bits in last word - 1111 1100 0000 0000
						; fromLastBits is for last word (far right)
						; of rect

; adjust toAlign to be the number of good bits in
; the first word.

  MOV  AL,loc.toAlign		; 0000 0001 1111 1111 (7)
  NEG  AL
  ADD  AL,pelsPerWord		; 1111 1111 1000 000  (9)
  MOV  loc.toAlign,AL		; going from right to left

; adjust to last dest word for copying backwards

  AND  AX,0
  MOV  AL,loc.toFullWords
  SHL  AX,1    ; double it as it counts bytes
  INC  AX
  INC  AX      ; partial word
  ADD  DI,AX	; start at far right side of rect

; adjust to last source word for copying backwards

  AND  AX,0
  MOV  AL,loc.fromFullWords
  SHL  AX,1    ; double it as it counts bytes
  INC  AX
  INC  AX      ; partial word
  ADD  SI,AX	; start at far right side of rect

$EJ

revYLoop:
  CMP  loc.lineCount,0
  JNE  SHORT revDoLastWord
  JMP  finished	    ; done, exit loop
revDoLastWord:
  DEC  loc.lineCount    ; decrement loop counter
  MOV  loc.startSI,SI   ; save source start location
  MOV  loc.startDI,DI   ; save dest start location
  STD			    ; go in reverse order
  LODSW			    ; get the first source word
  CMP  loc.sourceFormat, PcFormat        ; is source screenFormat?
  JNZ RevNoSwap1
  XCHG AL,AH            ; reverse the bytes in screen format

revNoSwap1:
  MOV  loc.firstWord,AX	    ; store the first word
  MOV  CL,loc.fromLastBits   ; 1111 1100 0000 0000 (6)
  SUB  CL,loc.toLastBits	    ; 1111 1111 1000 0000 (9)
  JL SHORT revToAlignLess1   ; toLastBits > fromLastBits
  JG SHORT revToAlignGreater ; fromLastBits > toLastBits
  JMP revSameAlignment	    ; fromLastBits = toLastBits
  
; In the following code: 
; DH := amount to shift to the left the first source word 
;       to align it to the dest position
; DL := (16 - DH); amount to shift the the right the next source
;       word to align it to the dest position


revToAlignGreater:
; Case: fromLastBits > toLastBits
; Ex:  fromLastBits  = 1111 1110 0000 0000 (7)
;      toLastBits    = 1111 1000 0000 0000 (5)
;
  MOV  DH,CL            ; DH := fromLastBits - toLastBits
  SHL  AX,CL		    ; move word left to align with dest
  NEG  CL
  ADD  CL,pelsPerWord   ; CL:= 16 - (fromLastBits - toLastBits)
  MOV  DL,CL		    ; DL := CL
  JMP SHORT revDoLast

revToAlignLess1:
; Case: toLastBits > fromLastBits
; Ex:  fromLastBits  = 1111 1000 0000 0000 (5)
;      toLastBits    = 1111 1110 0000 0000 (7)
;
  NEG  CL			    
  MOV  DL,CL		    ; DL := toLastBits - fromLastBits
  SHR  AX,CL		    ; move to the right the word to align with dest
  MOV  BX,AX		    ; save word
  LODSW			    ; get next word
  CMP  loc.sourceFormat, PcFormat        ; is source screenFormat?
  JNZ RevNoSwap2
  XCHG AL,AH

revNoSwap2:
  MOV  loc.firstWord,AX	; store this as the first word
  NEG  CL
  ADD  CL,pelsPerWord   
  MOV  DH,CL			; DH amt to shift to the left the source word
  SHL  AX,CL			; align it
  OR   AX,BX			; put the 1st 2 source words together

revDoLast:
  MOV  BX,allOnes		; mask
  MOV  CL,loc.toLastBits	; mask for the data to right of wor
  SHR  BX,CL			; move mask
  NOT  BX				; 
  AND  AX,BX			; mask out bits not in rect
  NOT  BX				; mask for bits outside rect

  CMP  loc.destFormat, PcFormat        ; is source screenFormat?
  JNZ RevNoSwap3
  XCHG BL,BH			 ; reverse bytes if screen format
  AND  BX,ES:[DI]		 ; get bits outside rect
  XCHG BL,BH			 ; reverse bytes if screen format
  JMP SHORT revNoSwap4

revNoSwap3:
  AND  BX,ES:[DI]		 ; gridformat

revNoSwap4:
  OR   AX,BX			 ; merge source bits with dest bits
  XOR  CH,CH
  ADD  CX,loc.bitCount
  CMP  CX,pelsPerWord
  JLE SHORT revFirstWord	 ; only 1 word to do
  CMP  loc.destFormat, PcFormat        ; is source screenFormat?
  JNZ RevNoSwap5
  XCHG AL,AH			 ; reverse the bytes before writing

revNoSwap5:
  STOSW				 ; store the 1st word

$EJ

;set up for word loop:
; put into CL both dest and source formats
  MOV CX, loc.sourceFormat
  AND CX, MaskFormatSource
  MOV AX, loc.destFormat
  SHL AX, 1			 ; move least significant bit over one
  AND AX, MaskFormatDest
  OR  CX, AX

  MOV  CH,loc.toFullWords
  PUSH BP
  MOV  BP,loc.firstWord	 ; BP := previous word
  INC  CH                 ; include once through for partial word
revMiddleLoop:
  MOV  BX,BP			 ; get the old word
  XCHG  CL,DL			 ; swap format and shift count
  SHR  BX,CL			 ; use most significant bits of old word
  XCHG CL, DL			 ; swap again
  LODSW				 ; get the next word
  TEST CL, maskFormatSource ; is it in screen format?
  JNZ revNoSwap6
  XCHG AL,AH			 ; reverse bytes in screen format

revNoSwap6:
  MOV  BP,AX			 ; save this as old word
  XCHG  CL,DH			 ; swap format and shift count
  SHL  AX,CL			 ; use least significant bits of this word
  OR   AX,BX			 ; merge the two word together
  XCHG CL, DH			 ; swap again
  DEC  CH				 ; toFullWords = toFullWords - 1
  JE SHORT revEndLoop
  TEST CL, MaskFormatDest ; is dest in screen format?
  JNZ  revNoSwap7
  XCHG AL,AH			 ; reverse bytes in screen mode

revNoSwap7:
  STOSW
  JMP SHORT revMiddleLoop
revEndLoop:
  POP  BP				 ; end of loop restore loc

revFirstWord:
  MOV  CH,loc.toAlign	 ; get # bits in rect of last word
  CMP  CH,0			 ; ex: 0000 0000 0111 1111
  JNE  revContinue
  JMP  revEndYLoop		 ; no bits in last word

revConTinue:
  MOV  BX,allOnes		 ; mask
  MOV  CL,CH			 ; CL := toAlign
  SHL  BX,CL			 ; use mask to get bits outside of rect
  NOT  BX				 ; now inside rect
  AND  AX,BX			 ; save only those bits
  NOT  BX				 ; now outside rect
  CMP loc.destFormat, PcFormat
  JNZ revNoSwap8
  XCHG BL,BH			 ; reverse if in screen format
  AND  BX,ES:[DI]
  XCHG AL,AH
  JMP SHORT revNoSwap9

revNoSwap8:
  AND  BX,ES:[DI]

revNoSwap9:
  OR   AX,BX			; merge the words
  STOSW				; store the last word
  JMP  revEndYLoop

$EJ

revSameAlignment:
; Case: toLastBits = fromLastBits
; Ex:  fromLastBits  = 1111 1110 0000 0000 (7)
;      toLastBits    = 1111 1110 0000 0000 (7)
;

  MOV  DX, loc.destFormat        ; store the dest format in DX
  CMP  loc.toLastBits,0		   ; are we word aligned?
  JNE   SHORT revSameAlignment2
  DEC   DI				   ; if so count first word in loop
  DEC   DI				   ; 1 word = 2 bytes
  JMP   revMiddleWords

revSameAlignment2:

  MOV  BX,allOnes			   ; mask
  MOV  CL,loc.toLastBits		   ; 
  SHR  BX,CL				   ; mask for bits outside rect
  NOT  BX					   ; now inside
  AND  AX,BX				   ; mask only the bits we want in rect
  NOT  BX					   ; now outside
  CMP  DL, PcFormat              ; are we in screen mode?
  JNZ revNoSwap10
  XCHG BL,BH				   ; reverse bytes in screen mode
  AND  BX,ES:[DI]			   ; get bit image of bits outside rect
  XCHG BL,BH				   ; reverse bytes
  JMP SHORT revNoSwap11

revNoSwap10:
  AND  BX,ES:[DI]			   ; get bit image of bits outside rect

revNoSwap11:
  OR   AX,BX				   ; merge source bits with dest bits
  XOR  CH,CH
  ADD  CX,loc.bitCount
  CMP  CX,pelsPerWord
  JLE  SHORT revNoMiddle		   ; only 1 word to copy


  CMP DL, PcFormat               ; screen format?
  JNZ revNoSwap12
  XCHG AL,AH				   ; reverse bytes in screen format

revNoSwap12:
  STOSW

revMiddleWords:
  CMP  loc.toFullWords,0
  JE   revPartialFirstWord
  AND  CX,0
  MOV  CL,loc.toFullWords
  MOV  BX, loc.sourceFormat
  SUB  BX, loc.destFormat
  JZ   revMoveTheBytes
revTopALoop:
  LODSW				 ; get the word
  XCHG AL, AH			 ; swap the bytes
  STOSW
  LOOP revTopALoop
  JMP SHORT revPartialFirstWord

revMoveTheBytes:
  REP  MOVSW				  ; move the entire row

revPartialFirstWord:
  CMP  loc.toAlign,0
  JE   SHORT revEndYLoop
  LODSW
  CMP loc.sourceFormat, PcFormat  ; is source screen format
  JNZ revNoMiddle
  XCHG AL,AH				    ; reverse bytes in screen format

revNoMiddle:
  MOV  BX,allOnes			    ; mask
  MOV  CL,loc.toAlign		    ; 0000 0000 0011 1111 (6)
  SHL  BX,CL				    ; mask to get bits outside rect
  NOT  BX					    ; now inside
  AND  AX,BX				    ; keep only bits in the rect
  NOT  BX					    ; now outside
  CMP  DL, PcFormat			    ; is dest = screenFormat?
  JNZ  revNoSwap13
  XCHG BL,BH				    ; reverse bytes in screen mode
  AND  BX,ES:[DI]
  XCHG AL,AH
  JMP SHORT revNoSwap14

revNoSwap13:
  AND  BX,ES:[DI]			    ; get bits outside of rect

revNoSwap14:
  OR   AX,BX				    ; merge the bits
  STOSW					    ; store the last word

$EJ

revEndYLoop:
  MOV  SI,loc.startSI		    ; restore start locations
  MOV  DI,loc.startDI		    ; for source and dest
  CMP  loc.sourceFormat, PcFormat ; screen format?
  JE   revScreenFmt
  ADD  SI, loc.source_bpl 	    ; gridformat - just add
  JMP  revAdjustDestPtr

revScreenFmt:
  XOR   loc.lineAltSource,1	    ; check if odd/even scan line
  JZ    revNextsourceEven

  ADD  SI,loc.sourceHeight
  JMP  revAdjustDestPtr

revNextSourceEven:
  SUB  SI,loc.sourceHeight
  ADD  SI,loc.source_BPL          ;yes sub bytesInLine

revAdjustDestPtr:
  CMP  loc.destFormat, PcFormat   ; screen format?
  JE   revScreenFmt1
  ADD  DI, loc.dest_bpl 	         ; gridformat - just add
  JMP  revYLoop

revScreenFmt1:
  XOR   loc.lineAltDest,1
  Jz    revNextDestEven

  ADD  DI,loc.destHeight
  JMP  revYLoop

revNextDestEven:
  SUB  DI,loc.destHeight
  ADD  DI,loc.dest_BPL        ;yes sub bytesInLine

  JMP  revYLoop
          
CpCopyRectangle ENDP

  PURGE params
  PURGE oldBP
  PURGE oldDS
  PURGE returnIP
  PURGE returnCS
  PURGE toX
  PURGE toY
  PURGE fromX
  PURGE fromY
  PURGE bitCount
  PURGE localBytes
  PURGE loc
  PURGE paramBytes


Sysdep_CODE ENDS

  END
