$NOLIST DEBUG

NAME  Grafix

Sysdep_CGROUP GROUP Sysdep_CODE

PUBLIC  GfxSetPixel, GfxClrPixel, GfxInvertPixel, GfxTestPixel
PUBLIC  GfxDrawLine, GfxEraseLine, GfxInvertLine

zero        EQU  0
evenMask    EQU  0FFFEH         ; make a word even
true        EQU  0FFFFH         
screenSeg   EQU  0B800H

$EJ
Sysdep_CODE SEGMENT PUBLIC 'CODE'
ASSUME  CS:Sysdep_CGROUP

;**************************************************
;*
;*    GfxSetpixel(screen, bytesPerLine, winHeight, x, y: Word)
;*
;* This will set the pixel at X = horizontal
;* and Y = vertical.
;* (0,0) is the upper lefthand corner.
;*
;* REGISTERS CHANGED: AX, BX, CX, DX, ES, DI, SI
;*
;**************************************************

;  Params for GfxSetPixel, GfxClrPixel, GfxInvertPixel
;  and GfxTestPixel

params STRUC
  oldDS        DW  ?
  oldBp        DW  ?
;-----------
  returnIP     DW  ?
  returnCS     DW  ?
;-----------
  y            DW  ?
  x            DW  ?
  winHeight    DW  ?
  bytesPerLine DW  ?
  screen       DW  ?
params ENDS

loc EQU [BP]
paramBytes EQU 10

GfxSetPixel PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   SI, 0080H               ; MSbit in LSByte
  CALL  SetUpPixel

  OR    DS:[BX], SI             ; set the pixel

  POP   DS
  POP   BP
  RET   paramBytes
GfxSetPixel ENDP



;***************************************************
;*
;*    GfxClrPixel(screen, bytesPerLine, winHeight, x, y : word)
;*
;* This is exactly the same as SetPixel except
;* for the last two statements
;*
;*****************************************************

GfxClrPixel PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   SI, 0FF7FH
  CALL  SetUpPixel

  AND   DS:[BX], SI

  POP   DS
  POP   BP
  RET   paramBytes
GfxClrPixel ENDP
$EJ

;***************************************************
;*
;*    GfxInvertPixel(screen, bytesPerLine, winHeight, x, y : word)
;*
;* This is exactly the same as SetPixel except
;* for the last two statements
;*
;*****************************************************

GfxInvertPixel PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   SI, 0080H
  CALL  SetUpPixel

  XOR   DS:[BX], SI

  POP   DS
  POP   BP
  RET   paramBytes
GfxInvertPixel ENDP


;***************************************************
;*
;*    GfxTestPixel(screen, bytesPerLine, winHeight, x, y : word)
;*
;* This is exactly the same as SetPixel except
;* for the last two statements
;*
;*****************************************************

GfxTestPixel PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   SI, 0080H
  CALL  SetUpPixel

  MOV   AX, DS:[BX]    ;Get the word
  AND   AX, SI         ;Mask out all other bits
  JZ    NotSet         ;do nothing if not set
  MOV   AX, true       ;Set to true if bit is set.
NotSet:

  POP   DS
  POP   BP
  RET   paramBytes
GfxTestPixel ENDP

$EJ
;**************************************************
;*
;*    SetUpPixel
;*
;*  entry: 
;*          SI = original mask
;*
;*  Does the setup for Set- and Clr- Pixel.
;*
;*  exit:  BX ^ word in display
;*         DS ^ start of display buffer
;*         SI = rotated mask
;*
;*************************************************

SetUpPixel PROC  NEAR
  MOV   CX, loc.y
  MOV   BX, loc.x          ; BX = x
  MOV   AX, loc.screen
  MOV   DS, AX

  MOV   DI,2000H
  CMP   AX,screenSeg
  JZ    usingScreen
  MOV   AX,loc.winHeight
  SHR   AX,1
  ADC   AX,0
  IMUL  loc.bytesPerLine
  MOV  DI,AX
usingScreen:
  MOV  AX, CX
  SHR  AX,1
  JC   oddLine
  MOV  DI,0
oddline:
  MOV  DX, loc.bytesPerLine
  MUL  DX
  MOV  CX, AX                ; CX = CX * byteWidth
  ADD  CX, DI
  MOV  AX, BX                ; AX = x
  SAR  BX, 1
  SAR  BX, 1
  SAR  BX, 1
  SAR  BX, 1                 ; ^word = x/16
  SHL  BX, 1                 ; make it a byte
  ADD  BX, CX                ; BX ^ word in display

  AND  AX, 000FH             ; AX = x MOD 16
  MOV  CX, AX
