]> git.sur5r.net Git - cc65/blob - libsrc/c64/mou/c64-joy.s
Minor comment harmonization.
[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 .rodata
110
111 ; Default values for below variables
112 ; (We use ".proc" because we want to define both a label and a scope.)
113
114 .proc   DefVars
115         .word   SCREEN_HEIGHT/2         ; YPos
116         .word   SCREEN_WIDTH/2          ; XPos
117         .word   0                       ; XMin
118         .word   0                       ; YMin
119         .word   SCREEN_WIDTH            ; XMax
120         .word   SCREEN_HEIGHT           ; YMax
121         .byte   0                       ; Buttons
122 .endproc
123
124 .code
125
126 ;----------------------------------------------------------------------------
127 ; INSTALL routine. Is called after the driver is loaded into memory. If
128 ; possible, check if the hardware is present.
129 ; Must return an MOUSE_ERR_xx code in a/x.
130
131 INSTALL:
132
133 ; Initialize variables. Just copy the default stuff over
134
135         ldx     #.sizeof(DefVars)-1
136 @L1:    lda     DefVars,x
137         sta     Vars,x
138         dex
139         bpl     @L1
140
141 ; Be sure the mouse cursor is invisible and at the default location. We
142 ; need to do that here, because our mouse interrupt handler doesn't set the
143 ; mouse position if it hasn't changed.
144
145         sei
146         jsr     CHIDE
147         lda     XPos
148         ldx     XPos+1
149         jsr     CMOVEX
150         lda     YPos
151         ldx     YPos+1
152         jsr     CMOVEY
153         cli
154
155 ; Done, return zero (= MOUSE_ERR_OK)
156
157         ldx     #$00
158         txa
159         rts
160
161 ;----------------------------------------------------------------------------
162 ; UNINSTALL routine. Is called before the driver is removed from memory.
163 ; No return code required (the driver is removed from memory on return).
164
165 UNINSTALL       = HIDE                  ; Hide cursor on exit
166
167 ;----------------------------------------------------------------------------
168 ; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
169 ; a counter for calls to show/hide, and the driver entry point is only called
170 ; if the mouse is currently visible and should get hidden. For most drivers,
171 ; no special action is required besides hiding the mouse cursor.
172 ; No return code required.
173
174 HIDE:   sei
175         jsr     CHIDE
176         cli
177         rts
178
179 ;----------------------------------------------------------------------------
180 ; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
181 ; a counter for calls to show/hide, and the driver entry point is only called
182 ; if the mouse is currently hidden and should become visible. For most drivers,
183 ; no special action is required besides enabling the mouse cursor.
184 ; No return code required.
185
186 SHOW:   sei
187         jsr     CSHOW
188         cli
189         rts
190
191 ;----------------------------------------------------------------------------
192 ; SETBOX: Set the mouse bounding box. The parameters are passed as they come
193 ; from the C program, that is, a pointer to a mouse_box struct in a/x.
194 ; No checks are done if the mouse is currently inside the box, this is the job
195 ; of the caller. It is not necessary to validate the parameters, trust the
196 ; caller and save some code here. No return code required.
197
198 SETBOX: sta     ptr1
199         stx     ptr1+1                  ; Save data pointer
200
201         ldy     #.sizeof (MOUSE_BOX)-1
202         sei
203
204 @L1:    lda     (ptr1),y
205         sta     XMin,y
206         dey
207         bpl     @L1
208
209         cli
210         rts
211
212 ;----------------------------------------------------------------------------
213 ; GETBOX: Return the mouse bounding box. The parameters are passed as they
214 ; come from the C program, that is, a pointer to a mouse_box struct in a/x.
215
216 GETBOX: sta     ptr1
217         stx     ptr1+1                  ; Save data pointer
218
219         ldy     #.sizeof (MOUSE_BOX)-1
220
221 @L1:    lda     XMin,y
222         sta     (ptr1),y
223         dey
224         bpl     @L1
225
226         rts
227
228 ;----------------------------------------------------------------------------
229 ; MOVE: Move the mouse to a new position. The position is passed as it comes
230 ; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
231 ; remove the parameter from the stack on return.
232 ; No checks are done if the new position is valid (within the bounding box or
233 ; the screen). No return code required.
234 ;
235
236 MOVE:   sei                             ; No interrupts
237
238         sta     YPos
239         stx     YPos+1                  ; New Y position
240         jsr     CMOVEY                  ; Set it
241
242         ldy     #$01
243         lda     (sp),y
244         sta     XPos+1
245         tax
246         dey
247         lda     (sp),y
248         sta     XPos                    ; New X position
249
250         jsr     CMOVEX                  ; Move the cursor
251
252         cli                             ; Allow interrupts
253         rts
254
255 ;----------------------------------------------------------------------------
256 ; BUTTONS: Return the button mask in a/x.
257
258 BUTTONS:
259         lda     Buttons
260         ldx     #$00
261         rts
262
263 ;----------------------------------------------------------------------------
264 ; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
265 ; No return code required.
266
267 POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
268
269         sei                             ; Disable interrupts
270         lda     XPos                    ; Transfer the position
271         sta     (ptr1),y
272         lda     XPos+1
273         iny
274         sta     (ptr1),y
275         lda     YPos
276         iny
277         sta     (ptr1),y
278         lda     YPos+1
279         cli                             ; Enable interrupts
280
281         iny
282         sta     (ptr1),y                ; Store last byte
283
284         rts                             ; Done
285
286 ;----------------------------------------------------------------------------
287 ; INFO: Returns mouse position and current button mask in the MOUSE_INFO
288 ; struct pointed to by ptr1. No return code required.
289 ;
290 ; We're cheating here to keep the code smaller: The first fields of the
291 ; mouse_info struct are identical to the mouse_pos struct, so we will just
292 ; call _mouse_pos to initialize the struct pointer and fill the position
293 ; fields.
294
295 INFO:   jsr     POS
296
297 ; Fill in the button state
298
299         lda     Buttons
300         ldy     #MOUSE_INFO::BUTTONS
301         sta     (ptr1),y
302
303         rts
304
305 ;----------------------------------------------------------------------------
306 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
307 ; specific data in ptr1, and the ioctl code in A.
308 ; Must return an error code in a/x.
309 ;
310
311 IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
312         ldx     #>MOUSE_ERR_INV_IOCTL
313         rts
314
315 ;----------------------------------------------------------------------------
316 ; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
317 ; (so be careful). The routine MUST return carry set if the interrupt has been
318 ; 'handled' - which means that the interrupt source is gone. Otherwise it
319 ; MUST return carry clear.
320 ;
321
322 ; Avoid crosstalk between the keyboard and a joystick.
323
324 IRQ:    ldy     #%00000000              ; Set ports A and B to input
325         sty     CIA1_DDRB
326         sty     CIA1_DDRA               ; Keyboard won't look like joystick
327         lda     CIA1_PRB                ; Read Control-Port 1
328         dec     CIA1_DDRA               ; Set port A back to output
329         eor     #%11111111              ; Bit goes up when switch goes down
330         beq     @Save                   ;(bze)
331         dec     CIA1_DDRB               ; Joystick won't look like keyboard
332         sty     CIA1_PRB                ; Set "all keys pushed"
333 @Save:  sta     Temp
334
335 ; Check for a pressed button and place the result into Buttons
336
337         ldx     #$00                    ; Assume no button pressed
338         and     #JOY::FIRE              ; Check fire button
339         beq     @L0                     ; Jump if not pressed
340         ldx     #MOUSE_BTN_LEFT         ; Left (only) button is pressed
341 @L0:    stx     Buttons
342
343 ; Check left/right
344
345         lda     Temp                    ; Read joystick #0
346         and     #(JOY::LEFT | JOY::RIGHT)
347         beq     @SkipX                  ;
348
349 ; We will cheat here and rely on the fact that either the left, OR the right
350 ; bit can be active
351
352         and     #JOY::RIGHT             ; Check RIGHT bit
353         bne     @Right
354         lda     #$FF
355         tax
356         bne     @AddX                   ; Branch always
357 @Right: lda     #$01
358         ldx     #$00
359
360 ; Calculate the new X coordinate (--> a/y)
361
362 @AddX:  add     XPos
363         tay                             ; Remember low byte
364         txa
365         adc     XPos+1
366         tax
367
368 ; Limit the X coordinate to the bounding box
369
370         cpy     XMin
371         sbc     XMin+1
372         bpl     @L1
373         ldy     XMin
374         ldx     XMin+1
375         jmp     @L2
376 @L1:    txa
377
378         cpy     XMax
379         sbc     XMax+1
380         bmi     @L2
381         ldy     XMax
382         ldx     XMax+1
383 @L2:    sty     XPos
384         stx     XPos+1
385
386 ; Move the mouse pointer to the new X pos
387
388         tya
389         jsr     CMOVEX
390
391 ; Calculate the Y movement vector
392
393 @SkipX: lda     Temp                    ; Read joystick #0
394         and     #(JOY::UP | JOY::DOWN)  ; Check up/down
395         beq     @SkipY                  ;
396
397 ; We will cheat here and rely on the fact that either the up, OR the down
398 ; bit can be active
399
400         lsr     a                       ; Check UP bit
401         bcc     @Down
402         lda     #$FF
403         tax
404         bne     @AddY
405 @Down:  lda     #$01
406         ldx     #$00
407
408 ; Calculate the new Y coordinate (--> a/y)
409
410 @AddY:  add     YPos
411         tay                             ; Remember low byte
412         txa
413         adc     YPos+1
414         tax
415
416 ; Limit the Y coordinate to the bounding box
417
418         cpy     YMin
419         sbc     YMin+1
420         bpl     @L3
421         ldy     YMin
422         ldx     YMin+1
423         jmp     @L4
424 @L3:    txa
425
426         cpy     YMax
427         sbc     YMax+1
428         bmi     @L4
429         ldy     YMax
430         ldx     YMax+1
431 @L4:    sty     YPos
432         stx     YPos+1
433
434 ; Move the mouse pointer to the new X pos
435
436         tya
437         jsr     CMOVEY
438
439 ; Done
440
441 @SkipY: clc                             ; Interrupt not handled
442         rts
443