þ a‹R þ w Qþ mP9     þ hý	 oP     þ nSystem-wide$NOLIST

;Bits.asm -- bit boundary block operations

  NAME Bits

Sysdep_CGROUP GROUP Sysdep_CODE

PUBLIC BitInvertHLine, BitEraseHLine, CpCopyRectangle
PUBLIC StartPosition

Sysdep_CODE SEGMENT PUBLIC 'CODE'
  ASSUME CS:Sysdep_CGROUP

bitsPerWord EQU 16
allOnes     EQU 0FFFFH

;------------------
%*DEFINE(DivAxBy16)
 (SAR  AX,1
  SAR  AX,1
  SAR  AX,1
  SAR  AX,1)


;----------------------
StartPosition PROC NEAR
;----------------------
;computes word address & shift count, given x & y
;INPUT: 
;  AX = y,  
;  BX = x, 
;  DX = bytesPerLine
;
;OUTPUT: 
;  DI = offset to word, 
;  CX = shift count
;  DX = bytesPerLine
;
  PUSH DX
  IMUL DX
  MOV  DI,AX   ;offset to beginning of line
  MOV  AX,BX
  %DivAxBy16   ; = words within line
  SAL  AX,1    ;  * 2 = bytes within line
  ADD  DI,AX

  AND  BX,0FH
  MOV  CX,BX
  POP  DX
  RET
StartPosition ENDP

$EJ

;-----------------------------------------
;Parameters for InvertHLine and EraseHLine
;-----------------------------------------
params STRUC
lastBits DW ?  ;how many bits in last word
;-----------
oldBp  DW  ?
returnIP DW  ?
returnCS DW  ?
;-----------
w            DW  ?   ;width
x            DW  ?
y            DW  ?
windowHeight DW  ?
bytesPerLine DW  ?
screen       DW  ?
params ENDS

localBytes EQU 2
loc EQU [BP-localBytes]
paramBytes EQU 12

;----------------------------------------------
;This macro is the body for the HLine functions
;----------------------------------------------
%*DEFINE(DoHLine(OP,notMask))
 LOCAL doFirst middle middleLoop lastWord epilogue
(
  PUSH BP
  MOV  BP,SP
  SUB  SP, localBytes
  MOV  AX, loc.screen
  MOV  ES,AX

  MOV  AX,loc.y
  MOV  BX,loc.x
  MOV  DX, loc.bytesPerLine
  CALL StartPosition

;first word:
  MOV  AX,CX
  MOV  BX,bitsPerWord
  SUB  BX,AX
  MOV  AX,0FFFFH
  SHR  AX,CL
  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
  %OP  ES:[DI],AX
  JMP  SHORT %epilogue
%doFirst:
  %IF (%EQS(%notMask,true)) THEN (NOT  AX) FI
  %OP  ES:[DI],AX
  INC  DI
  INC  DI

$EJ

%middle:
  MOV  AX,loc.w
  SUB  AX,BX      ;bits already done
  MOV  BX,AX      ;bit count
  %DivAxBy16
  MOV  CX,AX           ;word count
  AND  BX,0FH
  MOV  loc.lastBits,BX
  %IF (%EQS(%notMask,true))
    THEN (MOV  AX,0)
    ELSE (MOV  AX,0FFFFH)
   FI
  CMP  CX,0
  JLE  SHORT %lastWord

%middleLoop:
  %OP  ES:[DI],AX
  INC  DI
  INC  DI
  DEC  CX
  JNZ  %middleLoop

%lastWord:
  CMP  loc.lastBits,0
  JE   SHORT %epilogue
  MOV  CX,bitsPerWord
  SUB  CX,loc.lastBits
  MOV  AX,0FFFFH
  SHL  AX,CL
  %IF (%EQS(%notMask,true)) THEN (NOT  AX) FI
  %OP  ES:[DI],AX

%epilogue:
  MOV  SP,BP
  POP  BP
  RET  paramBytes
)

$EJ

