$NOLIST DEBUG

NAME  Grafix

CGROUP GROUP CODE

EXTRN   screenSeg:ABS

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

PUBLIC  InitWinParms, StartPosition, RestoreEmsSlot

EXTRN   emsBase:WORD

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

pcFormat    EQU  0
gridFormat  EQU  1
$EJ

CODE SEGMENT PUBLIC 'CODE'
ASSUME  CS: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

  CALL  SetUpPixel
  MOV   AX, SI
  MOV   SI, 0080H               ; MSbit in LSByte
  ROR   SI, CL                  ; rotate into position

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

  INC   AX                   ; if old ems slot is -1
  JZ    GfxSetPixelRet       ; then don't restore

  DEC   AX                   ; old ems slot
  CALL  RestoreEmsSlot

GfxSetPixelRet:
  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

  CALL  SetUpPixel
  MOV   AX, SI
  MOV   SI, 0FF7FH
  ROR   SI, CL                 ; rotate to position

  AND   DS:[DI], SI

  INC   AX                   ; if old ems slot is -1
  JZ    GfxClrPixelRet       ; then don't restore

  DEC   AX                   ; old ems slot
  CALL  RestoreEmsSlot

GfxClrPixelRet:
  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

  CALL  SetUpPixel
  MOV   AX, SI
  MOV   SI, 0080H
  ROR   SI, CL               ; rotate to position

  XOR   DS:[DI], SI

  INC   AX                   ; if old ems slot is -1
  JZ    GfxInvertPixelRet    ; then don't restore

  DEC   AX                   ; old ems slot
  CALL  RestoreEmsSlot

GfxInvertPixelRet:
  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

  CALL  SetUpPixel
  MOV   BX, SI          ; save ems slot in BX
  MOV   SI, 0080H
  ROR   SI, CL          ; rotate to position

  MOV   AX, DS:[DI]     ;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:
  INC   BX              ; if old ems slot = -1
  JZ    GfxTestPixelRet ; then don't restore

  PUSH  AX
  DEC   BX              ; ems slot
  XCHG  AX, BX          ; passed in AX
  CALL  RestoreEmsSlot  ; this call preserves BX
  POP   AX

GfxTestPixelRet:
  POP   DS
  POP   BP
  RET   paramBytes
GfxTestPixel ENDP
$EJ

;**************************************************
;*
;*    SetUpPixel
;*
;*  entry: 
;*          nothing
;*
;*  Does the setup for Set- and Clr- Pixel.
;*
;*  exit:  CL = rotate amount
;*         DI ^ word in display
;*         DS ^ start of display buffer
;*         SI = old ems slot (or none)
;*
;*************************************************

SetUpPixel PROC  NEAR
  MOV   AX, loc.screen
  MOV   DX, loc.bytesPerLine
  MOV   DI, loc.winHeight

  CALL  InitWinParms
  MOV   DS, AX

  MOV   AX, loc.y
  MOV   BX, loc.x          ; BX = x

  CALL  StartPosition
  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
  adjustDelta  DW  ?
  rewindDelta  DW  ?
  edge         DB  ?
  oddlineFlag  DB  ?
;-----------
  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 6
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
  CALL  AInitLine
  PUSH  SI
  MOV   SI, 0080H            ; SI = original mask
  ROR   SI, CL               ; rotate to starting pos
  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  AllDone 

LoopOnAB:
  ADD   DX, BX               ; temp += dy
  JS    A10

  SUB   DX, AX               ; temp -= dx

  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4
  JNE   GoToNextBuffer

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone

GoToNextBuffer:
  ADD   DI,loc.winHeight
  CMP   AL,loc.edge
  JA    AdjustDone

  ADD   DI,loc.adjustDelta

AdjustDone:
  XCHG  loc.oddLineFlag, AL

A10:
  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

AllDone:
  JMP   SHORT 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
  TEST  SI,80H
  JZ    C10

  ADD   DI, 2                ; DI ^ next word

C10:
  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4
  JNE   GoToNextBuffer2

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone2

