Metropoli BBS
VIEWER: mouse.asm MODE: TEXT (ASCII)
comment ^
included by video.asm
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Mouse Driver  v1.32
 by : Peter Quiring
 date: April 10,96
 updated: May 1,96
 updated: Oct 18,96
 updated: Dec 5,96  (added support for text modes - easy)
                    (added mouse_setcursor_text(color) )
 updated: Mar 12,97 (supports text modes properly)

 Generic mouse handler. (for 24/16/8 bit modes and Text modes)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!^

.data
m_paws db 0    ;special printing mouse thingy
m_pp dd offset v_pp   ;are switched to v_?p2 when using _v_buffer
m_gp dd offset v_gp
maxsiz equ 64  ;maximum size of cursor (x and y) must be a power of 2
moved db 0
cursor_color db 0  ;used in text mode only
backbuf db (maxsiz*maxsiz) dup (0) ;temp buffer
  dd ?   ;very important!!
cbb dd offset backbuf   ;current backbuf
oldbank db 0   ;backup of vesa_current_bank
oldplane_wr db 0  ;backup of modex_plane_wr
oldplane_rd db 0  ;backup of modex_plane_rd
align 4
prevx dw 0
prevy dw 0
inited db 0
align 4
xpos dw 0
ypos dw 0
default_cur dd 01010101h    ;just in case!
cur dd offset default_cur
userproc dd 0  ;callback routine
userproc_s dd 0  ;callback routine
curx db 1 ;size x
cury db 1 ;size y
hpx dw 0 ;hot point x
hpy dw 0 ;hot point y
mouse_con dw 0 ;cursor on?
but dw 0 ;button states
minx dw 0
miny dw 0
maxx dw (319)  ;These are absolute min that maxx,maxy could be
maxy dw (199) 
mx dw 0  ;the x after ratio shift
my dw 0  ;the y after ratio shift
once db 0  ;set the 1st time mouse_hand is used
xratio db 1
yratio db 1

.code

mouse_hand  proto

mouse_init proc
  pushad
  mov ax,0
  int 33h
  cmp ax,0
  jnz @f
  popad
  mov eax,ERROR
  ret
@@:
  .if inited
    popad
    mov eax,ERROR
    ret
  .endif
  mov inited,1

  push cs
  pop es
  mov edx,offset mouse_hand
  mov cx,0ffh  ;all movement & buttons cause a callback
  mov ax,0ch
  int 33h   ;PMODE/W extender INT
  mov es,seldata

  ;setup self-uninit
  callp atexit,offset mouse_uninit

  ;setup X/Y
  mov ax,wptr[_v_x]
  dec ax
  mov maxx,ax
  mov ax,wptr[_v_y]
  dec ax
  mov maxy,ax

  popad
  xor eax,eax
  ret
mouse_init endp

mouse_uninit proc ;will self-uninit during atexit procedure
  pushad

  ;disable callback thingy
  push dptr 0  ;32bit !!!
  pop es
  xor edx,edx
  xor ecx,ecx  ;no movement & buttons cause a callback
  mov ax,0ch
  int 33h
  mov es,seldata

  mov inited,0
  popad
  ret
mouse_uninit endp

;mask for callbacks
; CX = : bit # : cause because...
;          0     cursor moved
;          1     left pressed
;          2     left released
;          3     right pressed
;          4     right released
;        5-15    not used
          ;5,6=middle button (if 3 button)

align 4
mouse_setcursor proc uses ebx,_cur:dword
  ;ONLY called when mouse is off or from the callback
  mov ebx,_cur
  mov cur,ebx
  add cur,sizeof g_mousehead
  xor eax,eax
  mov ax,[ebx].g_mousehead.x
  cmp al,maxsiz     ;FIX : v2.03 Beta #4 : this was 'and'?
  jbe @f
  mov al,maxsiz
@@:        
  mov curx,al
  mov ax,[ebx].g_mousehead.y
  cmp al,maxsiz     ;FIX : v2.03 Beta #4 : this was 'and'?
  jbe @f
  mov al,maxsiz