;----------------------------------------
BitInvertHLine PROC FAR
;PROCEDURE BitInvertHLine (screen,
;                          bytesPerLine,
;                          windowHeight
;                          y, x, w: 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: 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

$EJ
$LIST
;*************************************************
;*
;* PROCEDURE CpCopyRectangle (source,
;*                            sourcebytesPerLine,
;*                            sourceWindowHeight: INTEGER;
;*                            sourceFormat: BYTE;
;*                            dest,
;*                            destbytesPerLine,
;*                            destWindowHeight: INTEGER;
;*                            destFormat: BYTE;
;*                            fromX, fromY, 
;*                            toX, toY,
;*                            extentX, extentY:Integer;
;*                            mode: BYTE;
;*************************************************
;Moves horizontal lines of bits from one place on
;the screen to another


;---------------------------------
;parameters & locals for CpCopyRectangle
;---------------------------------
params STRUC
  firstWord  DW ?
  fromAlign  DB ?
  toAlign    DB ?
  fromFullWords  DB ?
  toFullWords DB ?
  toLastBits DB ?
  fromLastBits DB ?
  startSI    DW ?
  startDI    DW ?
;---------------
  oldBP      DW ?
  oldDS      DW ?
  returnIP   DW ?
  returnCS   DW ?
;---------------
  mode       DW ?
  lineCount  DW ?
  bitCount   DW ?
  toY        DW ?
  toX        DW ?
  fromY      DW ?
  fromX      DW ?
  destFormat DW ?
  dest_wh    DW ?
  dest_bpl   DW ?
  dest       DW ?
  sourceFormat DW ?
  source_wh  DW ?
  source_bpl DW ?
  source     DW ?
params ENDS

localBytes EQU 12
loc EQU [BP-localBytes]
paramBytes EQU 30

$EJECT
$LIST
CpCopyRectangle PROC FAR
  PUSH DS
  PUSH BP
  MOV  BP,SP
  SUB  SP,localBytes

  MOV  AX, loc.source
  MOV  DS,AX
  MOV  BX, loc.dest
  MOV  ES,BX

  CMP  AX, BX
  JNE  crCalcStart  ; are we in same window ?

  MOV  AX,loc.fromY
  CMP  AX,loc.toY
  JGE SHORT crCalcStart
;do lines in reverse order
  ADD  AX,loc.lineCount
  DEC  AX
  MOV  loc.fromY,AX
  MOV  AX,loc.toY
  ADD  AX,loc.lineCount
  DEC  AX
  MOV  loc.toY,AX

crCalcStart:
  MOV  AX, loc.fromY
  MOV  BX, loc.fromX
  MOV  DX, loc.source_bpl
  CALL StartPosition
  MOV  SI,DI
  MOV  loc.fromAlign,CL

  MOV  AX,loc.toY
  MOV  BX,loc.toX
  MOV  DX, loc.dest_bpl
  CALL StartPosition
  MOV  loc.toAlign,CL

  MOV  AX,loc.bitCount
  NEG  CX              ;returned by StartPosition
  ADD  CX,bitsPerWord  ;CX := 16 - CX
  SUB  AX,CX           ;bitCount - (16-toAlign)
  JG SHORT crCalcWords
  ADD  AX,bitsPerWord  ;16 - bitCount - toAlign
  MOV  loc.toFullWords,0
  MOV  loc.toLastBits,AL
  JMP SHORT crYLoop
crCalcWords:
  MOV  BL,bitsPerWord
  DIV  BL
  MOV  loc.toFullWords,AL
  MOV  loc.toLastBits,AH

  MOV  AX,loc.fromY
  CMP  AX,loc.toY
  JNE  crYLoop
  MOV  AX,loc.fromX
  CMP  AX,loc.toX
  JGE  crYLoop

  MOV  AX, DS
  CMP  AX, loc.dest
  JNE  crYLoop  ; are we in same window ?

  JMP  crReverseCopy

$EJ

crYLoop:
  CMP  loc.lineCount,0
  JNE SHORT crDoFirstWord
  JMP  crFinished
crDoFirstWord:
  DEC  loc.lineCount
  MOV  loc.startSI,SI
  MOV  loc.startDI,DI
  CLD
  LODSW
  MOV  loc.firstWord,AX
  MOV  CL,loc.toAlign
  SUB  CL,loc.fromAlign
  JL SHORT crToAlignLess1
  JG SHORT crToAlignGreater
  JMP crSameAlignment
  
crToAlignGreater:
  MOV  DH,CL            ;toAlign-fromAlign
  SHR  AX,CL
  NEG  CL
  ADD  CL,bitsPerWord   ;CL:=16-CL
  MOV  DL,CL
  JMP SHORT crDoFirst
crToAlignLess1:
  NEG  CL               ;fromAlign-toAlign
  MOV  DL,CL
  SHL  AX,CL
  MOV  BX,AX
  LODSW
  MOV  loc.firstWord,AX
  NEG  CL
  ADD  CL,bitsPerWord   ;CL:=16-CL
  MOV  DH,CL
  SHR  AX,CL
  OR   AX,BX
crDoFirst:
  MOV  BX,allOnes
  MOV  CL,loc.toAlign
  SHR  BX,CL
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
  MOV  CH,0
  ADD  CX,loc.bitCount
  CMP  CX,bitsPerWord
  JLE SHORT crLastWord
  STOSW

$EJ

;set up for word loop:
  MOV  CH,loc.toFullWords
  PUSH BP
  MOV  BP,loc.firstWord
  INC  CH     ;include once through for partial word
crMiddleLoop:
  MOV  BX,BP
  MOV  CL,DL
  SHL  BX,CL
  LODSW
  MOV  BP,AX
  MOV  CL,DH
  SHR  AX,CL
  OR   AX,BX
  DEC  CH
  JE SHORT crEndLoop
  STOSW
  JMP SHORT crMiddleLoop
crEndLoop:
  POP  BP

crLastWord:
  MOV  CH,loc.toLastBits
  CMP  CH,0
  JE SHORT crEndYLoop

  MOV  BX,allOnes
  MOV  CL,loc.toLastBits
  SHR  BX,CL           ;0000111111111111
  NOT  BX              ;1111000000000000
  AND  AX,BX
  NOT  BX              ;0000111111111111
  AND  BX,ES:[DI]
  OR   AX,BX
  STOSW
  JMP  crEndYLoop

$EJ

crSameAlignment:
  CMP  loc.toAlign,0
  JNE  SHORT crFirstWord
  CMP  loc.toFullWords, 0
  JE   SHORT crFirstWord1
  JMP  crMiddleWords

crFirstWord:
  MOV  BX,allOnes
  MOV  CL,loc.toAlign
  SHR  BX,CL
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
crFirstWord1:
  MOV  CH,0
  ADD  CX,loc.bitCount
  CMP  CX,bitsPerWord
  JLE  SHORT crNoMiddle

crMiddleWords:
  STOSW
  CMP  loc.toFullWords,0
  JE   crPartialLastWord
  AND  CX,0
  MOV  CL,loc.toFullWords
  REP  MOVSW

crPartialLastWord:
  CMP  loc.toLastBits,0
  JE   SHORT crEndYLoop
  LODSW

crNoMiddle:
  MOV  BX,allOnes
  MOV  CL,loc.toLastBits
  SHR  BX,CL           ;0000111111111111
  NOT  BX              ;1111000000000000
  AND  AX,BX
  NOT  BX              ;0000111111111111
  AND  BX,ES:[DI]
  OR   AX,BX
  STOSW

$EJ

crEndYLoop:
  MOV  SI,loc.startSI
  MOV  DI,loc.startDI
  MOV  AX, DS
  MOV  BX, ES
  CMP  AX, BX
  JNE  crYForward  ;is it in the same window?

  MOV  AX,loc.fromY
  CMP  AX,loc.toY
  JGE  SHORT crYForward
crYBackward:
  SUB  SI,loc.source_bpl
  SUB  DI,loc.dest_bpl
  JMP  crYLoop
crYForward:
  ADD  SI,loc.source_bpl
  ADD  DI,loc.dest_bpl
  JMP  crYLoop

crFinished:
  MOV  SP,BP
  POP  BP
  POP  DS
  RET  paramBytes

$EJ

; 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.

crReverseCopy:

; compute fromLastBits

  MOV  AX,loc.bitCount
  MOV  CL,loc.fromAlign
  AND  CH,0
  NEG  CX
  ADD  CX,bitsPerWord  ;CX := 16 - CX
  SUB  AX,CX           ;bitCount - (16-toAlign)
  JG   SHORT crRevCalcWords
  ADD  AX,bitsPerWord  ;16 - bitCount - toAlign
  MOV  loc.fromFullWords,0
  MOV  loc.fromLastBits,AL
  JMP  SHORT crRevYLoop
crRevCalcWords:
  MOV  BL,bitsPerWord
  DIV  BL
  MOV  loc.fromFullWords,AL
  MOV  loc.fromLastBits,AH

; adjust toAlign to be the number of good bits in
; the first word.

  MOV  AL,loc.toAlign
  NEG  AL
  ADD  AL,bitsPerWord
  MOV  loc.toAlign,AL

; 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

; 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

$EJ

crRevYLoop:
  CMP  loc.lineCount,0
  JNE  SHORT crRevDoLastWord
  JMP  crFinished
crRevDoLastWord:
  DEC  loc.lineCount
  MOV  loc.startSI,SI
  MOV  loc.startDI,DI
  STD
  LODSW

  MOV  loc.firstWord,AX
  MOV  CL,loc.fromLastBits
  SUB  CL,loc.toLastBits
  JL SHORT crRevToAlignLess1
  JG SHORT crRevToAlignGreater
  JMP crRevSameAlignment
  
crRevToAlignGreater:
  MOV  DH,CL            ;toAlign-fromAlign
  SHL  AX,CL
  NEG  CL
  ADD  CL,bitsPerWord   ;CL:=16-CL
  MOV  DL,CL
  JMP SHORT crRevDoLast
crRevToAlignLess1:
  NEG  CL
  MOV  DL,CL
  SHR  AX,CL
  MOV  BX,AX
  LODSW
  MOV  loc.firstWord,AX
  NEG  CL
  ADD  CL,bitsPerWord   ;CL:=16-CL
  MOV  DH,CL
  SHL  AX,CL
  OR   AX,BX
crRevDoLast:
  MOV  BX,allOnes
  MOV  CL,loc.toLastBits
  SHR  BX,CL
  NOT  BX
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
  MOV  CH,0
  ADD  CX,loc.bitCount
  CMP  CX,bitsPerWord
  JLE SHORT crRevFirstWord
  STOSW

$EJ

;set up for word loop:

  MOV  CH,loc.toFullWords
  PUSH BP
  MOV  BP,loc.firstWord
  INC  CH     ;include once through for partial word
crRevMiddleLoop:
  MOV  BX,BP
  MOV  CL,DL
  SHR  BX,CL
  LODSW
  MOV  BP,AX
  MOV  CL,DH
  SHL  AX,CL
  OR   AX,BX
  DEC  CH
  JE SHORT crRevEndLoop
  STOSW
  JMP SHORT crRevMiddleLoop
crRevEndLoop:
  POP  BP

crRevFirstWord:
  MOV  CH,loc.toAlign
  CMP  CH,0
  JE SHORT crRevEndYLoop

  MOV  BX,allOnes
  MOV  CL,CH
  SHL  BX,CL
  NOT  BX
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
  STOSW
  JMP  crRevEndYLoop

$EJ

crRevSameAlignment:
  CMP  loc.toLastBits,0
  JNE  SHORT crRevDoFirstWord
;   DI = DI - 2
  DEC DI
  DEC DI
  JMP SHORT crRevMiddleWords

crRevDoFirstWord:
  MOV  BX,allOnes
  MOV  CL,loc.toLastBits
  SHR  BX,CL
  NOT  BX
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
  MOV  CH,0
  ADD  CX,loc.bitCount
  CMP  CX,bitsPerWord
  JLE  SHORT crRevNoMiddle
  STOSW

crRevMiddleWords:
  CMP  loc.toFullWords,0
  JE   crRevPartialFirstWord
  AND  CX,0
  MOV  CL,loc.toFullWords
  REP  MOVSW

crRevPartialFirstWord:
  CMP  loc.toAlign,0
  JE   SHORT crRevEndYLoop
  LODSW

crRevNoMiddle:
  MOV  BX,allOnes
  MOV  CL,loc.toAlign
  SHL  BX,CL
  NOT  BX
  AND  AX,BX
  NOT  BX
  AND  BX,ES:[DI]
  OR   AX,BX
  STOSW

$EJ

crRevEndYLoop:
  MOV  SI,loc.startSI
  MOV  DI,loc.startDI
  ADD  SI,loc.source_bpl
  ADD  DI,loc.dest_bpl
$LIST
  JMP  crRevYLoop


CpCopyRectangle ENDP
$NOLIST
  PURGE params
  PURGE oldBP
  PURGE oldDS
  PURGE returnIP
  PURGE returnCS
  PURGE toX
  PURGE toY
  PURGE fromX
  PURGE fromY
  PURGE source
  PURGE dest
  PURGE bitCount
  PURGE localBytes
  PURGE loc
  PURGE paramBytes
  PURGE source_bpl
  PURGE source_wh
  PURGE dest_bpl
  PURGE dest_wh
  PURGE firstWord
  PURGE fromAlign
  PURGE toAlign
  PURGE fromFullWords
  PURGE toFullWords
  PURGE toLastBits
  PURGE fromLastBits
  PURGE startSI
  PURGE startDI
  PURGE lineCount


Sysdep_CODE ENDS

  END
