;		 R A T P A I N T
;			  by Nathan Hull
;
;	  This program demonstrates the use of the standard Microsoft
;	  mouse driver, but assumes a three button mouse.  The program is
;	  written in medium resolution graphics (320 x 200 - 4 colours).
;	  Before the program is run, the mouse driver (INT 33H) must be
;	  installed.  Pushing the left button down will allow the user to
;	  draw with the mouse cursor.  Pushing the middle button down will
;	  cycle the pen color through the range 0 - 3.	Pushing the right
;	  button will cycle the palette and the background color.  The program
;	  terminates when any key is pressed.

	  .model small

;-------------------------------------------------------------------------------
	  .code
start:	  mov	 ax,@data
	  mov	 ds,ax		       ;set DS to data segment
	  mov	 es,ax		       ;set ES to data segment also

	  call	 set_med_res	       ;set medium resolution graphics
	  call	 mouse_reset
	  mov	 dx, offset pen_01_cursor    ; start with pen 01
	  call	 set_mouse_cursor      ;set to circle1 mouse cursor
	  call	 mouse_show	       ;display mouse cursor
	  call	 mouse_speed
	  call	 mouse_read	       ;extra position to init mouse_x, _y

;///////////////////////////////////////////////////////////////////////////////
;	     This is the main loop of the program
;///////////////////////////////////////////////////////////////////////////////

draw_lp:  call	 mouse_read	       ;find mouse position, put in mouse_x, _y
;					and check for button presses

	  cmp	 right_pressed, 0      ;jump if right is up
	  je	 colour_check
	  call	 colour_cycle
	  mov	 right_pressed, 0

colour_check:
	  cmp	 mid_pressed, 0        ;jump if middle is up
	  je	 pen_check
	  call	 pen_cycle
	  mov	 mid_pressed, 0

pen_check:
	  cmp	 left_pressed, 0       ;jump if pen (left button) is up
	  je	 end_check
	  call	 mouse_paint
	  mov	 left_pressed, 0

end_check:
	  mov	 ah, 0bh	       ;dos function to see if key present
	  int	 21h
	  cmp	 al, 0ffh	       ;=0ffh if key is ready
	  jne	 draw_lp	       ;jump if no key

          mov    ah, 07h               ;get character so it doesn't display
	  int	 21h

	  call	 color_text_80	       ;go back to text mode
	  mov	 ax,4c00h
          int    21h                   ;i'm outa here

	  page
;///////////////////////////////////////////////////////////////////////////////
;	  Mouse_speed increases the distance the mouse must travel
;	  on the pad in relation to the screen distance.  In this program
;	  the effect is to have more pixels drawn on the screen when the
;	  mouse is moved.
;///////////////////////////////////////////////////////////////////////////////
mouse_speed proc
	  push	 ax
	  push	 cx
	  push	 dx
	  mov	 ax, 15 	       ;set speed threshold
	  mov	 cx, 24 	       ;24 per 8 pixels
	  mov	 dx, 48 	       ;48 steps per 8 pixels
	  int	 33h
	  pop	 dx
	  pop	 cx
	  pop	 ax
	  ret
mouse_speed endp

;///////////////////////////////////////////////////////////////////////////////
;	  mouse_show displays the current mouse cursor
;///////////////////////////////////////////////////////////////////////////////
mouse_show proc
	  push	 ax
	  mov	 ax, 1
	  int	 33h
	  pop	 ax
	  ret
mouse_show endp

;///////////////////////////////////////////////////////////////////////////////
;	  mouse_hide turns off the mouse cursor display.  This is necessary when
;	  writing pixels under the cursor.  If the cursor is not turned off,
;	  memory contention problems may result.
;///////////////////////////////////////////////////////////////////////////////
mouse_hide proc
	  push	 ax
	  mov	 ax, 2
	  int	 33h
	  pop	 ax
	  ret
mouse_hide endp

	  page