GoToNextBuffer2:
  ADD   DI,loc.winHeight
  CMP   AL,loc.edge
  JA    AdjustDone2

  ADD   DI,loc.adjustDelta

AdjustDone2:
  XCHG  loc.oddLineFlag, AL

  OR    DS:[DI], SI             ; set the bit
  LOOP  LoopOnCD

LoopDone:
  POP   AX
  INC   AX
  JZ    GfxDrawLineRet

  DEC   AX
  CALL  RestoreEmsSlot

GfxDrawLineRet:
  MOV   SP,BP
  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
  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
  CALL  AInitLine
  PUSH  SI
  MOV   SI, 0FF7FH           ; SI = original mask
  ROR   SI, CL               ; rotate to starting pos
  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

  SAR   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

  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4                ; 4 buffers in screen mode
  JNE   GoToNextBuffer3

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone3

GoToNextBuffer3:
  ADD   DI,loc.winHeight
  CMP   AL,loc.edge
  JA    AdjustDone3

  ADD   DI,loc.adjustDelta

AdjustDone3:
  XCHG  loc.oddLineFlag, AL

ESameY:
  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   SHORT ELoopDone      ; all finished

ECaseCD:                     ; case CD
  MOV   CX, BX               ; loop 'dy' times
  MOV   DX, BX
  CMP   DX, 1
  JLE   ENoShift2

  SAR   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
  NOT   SI
  TEST  SI,80H
  JZ    EC9

  ADD   DI, 2                ; DI ^ next word

EC9:
  NOT   SI

EC10:
  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4
  JNE   GoToNextBuffer4

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone4

GoToNextBuffer4:
  ADD   DI,loc.winHeight
  CMP   AL,loc.edge
  JA    AdjustDone4

  ADD   DI,loc.adjustDelta

AdjustDone4:
  XCHG  loc.oddLineFlag, AL

  AND   DS:[DI], SI          ; clear the bit
  LOOP  ELoopOnCD

ELoopDone:
  POP   AX
  INC   AX
  JZ    GfxEraseLineRet

  DEC   AX
  CALL  RestoreEmsSlot

GfxEraseLineRet:
  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

  CALL  AInitLine
  PUSH  SI
  MOV   SI, 0080H            ; SI = original mask
  ROR   SI, CL               ; rotate to starting pos
  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

  SAR   DX, 1

XNoShift:
  NEG   DX                   ; DX = - dx /2
  JCXZ  XAllDone

XLoopOnAB:
  ADD   DX, BX               ; temp += dy
  JS    XA10                 ; JUMP IF RESULT < 0
  SUB   DX, AX               ; temp -= dx

  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4
  JNE   GoToNextBuffer5

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone5

GoToNextBuffer5:
  ADD   DI, loc.winHeight
  CMP   AL, loc.edge
  JA    AdjustDone5

  ADD   DI, loc.adjustDelta

AdjustDone5:
  XCHG  loc.oddLineFlag, AL

XA10:
  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

XAllDone:
  JMP   XLoopDone

XCaseCD:                     ; MUST BE CASE C OR D
  MOV   CX, BX               ; LOOP dy TIMES
  MOV   DX, BX
  CMP   DX, 1
  JLE   XNoShift2

  SAR   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
  TEST  SI,80H
  JZ    XC10

  ADD   DI,2                 ; DI ^ next word

XC10:
  XCHG  AL, loc.oddLineFlag
  INC   AL
  CMP   AL, 4
  JNE   GoToNextBuffer6

  SUB   DI, loc.rewindDelta
  ADD   DI, loc.bytesPerLine
  XOR   AL, AL
  JMP   SHORT AdjustDone6

GoToNextBuffer6:
  ADD   DI,loc.winHeight
  CMP   AL,loc.edge
  JA    AdjustDone6

  ADD   DI,loc.adjustDelta

AdjustDone6:
  XCHG  loc.oddLineFlag, AL

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

XLoopDone:
  POP   AX
  INC   AX
  JZ    GfxInvertLineRet

  DEC   AX
  CALL  RestoreEmsSlot

