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