]> git.sur5r.net Git - cc65/blob - libsrc/c64/c64-joymouse.s
Don't hardcode the address of the SYS call for the startup code of the
[cc65] / libsrc / c64 / c64-joymouse.s
1 ;
2 ; Driver for a "joystick mouse".
3 ;
4 ; Ullrich von Bassewitz, 2004-03-29, 2009-09-26
5 ;
6
7         .include        "zeropage.inc"
8         .include        "mouse-kernel.inc"
9         .include        "c64.inc"
10
11         .macpack        generic
12
13 ; ------------------------------------------------------------------------
14 ; Header. Includes jump table
15
16 .segment        "JUMPTABLE"
17
18 HEADER:
19
20 ; Driver signature
21
22         .byte   $6d, $6f, $75           ; "mou"
23         .byte   MOUSE_API_VERSION       ; Mouse driver API version number
24
25 ; Jump table.
26
27         .addr   INSTALL
28         .addr   UNINSTALL
29         .addr   HIDE
30         .addr   SHOW
31         .addr   SETBOX
32         .addr   GETBOX
33         .addr   MOVE
34         .addr   BUTTONS
35         .addr   POS
36         .addr   INFO
37         .addr   IOCTL
38         .addr   IRQ
39
40 ; Mouse driver flags
41
42         .byte   MOUSE_FLAG_LATE_IRQ
43
44 ; Callback table, set by the kernel before INSTALL is called
45
46 CHIDE:  jmp     $0000                   ; Hide the cursor
47 CSHOW:  jmp     $0000                   ; Show the cursor
48 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
49 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
50
51
52 ;----------------------------------------------------------------------------
53 ; Constants
54
55 SCREEN_HEIGHT   = 200
56 SCREEN_WIDTH    = 320
57
58 .enum   JOY
59         UP      = $01
60         DOWN    = $02
61         LEFT    = $04
62         RIGHT   = $08
63         FIRE    = $10
64 .endenum
65
66 ;----------------------------------------------------------------------------
67 ; Global variables. The bounding box values are sorted so that they can be
68 ; written with the least effort in the SETBOX and GETBOX routines, so don't
69 ; reorder them.
70
71 .bss
72
73 Vars:
74 YPos:           .res    2               ; Current mouse position, Y
75 XPos:           .res    2               ; Current mouse position, X
76 XMin:           .res    2               ; X1 value of bounding box
77 YMin:           .res    2               ; Y1 value of bounding box
78 XMax:           .res    2               ; X2 value of bounding box
79 YMax:           .res    2               ; Y2 value of bounding box
80 Buttons:        .res    1               ; Button mask
81
82 ; Temporary value used in the int handler
83
84 Temp:           .res    1
85
86 ; Default values for above variables
87
88 .rodata
89
90 .proc   DefVars
91         .word   SCREEN_HEIGHT/2         ; YPos
92         .word   SCREEN_WIDTH/2          ; XPos
93         .word   0                       ; XMin
94         .word   0                       ; YMin
95         .word   SCREEN_WIDTH            ; XMax
96         .word   SCREEN_HEIGHT           ; YMax
97         .byte   0                       ; Buttons
98 .endproc
99
100 .code
101
102 ;----------------------------------------------------------------------------
103 ; INSTALL routine. Is called after the driver is loaded into memory. If
104 ; possible, check if the hardware is present.
105 ; Must return an MOUSE_ERR_xx code in a/x.
106
107 INSTALL:
108
109 ; Initialize variables. Just copy the default stuff over
110
111         ldx     #.sizeof(DefVars)-1
112 @L1:    lda     DefVars,x
113         sta     Vars,x
114         dex
115         bpl     @L1
116
117 ; Be sure the mouse cursor is invisible and at the default location. We
118 ; need to do that here, because our mouse interrupt handler doesn't set the
119 ; mouse position if it hasn't changed.
120
121         sei
122         jsr     CHIDE
123         lda     XPos
124         ldx     XPos+1
125         jsr     CMOVEX
126         lda     YPos
127         ldx     YPos+1
128         jsr     CMOVEY
129         cli
130
131 ; Done, return zero (= MOUSE_ERR_OK)
132
133         ldx     #$00
134         txa
135         rts
136
137 ;----------------------------------------------------------------------------
138 ; UNINSTALL routine. Is called before the driver is removed from memory.
139 ; No return code required (the driver is removed from memory on return).
140
141 UNINSTALL       = HIDE                  ; Hide cursor on exit
142
143 ;----------------------------------------------------------------------------
144 ; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
145 ; a counter for calls to show/hide, and the driver entry point is only called
146 ; if the mouse is currently visible and should get hidden. For most drivers,
147 ; no special action is required besides hiding the mouse cursor.
148 ; No return code required.
149
150 HIDE:   sei
151         jsr     CHIDE
152         cli
153         rts
154
155 ;----------------------------------------------------------------------------
156 ; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
157 ; a counter for calls to show/hide, and the driver entry point is only called
158 ; if the mouse is currently hidden and should become visible. For most drivers,
159 ; no special action is required besides enabling the mouse cursor.
160 ; No return code required.
161
162 SHOW:   sei
163         jsr     CSHOW
164         cli
165         rts
166
167 ;----------------------------------------------------------------------------
168 ; SETBOX: Set the mouse bounding box. The parameters are passed as they come
169 ; from the C program, that is, a pointer to a mouse_box struct in a/x.
170 ; No checks are done if the mouse is currently inside the box, this is the job
171 ; of the caller. It is not necessary to validate the parameters, trust the
172 ; caller and save some code here. No return code required.
173
174 SETBOX: sta     ptr1
175         stx     ptr1+1                  ; Save data pointer
176
177         ldy     #.sizeof (MOUSE_BOX)-1
178         sei
179
180 @L1:    lda     (ptr1),y
181         sta     XMin,y
182         dey
183         bpl     @L1
184
185         cli
186         rts
187
188 ;----------------------------------------------------------------------------
189 ; GETBOX: Return the mouse bounding box. The parameters are passed as they
190 ; come from the C program, that is, a pointer to a mouse_box struct in a/x.
191
192 GETBOX: sta     ptr1
193         stx     ptr1+1                  ; Save data pointer
194
195         ldy     #.sizeof (MOUSE_BOX)-1
196         sei
197
198 @L1:    lda     XMin,y
199         sta     (ptr1),y
200         dey
201         bpl     @L1
202
203         cli
204         rts
205
206 ;----------------------------------------------------------------------------
207 ; MOVE: Move the mouse to a new position. The position is passed as it comes
208 ; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
209 ; remove the parameter from the stack on return.
210 ; No checks are done if the new position is valid (within the bounding box or
211 ; the screen). No return code required.
212 ;
213
214 MOVE:   sei                             ; No interrupts
215
216         sta     YPos
217         stx     YPos+1                  ; New Y position
218         jsr     CMOVEY                  ; Set it
219
220         ldy     #$01
221         lda     (sp),y
222         sta     XPos+1
223         tax
224         dey
225         lda     (sp),y
226         sta     XPos                    ; New X position
227
228         jsr     CMOVEX                  ; Move the cursor
229
230         cli                             ; Allow interrupts
231         rts
232
233 ;----------------------------------------------------------------------------
234 ; BUTTONS: Return the button mask in a/x.
235
236 BUTTONS:
237         lda     Buttons
238         ldx     #$00
239         rts
240
241 ;----------------------------------------------------------------------------
242 ; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
243 ; No return code required.
244
245 POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
246
247         sei                             ; Disable interrupts
248         lda     XPos                    ; Transfer the position
249         sta     (ptr1),y
250         lda     XPos+1
251         iny
252         sta     (ptr1),y
253         lda     YPos
254         iny
255         sta     (ptr1),y
256         lda     YPos+1
257         cli                             ; Enable interrupts
258
259         iny
260         sta     (ptr1),y                ; Store last byte
261
262         rts                             ; Done
263
264 ;----------------------------------------------------------------------------
265 ; INFO: Returns mouse position and current button mask in the MOUSE_INFO
266 ; struct pointed to by ptr1. No return code required.
267 ;
268 ; We're cheating here to keep the code smaller: The first fields of the
269 ; mouse_info struct are identical to the mouse_pos struct, so we will just
270 ; call _mouse_pos to initialize the struct pointer and fill the position
271 ; fields.
272
273 INFO:   jsr     POS
274
275 ; Fill in the button state
276
277         lda     Buttons
278         ldy     #MOUSE_INFO::BUTTONS
279         sta     (ptr1),y
280
281         rts
282
283 ;----------------------------------------------------------------------------
284 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
285 ; specific data in ptr1, and the ioctl code in A.
286 ; Must return an error code in a/x.
287 ;
288
289 IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
290         ldx     #>MOUSE_ERR_INV_IOCTL
291         rts
292
293 ;----------------------------------------------------------------------------
294 ; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
295 ; (so be careful). The routine MUST return carry set if the interrupt has been
296 ; 'handled' - which means that the interrupt source is gone. Otherwise it
297 ; MUST return carry clear.
298 ;
299
300 IRQ:    lda     #$7F
301         sta     CIA1_PRA
302         lda     CIA1_PRB                ; Read joystick #0
303         and     #$1F
304         eor     #$1F                    ; Make all bits active high
305         sta     Temp
306
307 ; Check for a pressed button and place the result into Buttons
308
309         ldx     #$00                    ; Assume no button pressed
310         and     #JOY::FIRE              ; Check fire button
311         beq     @L0                     ; Jump if not pressed
312         ldx     #MOUSE_BTN_LEFT         ; Left (only) button is pressed
313 @L0:    stx     Buttons
314
315 ; Check left/right
316
317         lda     Temp                    ; Read joystick #0
318         and     #(JOY::LEFT | JOY::RIGHT)
319         beq     @SkipX                  ;
320
321 ; We will cheat here and rely on the fact that either the left, OR the right
322 ; bit can be active
323
324         and     #JOY::RIGHT             ; Check RIGHT bit
325         bne     @Right
326         lda     #$FF
327         tax
328         bne     @AddX                   ; Branch always
329 @Right: lda     #$01
330         ldx     #$00
331
332 ; Calculate the new X coordinate (--> a/y)
333
334 @AddX:  add     XPos
335         tay                             ; Remember low byte
336         txa
337         adc     XPos+1
338         tax
339
340 ; Limit the X coordinate to the bounding box
341
342         cpy     XMin
343         sbc     XMin+1
344         bpl     @L1
345         ldy     XMin
346         ldx     XMin+1
347         jmp     @L2
348 @L1:    txa
349
350         cpy     XMax
351         sbc     XMax+1
352         bmi     @L2
353         ldy     XMax
354         ldx     XMax+1
355 @L2:    sty     XPos
356         stx     XPos+1
357
358 ; Move the mouse pointer to the new X pos
359
360         tya
361         jsr     CMOVEX
362
363 ; Calculate the Y movement vector
364
365 @SkipX: lda     Temp                    ; Read joystick #0
366         and     #(JOY::UP | JOY::DOWN)  ; Check up/down
367         beq     @SkipY                  ;
368
369 ; We will cheat here and rely on the fact that either the up, OR the down
370 ; bit can be active
371
372         lsr     a                       ; Check UP bit
373         bcc     @Down
374         lda     #$FF
375         tax
376         bne     @AddY
377 @Down:  lda     #$01
378         ldx     #$00
379
380 ; Calculate the new Y coordinate (--> a/y)
381
382 @AddY:  add     YPos
383         tay                             ; Remember low byte
384         txa
385         adc     YPos+1
386         tax
387
388 ; Limit the Y coordinate to the bounding box
389
390         cpy     YMin
391         sbc     YMin+1
392         bpl     @L3
393         ldy     YMin
394         ldx     YMin+1
395         jmp     @L4
396 @L3:    txa
397
398         cpy     YMax
399         sbc     YMax+1
400         bmi     @L4
401         ldy     YMax
402         ldx     YMax+1
403 @L4:    sty     YPos
404         stx     YPos+1
405
406 ; Move the mouse pointer to the new X pos
407
408         tya
409         jsr     CMOVEY
410
411 ; Done
412
413 @SkipY: clc                             ; Interrupt not handled
414         rts
415