GfxInvertLineRet:
  MOV   SP,BP
  POP   DS
  POP   BP
  RET   paramBytes
GfxInvertLine ENDP
$EJ

;*************************************************
;*
;*    AInitLine
;*
;*  entry:  AX = x
;*          BX = y
;*          CX = dx    (dx >= 0)
;*          DX = dy
;*  
;*  exit:   SI = rotated mask
;*  exit:   AX = dx
;*          BX = dy
;*          CL = rotate amount
;*          DI ^ word in display
;*          SI = ems slot to restore
;*          loc.bytesPerLine = +- wordWidth
;*
;************************************************

AInitLine PROC NEAR
  PUSH CX                     ; save dx for later
  PUSH DX                     ; save dy for later

  PUSH AX                     ; save x pos for later
  PUSH BX                     ; save y pos for later

  MOV  AX, loc.screen
  MOV  DX, loc.bytesPerLine
  MOV  DI, loc.winHeight

  CALL InitWinParms

  MOV  DS, AX
  MOV  loc.edge, CL
  MOV  loc.rewindDelta, BX
  MOV  loc.winHeight, DI

  POP  AX                     ; y Pos
  POP  BX                     ; x Pos
  CALL StartPosition

  MOV  loc.adjustDelta, DX    ; adjustDelta same as bytesPerLine, except below
  MOV  loc.oddLineFlag, AL

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

  CMP  BX, zero
  JGE  DYGE0                  ; dy >= 0

  NEG  BX                     ; dy = |dy|

  ADD  loc.winHeight, DX      ; need to round up for backwards case
  NEG  loc.winHeight          ; want to be doing the opposite with
  NEG  loc.bytesPerLine       ; all of these since the line is being
  NEG  loc.rewindDelta        ; drawn from bottom to top

  NOT  loc.oddLineFlag
  AND  loc.oddLineFlag, 11b   ; oddLineFlag = (3 - oddLineFlag)

  NOT  loc.edge
  AND  loc.edge, 11b          ; edge = (3 - edge)

DYGE0:
  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
  PURGE oddLineFlag
  PURGE edge
  PURGE rewindDelta
  PURGE adjustDelta
$EJ

; Init Win Parms

; INPUT
;   AX = screen address segment
;   DX = bytes Per Line
;   DI = windowHeight

; OUTPUT
;   AX = screen address segment (maybe changed if in EMS)
;   BX = rewindDelta (bankHeight * 3 + edge fudge factors)
;   CX = edge (CH = scrFormat = pcFormat; CL = edge)
;   DX = bytes Per Line
;   DI = bankHeight, actually bankHeightInBytes
;   SI = ems slot to be restored (or -1 if not swapped out)

InitWinParms PROC NEAR
  MOV  CX, AX                 ; Save screen seg in CX
  MOV  SI, 0FFFFH             ; ems slot for "not swapped" case
  CMP  AX, screenSeg
  MOV  AX, 2000H
  JE   InitWinUsingScreen

  MOV  AX, CX                 ; Restore AX to screen seg again
  AND  AX, 0FF80H
  CMP  AX, CS:emsBase
  JNE  AltScreenNotInEms

  PUSHF                       ; Saving direction flag (just in case)
  PUSH ES
  PUSH DI
  PUSH DX

  AND  CX, 07FH               ; low 7 bits of window addr has ems slot

  PUSH CX                     ; Pass slot to set (BYTE parameter)
  INT  71H                    ; Cp Subsystem Call
  DB   4EH                    ; CpSetActiveSlot

  MOV  AH, 0
  MOV  SI, AX                 ; Save old slot in SI

  POP  DX
  POP  DI
  POP  ES
  POPF

  MOV  CX, CS:emsBase

AltScreenNotInEms:
  MOV  AX, DI
  SHR  AX, 1
  SHR  AX, 1
  MOV  BX, DX                 ; Save bytesPerLine
  MUL  DX                     ; bankHeight = (winHeight DIV 4) * bytesPerLine
  MOV  DX, BX                 ; Restore bytesPerLine