@@:
  mov cury,al
  mov ax,[ebx].g_mousehead.hx
  mov hpx,ax
  mov ax,[ebx].g_mousehead.hy
  mov hpy,ax
  xor eax,eax
  ret
mouse_setcursor endp

mouse_setcursor_text proc,_color:byte
  mov al,_color
  mov cursor_color,al
  ret
mouse_setcursor_text endp

; NOTE : all functions that do clipping are not complete clipping
;      : the image must still have at least 1 pixel on screen (like a mouse ptr)
;      : these clipping puts/gets are for mouse functions only!
;clipping
;this is used by the mouse functions

align 4
put0c proc private,x:dword,xl:dword,y:dword,yl:dword  ;uses delta method of cords
  local sadd:dword ;src add
  local oxl:dword ;old x length
  
  .if curmode==G_VESA && (banking)
    mov al,vesa_current_bank
    mov oldbank,al
  .elseif curmode==G_MODEX
    mov al,modex_plane_wr
    mov oldplane_wr,al
  .endif
  mov eax,xl
  mov oxl,eax
  mov sadd,0
  mov esi,cur   ;source is the cursor
;do clipping
  .if x&080000000h
    neg x
    mov eax,x
    sub xl,eax
    add sadd,eax
    mul _v_bypp
    add esi,eax
    mov x,0
  .endif
  .if y&080000000h
    neg y
    mov eax,y
    sub yl,eax
    mul oxl
    mul _v_bypp
    add esi,eax
    mov y,0
  .endif
  mov eax,x
  add eax,xl
  cmp eax,_v_x
  jbe @f
  sub eax,_v_x  ;ax=overflow
  sub xl,eax
  add sadd,eax
@@:
  mov eax,y
  add eax,yl
  cmp eax,_v_y
  jbe @f
  sub eax,_v_y
  sub yl,eax
@@:
  ;start calc's
  mov ecx,xl
  mov eax,sadd
  mul _v_bypp
  mov edx,eax  ;=sadd
  mov ebx,x
put0_1:
  cmp byte ptr[esi],0   ;BUG: gotta check all bytes! (test w/ masks?)
  jz @f
  callp m_pp,x,y,dword ptr[esi]  ;EAX gone...
@@:
  inc x
  add esi,_v_bypp
  dec ecx
  jnz put0_1
put0_3:
  add esi,edx
  mov ecx,xl
  mov x,ebx
  inc y
  dec yl
  jnz put0_1  ;loop
  .if curmode==G_VESA && banking
    callp vesa_setbank,oldbank
  .elseif curmode==G_MODEX
    mov ah,oldplane_wr
    mov al,2
    mov dx,vga_SC_INDEX
    mov modex_plane_wr,ah
    out dx,ax  ;must restore plane #
  .endif
  
  ret
put0c endp

;put clipping
;used by mouse support
align 4
putc proc private,x:dword,xl:dword,y:dword,yl:dword
  local sadd:dword
  local oxl:dword

  .if curmode==G_VESA && banking
    mov al,vesa_current_bank
    mov oldbank,al
  .elseif curmode==G_MODEX
    mov al,modex_plane_wr
    mov oldplane_wr,al
  .endif
  mov eax,xl
  mov oxl,eax
  mov sadd,0
  mov esi,cbb
;do clipping
  .if x&080000000h
    neg x
    mov eax,x
    sub xl,eax
    add sadd,eax
    mul _v_bypp
    add esi,eax
    mov x,0
  .endif
  .if y&080000000h
    neg y
    mov eax,y
    sub yl,eax
    mul oxl
    mul _v_bypp
    add esi,eax
    mov y,0
  .endif
  mov eax,x
  add eax,xl
  cmp eax,_v_x
  jbe @f
  sub eax,_v_x
  sub xl,eax
  add sadd,eax
@@:
  mov eax,y
  add eax,yl
  cmp eax,_v_y
  jbe @f
  sub eax,_v_y
  sub yl,eax
@@:
  mov ecx,xl
  mov eax,sadd
  mul _v_bypp
  mov edx,eax
  mov ebx,x
