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