;///////////////////////////////////////////////////////////////////////////////
;	  mouse_read gets the x and y positions of the mouse and puts them
;	  into mouse_x and mouse_y.  The x position is adjusted for med res
;	  graphics.  The routine also sets the variables for left, med, and
;	  right_pressed, and sets med and right_release variables.  These
;	  last two variables are necessary to insure that holding down one
;	  of these buttons does not result in a continuing cycle of colours.
;///////////////////////////////////////////////////////////////////////////////
mouse_read proc
	  push	 ax
	  push	 bx
	  push	 cx
	  push	 dx

	  mov	 ax, 3		       ;get mouse pos and status
	  int	 33h

	  shr	 bx, 1		       ;is the left button pressed?
	  jnc	 check_right
	  mov	 left_pressed, 1       ;set left flag if so
check_right:
	  shr	 bx, 1		       ;is the right button pressed?
	  jnc	 right_up	       ;   jump if not pressed
	  cmp	 right_release, 1      ;has the right button been released
	  jne	 check_mid	       ;  since it was last pressed?
	  mov	 right_pressed, 1      ;set right flag if newly pressed
	  mov	 right_release, 0      ;  and put right release to false
	  jmp	 check_mid
right_up: mov	 right_release, 1      ;set release to true
check_mid:
	  shr	 bx, 1		       ;is the middle button pressed?
	  jnc	 mid_up 	       ;   jump if not pressed
	  cmp	 mid_release, 1        ;has the mid button been released
	  jne	 check_x_y	       ;  since it was last pressed?
	  mov	 mid_pressed, 1        ;set mid flag if newly pressed
	  mov	 mid_release, 0        ;  and put mid release to false
	  jmp	 check_x_y
mid_up:   mov	 mid_release, 1        ;set release to true

check_x_y:
	  shr	 cx, 1
	  mov	 mouse_change, 0       ;assume no x or y change
	  cmp	 mouse_x, cx	       ;has the x coordinate changed?
	  je	 y_change
	  mov	 mouse_x, cx	       ;record x position
	  mov	 mouse_change, 1       ;set change flag
y_change: cmp	 mouse_y, dx	       ;has the y coordinate changed?
	  je	 pos_done
	  mov	 mouse_y, dx	       ;record y position
	  mov	 mouse_change, 1
pos_done:
	  pop	 dx
	  pop	 cx
	  pop	 bx
	  pop	 ax
	  ret
mouse_read endp

	  page
;///////////////////////////////////////////////////////////////////////////////
;	  pen_cycle not only cycles through the four display colours (including
;	  the background), but sets up the call for installing the new mouse
;	  cursor for the new pen.
;///////////////////////////////////////////////////////////////////////////////
pen_cycle proc
	  inc	 pen_colour
	  and	 pen_colour, 011B
	  cmp	 pen_colour, 11b
	  jne	 pen_2
	  mov	 dx, offset pen_11_cursor
	  jmp	 cursor_call
pen_2:	  cmp	 pen_colour, 10b
	  jne	 pen_1
	  mov	 dx, offset pen_10_cursor
	  jmp	 cursor_call
pen_1: cmp	 pen_colour, 01b
	  jne	 pen_0
	  mov	 dx, offset pen_01_cursor
	  jmp	 cursor_call
pen_0:	  mov	 dx, offset pen_00_cursor
cursor_call:
	  call	 set_mouse_cursor
	  ret
pen_cycle endp

;///////////////////////////////////////////////////////////////////////////////
;	  mouse_reset installs the mouse for the program and checks
;	  for its presence.
;///////////////////////////////////////////////////////////////////////////////
mouse_reset proc
	  push	 ax
	  mov	 ax, 0
	  int	 33h
	  cmp	 ax, 0		       ;is mouse installed?
	  jne	 reset_done
	  call	 color_text_80
	  mov	 dx, offset install_error
	  mov	 ah, 09h	       ;print install error message
	  int	 21h
	  mov	 ax,4c00h
	  int	 21h		       ;abort program
reset_done:
	  pop	 ax
	  ret
mouse_reset endp

	  page