putc_1:
  callp m_pp,x,y,dword ptr[esi]   ;EAX destroyed
  add esi,_v_bypp
  inc x
  dec ecx
  jnz putc_1
  mov x,ebx
  inc y
  add esi,edx
  mov ecx,xl
  dec yl
  jnz putc_1  ;loop
  .if curmode==G_VESA && banking
    callp vesa_setbank,oldbank
  .elseif curmode==G_MODEX
    mov ah,oldplane_wr
    mov al,2
    mov dx,vga_SC_INDEX
    mov modex_plane_wr,ah
    out dx,ax  ;must restore plane #
  .endif
  ret
putc endp

;clipping!
;this is used by the mouse functions
align 4
getc proc private,x:dword,xl:dword,y:dword,yl:dword
  local dadd:dword
  local oxl:dword
  .if curmode==G_VESA && banking
    mov al,vesa_current_bank
    mov oldbank,al
  .elseif curmode==G_MODEX
    mov al,modex_plane_rd
    mov oldplane_rd,al
  .endif
  mov eax,xl
  mov oxl,eax
  mov dadd,0
  mov edi,cbb
;do clipping
  .if x&080000000h
    neg x
    mov eax,x
    sub xl,eax
    add dadd,eax
    mul _v_bypp
    add edi,eax
    mov x,0
  .endif
  .if y&080000000h
    neg y
    mov eax,y
    sub yl,eax
    mul oxl
    mul _v_bypp
    add edi,eax
    mov y,0
  .endif
  mov eax,x
  add eax,xl
  cmp eax,_v_x
  jbe @f
  sub eax,_v_x
  sub xl,eax
  add dadd,eax
@@:
  mov eax,y
  add eax,yl
  cmp eax,_v_y
  jbe @f
  sub eax,_v_y
  sub yl,eax
@@:
  mov ecx,xl
  mov eax,dadd
  mul _v_bypp
  mov edx,eax
  mov ebx,x
@@:
  callp m_gp,x,y
  mov [edi],eax
  add edi,_v_bypp
  inc x
  dec ecx
  jnz @b
  add edi,edx
  mov ecx,xl
  mov x,ebx
  inc y
  dec yl
  jnz @b  ;loop
  .if curmode==G_VESA && banking
    callp vesa_setbank,oldbank
  .elseif curmode==G_MODEX
    mov ah,oldplane_rd
    mov al,4
    mov dx,vga_GC_INDEX
    mov modex_plane_rd,ah
    out dx,ax  ;must restore plane #
  .endif
  ret
getc endp

;prints mouse cursor
align 4
_print proc private
  local tx:word,ty:word
  
  .if curmode==G_TEXT
    mov eax,_v_x
    xor ebx,ebx
    mov bx,my
    shr bx,3  ;/8
    shl bx,1  ;*2
    mul ebx
    mov bx,mx
    shr bx,3  ;/8 *2
    shl bx,1  ;*2
    add eax,ebx
    add eax,_v_linear  ;0b8000h!
    inc eax
    mov bl,cursor_color
    xor [eax],bl
    ret
  .endif

  mov ax,mx
  sub ax,hpx
  mov tx,ax
  mov ax,my
  sub ax,hpy
  mov ty,ax

  .if m_paws
    mov m_pp,v_pp2
    mov m_gp,v_gp2
    callp getc,tx,curx,ty,cury
    callp put0c,tx,curx,ty,cury
    mov m_pp,v_pp
    mov m_gp,v_gp
    callp put0c,tx,curx,ty,cury
  .else
    callp getc,tx,curx,ty,cury
    callp put0c,tx,curx,ty,cury
  .endif

  ret
_print endp

align 4
erase proc private,one:byte
  local tx:word,ty:word

  .if curmode==G_TEXT
    mov eax,_v_x
    xor ebx,ebx
    mov bx,my
    shr bx,3
    shl bx,1
    mul ebx
    mov bx,mx
    shr bx,3
    shl bx,1
    add eax,ebx
    add eax,_v_linear  ;0b8000h!
    inc eax
    mov bl,cursor_color
    xor [eax],bl
    ret
  .endif

  mov ax,mx
  sub ax,hpx
  mov tx,ax
  mov ax,my
  sub ax,hpy
  mov ty,ax

  .if m_paws
    mov m_pp,v_pp2
    mov m_gp,v_gp2
    callp putc,tx,curx,ty,cury
    mov m_pp,v_pp
    mov m_gp,v_gp
    .if !one
      callp putc,tx,curx,ty,cury
    .endif
  .else
    callp putc,tx,curx,ty,cury
  .endif
  ret
