þ a‹R þ w Qþ mP9     þ hý	 oP     þ nSystem-wide$NOLIST

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         


$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, 8000H
  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, 7FFFH
  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, 8000H
  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, 8000H
  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  AX, CX
  MOV  DX, loc.bytesPerLine
  MUL  DX
  MOV  CX, AX                ; CX = CX * byteWidth

  MOV  AX, BX                ; AX = x
  SAR  BX, 1
  SAR  BX, 1
  SAR  BX, 1
  AND  BX, evenMask          ; BX = EVEN(x DIV 8)
  ADD  BX, CX                ; BX ^ word in display

  AND  AX, 000FH             ; AX = x MOD 16
  MOV  CX, AX
  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

$EJ
;*********************************************************
;*
;*    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
  oldDS        DW  ?
  oldBp        DW  ?
;-----------
  returnIP     DW  ?
  returnCS     DW  ?
;-----------
  y2           DW  ?
  x2           DW  ?
  y1           DW  ?
  x1           DW  ?
  winHeight    DW  ?
  bytesPerLine DW  ?
  screen       DW  ?
params ENDS

loc EQU [BP]
paramBytes EQU 14

GfxDrawLine PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   AX, loc.screen
  MOV   DS,AX

  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,8000H             ; 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
  ADD   DI, BP               ; y += wordWidth
A10:

  ROR   SI, 1                ; x += 1
  JNC   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
  JNC   C10
  ADD   DI, 2                ; DI ^ next word

C10:
  ADD   DI, BP               ; next row of bytes
  OR    DS:[DI], SI             ; set the bit
  LOOP  LoopOnCD

LoopDone:
  POP   DS
  POP   BP
  RET   paramBytes
GfxDrawLine ENDP

$EJ
;******************************************
;*                                        *
;*  GfxEraseLine(screen, bytesPerLine,    *
;*               windowHeight,            *
;*               x1, y1, x2, y2)          *
;*                                        *
;******************************************

GfxEraseLine PROC FAR
  PUSH  BP
  PUSH  DS
  MOV   BP, SP

  MOV   AX, loc.screen
  MOV   DS,AX

  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, 7FFFH            ; 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  ELoopDone

ELoopOnAB:
  ADD   DX, BX               ; temp += dy
  JS    ESameY               ; next y value ?
  SUB   DX, AX               ; temp -= dx
  ADD   DI, BP               ; y += wordWidth
ESameY:
  ROR   SI, 1                ; x += 1
  JC    ESameX               ; on to next word?
  ADD   DI, 2                ; yes
ESameX:
  AND   DS:[DI], SI             ; erase the bit
  LOOP  ELoopOnAB
  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
  JC    EC10
  ADD   DI, 2                ; DI ^ next word
EC10:
  ADD   DI, BP               ; y += increment
  AND   DS:[DI], SI             ; clear the bit
  LOOP  ELoopOnCD

ELoopDone:
  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
  
  MOV   AX, loc.screen
  MOV   DS, AX

  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, 8000H         ; 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
  ADD  DI,BP    ; Y += INC Y
XA10:
  ROR  SI,1    ; X += 1
  JNC  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
  JNC  XC10
  ADD  DI,2    ; DI ^ next word
XC10:
  ADD  DI,BP    ; Y += INC Y
  XOR  DS:[DI],SI    ; SET THE BIT
  LOOP  XLoopOnCD

XLoopDone:
  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
;*          BP = +- wordWidth
;*
;************************************************

AInitLine PROC NEAR
  PUSH  CX
  PUSH  DX

  PUSH  AX              ; save during multiply

  MOV   AX, BX
  MOV   CX, loc.bytesPerLine
  MUL   CX
  MOV   DI, AX          ; DI = y * byteWidth
  POP   AX

  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   BP, loc.bytesPerLine
  CMP   BX, zero
  JGE   DYGE0           ; dy >= 0
  NEG   BP
  NEG   BX              ; dy = |dy|
DYGE0:
$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