;///////////////////////////////////////////////////////////////////////////////
;	  set_mouse_cursor installs a mouse cursor and defines the hot spot.
;	  The cursor is defined as a 16 x 16 bit array.  In med res, two bits
;	  define each pixel.  Thus, the pixel array is actually 8 x 16.
;	  Input: DX set to the address of the mouse graphics cursor
;///////////////////////////////////////////////////////////////////////////////
HOT_SPOT_X EQU 7		       ;center of hot spot for the mouse
HOT_SPOT_Y EQU 7		       ; cursor.  The cursor is 16 x 16

set_mouse_cursor proc
   PUSH BX
   PUSH CX

   MOV AX, 9
   MOV BX, HOT_SPOT_X
   MOV CX, HOT_SPOT_Y
   INT 33H

   POP CX
   POP BX
   RET
set_mouse_cursor endp


;///////////////////////////////////////////////////////////////////////////////
;	  set_med_res puts the screen in 320 x 200 4 colour mode
;///////////////////////////////////////////////////////////////////////////////
set_med_res proc
	  push	 ax
	  mov	 ah, 0		       ;set video mode subfunction
	  mov	 al, 4		       ;medium resolution graphics
	  int	 10h
	  pop	 ax
	  ret
set_med_res endp

;///////////////////////////////////////////////////////////////////////////////
;	  colour_cycle changes the screen by incrementing first the palette
;	  and then the background colour.  Thus, there are 32 combinations.
;///////////////////////////////////////////////////////////////////////////////
colour_cycle proc
	  push	 ax
	  push	 bx
	  inc	 palette
	  cmp	 palette, 2	       ;have we done both palettes?
	  jb	 palette_change
	  mov	 palette, 0	       ;if so, change palette and background
	  inc	 background
	  and	 background, 01111b
	  mov	 ah, 0bh	       ;change background
	  mov	 bh, 0
	  mov	 bl, background
	  int	 10h
palette_change:
	  mov	 ah, 0bh	       ;change palette
	  mov	 bh, 1
	  mov	 bl, palette
	  int	 10h
	  pop	 bx
	  pop	 ax
	  ret
colour_cycle endp

	  page
;///////////////////////////////////////////////////////////////////////////////
;	  mouse_paint paints a single pixel on the screen at mouse_x, mouse_y
;	  using pen_colour as the colour of the pixel.	Note that it turns off
;	  the mouse cursor momentarily to do so.  Also note that it only draws
;	  a pixel if the mouse has actually changed position.
;///////////////////////////////////////////////////////////////////////////////
mouse_paint proc
	  push	 ax
	  push	 bx
	  push	 cx
	  push	 dx
	  cmp	 mouse_change, 0
	  je	 paint_done
	  call	 mouse_hide

	  mov	 al, pen_colour
	  mov	 cx, mouse_x
	  mov	 dx, mouse_y
	  mov	 ah, 0ch	       ;write-a-dot bios call
	  int	 10h

	  call	 mouse_show
	  mov	 mouse_change, 0       ;reset mouse change flag
paint_done:
	  pop	 dx
	  pop	 cx
	  pop	 bx
	  pop	 ax
	  ret
mouse_paint endp

;///////////////////////////////////////////////////////////////////////////////
;	  color_text_80 returns the screen to 80 x 25 colour text mode
;///////////////////////////////////////////////////////////////////////////////
color_text_80 proc
	  push	 ax
	  mov	 ah, 0		       ;set video mode
	  mov	 al, 3
	  int	 10h
	  pop	 ax
	  ret
color_text_80 endp
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
	  .data
install_error    db 'The mouse driver has not been installed - '
                 db 'Program aborted.$'

mouse_x       dw ?		       ;current x coordinate of mouse cursor
mouse_y       dw ?		       ;current y coordinate of mouse cursor

mouse_change  db 0		       ;tells if mouse_x, or _y has changed

pen_colour    db 01b		       ;start colour with pen 1 (green/cyan)
palette       db  1b		       ;start with palette 1 (cyan/mag/white)
background    db 00b		       ;start with background black

left_pressed  db 0		       ;start with left not pressed
mid_pressed   db 0		       ;start with middle not pressed
right_pressed db 0		       ;start with right not pressed

