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