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