;  SHL  CL,1
  ROR  SI, CL                ; rotate to position

  RET
SetUpPixel ENDP

  PURGE params
  PURGE oldBP
  PURGE oldDS
  PURGE returnIP
  PURGE returnCS
  PURGE x
  PURGE y
  PURGE bytesPerLine
  PURGE screen
  PURGE loc
  PURGE paramBytes
  PURGE winHeight
  PURGE oddLine
$EJ
$LIST
;*********************************************************
;*
;*    GfxDrawLine(screen, bytesPerLine, winHeight, x1, y1, x2, y2)
;*
;* This will draw a line between the two points (x1, y1) and
;* (x2, y2).  It uses the DDA algorithm.  The act of
;* drawing a line is split into two cases for efficiency.
;*
;*  case AB: dx >= dy
;*  case CD: dx < dy
;*
;* During the inner loop of each case, the registers mean:
;*
;*  AX = dx            BX = dy
;*  CX = loop counter  DX = temp
;*  SI = mask          DI ^ display buffer
;*  BP = +- wordWidth
;*
;********************************************************

;  Params for GfxDrawLine, GfxEraseLine, GfxInvertLine

params STRUC
  oddlineFlag  DW  ?
  oldDS        DW  ?
  oldBp        DW  ?
;-----------
  returnIP     DW  ?
  returnCS     DW  ?
;-----------
  y2           DW  ?
  x2           DW  ?
  y1           DW  ?
  x1           DW  ?
  winHeight    DW  ?
  bytesPerLine DW  ?
  screen       DW  ?
params ENDS
localBytes EQU 2
loc EQU [BP-localBytes]
paramBytes EQU 14

GfxDrawLine PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP
  SUB   SP,localBytes

  MOV   DX, loc.y2          ; DX = y2
  MOV   CX, loc.x2          ; CX = x2
  MOV   BX, loc.y1          ; BX = y1
  MOV   AX, loc.x1          ; AX = x1

  CMP   AX, CX               ; x1 <= x2 ?
  JLE   Line10
  XCHG  AX, CX
  XCHG  BX, DX               ; now x1 <= x2
Line10:

  SUB   CX, AX               ; CX = dx ( >= 0)
  SUB   DX, BX               ; DX = dy
  MOV   SI,0080H             ; SI = original mask
  CALL  AInitLine
  OR    DS:[DI],SI
  CMP   AX, BX               ; dx < dy ?
  JL    CaseCD               ; yes -> case CD

CaseAB:            ; case AB
  MOV   CX, AX               ; loop 'dx' times
  MOV   DX, AX
  CMP   DX, 1
  JLE   NoShift
  SAR   DX, 1
NoShift:
  NEG   DX                   ; DX = -dx/2; DX > 0!!
  JCXZ  LoopDone

LoopOnAB:
  ADD   DX, BX               ; temp += dy
  JS    A10
  SUB   DX, AX               ; temp -= dx
;
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    A10
AddBytesPerLine:
  ADD   DI,loc.bytesPerLine
  JMP   A10
NextLineOdd:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine
A10:

  ROR   SI, 1                ; x += 1
;  ROR   SI, 1                ; x += 1
  TEST  SI,80H
  JZ    SameX
  ADD   DI, 2                ; DI ^ next word
SameX:
  OR    DS:[DI], SI             ; set the bit
  LOOP  LoopOnAB
  JMP   LoopDone

CaseCD:            ; case CD
  MOV   CX, BX               ; loop 'dy' times
  MOV   DX, BX
  CMP   DX, 1
  JLE   NoShift2
  SAR   DX, 1
NoShift2:
  NEG   DX                   ; DX = -dy / 2
  JCXZ  LoopDone

LoopOnCD:
  ADD   DX, AX               ; temp += dx
  JS    C10
  SUB   DX, BX               ; temp -= dy
  ROR   SI, 1                ; x += 1
;  ROR   SI, 1                ; x += 1
  TEST  SI,80H
  JZ    C10
  ADD   DI, 2                ; DI ^ next word

C10:
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd2
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    C11
AddBytesPerLine2:
  ADD   DI,loc.bytesPerLine
  JMP   C11
NextLineOdd2:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine2
C11:
  OR    DS:[DI], SI             ; set the bit
  LOOP  LoopOnCD

LoopDone:
  MOV   SP,BP
  POP   DS
  POP   BP
  RET   paramBytes
GfxDrawLine ENDP
$NOLIST
$EJ
;******************************************
;*                                        *
;*  GfxEraseLine(screen, bytesPerLine,    *
;*               windowHeight,            *
;*               x1, y1, x2, y2)          *
;*                                        *
;******************************************

