;-----------------------------------------------------------------------
; MODULE XMOUSE
;
; Mouse functions functions for all MODE X 256 Color resolutions
;
; Compile with Tasm.
; C callable.
;
; ****** XLIB - Mode X graphics library                ****************
; ******                                               ****************
; ****** Written By Themie Gouthas                     ****************
;
; This module is based on Shane Hyde's module of the same name,
; posted to Rec.Games.Programmer, October 92.
;
;
; egg@dstos3.dsto.gov.au
; teg@bart.dsto.gov.au
;
; mouse cursor shape by Tiaan A Geldenhuys
;
;-----------------------------------------------------------------------

COMMENT  $


This is a module implementing very basic mouse functions.

It does not support the full functionality of:

  SPLIT SCREENS
  SCROLLED WINDOWS
  VIRTUAL WINDOWS

	      --------------------------------------

	      MS Mouse Driver Functions

	      Mouse Initialization                 0
	      Show Cursor                          1
	      Hide Cursor                          2
	      Get Mouse Position & Button Status   3
	      Set Mouse Cursor Position            4
	      Get Button Press Information         5
	      Get Button Release Information       6
	      Set Min/Max Horizontal Position      7
	      Set Min/Max Vertical Position        8
	      Define Graphics Cursor Block         9
	      Define Text Cursor                  10
	      Read Mouse Motion Counters          11
	      Define Event Handler                12
	      Light Pen Emulation Mode ON         13
	      Light Pen Emulation Mode OFF        14
	      Set Mouse Mickey/Pixel Ratio        15
	      Conditional Hide Cursor             16
	      Set Double-Speed Threshold          19
	      --------------------------------------
$

include xlib.inc
include xdetect.inc

.data

global   _MouseInstalled    :word
global   _MouseHidden       :word
global   _MouseButtonStatus :word
global   _MouseX            :word
global   _MouseY            :word
global   _MouseFrozen       :byte
global   _MouseColor        :byte

global   _x_define_mouse_cursor :proc
global   _x_show_mouse          :proc
global   _x_hide_mouse          :proc
global   _x_mouse_remove        :proc
global   _x_position_mouse      :proc
global   _x_put_cursor          :proc
global   _x_update_mouse        :proc
global   _x_mouse_init          :proc
global   _x_mouse_window        :proc


ALIGN 2



      InitMouseDef db 00000001b  ; Default mouse mask, note the reverse order
		db 00000011b
		db 00000111b
		db 00001111b
		db 00011111b
		db 00111111b
		db 01111111b
		db 11111111b
		db 00011111b
		db 00011011b
		db 00110001b
		db 00110000b
		db 01100000b
		db 01100000b



COMMENT $

   Old mouse definition

   InitMouseDef db 00000001b  ; Default mouse mask, note the reverse order
		db 00000011b
		db 00000111b
		db 00001111b
		db 00011111b
		db 00111111b
		db 01111111b
		db 11111111b
		db 00011100b
		db 00111100b
		db 01111100b
		db 00000000b
		db 00000000b
		db 00000000b

$

    MouseMask          db 168 dup(?)
    OldHandlerSeg      dw  ?
    OldHandlerOffs     dw  ?
    OldHandlerMask     dw  ?
    OldX               dw  ?
    OldY               dw  ?
    OldScrnOffs        dw  ?

    BGSaveOffs         dw  0

   _MouseInstalled     dw 0     ; Flag indicating whether mouse is installed
   _MouseHidden        dw 0     ; Flag indicating whether mouse is hidden
   _MouseButtonStatus  dw 0     ; Holds current button press information
   _MouseX             dw 0     ; Coords of cursor hot spot
   _MouseY             dw 0
   _MouseFrozen        db 0     ; Mouse motion enable/disable control
   _MouseColor         db 0     ; Mouse cursor colour

   inhandler           db 0
.code

