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