erase endp

align 4
mouse_setpos proc,x:dword,y:dword
  pushad
  push mouse_con
  call mouse_off
  mov eax,x
  mov cl,xratio
  sal eax,cl   ;signed shift left
  mov xpos,ax
  mov eax,y
  mov cl,yratio
  sal eax,cl
  mov ypos,ax
  call update
  pop ax
  .if ax==1
    call mouse_on
  .endif
  popad
  ret
mouse_setpos endp

align 4
mouse_hand proc private
  ;entry : ax=mask w/ condtion that cause this call
  ;      : bx=buttons (bit 0=left,bit 1=right)
  ;      : cx=horz pos  (I ignore)
  ;      : dx=vert pos  (")
  ;      : si=horz counts (mickeys)
  ;      : di=vert " (")
  pushad
  pushf
  push ds
  push es
  push fs
  push gs
  mov ds,cs:seldata
  mov es,seldata
  mov fs,seldata
  mov gs,selzero
  cld
  ; mov mask1,ax
  ; FIX : Ver 1.1 : Ignore ax cause Win95 does not return it properly!!!
  ;       instead use BX which is easier anyways!
  mov but,bx

  .if once
    mov ax,prevx
    mov prevx,si
    sub si,ax
    mov ax,prevy
    mov prevy,di
    sub di,ax
  .else
    mov prevx,si
    mov prevy,di
    xor si,si
    xor di,di
    mov once,1
  .endif

  test mouse_con,1
  jz @f
  .if (si!=0) || (di!=0)
    pushad
    callp erase,0
    popad
    mov moved,1 ;it moved!
  .else
@@:
    mov moved,0
  .endif

;change X/Y values
  add xpos,si
  add ypos,di
  call update

done:
  .if userproc
    mov edi,userproc_s
    mov ax,mx
    mov [edi].mouse_user.x,ax
    mov ax,my
    mov [edi].mouse_user.y,ax
    mov ax,but
    mov [edi].mouse_user.but,ax
    call [userproc]  ;this is good for changing the cursor shape for new areas
  .endif
  .if (mouse_con)&&(moved)
    call _print
  .endif 
exit_hand:
  pop gs
  pop fs
  pop es
  pop ds
  popf
  popad
  retf
mouse_hand endp

align 4
mouse_setspd proc,xr:byte,yr:byte  ; (1-4 , 1-4)
  local x:word,y:word
  pushad
  push mouse_con
  call mouse_off

  mov ax,xpos
  mov cl,xratio
  sar ax,cl   ;signed shift
  mov x,ax
  mov ax,ypos
  mov cl,yratio
  sar ax,cl
  mov y,ax

  mov al,xr
  dec al
  and al,3
  ;al=0-3
  mov xratio,al

  mov al,yr
  dec al
  and al,3
  ;al=0-3
  mov yratio,al

  mov ax,x
  mov cl,xratio
  sal ax,cl   ;signed shift
  mov xpos,ax
  mov ax,y
  mov cl,yratio
  sal ax,cl
  mov ypos,ax

  call update
  pop ax
  .if ax==1
    call mouse_on
  .endif

  popad
  ret
mouse_setspd endp

align 4
mouse_setwin proc,x1:dword,y1:dword,x2:dword,y2:dword
  pushad
  push mouse_con
  call mouse_off
;x
  mov eax,x1
  mov minx,ax
  mov eax,x2
  mov maxx,ax
;y
  mov eax,y1
  mov miny,ax
  mov eax,y2
  mov maxy,ax

  call update
  pop ax
  .if ax==1
    call mouse_on
  .endif
  popad
  ret
mouse_setwin endp

