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