GfxEraseLine PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP
  SUB   SP,localBytes
  MOV   DX, loc.y2          ; DX = y2
  MOV   CX, loc.x2          ; CX = x2
  MOV   BX, loc.y1          ; BX = y1
  MOV   AX, loc.x1          ; AX = x1

  CMP   AX, CX               ; x1 <= x2 ?
  JLE   ELine10
  XCHG  AX, CX
  XCHG  BX, DX               ; now x1 < x2

ELine10:
  SUB   CX, AX               ; CX = dx ( >= 0 )
  SUB   DX, BX               ; DX = dy
  MOV   SI, 0FF7FH            ; SI = original mask
  CALL  AInitLine
  AND   DS:[DI], SI
  CMP   AX, BX               ; dx < dy ?
  JL    ECaseCD              ; yes -> case CD

ECaseAB:           ; case AB
  MOV   CX, AX               ; loop 'dx' times
  MOV   DX, AX
  CMP   DX, 1
  JLE   ENoShift
  SHR   DX, 1
ENoShift:
  NEG   DX                   ; DX = - dx / 2
  JCXZ  EallDone

ELoopOnAB:
  ADD   DX, BX               ; temp += dy
  JS    ESameY               ; next y value ?
  SUB   DX, AX               ; temp -= dx
;
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd3
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    ESameY
AddBytesPerLine3:
  ADD   DI,loc.bytesPerLine
  JMP   ESameY
NextLineOdd3:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine3

ESameY:
  ROR   SI, 1                ; x += 1
;  ROR   SI, 1                ; x += 1
  NOT   SI
  TEST  SI, 80H
  JZ    ESameX               ; on to next word?
  ADD   DI, 2                ; yes
ESameX:
  NOT   SI
  AND   DS:[DI], SI             ; erase the bit
  LOOP  ELoopOnAB
EallDone:
  JMP   ELoopDone            ; all finished

ECaseCD:           ; case CD
  MOV   CX, BX               ; loop 'dy' times
  MOV   DX, BX
  CMP   DX, 1
  JLE   ENoShift2
  SHR   DX, 1
ENoShift2:
  NEG   DX                   ; DX = -dy / 2
  JCXZ  ELoopDone
ELoopOnCD:
  ADD   DX, AX               ; temp += dx
  JS    EC10
  SUB   DX, BX               ; temp -= dy
  ROR   SI, 1                ; x += 1
;  ROR   SI, 1                ; x += 1
  NOT   SI
  TEST  SI,80H
  JZ    EC9
  ADD   DI, 2                ; DI ^ next word
EC9:
  NOT   SI
EC10:
;
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd4
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    adjustDone4
AddBytesPerLine4:
  ADD   DI,loc.bytesPerLine
  JMP   adjustDone4
NextLineOdd4:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine4
adjustDone4:
;
  AND   DS:[DI], SI             ; clear the bit
  LOOP  ELoopOnCD

ELoopDone:
  MOV   SP,BP
  POP   DS
  POP   BP
  RET   paramBytes
GfxEraseLine ENDP

$EJ
;****************************************
;*
;*    GfxInvertLine(screen, bytesPerLine, 
;*                  windowHeight, X, Y, X1, Y1)
;*
;****************************************

GfxInvertLine PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP,SP
  SUB   SP,localBytes

  MOV   DX, loc.y2          ; DX = y2
  MOV   CX, loc.x2          ; CX = x2
  MOV   BX, loc.y1          ; BX = y1
  MOV   AX, loc.x1          ; AX = x1

  CMP  AX, CX    ; IS X <= X1
  JLE  XLine10    ; YES -> JUMP

  XCHG  AX, CX
  XCHG  BX, DX    ; NOW X < X1
XLine10:
  SUB  CX, AX    ; CX = dx (dx >= 0)
  SUB  DX, BX    ; DX = dy

  MOV   SI, 0080H         ; SI = original mask
  CALL  AInitLine
  XOR   DS:[DI], SI

  CMP  AX, BX    ; IS dx < dy ?
  JL  XCaseCD    ; YES -> GOTO CASES C,D

XCaseAB:      ; MUST BE CASE A OR B
  MOV  CX, AX    ; LOOP dx TIMES
  MOV  DX, AX
  CMP   DX, 1
  JLE   XNoShift
  SHR   DX, 1
XNoShift:
  NEG  DX    ; DX = - dx /2
  JCXZ  XLoopDone