;----------------------------------------------------------------------
; Local function that updates the cursor position
;
; Destroys SI,DI,AX,BX
;
;----------------------------------------------------------------------
proc update_cursor near
   WaitVsyncStart

   mov di,[OldScrnOffs]             ; Delete cursor (restore old background)
   mov ax,[OldY]
   mov bx,[OldX]

   call restorebg

   mov si,[_VisiblePageOffs]        ; Save cursor background
   mov ax,[_MouseY]
   mov bx,[_MouseX]
   mov [OldScrnOffs],si
   mov [OldY],ax
   mov [OldX],bx
   call getbg

   push [_VisiblePageOffs]          ; Draw the cursor
   mov  ax,[_ScrnPhysicalHeight]
   push ax
   mov  ax,0
   push ax
   push [OldY]
   push [OldX]
   call _x_put_cursor
   add  sp,10
   ret
update_cursor endp


;----------------------------------------------------------------------
; x_mouse_init - Initialise Mode X mouse handler
;
; C Prototype
;
;  int x_mouse_init()
;
; This is the first function you must call before using any of the mouse
; functions
;
; WARNING: This function uses and updates "NonVisual_Offset" to allocate
;          video ram for the saved mouse background.
;
; This mouse code uses the fastest possible techniques to save and restore
; mouse backgrounds and to draw the mouse cursor.
;
; LIMITATIONS: No clipping is supported horizontally for the mouse cursor
;              No validity checking is performed for NonVisual_Offs
;
;
; **WARNING** Hide or freeze mouse while drawing using any of the other
;             Modules. VGA register settings are not preserved which will
;             result in unpredictable drawing behavior.
;             If you know the drawing will occur away from the mouse cursor
;             set MouseFrozen to TRUE (1), do your drawing then set it to
;             FALSE (0). Alternatively call "x_hide_mouse", perform your
;             drawing and then call "x_show_mouse"
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_mouse_init proc
     push  bp
     mov   bp,sp

     cmp   [_MouseButtonCount],0  ; Dont initialize if mouse detection
     jne   @@DontInitialize       ; or initialization function previously
				  ;   called
     xor   ax,ax                  ; FUNC 0: Mouse Initialization
     int   33h                    ;
     or    ax,ax                  ; Is there a mouse installed ?
     jz    @@Done
     mov   [_MouseButtonCount],bx ; Set the button count

