]> git.sur5r.net Git - cc65/commitdiff
Added Koala Pad mouse drivers written by Stefan Haubenthal.
authoruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Tue, 5 Aug 2008 20:54:35 +0000 (20:54 +0000)
committeruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Tue, 5 Aug 2008 20:54:35 +0000 (20:54 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@3877 b7a2c559-68d2-44c3-8de9-860c34a00d81

doc/c128.sgml
doc/c64.sgml
libsrc/Makefile
libsrc/c128/Makefile
libsrc/c128/c128-potmouse.s [new file with mode: 0644]
libsrc/c64/Makefile
libsrc/c64/c64-potmouse.s [new file with mode: 0644]

index 37c428d12b4cd8567e51b46878315b7b9c3d0cca..174ffdcc060b3ccb950d4949f4c28dae11b252e5 100644 (file)
@@ -57,7 +57,7 @@ Special locations:
 <descrip>
   <tag/Text screen/
   The text screen is located at &dollar;400 (as in the standard setup).
-                                                                 
+
   <tag/Stack/
   The C runtime stack is located at &dollar;CFFF and growing downwards.
 
@@ -235,8 +235,20 @@ missing on VDC and are translated to the two colors missing from VIC palette.
 
 <sect1>Mouse drivers<p>
 
-Currently no drivers available (in fact, the API for loadable mouse drivers
-does not exist).
+<descrip>
+
+  <tag><tt/c128-1351.mou/</tag>
+  Supports a standard mouse connected to port #0 of the C128.
+
+  <tag><tt/c128-joy.mou/</tag>
+  Supports a mouse emulated by a standard joystick e.g. 1350 mouse in port
+  #1 of the C128.
+
+  <tag><tt/c128-pot.mou/</tag>
+  Supports a potentiometer device e.g. Koala Pad connected to port #1 of
+  the C128.
+
+</descrip><p>
 
 
 <sect1>RS232 device drivers<p>
index c058f73f3d98ec1fd1477513d3a317cc235f7df2..370cb4422cc5afc124581e5e136c9160a6b32c66 100644 (file)
@@ -233,7 +233,12 @@ configuration.
   Supports a standard mouse connected to port #0 of the C64.
 
   <tag><tt/c64-joy.mou/</tag>
-  Supports a mouse emulated by a standard joystick in port #0 of the C64.
+  Supports a mouse emulated by a standard joystick e.g. 1350 mouse in port
+  #1 of the C64.
+
+  <tag><tt/c64-pot.mou/</tag>
+  Supports a potentiometer device e.g. Koala Pad connected to port #1 of
+  the C64.
 
 </descrip><p>
 
index 8fb21da0ee5b8e4c9c35b384007902dd94bb582a..f134e604053cd9290b4e55298cd3b418e6f397b6 100644 (file)
@@ -117,6 +117,7 @@ c64lib:
        cp c64/*.joy .
        cp c64/c64-1351.mou .
        cp c64/c64-joymouse.mou c64-joy.mou
+       cp c64/c64-potmouse.mou c64-pot.mou
        cp c64/*.ser .
        cp c64/c64-320-200-2.tgi c64-hi.tgi
 
@@ -133,6 +134,7 @@ c128lib:
        cp c128/*.joy .
        cp c128/c128-1351.mou .
        cp c128/c128-joymouse.mou c128-joy.mou
+       cp c128/c128-potmouse.mou c128-pot.mou
        cp c128/*.ser .
        cp c128/c128-640-200-2.tgi c128-vdc.tgi
        cp c128/c128-640-480-2.tgi c128-vdc2.tgi
index 46f229c0c085607ad2d6f2c35e1811596d9386c4..87df0f76e03d3e075a27f664e117b0dbd6fe4197 100644 (file)
@@ -74,7 +74,7 @@ EMDS = c128-georam.emd c128-ram.emd c128-ramcart.emd c128-reu.emd c128-vdc.emd
 
 JOYS = c128-ptvjoy.joy c128-stdjoy.joy
 
-MOUS = c128-1351.mou c128-joymouse.mou
+MOUS = c128-1351.mou c128-joymouse.mou c128-potmouse.mou
 
 SERS = c128-swlink.ser
 
diff --git a/libsrc/c128/c128-potmouse.s b/libsrc/c128/c128-potmouse.s
new file mode 100644 (file)
index 0000000..fdb4576
--- /dev/null
@@ -0,0 +1,371 @@
+;
+; Driver for a potentiometer "mouse" e.g. Koala Pad
+;
+; Ullrich von Bassewitz, 2004-03-29
+; Stefan Haubenthal, 2006-08-20
+;
+
+       .include        "zeropage.inc"
+       .include        "mouse-kernel.inc"
+       .include        "c128.inc"
+
+       .macpack        generic
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+.segment       "JUMPTABLE"
+
+HEADER:
+
+; Driver signature
+
+       .byte   $6d, $6f, $75           ; "mou"
+       .byte   MOUSE_API_VERSION       ; Mouse driver API version number
+
+; Jump table.
+
+       .addr   INSTALL
+       .addr   UNINSTALL
+       .addr   HIDE
+       .addr   SHOW
+       .addr   BOX
+       .addr   MOVE
+       .addr   BUTTONS
+       .addr   POS
+       .addr   INFO
+       .addr   IOCTL
+       .addr   IRQ
+
+; Callback table, set by the kernel before INSTALL is called
+
+CHIDE: jmp     $0000                   ; Hide the cursor
+CSHOW: jmp     $0000                   ; Show the cursor
+CMOVEX: jmp    $0000                   ; Move the cursor to X coord
+CMOVEY: jmp    $0000                   ; Move the cursor to Y coord
+
+
+;----------------------------------------------------------------------------
+; Constants
+
+SCREEN_HEIGHT  = 200
+SCREEN_WIDTH   = 320
+
+.enum  JOY
+       UP      = $01
+       DOWN    = $02
+       LEFT    = $04
+       RIGHT   = $08
+       FIRE    = $10
+.endenum
+
+;----------------------------------------------------------------------------
+; Global variables. The bounding box values are sorted so that they can be
+; written with the least effort in the BOX routine, so don't reorder them.
+
+.bss
+
+Vars:
+YPos:          .res    2               ; Current mouse position, Y
+XPos:          .res    2               ; Current mouse position, X
+YMax:          .res    2               ; Y2 value of bounding box
+XMax:          .res    2               ; X2 value of bounding box
+YMin:          .res    2               ; Y1 value of bounding box
+XMin:          .res    2               ; X1 value of bounding box
+Buttons:       .res    1               ; Button mask
+
+; Temporary value used in the int handler
+
+Temp:          .res    1
+
+; Default values for above variables
+
+.rodata
+
+.proc  DefVars
+       .word   SCREEN_HEIGHT/2         ; YPos
+       .word   SCREEN_WIDTH/2          ; XPos
+       .word   SCREEN_HEIGHT           ; YMax
+       .word   SCREEN_WIDTH            ; XMax
+       .word   0                       ; YMin
+       .word   0                       ; XMin
+       .byte   0                       ; Buttons
+.endproc
+
+.code
+
+;----------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. If
+; possible, check if the hardware is present.
+; Must return an MOUSE_ERR_xx code in a/x.
+
+INSTALL:
+
+; Initialize variables. Just copy the default stuff over
+
+       ldx     #.sizeof(DefVars)-1
+@L1:   lda     DefVars,x
+       sta     Vars,x
+       dex
+       bpl     @L1
+
+; Be sure the mouse cursor is invisible and at the default location. We
+; need to do that here, because our mouse interrupt handler doesn't set the
+; mouse position if it hasn't changed.
+
+       sei
+       jsr     CHIDE
+       lda     XPos
+       ldx     XPos+1
+       jsr     CMOVEX
+       lda     YPos
+       ldx     YPos+1
+       jsr     CMOVEY
+       cli
+
+; Done, return zero (= MOUSE_ERR_OK)
+
+       ldx     #$00
+       txa
+       rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; No return code required (the driver is removed from memory on return).
+
+UNINSTALL      = HIDE                  ; Hide cursor on exit
+
+;----------------------------------------------------------------------------
+; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently visible and should get hidden. For most drivers,
+; no special action is required besides hiding the mouse cursor.
+; No return code required.
+
+HIDE:  sei
+       jsr     CHIDE
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently hidden and should become visible. For most drivers,
+; no special action is required besides enabling the mouse cursor.
+; No return code required.
+
+SHOW:  sei
+       jsr     CSHOW
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; BOX: Set the mouse bounding box. The parameters are passed as they come from
+; the C program, that is, maxy in a/x and the other parameters on the stack.
+; The C wrapper will remove the parameters from the stack when the driver
+; routine returns.
+; No checks are done if the mouse is currently inside the box, this is the job
+; of the caller. It is not necessary to validate the parameters, trust the
+; caller and save some code here. No return code required.
+
+BOX:   ldy     #5
+       sei
+       sta     YMax
+       stx     YMax+1
+
+@L1:   lda     (sp),y
+       sta     XMax,y
+       dey
+       bpl     @L1
+
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; MOVE: Move the mouse to a new position. The position is passed as it comes
+; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
+; remove the parameter from the stack on return.
+; No checks are done if the new position is valid (within the bounding box or
+; the screen). No return code required.
+;
+
+MOVE:  sei                             ; No interrupts
+
+       sta     YPos
+       stx     YPos+1                  ; New Y position
+       jsr     CMOVEY                  ; Set it
+
+       ldy     #$01
+       lda     (sp),y
+       sta     XPos+1
+       tax
+       dey
+       lda     (sp),y
+       sta     XPos                    ; New X position
+
+       jsr     CMOVEX                  ; Move the cursor
+
+       cli                             ; Allow interrupts
+       rts
+
+;----------------------------------------------------------------------------
+; BUTTONS: Return the button mask in a/x.
+
+BUTTONS:
+       lda     Buttons
+       ldx     #$00
+       rts
+
+;----------------------------------------------------------------------------
+; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
+; No return code required.
+
+POS:   ldy     #MOUSE_POS::XCOORD      ; Structure offset
+
+       sei                             ; Disable interrupts
+       lda     XPos                    ; Transfer the position
+       sta     (ptr1),y
+       lda     XPos+1
+       iny
+       sta     (ptr1),y
+       lda     YPos
+       iny
+       sta     (ptr1),y
+       lda     YPos+1
+       cli                             ; Enable interrupts
+
+       iny
+       sta     (ptr1),y                ; Store last byte
+
+       rts                             ; Done
+
+;----------------------------------------------------------------------------
+; INFO: Returns mouse position and current button mask in the MOUSE_INFO
+; struct pointed to by ptr1. No return code required.
+;
+; We're cheating here to keep the code smaller: The first fields of the
+; mouse_info struct are identical to the mouse_pos struct, so we will just
+; call _mouse_pos to initialize the struct pointer and fill the position
+; fields.
+
+INFO:  jsr     POS
+
+; Fill in the button state
+
+       lda     Buttons
+       ldy     #MOUSE_INFO::BUTTONS
+       sta     (ptr1),y
+
+       rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL: lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
+       ldx     #>MOUSE_ERR_INV_IOCTL
+       rts
+
+;----------------------------------------------------------------------------
+; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
+; (so be careful).
+;
+
+IRQ:   lda     #$7F
+       sta     CIA1_PRA
+       lda     CIA1_PRB                ; Read port #1
+       and     #%00001100
+       eor     #%00001100              ; Make all bits active high
+       asl
+       sta     Buttons
+       lsr
+       lsr
+       lsr
+       and     #%00000001
+       ora     Buttons
+       sta     Buttons
+       ldx     #%01000000
+       stx     CIA1_PRA
+       ldy     #0
+:      dey
+       bne     :-
+       ldx     SID_ADConv1
+       stx     XPos
+       ldx     SID_ADConv2
+       stx     YPos
+
+       lda     #$FF
+       tax
+       bne     @AddX                   ; Branch always
+       lda     #$01
+       ldx     #$00
+
+; Calculate the new X coordinate (--> a/y)
+
+@AddX: add     XPos
+       tay                             ; Remember low byte
+       txa
+       adc     XPos+1
+       tax
+
+; Limit the X coordinate to the bounding box
+
+       cpy     XMin
+       sbc     XMin+1
+       bpl     @L1
+       ldy     XMin
+       ldx     XMin+1
+       jmp     @L2
+@L1:   txa
+
+       cpy     XMax
+       sbc     XMax+1
+       bmi     @L2
+       ldy     XMax
+       ldx     XMax+1
+@L2:   sty     XPos
+       stx     XPos+1
+
+; Move the mouse pointer to the new X pos
+
+       tya
+       jsr     CMOVEX
+
+       lda     #$FF
+       tax
+       bne     @AddY
+@Down: lda     #$01
+       ldx     #$00
+
+; Calculate the new Y coordinate (--> a/y)
+
+@AddY: add     YPos
+       tay                             ; Remember low byte
+       txa
+       adc     YPos+1
+       tax
+
+; Limit the Y coordinate to the bounding box
+
+       cpy     YMin
+       sbc     YMin+1
+       bpl     @L3
+       ldy     YMin
+       ldx     YMin+1
+       jmp     @L4
+@L3:   txa
+
+       cpy     YMax
+       sbc     YMax+1
+       bmi     @L4
+       ldy     YMax
+       ldx     YMax+1
+@L4:   sty     YPos
+       stx     YPos+1
+
+; Move the mouse pointer to the new X pos
+
+       tya
+       jmp     CMOVEY
index ca0e27852ee6dfc8afbbc7019c69aaf28040ab53..90b5c3ebb60433eecc8a730e5ff441d862abea12 100644 (file)
@@ -72,7 +72,7 @@ EMDS = c64-georam.emd c64-ram.emd c64-ramcart.emd c64-reu.emd c64-vdc.emd dtv-hi
 
 JOYS = c64-hitjoy.joy c64-numpad.joy c64-ptvjoy.joy c64-stdjoy.joy
 
-MOUS = c64-1351.mou c64-joymouse.mou
+MOUS = c64-1351.mou c64-joymouse.mou c64-potmouse.mou
 
 SERS = c64-swlink.ser
 
diff --git a/libsrc/c64/c64-potmouse.s b/libsrc/c64/c64-potmouse.s
new file mode 100644 (file)
index 0000000..cd2bc95
--- /dev/null
@@ -0,0 +1,371 @@
+;
+; Driver for a potentiometer "mouse" e.g. Koala Pad
+;
+; Ullrich von Bassewitz, 2004-03-29
+; Stefan Haubenthal, 2006-08-20
+;
+
+       .include        "zeropage.inc"
+       .include        "mouse-kernel.inc"
+       .include        "c64.inc"
+
+       .macpack        generic
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table
+
+.segment       "JUMPTABLE"
+
+HEADER:
+
+; Driver signature
+
+       .byte   $6d, $6f, $75           ; "mou"
+       .byte   MOUSE_API_VERSION       ; Mouse driver API version number
+
+; Jump table.
+
+       .addr   INSTALL
+       .addr   UNINSTALL
+       .addr   HIDE
+       .addr   SHOW
+       .addr   BOX
+       .addr   MOVE
+       .addr   BUTTONS
+       .addr   POS
+       .addr   INFO
+       .addr   IOCTL
+       .addr   IRQ
+
+; Callback table, set by the kernel before INSTALL is called
+
+CHIDE: jmp     $0000                   ; Hide the cursor
+CSHOW: jmp     $0000                   ; Show the cursor
+CMOVEX: jmp    $0000                   ; Move the cursor to X coord
+CMOVEY: jmp    $0000                   ; Move the cursor to Y coord
+
+
+;----------------------------------------------------------------------------
+; Constants
+
+SCREEN_HEIGHT  = 200
+SCREEN_WIDTH   = 320
+
+.enum  JOY
+       UP      = $01
+       DOWN    = $02
+       LEFT    = $04
+       RIGHT   = $08
+       FIRE    = $10
+.endenum
+
+;----------------------------------------------------------------------------
+; Global variables. The bounding box values are sorted so that they can be
+; written with the least effort in the BOX routine, so don't reorder them.
+
+.bss
+
+Vars:
+YPos:          .res    2               ; Current mouse position, Y
+XPos:          .res    2               ; Current mouse position, X
+YMax:          .res    2               ; Y2 value of bounding box
+XMax:          .res    2               ; X2 value of bounding box
+YMin:          .res    2               ; Y1 value of bounding box
+XMin:          .res    2               ; X1 value of bounding box
+Buttons:       .res    1               ; Button mask
+
+; Temporary value used in the int handler
+
+Temp:          .res    1
+
+; Default values for above variables
+
+.rodata
+
+.proc  DefVars
+       .word   SCREEN_HEIGHT/2         ; YPos
+       .word   SCREEN_WIDTH/2          ; XPos
+       .word   SCREEN_HEIGHT           ; YMax
+       .word   SCREEN_WIDTH            ; XMax
+       .word   0                       ; YMin
+       .word   0                       ; XMin
+       .byte   0                       ; Buttons
+.endproc
+
+.code
+
+;----------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. If
+; possible, check if the hardware is present.
+; Must return an MOUSE_ERR_xx code in a/x.
+
+INSTALL:
+
+; Initialize variables. Just copy the default stuff over
+
+       ldx     #.sizeof(DefVars)-1
+@L1:   lda     DefVars,x
+       sta     Vars,x
+       dex
+       bpl     @L1
+
+; Be sure the mouse cursor is invisible and at the default location. We
+; need to do that here, because our mouse interrupt handler doesn't set the
+; mouse position if it hasn't changed.
+
+       sei
+       jsr     CHIDE
+       lda     XPos
+       ldx     XPos+1
+       jsr     CMOVEX
+       lda     YPos
+       ldx     YPos+1
+       jsr     CMOVEY
+       cli
+
+; Done, return zero (= MOUSE_ERR_OK)
+
+       ldx     #$00
+       txa
+       rts
+
+;----------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory.
+; No return code required (the driver is removed from memory on return).
+
+UNINSTALL      = HIDE                  ; Hide cursor on exit
+
+;----------------------------------------------------------------------------
+; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently visible and should get hidden. For most drivers,
+; no special action is required besides hiding the mouse cursor.
+; No return code required.
+
+HIDE:  sei
+       jsr     CHIDE
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
+; a counter for calls to show/hide, and the driver entry point is only called
+; if the mouse is currently hidden and should become visible. For most drivers,
+; no special action is required besides enabling the mouse cursor.
+; No return code required.
+
+SHOW:  sei
+       jsr     CSHOW
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; BOX: Set the mouse bounding box. The parameters are passed as they come from
+; the C program, that is, maxy in a/x and the other parameters on the stack.
+; The C wrapper will remove the parameters from the stack when the driver
+; routine returns.
+; No checks are done if the mouse is currently inside the box, this is the job
+; of the caller. It is not necessary to validate the parameters, trust the
+; caller and save some code here. No return code required.
+
+BOX:   ldy     #5
+       sei
+       sta     YMax
+       stx     YMax+1
+
+@L1:   lda     (sp),y
+       sta     XMax,y
+       dey
+       bpl     @L1
+
+       cli
+       rts
+
+;----------------------------------------------------------------------------
+; MOVE: Move the mouse to a new position. The position is passed as it comes
+; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
+; remove the parameter from the stack on return.
+; No checks are done if the new position is valid (within the bounding box or
+; the screen). No return code required.
+;
+
+MOVE:  sei                             ; No interrupts
+
+       sta     YPos
+       stx     YPos+1                  ; New Y position
+       jsr     CMOVEY                  ; Set it
+
+       ldy     #$01
+       lda     (sp),y
+       sta     XPos+1
+       tax
+       dey
+       lda     (sp),y
+       sta     XPos                    ; New X position
+
+       jsr     CMOVEX                  ; Move the cursor
+
+       cli                             ; Allow interrupts
+       rts
+
+;----------------------------------------------------------------------------
+; BUTTONS: Return the button mask in a/x.
+
+BUTTONS:
+       lda     Buttons
+       ldx     #$00
+       rts
+
+;----------------------------------------------------------------------------
+; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
+; No return code required.
+
+POS:   ldy     #MOUSE_POS::XCOORD      ; Structure offset
+
+       sei                             ; Disable interrupts
+       lda     XPos                    ; Transfer the position
+       sta     (ptr1),y
+       lda     XPos+1
+       iny
+       sta     (ptr1),y
+       lda     YPos
+       iny
+       sta     (ptr1),y
+       lda     YPos+1
+       cli                             ; Enable interrupts
+
+       iny
+       sta     (ptr1),y                ; Store last byte
+
+       rts                             ; Done
+
+;----------------------------------------------------------------------------
+; INFO: Returns mouse position and current button mask in the MOUSE_INFO
+; struct pointed to by ptr1. No return code required.
+;
+; We're cheating here to keep the code smaller: The first fields of the
+; mouse_info struct are identical to the mouse_pos struct, so we will just
+; call _mouse_pos to initialize the struct pointer and fill the position
+; fields.
+
+INFO:  jsr     POS
+
+; Fill in the button state
+
+       lda     Buttons
+       ldy     #MOUSE_INFO::BUTTONS
+       sta     (ptr1),y
+
+       rts
+
+;----------------------------------------------------------------------------
+; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
+; specific data in ptr1, and the ioctl code in A.
+; Must return an error code in a/x.
+;
+
+IOCTL: lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
+       ldx     #>MOUSE_ERR_INV_IOCTL
+       rts
+
+;----------------------------------------------------------------------------
+; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
+; (so be careful).
+;
+
+IRQ:   lda     #$7F
+       sta     CIA1_PRA
+       lda     CIA1_PRB                ; Read port #1
+       and     #%00001100
+       eor     #%00001100              ; Make all bits active high
+       asl
+       sta     Buttons
+       lsr
+       lsr
+       lsr
+       and     #%00000001
+       ora     Buttons
+       sta     Buttons
+       ldx     #%01000000
+       stx     CIA1_PRA
+       ldy     #0
+:      dey
+       bne     :-
+       ldx     SID_ADConv1
+       stx     XPos
+       ldx     SID_ADConv2
+       stx     YPos
+
+       lda     #$FF
+       tax
+       bne     @AddX                   ; Branch always
+       lda     #$01
+       ldx     #$00
+
+; Calculate the new X coordinate (--> a/y)
+
+@AddX: add     XPos
+       tay                             ; Remember low byte
+       txa
+       adc     XPos+1
+       tax
+
+; Limit the X coordinate to the bounding box
+
+       cpy     XMin
+       sbc     XMin+1
+       bpl     @L1
+       ldy     XMin
+       ldx     XMin+1
+       jmp     @L2
+@L1:   txa
+
+       cpy     XMax
+       sbc     XMax+1
+       bmi     @L2
+       ldy     XMax
+       ldx     XMax+1
+@L2:   sty     XPos
+       stx     XPos+1
+
+; Move the mouse pointer to the new X pos
+
+       tya
+       jsr     CMOVEX
+
+       lda     #$FF
+       tax
+       bne     @AddY
+@Down: lda     #$01
+       ldx     #$00
+
+; Calculate the new Y coordinate (--> a/y)
+
+@AddY: add     YPos
+       tay                             ; Remember low byte
+       txa
+       adc     YPos+1
+       tax
+
+; Limit the Y coordinate to the bounding box
+
+       cpy     YMin
+       sbc     YMin+1
+       bpl     @L3
+       ldy     YMin
+       ldx     YMin+1
+       jmp     @L4
+@L3:   txa
+
+       cpy     YMax
+       sbc     YMax+1
+       bmi     @L4
+       ldy     YMax
+       ldx     YMax+1
+@L4:   sty     YPos
+       stx     YPos+1
+
+; Move the mouse pointer to the new X pos
+
+       tya
+       jmp     CMOVEY