XLoopOnAB:
  ADD  DX, BX    ; temp += dy
  JS  XA10    ; JUMP IF RESULT < 0
  SUB  DX, AX    ; temp -= dx
;
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd5
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    adjustDone5
AddBytesPerLine5:
  ADD   DI,loc.bytesPerLine
  JMP   adjustDone5
NextLineOdd5:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine5
adjustDone5:
;
;
XA10:
  ROR  SI,1    ; X += 1
;  ROR  SI,1    ; X += 1
  TEST SI,80H
  JZ   XA20
  ADD  DI,2    ; DI ^ next word
XA20:
  XOR  DS:[DI],SI    ; SET THE BIT
  LOOP  XLoopOnAB

  JMP   XLoopDone

XCaseCD:      ; MUST BE CASE C OR D
  MOV  CX, BX    ; LOOP dy TIMES
  MOV  DX, BX
  CMP   DX, 1
  JLE   XNoShift2
  SHR   DX, 1
XNoShift2:
  NEG  DX    ; DX = -dy / 2
  JCXZ  XLoopDone
XLoopOnCD:
  ADD  DX, AX    ; temp += dx
  JS  XC10    ; JUMP IF RESULT < 0
  SUB  DX, BX    ; temp -= dy
  ROR  SI,1    ; X += 1
;  ROR  SI,1    ; X += 1
  TEST SI,80H
  JZ   XC10
  ADD  DI,2    ; DI ^ next word
XC10:
  XOR   loc.oddlineFlag,1
  JNZ   NextLineOdd6
  SUB   DI, loc.winHeight
  CMP   loc.bytesPerLine,0
  JL    adjustDone6
AddBytesPerLine6:
  ADD   DI,loc.bytesPerLine
  JMP   adjustDone6
NextLineOdd6:
  ADD    DI,loc.WinHeight
  CMP    loc.bytesPerLine,0
  JL     AddBytesPerLine6
adjustDone6:

  XOR  DS:[DI],SI    ; SET THE BIT
  LOOP  XLoopOnCD

XLoopDone:
  MOV  SP,BP
  POP  DS
  POP  BP
  RET  paramBytes
GfxInvertLine ENDP

$EJ
;*************************************************
;*
;*    AInitLine
;*
;*  entry:  AX = x
;*          BX = y
;*          CX = dx    (dx >= 0)
;*          DX = dy
;*          SI = original mask
;*  
;*  exit:   SI = rotated mask
;*          DI ^ word in display
;*          AX = dx
;*          BX = dy
;*          loc.bytesPerLine = +- wordWidth
;*
;************************************************

AInitLine PROC NEAR
  PUSH  CX
  PUSH  DX

  PUSH  AX              ; save during multiply

  MOV   AX, loc.screen
  MOV   DS,AX

  CMP   AX,screenSeg
  MOV   AX,2000H
  JE    usingScreenSeg
  MOV   AX,loc.winHeight
  SHR   AX,1
  ADC   AX,0
  IMUL  loc.bytesPerLine
usingScreenSeg:
  MOV   loc.winHeight,AX

  MOV   AX, BX              ; y
  SHR   AX, 1
  MOV   DI, loc.winHeight
  MOV   loc.oddlineFlag,1

  JC    OddLine
  MOV   DI, 0
  MOV   loc.oddlineFlag,0
OddLine:

  MOV   CX, loc.bytesPerLine
  MUL   CX
  ADD   DI, AX          ; DI = y * byteWidth
  POP   AX              ; original x value

;  SHL   AX, 1
  MOV   CX, AX
  SAR   CX, 1
  SAR   CX, 1
  SAR   CX, 1
  AND   CX, evenMask    ; CX = EVEN(x DIV 8)
  ADD   DI, CX          ; DI ^ word in display

  AND   AX, 000FH       ; AX = x MOD 16
  MOV   CX, AX
  ROR   SI, CL          ; rotate to starting pos

  POP   BX              ; BX = dy
  POP   AX              ; AX = dx

  MOV   CX, loc.bytesPerLine
  CMP   BX, zero
  JGE   DYGE0           ; dy >= 0
  NEG   CX
  NEG   BX              ; dy = |dy|
DYGE0:
  MOV   loc.bytesPerLine,CX

$LIST
  RET
AInitLine ENDP

  PURGE params
  PURGE oldBP
  PURGE oldDS
  PURGE returnIP
  PURGE returnCS
  PURGE x1
  PURGE x2
  PURGE y1
  PURGE y2
  PURGE bytesPerLine
  PURGE screen
  PURGE loc
  PURGE paramBytes
  PURGE winHeight

Sysdep_CODE ENDS

  END