@@DontInitialize:

     mov   [_MouseInstalled],ax
     or    ax,ax                  ; Do we have an installed mouse driver ?
     jz    @@Done                 ; Nop!

     mov   ax,[_NonVisual_Offs];  ; Allocate VRAM for saved background
     mov   BGSaveOffs,ax

     add   ax,14*3
     mov   [_NonVisual_Offs],ax   ; Update NonVisualOffset

     mov   ax,02                  ; FUNC 2: Hide Cursor
     int   33h                    ;  (hide the mouse driver's default cursor)
     mov   _MouseInstalled,TRUE   ; Indicate user mouse driver present

     mov   ax,07h                 ; FUNC 7:Set min/max horizontal position
     mov   cx,0
     mov   dx,[_ScrnPhysicalPixelWidth]
     shl   dx,1                   ; Mult X by 2 as cursor steps by 2 pixels
     int   33h                    ; 0 < X < _ScrnPhysicalPixelWidth

     mov   ax,08h                 ; FUNC 8:Set min/max vertical position
     mov   cx,0
     mov   dx,_ScrnPhysicalHeight
     int   33h                    ; 0 < Y < _ScrnPhysicalHeight

     mov   ax,0fh                 ; FUNC 15: Mouse Hor/Vert resolution
     mov   cx,4                   ; Horiz speed  >> Value => << Speed
     mov   dx,8                   ; Vert Speed
     int   33h

     mov   ax,3                   ; FUNC 3: Get mouse pos & button status
     int   33h
     mov   [_MouseY],dx
     shr   cx,1
     mov   [_MouseX],cx

     mov   ax,12                  ; FUNC 12: Define Event Handler
     mov   bx,seg mouse_handler   ;  ES:DX -> Event handler
     mov   es,bx
     mov   dx,offset mouse_handler
     mov   cx,1fh                 ;  Set handler for all events
     int   33h



     mov   [_MouseHidden],TRUE    ; Mouse initially hidden

     push  ds                     ; Set the default cursor shape
     mov   ax,offset InitMouseDef
     push  ax
     call  _x_define_mouse_cursor
     add   sp,04h

     mov   ax,[_MouseInstalled]   ; Return MouseInstalled flag
@@Done:
     pop   bp
     ret
_x_mouse_init endp

;----------------------------------------------------------------------
; x_mouse_window - Define a mouse window
;
; C Prototype
;
;  void x_mouse_window(int x0, int y0, int x1, int y1);
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_mouse_window proc
ARG x0:word,y0:word,x1:word,y1:word
     push  bp
     mov   bp,sp

     mov   ax,7       ; FUNC 7: Set X range
     mov   cx,x0
     shl   cx,1
     mov   dx,x1
     shl   dx,1
     int   33h

     mov   ax,8       ; FUNC 8: Set Y range
     mov   cx,y0
     mov   dx,y1
     int   33h
     pop   bp
     ret
_x_mouse_window endp


;----------------------------------------------------------------------
; x_define_mouse_cursor - Define a mouse cursor from an input bitmask
;
; C Prototype
;
;  void x_define_mouse_cursor(char far *MouseDef, unsigned char MouseColor)
;
; WARNING: This function assumes MouseDef points to 14 bytes.
;
; Note: Bit order is in reverse. ie bit 7 represents pixel 0 ..
;       bit 0 represents pixel 7 in each "MouseDef" byte.
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_define_mouse_cursor proc
ARG MouseDef:dword,MouseColor:byte
     push  bp
     mov   bp,sp

     cmp  [_MouseInstalled],FALSE   ; Check whether we have installed
     je   @@Done                    ;  our mouse handler and leave if not

     mov  al,[MouseColor]           ; Set the mouse pointers color
     mov  [_MouseColor],al

     push  si
     push  di
     push  ds
     mov   ax,ds                ; ES:DI -> Stored plane mask for all
     mov   es,ax                ;   pixel alignments of mouse cursor
     mov   di,offset MouseMask
     lds   si,MouseDef
     xor   cl,cl                ; CL = current alignment (initially zero)
@@AlignmentLoop:
     push  si                   ; save MouseDef ptr for next alignment
     mov   dh,14                ; Init Row counter to Cursor Height
@@RowLoop:
     lodsb                      ; Load first cursor def byte each bit
				;  representing pixel in the row
     xor   ah,ah                ; AH is the shift overflow byte
     shl   ax,cl                ; Shift mask for current alignment

     mov   bl,al                ; store first three nibbles of ax into
     and   bl,0fh               ;  consecutive bytes in the destination
     mov   es:[di],bl           ;  buffer
     inc   di
     shr   al,4
     stosw

     dec   dh                   ; Next row for this alignment if there
     jnz   @@RowLoop            ;  are more to do

     pop   si                   ; point to the start of the cursor def.
     inc   cl                   ; Select next pixel alignment
     cmp   cl,4                 ; If there are more alignments to do
     jne   @@AlignmentLoop      ;   then jump

     pop   ds
     pop   di
     pop   si
@@Done:
     pop   bp
     ret
_x_define_mouse_cursor endp


;----------------------------------------------------------------------
; x_show_mouse - Shows a previously hidden mouse cursor
;
; C Prototype
;
;  void x_show_mouse()
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_show_mouse proc
     push  bp
     mov   bp,sp
     cmp   [_MouseInstalled],FALSE  ; Make sure our handler is installed
     je    @@Done
     cmp   [_MouseHidden],FALSE     ; If not hidden then exit
     je    @@Done
     push  si
     push  di


@@WaitEndOfHandler:               ; Make sure handler not currently active
     mov   cl,[inhandler]
     or    cl,cl
     jnz   @@WaitEndOfHandler


     mov   si,[_VisiblePageOffs]  ; Save mouse background and pos details
     mov   ax,[_MouseY]
     mov   bx,[_MouseX]
     mov   [OldScrnOffs],si
     mov   [OldY],ax
     mov   [OldX],bx
     call  getbg

     push [_VisiblePageOffs]      ; Draw cursor
     push [_ScrnLogicalHeight]
     xor  ax,ax
     push ax
     push [OldY]
     push [OldX]
     call _x_put_cursor
     add  sp,10

     mov   [_MouseHidden],FALSE   ; Indicate mouse cursor no longer hidden

     pop  di
     pop  si
@@Done:
     pop   bp
     ret
_x_show_mouse endp


;----------------------------------------------------------------------
; x_hide_mouse - Hides a previously visible mouse cursor
;
; C Prototype
;
;  void x_hide_mouse()
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_hide_mouse proc
     push  bp
     mov   bp,sp

     cmp   [_MouseInstalled],FALSE   ; Make sure our handler is installed
     je    @@Done
     cmp   [_MouseHidden],FALSE      ; If cursor is already hidden exit
     jne   @@Done
     push  si
     push  di

@@WaitEndOfHandler:            ; Make sure handler not currently active
     mov   cl,[inhandler]
     or    cl,cl
     jnz   @@WaitEndOfHandler

     mov   [_MouseHidden],TRUE       ; Delete mouse cursor
     mov   di,[OldScrnOffs]
     mov   ax,[OldY]
     mov   bx,[OldX]
     call  restorebg
     pop   di
     pop   si
@@Done:
     pop   bp
     ret
_x_hide_mouse endp


;----------------------------------------------------------------------
; x_remove_mouse - removes mouse handler
;
; C Prototype
;
;  void x_remove_mouse()
;
; NOTE: This function MUST be called before quitting the program if
;       a mouse handler has been installed
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_mouse_remove proc
    push  bp
    mov   bp,sp
    cmp   [_MouseInstalled],FALSE  ; Check whether we have installed
    je    @@Done                   ;  our mouse handler
    call  _x_hide_mouse
    mov   ax,12                    ; FUNC 12: Install event handler
    xor   cx,cx                    ; Disable all events
    int   33h
    mov   [_MouseInstalled],FALSE
@@Done:
    pop   bp
    ret
_x_mouse_remove endp


;----------------------------------------------------------------------
; x_position_mouse - Positions the mouse cursor at the specified location
;
; C Prototype
;
;  void x_position_mouse(int x, int y)
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_position_mouse proc
ARG  X:word,Y:word
     push  bp
     mov   bp,sp

@@WaitEndOfHandler:               ; Make sure handler not currently active
     mov   bl,[inhandler]

     or    bl,bl
     jnz   @@WaitEndOfHandler

     mov   ax,4
     mov   cx,X
     mov   dx,Y
     mov   [_MouseX],cx
     mov   [_MouseY],dx
     shl   cx,1

     mov   [inhandler],1
     int   33h

     ; The handler doesnt get called so need
     ; to update manually;

     cmp   [_MouseHidden],FALSE
     jne   @@NotVisible
     push  di si
     call  update_cursor
     pop   si di

@@NotVisible:
     mov   [inhandler],0
     pop   bp
     ret
_x_position_mouse endp

;----------------------------------------------------------------------
; x_update_mouse - Forces the mouse position to be updated and cursor
;                  to be redrawn.
;
; C Prototype
;
;  void x_update_mouse()
;
; Note this function is useful when you have set "MouseFrozen" to true.
; Allows the cursor position to be updated manually rather than
; automatically by the installed handler.
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_update_mouse proc
     push  bp
     mov   bp,sp
     cmp   [_MouseInstalled],FALSE   ; Make sure our handler is installed
     je    @@Done
     cmp   [_MouseHidden],FALSE      ; If cursor is already hidden exit
     jne   @@Done
     push  si
     push  di
     mov   ax,03h                 ; FUNC 3: get cursor pos / button status
     int   33h                    ; Update position variables first
     shr   cx,1
     mov   [_MouseX],cx
     mov   [_MouseY],dx
     mov   [_MouseButtonStatus],bx    ; Update button status
     call  update_cursor
     pop   di
     pop   si
@@Done:
     pop   bp
     ret
_x_update_mouse endp


;----------------------------------------------------------------------
; x_put_cursor - Draws the mouse cursor
;
; C Prototype
;
; void x_put_cursor(int X, int Y, int TopClip, int BottomClip, WORD ScrnOffs)
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
ALIGN 2
_x_put_cursor  proc
ARG X:word,Y:word,TopClip,BottomClip,ScrnOffs
LOCAL Height,TopRow,NextLineIncr:word=LocalStk
	push  bp
	mov   bp,sp
	sub   sp,LocalStk                 ; Create space for local variables
	push  si
	push  di
	push  ds
	mov   ax,@data
	mov   ds,ax
	cld

	mov   ax,14                   ; Get image height and save in AX
	mov   bx,Y
	; cx = top Row

	;;;;; CLIP PROCESSING FOR TOP CLIP BORDER ;;;;;;;;;;;;;;;;;;;;;

	mov   dx,[TopClip]            ; Compare u.l. Y coord with Top
	sub   dx,bx                   ; clipping border
	jle   @@NotTopClip            ; jump if  not clipped from above
	cmp   dx,ax
	jnl   @@NotVisible            ; jump if  is completely obscured
	mov   cx,dx
	sub   ax,dx
	add   bx,dx
	jmp   short @@VertClipDone

	;;;; EXIT FOR COMPLETELY OBSCURED  ;;;;;;;;;;;;;;;;;;;;;;

@@NotVisible:
	pop   ds
	pop   di                          ; restore registers
	pop   si
	mov   sp,bp                       ; dealloc local variables
	pop   bp
	ret

	;;;;; CLIP PROCESSING FOR BOTTOM CLIP BORDER ;;;;;;;;;;;;;;;;;;;

@@NotTopClip:
	mov   dx,[BottomClip]
	sub   dx,bx
	js    @@NotVisible
	mov   cx,0
	cmp   dx,ax
	jg    @@VertClipDone
	inc   dx
	mov   ax,dx

@@VertClipDone:

	mov   [Height],ax
	mov   [TopRow],cx

	mov   ax,SCREEN_SEG               ; Point es to VGA segment
	mov   es,ax

	mov   ax,bx
	mov   cx,[_ScrnLogicalByteWidth]  ; Find required screen address
	mul   cx
	mov   di,ax

	sub   cx,3                        ; Distance to next screen row
	mov   [NextLineIncr],cx


	mov   cx,[X]
	mov   bx,cx
	shr   cx,2
	add   di,cx
	and   bx,3
	add   di,[ScrnOffs]

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


	mov   ax,42
	mul   bx
	mov   si,offset MouseMask
	add   si,ax

	mov   ax,3                        ; Increment DS:BX and DS:SI to
	mul   [TopRow]                    ;  been clipped by the top border
	add   si,ax                       ; by the L.H.S border

	mov   dx,SC_INDEX                 ; Point SC register to map mask
	mov   al,MAP_MASK                 ; in preperation for masking data
	out   dx,al
	inc   dx                          ; Point dx to SC data register
	mov   ah,byte ptr [Height]        ; AH = Scanline loop counter
	mov   bl,[_MouseColor]
@@RowLoop:
	mov   cx,3                        ; Width in bytes across
@@ColLoop:
	lodsb                             ; load plane mask and set MAP MASK
	out   dx,al
	mov   es:[di],bl                  ; store color to specified planes
	inc   di
	loop  @@ColLoop

	add   di,[NextLineIncr]
	dec   ah
	jnz   @@RowLoop

	pop   ds
	pop   di                          ; restore registers
	pop   si
	mov   sp,bp                       ; dealloc local variables
	pop   bp
	ret
_x_put_cursor  endp


;----------------------------------------------------------------------
; getbg - saves cursor background
;
; C Prototype
;
; local function using register parameters
;
; si = screen offset
; ax  = y
; bx  = x
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
ALIGN 2
getbg  proc near

	push  bp
	mov   bp,sp
	push  ds
	cld

	mov   cx,[_ScrnLogicalByteWidth]  ;  by mult. dest Y coord by Screen
	mul   cx                          ;  width then adding screen offset
	add   si,ax                       ; Add Dest Screen Row to di
	sub   cx,3
	shr   bx,2
	add   si,bx
	mov   bx,cx

	mov   di,BGSaveOffs
	mov   ax,SCREEN_SEG               ; Point es to VGA segment
	mov   es,ax
	mov   ds,ax

	mov   dx,GC_INDEX                 ; Set bit mask for all bits from
	mov   ax,BIT_MASK                 ; VGA latches and none from CPU
	out   dx,ax

	mov   dx,SC_INDEX                 ; Point SC register to map mask
	mov   al,MAP_MASK           ; in preperation for masking data
	out   dx,al
	inc   dx
	mov   al,0fh
	out   dx,al

	mov   cx,14
@@Loop:
	movsb
	movsb
	movsb
	add si,bx
	loop @@Loop

mov     dx,GC_INDEX+1 ;restore the bit mask to its default,
	mov     al,0ffh       ; which selects all bits from the CPU
	out     dx,al         ; and none from the latches (the GC
			      ; Index still points to Bit Mask)

	pop   ds
	mov   sp,bp                       ; dealloc local variables
	pop   bp
	ret
getbg  endp

;----------------------------------------------------------------------
; restorebg - restores cursor background
;
; C Prototype
;
; local function using register parameters
;
; di = screen offset
; ax  = y
; bx  = x
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
ALIGN 2
restorebg  proc near
;di = scrn offs
;ax  = y
;bx  = x
	push  bp
	mov   bp,sp
	push  ds
	cld

	mov   cx,[_ScrnLogicalByteWidth]  ;  by mult. dest Y coord by Screen
	mul   cx                          ;  width then adding screen offset
	add   di,ax                       ; Add Dest Screen Row to di
	sub   cx,3
	shr   bx,2
	add   di,bx
	mov   si,BGSaveOffs
	mov   ax,SCREEN_SEG               ; Point es to VGA segment
	mov   es,ax
	mov   ds,ax

	mov   dx,GC_INDEX                 ; Set bit mask for all bits from
	mov   ax,BIT_MASK                 ; VGA latches and none from CPU
	out   dx,ax

	mov   dx,SC_INDEX                 ; Point SC register to map mask
	mov   al,MAP_MASK                 ; in preperation for masking data
	out   dx,al
	inc   dx
	mov   al,0fh
	out   dx,al

	mov   bx,cx
	mov   cx,14
@@Loop:
	movsb
	movsb
	movsb
	add di,bx
	loop @@Loop
	mov  dx,GC_INDEX+1 ;restore the bit mask to its default,
	mov  al,0ffh       ; which selects all bits from the CPU
	out  dx,al         ; and none from the latches (the GC
			   ; Index still points to Bit Mask)
	pop   ds
	pop   bp
	ret
restorebg  endp



;********************** The Mouse event handler *************************

ALIGN 2
mouse_handler proc far
   push  bp
   mov   bp,sp
   push  ds

   mov   di,@data                   ; Make sure DS points to data segment
   mov   ds,di
   cmp   [inhandler],1
   jne   @@NotActive
   pop   ds
   pop   bp
   ret

@@NotActive:
   mov   [inhandler],1
   mov   [_MouseButtonStatus],bx    ; Update button status
   test  ax,1                       ; Is this a motion event ?
   jz    @@Done                     ; No Jump

   shr  cx,1                        ; convert X coord to pixel coords
   mov  [_MouseX],cx                ; save mouse position
   mov  [_MouseY],dx

   cmp  [_MouseHidden],TRUE         ; If mouse hidden dont bother drawing
   je   @@Done

   cmp  [_MouseFrozen],TRUE         ; If mouse hidden dont bother drawing
   je   @@Done

   call update_cursor
@@Done:
   mov  [inhandler],0
   pop  ds
   pop  bp
   ret
mouse_handler endp


end