InitWinUsingScreen:
  XCHG DI, AX                 ; AX has actual windowHeight; DI has bankHeight
  XOR  BX, BX                 ; init rewindHeight to 0

  TEST AL, 11b                ; If edge is 0 (even mult of 4)
  JZ   InitWinAdd3Banks       ; then don't add fudge factor

  MOV  BX, DX                 ; fudge factor = 1 x bytesPerLine
  TEST AL, 10b                ; If edge is not 2 or 3
  JZ   InitWinAdd3Banks       ; then fudge factor is done

  ADD  BX, BX                 ; fudge factor is 2 x bytesPerLine
  TEST AL, 01b                ; If edge is not 3
  JZ   InitWinAdd3Banks       ; then fudge factor is done

  ADD  BX, DX                 ; else fudge factor is 3 x bytesPerLine

InitWinAdd3Banks:
  ADD  BX, DI
  ADD  BX, DI
  ADD  BX, DI

  XCHG CX, AX                 ; AX = scr seg (or EMS base); CX will become scr format and edge values 
  AND  CX, 11b                ; CH = 0 (pcFormat) and CL = edge

  RET
InitWinParms ENDP
$EJECT

; Restore EMS Slot

; INPUT
;   AX = ems slot to be restored
;   IT BETTER BE VALID, TOO! (i.e. check for AX=-1 before calling here)

; OUTPUT
;   Total destruction (within limits)

RestoreEmsSlot PROC NEAR
  PUSH AX                     ; Pass slot to restore (BYTE parameter)
  INT  71H                    ; Cp Subsystem Call
  DB   4EH                    ; CpSetActiveSlot
  RET
RestoreEmsSlot ENDP
$EJ

;----------------------
StartPosition PROC NEAR
;----------------------

;computes word address & shift count, given x & y

;INPUT: 
;   AX = y
;   BX = x
;   DX = bytesPerLine
;   DI = next buffer offset
;   CH = screenFormat
;   CL = edge
;
;OUTPUT:
;   DI = offset to word
;   CX = shift count
;   DX = bytesPerLine
;   AX = y mod 4 
;
  PUSH SI
  PUSH DX
  PUSH AX              ; save y for later
  XOR  SI, SI          ; initial ptr to buffer (bank 0)
  CMP  CH, pcFormat
  JNE  S0

  PUSH AX              ; Save y pos for later
  AND  AX, 11b         ; AX = AX MOD 4
  JZ   EndJmpTbl2      ; If even mult of 4 lines, then already in bank 0

  PUSH AX              ; Save bank that y reside in for later
  SHL  AX, 1
  NEG  AX              ; index into the 'add bank' table
  ADD  AX, OFFSET EndJmpTbl1
  JMP  AX

  ADD  SI, DI
  ADD  SI, DI
  ADD  SI, DI

EndJmpTbl1:
  POP  AX              ; restore bank that y resides in
  OR   CL, CL          ; if all banks are the same size
  JZ   EndJmpTbl2      ; then don't have to add fudge factor

  CMP  AL, CL          ; If y pos is < edge
  JB   UseLineCnt      ; then use y pos for fudge factor

  XCHG AX, CX          ; else use edge for fudge factor

UseLineCnt:
  SHL  AX, 1
  NEG  AX              ; index into the 'add fudge factor' table
  ADD  AX, OFFSET EndJmpTbl2
  JMP  AX

  ADD  SI, DX
  ADD  SI, DX
  ADD  SI, DX

EndJmpTbl2:
  POP  AX              ; restore y
  SHR  AX, 1
  SHR  AX, 1           ; y = y DIV 4 (gives offset of 'y' within its bank)

S0:
  MUL  DX
  ADD  AX, SI          ; SI = ptr to correct line

  MOV  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
  MOV  CX,0FH

  SHL  AX, 1           ; make it a byte count
  ADD  DI, AX          ; DI points to begin of word

  AND  CX, BX          ;  mask out for shift count
  POP  AX
  AND  AX, 11b         ; AX = y MOD 4

  POP  DX
  POP  SI
  RET
StartPosition ENDP


CODE ENDS

  END