mid_release   db 1		       ;has mid been released ? (1 = yes)
right_release db 1		       ;has right been released ? (1 = yes)

; The following arrays defined four different mouse cursors, one for each
; of the four pen colours.  Each one consists for a sixteen word screen mask
; followed by a sixteen word cursor mask.  The screen mask is ANDed with the
;screen contents.  The cursor mask is then XORed with the result.  The
;operational behavior of these bit arrays are summarized by the following
;table:
;	  screen mask	      cursor mask	  resulting bit(s)
;
;	       0		 0		       0
;	       0		 1		       1
;	       1		 0		       unchanged
;	       1		 1		       inverted
;
;Note that in the table actually works two bits at a time in 320 x 200
;graphics mode.  Thus, 00 is pen 0, 01 is pen 1, 10 is pen 2, and 11 is pen 3.

	  page
pen_00_cursor	 dw 1111111111111111b  ;screen mask
		 dw 1111111111111111b
		 dw 0001111111111000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1000001111000001b
		 dw 0000111111110000b
		 dw 0011111111111100b
		 dw 0000111111110000b
		 dw 1000001111000001b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0001111111111000b
		 dw 1111111111111111b
		 dw 1111111111111111b
		 dw 1111111111111111b

		 dw 0000000000000000b  ;cursor mask
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0100000000000001b
		 dw 0001001010000100b
		 dw 0000110000110000b
		 dw 0011000000001100b
		 dw 0100000000000010b
		 dw 0011000000001100b
		 dw 0000110000110000b
		 dw 0010000101001000b
		 dw 1000000000000010b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b

		 page
pen_01_cursor	 dw 1111111111111111b  ;screen mask
		 dw 1111111111111111b
		 dw 0001111111111000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1000001111000001b
		 dw 0000111111110000b
		 dw 0011111111111100b
		 dw 0000111111110000b
		 dw 1000001111000001b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0001111111111000b
		 dw 1111111111111111b
		 dw 1111111111111111b
		 dw 1111111111111111b

		 dw 0000000000000000b  ;cursor mask
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0100000000000001b
		 dw 0001000101000100b
		 dw 0000010000010000b
		 dw 0001000000000100b
		 dw 0100000000000001b
		 dw 0001000000000100b
		 dw 0000010000010000b
		 dw 0001000101000100b
		 dw 0100000000000001b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
	  page
pen_10_cursor	 dw 1111111111111111b  ;screen mask
		 dw 1111111111111111b
		 dw 0001111111111000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1000001111000001b
		 dw 0000111111110000b
		 dw 0011111111111100b
		 dw 0000111111110000b
		 dw 1000001111000001b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0001111111111000b
		 dw 1111111111111111b
		 dw 1111111111111111b
		 dw 1111111111111111b

		 dw 0000000000000000b  ;cursor mask
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1000000000000010b
		 dw 0010001010001000b
		 dw 0000100000100000b
		 dw 0010000000001000b
		 dw 1000000000000010b
		 dw 0010000000001000b
		 dw 0000100000100000b
		 dw 0010001010001000b
		 dw 1000000000000010b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b

	  page
pen_11_cursor	 dw 1111111111111111b  ;screen mask
		 dw 1111111111111111b
		 dw 0001111111111000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1000001111000001b
		 dw 0000111111110000b
		 dw 0011111111111100b
		 dw 0000111111110000b
		 dw 1000001111000001b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0001111111111000b
		 dw 1111111111111111b
		 dw 1111111111111111b
		 dw 1111111111111111b

		 dw 0000000000000000b  ;cursor mask
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 1100000000000011b
		 dw 0011001111001100b
		 dw 0000110000110000b
		 dw 0011000000001100b
		 dw 1100000000000011b
		 dw 0011000000001100b
		 dw 0000110000110000b
		 dw 0011001111001100b
		 dw 1100000000000011b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
		 dw 0000000000000000b
	  page
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
	  .stack 100h
;-------------------------------------------------------------------------------

	  end	 start