;update x/y with maxx/maxy and ratios
align 4
update proc private
  mov ax,xpos
  mov cl,xratio
  sar ax,cl
  cmp ax,minx
  jge @f
  mov ax,minx
  sal ax,cl
  mov xpos,ax
  sar ax,cl
@@:
  cmp ax,maxx
  jle @f
  mov ax,maxx
  sal ax,cl
  mov xpos,ax
  sar ax,cl
@@:
  mov mx,ax

  mov ax,ypos
  mov cl,yratio
  sar ax,cl
  cmp ax,miny
  jge @f
  mov ax,miny
  sal ax,cl
  mov ypos,ax
  sar ax,cl
@@:
  cmp ax,maxy
  jle @f
  mov ax,maxy
  sal ax,cl
  mov ypos,ax
  sar ax,cl
@@:
  mov my,ax

  ret
update endp

align 4
mouse_on proc
  .if !curx || !cury      ;mouse ptr not setup???
    mov eax,ERROR
    ret
  .endif
  pushad
  cli
  .if mouse_con==0
    .if userproc
      mov edi,userproc_s
      mov ax,mx
      mov [edi].mouse_user.x,ax
      mov ax,my
      mov [edi].mouse_user.y,ax
      mov ax,but
      mov [edi].mouse_user.but,ax
      call [userproc]
    .endif
    call _print
    mov mouse_con,1
  .endif
  sti
  popad
  ret
mouse_on endp

align 4
mouse_off proc
  pushad
  cli
  .if mouse_con==1
    mov mouse_con,0
    callp erase,0
  .endif
  sti
  popad
  ret
mouse_off endp

align 4
mouse_setuser proc,func:dword,us:dword
  push eax
  .if (func==NULLPROC) || (func==0)
    mov userproc,0
  .else
    mov eax,func    ;may be 0
    mov userproc,eax
    mov eax,us
    mov userproc_s,eax
  .endif
  pop eax
  xor eax,eax
  ret
mouse_setuser endp

align 4
v_pp proc private,x:dword,y:dword,col:dword
  pushad
  mov edi,_v_linear
  mov eax,y
  mul _v_bpsl
  mov ebx,eax
  mov eax,x
  mul _v_bypp
  add eax,ebx

  .if curmode==G_MODEX
    mov dx,vga_SC_INDEX 
    mov ebx,eax
    and eax,3
    mov cl,al
    inc ah ;ah=1
    shl ah,cl
    mov al,02h
    mov modex_plane_wr,ah
    out dx,ax     ; enable writes to plane ah
    shr ebx,2
    add edi,ebx
    mov al,bptr[col]
    mov [edi],al
    popad
    ret
  .endif

  .if curmode==G_VESA && banking
    xor edx,edx
    mov ebx,64*1024
    div ebx   ;OPT! : this div could be replaced w/ faster code
    ; al=bank # required
    ; edx=offset required (remainder)
    .if al!=vesa_current_bank
      callp vesa_setbank,al
    .endif
    add edi,edx
  .else
    add edi,eax
  .endif

  .if _v_bypp==1
    mov al,bptr[col]
    stosb
    popad
    ret
  .endif
  .if _v_bypp==2
    mov ax,wptr[col]
    stosw
    popad
    ret
  .endif
  .if _v_bypp==3
    mov al,bptr[col]
    stosb
    mov al,bptr[col+1]
    stosb
    mov al,bptr[col+2]
    stosb
    popad
    ret
  .endif
  ;_v_bypp=4
  mov eax,col
  stosd
  popad
  ret     ;FIX v 2.00: This was not here before!
v_pp endp

