]> git.sur5r.net Git - cc65/blob - libsrc/c128/mou/c128-joy.s
Workaround for "phantom" key presses in the C128 "joystick" mouse driver.
[cc65] / libsrc / c128 / mou / c128-joy.s
1 ;
2 ; Driver for a "joystick mouse".
3 ;
4 ; 2009-09-26, Ullrich von Bassewitz
5 ; 2014-03-17, Greg King
6 ;
7
8         .include        "zeropage.inc"
9         .include        "mouse-kernel.inc"
10         .include        "c128.inc"
11
12         .macpack        generic
13
14 IRQInd  = $2FD
15
16 ; ------------------------------------------------------------------------
17 ; Header. Includes jump table
18
19 .segment        "JUMPTABLE"
20
21 HEADER:
22
23 ; Driver signature
24
25         .byte   $6d, $6f, $75           ; "mou"
26         .byte   MOUSE_API_VERSION       ; Mouse driver API version number
27
28 ; Library reference
29
30 libref:
31         .addr   $0000
32
33 ; Jump table
34
35         .addr   INSTALL
36         .addr   UNINSTALL
37         .addr   HIDE
38         .addr   SHOW
39         .addr   SETBOX
40         .addr   GETBOX
41         .addr   MOVE
42         .addr   BUTTONS
43         .addr   POS
44         .addr   INFO
45         .addr   IOCTL
46         .addr   IRQ
47
48 ; Mouse driver flags
49
50         .byte   MOUSE_FLAG_LATE_IRQ
51
52 ; Callback table, set by the kernel before INSTALL is called
53
54 CHIDE:  jmp     $0000                   ; Hide the cursor
55 CSHOW:  jmp     $0000                   ; Show the cursor
56 CPREP:  jmp     $0000                   ; Prepare to move the cursor
57 CDRAW:  jmp     $0000                   ; Draw the cursor
58 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
59 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
60
61
62 ;----------------------------------------------------------------------------
63 ; Constants
64
65 SCREEN_HEIGHT   = 200
66 SCREEN_WIDTH    = 320
67
68 .enum   JOY
69         UP      = $01
70         DOWN    = $02
71         LEFT    = $04
72         RIGHT   = $08
73         FIRE    = $10
74 .endenum
75
76 ;----------------------------------------------------------------------------
77 ; data segment
78
79 .data
80
81 chainIRQ:
82         .byte   $4c                     ; JMP opcode
83         .word   0                       ; pointer to ROM IRQ handler (will be set at runtime)
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 INIT_save:      .res    1
102
103 ; Temporary value used in the int handler
104
105 Temp:           .res    1
106
107 ; Keyboard buffer fill level at start of interrupt
108
109 old_key_count:  .res    1
110
111 .rodata
112
113 ; Default values for above variables
114 ; (We use ".proc" because we want to define both a label and a scope.)
115
116 .proc   DefVars
117         .word   SCREEN_HEIGHT/2         ; YPos
118         .word   SCREEN_WIDTH/2          ; XPos
119         .word   0                       ; XMin
120         .word   0                       ; YMin
121         .word   SCREEN_WIDTH - 1        ; XMax
122         .word   SCREEN_HEIGHT - 1       ; YMax
123         .byte   0                       ; Buttons
124 .endproc
125
126 .code
127
128 ;----------------------------------------------------------------------------
129 ; INSTALL routine. Is called after the driver is loaded into memory. If
130 ; possible, check if the hardware is present.
131 ; Must return an MOUSE_ERR_xx code in a/x.
132
133 INSTALL:
134
135 ; Disable the BASIC interpreter's interrupt-driven sprite-motion code.
136 ; That allows direct access to the VIC-IIe's sprite registers.
137
138         lda     INIT_STATUS
139         sta     INIT_save
140         lda     #%11000000
141         sta     INIT_STATUS
142
143 ; Initialize variables. Just copy the default stuff over
144
145         ldx     #.sizeof(DefVars)-1
146 @L1:    lda     DefVars,x
147         sta     Vars,x
148         dex
149         bpl     @L1
150
151 ; Be sure the mouse cursor is invisible and at the default location. We
152 ; need to do that here, because our mouse interrupt handler doesn't set the
153 ; mouse position if it hasn't changed.
154
155         sei
156         jsr     CHIDE
157         lda     XPos
158         ldx     XPos+1
159         jsr     CMOVEX
160         lda     YPos
161         ldx     YPos+1
162         jsr     CMOVEY
163         cli
164
165 ; Initialize our IRQ magic
166
167         lda     IRQInd+1
168         sta     chainIRQ+1
169         lda     IRQInd+2
170         sta     chainIRQ+2
171         lda     libref
172         sta     ptr3
173         lda     libref+1
174         sta     ptr3+1
175         ldy     #2
176         lda     (ptr3),y
177         sta     IRQInd+1
178         iny
179         lda     (ptr3),y
180         sta     IRQInd+2
181         iny
182         lda     #<(callback-1)
183         sta     (ptr3),y
184         iny
185         lda     #>(callback-1)
186         sta     (ptr3),y
187         iny
188         lda     #<(chainIRQ-1)
189         sta     (ptr3),y
190         iny
191         lda     #>(chainIRQ-1)
192         sta     (ptr3),y
193
194 ; Done, return zero (= MOUSE_ERR_OK)
195
196         ldx     #$00
197         txa
198         rts
199
200 ;----------------------------------------------------------------------------
201 ; UNINSTALL routine. Is called before the driver is removed from memory.
202 ; No return code required (the driver is removed from memory on return).
203
204 UNINSTALL:
205
206         lda     chainIRQ+1
207         sta     IRQInd+1
208         lda     chainIRQ+2
209         sta     IRQInd+2
210
211         jsr     HIDE                    ; Hide cursor on exit
212         lda     INIT_save
213         sta     INIT_STATUS
214         rts
215
216 ;----------------------------------------------------------------------------
217 ; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
218 ; a counter for calls to show/hide, and the driver entry point is only called
219 ; if the mouse is currently visible and should get hidden. For most drivers,
220 ; no special action is required besides hiding the mouse cursor.
221 ; No return code required.
222
223 HIDE:   sei
224         jsr     CHIDE
225         cli
226         rts
227
228 ;----------------------------------------------------------------------------
229 ; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
230 ; a counter for calls to show/hide, and the driver entry point is only called
231 ; if the mouse is currently hidden and should become visible. For most drivers,
232 ; no special action is required besides enabling the mouse cursor.
233 ; No return code required.
234
235 SHOW:   sei
236         jsr     CSHOW
237         cli
238         rts
239
240 ;----------------------------------------------------------------------------
241 ; SETBOX: Set the mouse bounding box. The parameters are passed as they come
242 ; from the C program, that is, a pointer to a mouse_box struct in a/x.
243 ; No checks are done if the mouse is currently inside the box, this is the job
244 ; of the caller. It is not necessary to validate the parameters, trust the
245 ; caller and save some code here. No return code required.
246
247 SETBOX: sta     ptr1
248         stx     ptr1+1                  ; Save data pointer
249
250         ldy     #.sizeof (MOUSE_BOX)-1
251         sei
252
253 @L1:    lda     (ptr1),y
254         sta     XMin,y
255         dey
256         bpl     @L1
257
258         cli
259         rts
260
261 ;----------------------------------------------------------------------------
262 ; GETBOX: Return the mouse bounding box. The parameters are passed as they
263 ; come from the C program, that is, a pointer to a mouse_box struct in a/x.
264
265 GETBOX: sta     ptr1
266         stx     ptr1+1                  ; Save data pointer
267
268         ldy     #.sizeof (MOUSE_BOX)-1
269         sei
270
271 @L1:    lda     XMin,y
272         sta     (ptr1),y
273         dey
274         bpl     @L1
275
276         cli
277         rts
278
279 ;----------------------------------------------------------------------------
280 ; MOVE: Move the mouse to a new position. The position is passed as it comes
281 ; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
282 ; remove the parameter from the stack on return.
283 ; No checks are done if the new position is valid (within the bounding box or
284 ; the screen). No return code required.
285 ;
286
287 MOVE:   sei                             ; No interrupts
288
289         sta     YPos
290         stx     YPos+1                  ; New Y position
291         jsr     CMOVEY                  ; Set it
292
293         ldy     #$01
294         lda     (sp),y
295         sta     XPos+1
296         tax
297         dey
298         lda     (sp),y
299         sta     XPos                    ; New X position
300
301         jsr     CMOVEX                  ; Move the cursor
302
303         cli                             ; Allow interrupts
304         rts
305
306 ;----------------------------------------------------------------------------
307 ; BUTTONS: Return the button mask in a/x.
308
309 BUTTONS:
310         lda     Buttons
311         ldx     #$00
312         rts
313
314 ;----------------------------------------------------------------------------
315 ; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
316 ; No return code required.
317
318 POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
319
320         sei                             ; Disable interrupts
321         lda     XPos                    ; Transfer the position
322         sta     (ptr1),y
323         lda     XPos+1
324         iny
325         sta     (ptr1),y
326         lda     YPos
327         iny
328         sta     (ptr1),y
329         lda     YPos+1
330         cli                             ; Enable interrupts
331
332         iny
333         sta     (ptr1),y                ; Store last byte
334
335         rts                             ; Done
336
337 ;----------------------------------------------------------------------------
338 ; INFO: Returns mouse position and current button mask in the MOUSE_INFO
339 ; struct pointed to by ptr1. No return code required.
340 ;
341 ; We're cheating here to keep the code smaller: The first fields of the
342 ; mouse_info struct are identical to the mouse_pos struct, so we will just
343 ; call _mouse_pos to initialize the struct pointer and fill the position
344 ; fields.
345
346 INFO:   jsr     POS
347
348 ; Fill in the button state
349
350         lda     Buttons
351         ldy     #MOUSE_INFO::BUTTONS
352         sta     (ptr1),y
353
354         rts
355
356 ;----------------------------------------------------------------------------
357 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
358 ; specific data in ptr1, and the ioctl code in A.
359 ; Must return an error code in a/x.
360 ;
361
362 IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
363         ldx     #>MOUSE_ERR_INV_IOCTL
364         rts
365
366 ;----------------------------------------------------------------------------
367 ; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
368 ; (so be careful). The routine MUST return carry set if the interrupt has been
369 ; 'handled' - which means that the interrupt source is gone. Otherwise it
370 ; MUST return carry clear.
371 ;
372
373 IRQ:    jsr     CPREP
374         lda     KEY_COUNT
375         sta     old_key_count
376         lda     #$7F
377         sta     CIA1_PRA
378         lda     CIA1_PRB                ; Read joystick #0
379         and     #$1F
380         eor     #$1F                    ; Make all bits active high
381         sta     Temp
382
383 ; Check for a pressed button and place the result into Buttons
384
385         ldx     #$00                    ; Assume no button pressed
386         and     #JOY::FIRE              ; Check fire button
387         beq     @L0                     ; Jump if not pressed
388         ldx     #MOUSE_BTN_LEFT         ; Left (only) button is pressed
389 @L0:    stx     Buttons
390
391 ; Check left/right
392
393         lda     Temp                    ; Read joystick #0
394         and     #(JOY::LEFT | JOY::RIGHT)
395         beq     @SkipX                  ;
396
397 ; We will cheat here and rely on the fact that either the left, OR the right
398 ; bit can be active
399
400         and     #JOY::RIGHT             ; Check RIGHT bit
401         bne     @Right
402         lda     #$FF
403         tax
404         bne     @AddX                   ; Branch always
405 @Right: lda     #$01
406         ldx     #$00
407
408 ; Calculate the new X coordinate (--> a/y)
409
410 @AddX:  add     XPos
411         tay                             ; Remember low byte
412         txa
413         adc     XPos+1
414         tax
415
416 ; Limit the X coordinate to the bounding box
417
418         cpy     XMin
419         sbc     XMin+1
420         bpl     @L1
421         ldy     XMin
422         ldx     XMin+1
423         jmp     @L2
424 @L1:    txa
425
426         cpy     XMax
427         sbc     XMax+1
428         bmi     @L2
429         ldy     XMax
430         ldx     XMax+1
431 @L2:    sty     XPos
432         stx     XPos+1
433
434 ; Move the mouse pointer to the new X pos
435
436         tya
437         jsr     CMOVEX
438
439 ; Calculate the Y movement vector
440
441 @SkipX: lda     Temp                    ; Read joystick #0
442         and     #(JOY::UP | JOY::DOWN)  ; Check up/down
443         beq     @SkipY                  ;
444
445 ; We will cheat here and rely on the fact that either the up, OR the down
446 ; bit can be active
447
448         lsr     a                       ; Check UP bit
449         bcc     @Down
450         lda     #$FF
451         tax
452         bne     @AddY
453 @Down:  lda     #$01
454         ldx     #$00
455
456 ; Calculate the new Y coordinate (--> a/y)
457
458 @AddY:  add     YPos
459         tay                             ; Remember low byte
460         txa
461         adc     YPos+1
462         tax
463
464 ; Limit the Y coordinate to the bounding box
465
466         cpy     YMin
467         sbc     YMin+1
468         bpl     @L3
469         ldy     YMin
470         ldx     YMin+1
471         jmp     @L4
472 @L3:    txa
473
474         cpy     YMax
475         sbc     YMax+1
476         bmi     @L4
477         ldy     YMax
478         ldx     YMax+1
479 @L4:    sty     YPos
480         stx     YPos+1
481
482 ; Move the mouse pointer to the new X pos
483
484         tya
485         jsr     CMOVEY
486
487 ; Done
488
489 @SkipY: jsr     CDRAW
490         clc                             ; Interrupt not "handled"
491         rts
492
493 ;----------------------------------------------------------------------------
494 ; Called after ROM IRQ handler has been run.
495 ; Check if there was joystick activity before and/or after the ROM handler.
496 ; If there was activity, discard the key presses since they are most
497 ; probably "phantom" key presses.
498
499 callback:
500         ldx     old_key_count
501         cpx     KEY_COUNT
502         beq     @nokey
503
504         lda     Temp                    ; keypress before?
505         bne     @discard_key            ; yes, discard key
506
507         lda     #$7F
508         sta     CIA1_PRA
509         lda     CIA1_PRB                ; Read joystick #0
510         and     #$1F
511         eor     #$1F                    ; keypress after
512         beq     @nokey                  ; no, probably a real key press
513
514 @discard_key:
515         stx     KEY_COUNT               ; set old keyboard buffer fill level
516
517 @nokey: rts