align 4
v_gp proc private,x:dword,y:dword
  pushad
  mov esi,_v_linear
  mov eax,y
  mul _v_bpsl
  mov ebx,eax
  mov eax,x
  mul _v_bypp
  add eax,ebx

  .if curmode==G_MODEX
    mov dx,vga_GC_INDEX 
    mov ebx,eax
    and eax,3
    mov ah,al
    mov al,4
    mov modex_plane_rd,ah
    out dx,ax     ; enable reads to plane ah
    shr ebx,2
    add esi,ebx
    xor eax,eax
    mov al,[esi]
    mov [esp+7*4],eax
    popad
    ret
  .endif

  .if curmode==G_VESA && banking
    xor edx,edx
    mov ebx,64*1024
    div ebx
    ; al=bank # required
    ; edx=offset required (remainder)
    .if al!=vesa_current_bank
      callp vesa_setbank,al
    .endif
    add esi,edx
  .else
    add esi,eax
  .endif

  .if _v_bypp==1
    lodsb
    mov [esp+7*4],eax
    popad
    ret
  .endif
  .if _v_bypp==2
    lodsw
    mov [esp+7*4],eax
    popad
    ret
  .endif
  .if _v_bypp==3
    xor eax,eax 
    lodsw
    shl eax,8
    lodsb
    mov [esp+7*4],eax
    popad
    ret
  .endif
  ;_v_bypp=4
  lodsd
  mov [esp+7*4],eax
  popad
  ret
v_gp endp

;The next 2 use _v_buffer instead of using _v_linear
align 4
v_pp2 proc private,x:dword,y:dword,col:dword
  pushad
  mov edi,_v_buffer
  mov eax,y
  mul _v_xb
  add edi,eax
  mov eax,x
  mul _v_bypp
  add edi,eax

  .if _v_bypp==1
    mov al,bptr[col]
    stosb
    popad
    ret
  .endif
  .if _v_bypp==2
    mov ax,wptr[col]
    stosw
    popad
    ret
  .endif
  .if _v_bypp==3
    mov al,bptr[col]
    stosb
    mov al,bptr[col+1]
    stosb
    mov al,bptr[col+2]
    stosb
    popad
    ret
  .endif
  ;_v_bypp=4
  mov eax,[col]
  stosd
  popad
  ret
v_pp2 endp

align 4
v_gp2 proc private,x:dword,y:dword
  pushad
  mov esi,_v_buffer
  mov eax,y
  mul _v_xb
  add esi,eax
  mov eax,x
  mul _v_bypp
  add esi,eax
  .if _v_bypp==1
    lodsb
    mov [esp+7*4],eax
    popad
    ret
  .endif
  .if _v_bypp==2
    lodsw
    mov [esp+7*4],eax
    popad
    ret
  .endif
  .if _v_bypp==3
    lodsw
    shl eax,8
    lodsb
    mov [esp+7*4],eax
    popad           
    ret       
  .endif
  ;_v_bypp=4
  lodsd
  mov [esp+7*4],eax
  popad           
  ret       
v_gp2 endp

.data
ptrtmp db (sizeof g_mousehead) dup (?)

.code
align 4
mouse_loadcursor proc,nam:dword
  local h:word
  local buf:dword
  mov buf,0
  pushad
  callp open,nam,0
  cmp eax,ERROR
  jnz @f
  popad
  mov eax,ERROR
  ret
@@:
  mov h,ax
  callp read,ax,offset ptrtmp,sizeof g_mousehead
  .if eax!=sizeof g_mousehead
bad:
    callp free,buf
    callp close,h
    popad
    mov eax,ERROR
    ret
  .endif
  mov esi,offset ptrtmp
  cmp dptr[esi],01a525450h  ;PTR,27
  jnz bad
  movzx eax,[esi].g_mousehead.x
  movzx ebx,[esi].g_mousehead.y
  movzx ecx,[esi].g_mousehead.bypp
  mov dl,[esi].g_mousehead.flg

  .if dl
    jmp bad
  .endif

  mul ebx
  mul ecx
  mov ebx,eax
  add eax,sizeof g_mousehead
  callp malloc,eax
  .if eax==ERROR
    jmp bad
  .endif
  mov buf,eax
  mov esi,offset ptrtmp
  mov edi,eax
  mov ecx,sizeof g_mousehead
  rep movsb

  mov ecx,eax
  add ecx,sizeof g_mousehead
  callp read,h,ecx,ebx
  cmp eax,ebx
  jnz bad
  callp close,h
  popad
  mov eax,buf
  ret
mouse_loadcursor endp

[ RETURN TO DIRECTORY ]