]> git.sur5r.net Git - cc65/blob - libsrc/c64/mou/c64-1351.s
Added library reference address to lightpen driver header.
[cc65] / libsrc / c64 / mou / c64-1351.s
1 ;
2 ; Driver for the 1351 proportional mouse. Parts of the code are from
3 ; the Commodore 1351 mouse users guide.
4 ;
5 ; 2009-09-26, Ullrich von Bassewitz
6 ; 2010-02-06, Greg King
7 ;
8 ; The driver prevents the keyboard from interfering by changing the
9 ; keyboard's output port into an input port while the driver reads its
10 ; controller device.  That disables a wire that is left active by the
11 ; Kernal.  That wire is used by the STOP-key to break out of BASIC
12 ; programs -- CC65 programs don't use that feature.  The wire is shared
13 ; by these keys: STOP, "Q", Commodore, Space, "2", CTRL, Left-Arrow, and
14 ; "1".  I listed them, in order, from bit 7 over to bit 0.  The
15 ; rightmost five keys can look like joystick switches.
16 ;
17 ; The driver prevents the mouse/joystick from interfering by "blinding"
18 ; the keyboard scanner while any button/switch is active.  It changes
19 ; the input port into an output port, then stores all zero-bits in that
20 ; port's latch.  Reading from an output port sees the bitwise-AND of the
21 ; latch and the input signals.  Therefore, the scanner thinks that eight
22 ; keys are being pushed at the same time.  It doesn't know what to do
23 ; about that condition; so, it does nothing.  The driver lets the
24 ; scanner see normally, again, when no buttons/switches are active.
25 ;
26
27         .include        "zeropage.inc"
28         .include        "mouse-kernel.inc"
29         .include        "c64.inc"
30
31         .macpack        generic
32
33 ; ------------------------------------------------------------------------
34 ; Header. Includes jump table
35
36 .segment        "JUMPTABLE"
37
38 HEADER:
39
40 ; Driver signature
41
42         .byte   $6d, $6f, $75           ; "mou"
43         .byte   MOUSE_API_VERSION       ; Mouse driver API version number
44
45 ; Library reference
46
47         .addr   $0000
48
49 ; Jump table
50
51         .addr   INSTALL
52         .addr   UNINSTALL
53         .addr   HIDE
54         .addr   SHOW
55         .addr   SETBOX
56         .addr   GETBOX
57         .addr   MOVE
58         .addr   BUTTONS
59         .addr   POS
60         .addr   INFO
61         .addr   IOCTL
62         .addr   IRQ
63
64 ; Mouse driver flags
65
66         .byte   MOUSE_FLAG_LATE_IRQ
67
68 ; Callback table, set by the kernel before INSTALL is called
69
70 CHIDE:  jmp     $0000                   ; Hide the cursor
71 CSHOW:  jmp     $0000                   ; Show the cursor
72 CMOVEX: jmp     $0000                   ; Move the cursor to X coord
73 CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
74
75
76 ;----------------------------------------------------------------------------
77 ; Constants
78
79 SCREEN_HEIGHT   = YSIZE * 8 - 1         ; (origin is zero)
80 SCREEN_WIDTH    = XSIZE * 8 - 1
81
82 ;----------------------------------------------------------------------------
83 ; Global variables. The bounding box values are sorted so that they can be
84 ; written with the least effort in the SETBOX and GETBOX routines, so don't
85 ; reorder them.
86
87 .bss
88
89 Vars:
90 OldPotX:        .res    1               ; Old hw counter values
91 OldPotY:        .res    1
92
93 XPos:           .res    2               ; Current mouse position, X
94 YPos:           .res    2               ; Current mouse position, Y
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 status bits
100
101 OldValue:       .res    1               ; Temp for MoveCheck routine
102 NewValue:       .res    1               ; Temp for MoveCheck routine
103
104 ; Default values for above variables
105
106 .rodata
107
108 ; (We use ".proc" because we want to define both a label and a scope.)
109
110 .proc   DefVars
111         .byte   0, 0                    ; OldPotX/OldPotY
112         .word   SCREEN_WIDTH/2          ; XPos
113         .word   SCREEN_HEIGHT/2         ; YPos
114         .word   0                       ; XMin
115         .word   0                       ; YMin
116         .word   SCREEN_WIDTH            ; XMax
117         .word   SCREEN_HEIGHT           ; YMax
118         .byte   %00000000               ; Buttons
119 .endproc
120
121 .code
122
123 ;----------------------------------------------------------------------------
124 ; INSTALL routine. Is called after the driver is loaded into memory. If
125 ; possible, check if the hardware is present.
126 ; Must return an MOUSE_ERR_xx code in a/x.
127
128 INSTALL:
129
130 ; Initialize variables. Just copy the default stuff over
131
132         ldx     #.sizeof(DefVars)-1
133 @L1:    lda     DefVars,x
134         sta     Vars,x
135         dex
136         bpl     @L1
137
138 ; Be sure the mouse cursor is invisible and at the default location. We
139 ; need to do that here, because our mouse interrupt handler doesn't set the
140 ; mouse position if it hasn't changed.
141
142         sei
143         jsr     CHIDE
144         lda     XPos
145         ldx     XPos+1
146         jsr     CMOVEX
147         lda     YPos
148         ldx     YPos+1
149         jsr     CMOVEY
150         cli
151
152 ; Done, return zero (= MOUSE_ERR_OK)
153
154         ldx     #$00
155         txa
156         rts
157
158 ;----------------------------------------------------------------------------
159 ; UNINSTALL routine. Is called before the driver is removed from memory.
160 ; No return code required (the driver is removed from memory on return).
161
162 UNINSTALL       = HIDE                  ; Hide cursor on exit
163
164 ;----------------------------------------------------------------------------
165 ; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
166 ; a counter for calls to show/hide, and the driver entry point is only called
167 ; if the mouse is currently visible and should get hidden. For most drivers,
168 ; no special action is required besides hiding the mouse cursor.
169 ; No return code required.
170
171 HIDE:   sei
172         jsr     CHIDE
173         cli
174         rts
175
176 ;----------------------------------------------------------------------------
177 ; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
178 ; a counter for calls to show/hide, and the driver entry point is only called
179 ; if the mouse is currently hidden and should become visible. For most drivers,
180 ; no special action is required besides enabling the mouse cursor.
181 ; No return code required.
182
183 SHOW:   sei
184         jsr     CSHOW
185         cli
186         rts
187
188 ;----------------------------------------------------------------------------
189 ; SETBOX: Set the mouse bounding box. The parameters are passed as they come
190 ; from the C program, that is, a pointer to a mouse_box struct in a/x.
191 ; No checks are done if the mouse is currently inside the box, this is the job
192 ; of the caller. It is not necessary to validate the parameters, trust the
193 ; caller and save some code here. No return code required.
194
195 SETBOX: sta     ptr1
196         stx     ptr1+1                  ; Save data pointer
197
198         ldy     #.sizeof (MOUSE_BOX)-1
199         sei
200
201 @L1:    lda     (ptr1),y
202         sta     XMin,y
203         dey
204         bpl     @L1
205
206         cli
207         rts
208
209 ;----------------------------------------------------------------------------
210 ; GETBOX: Return the mouse bounding box. The parameters are passed as they
211 ; come from the C program, that is, a pointer to a mouse_box struct in a/x.
212
213 GETBOX: sta     ptr1
214         stx     ptr1+1                  ; Save data pointer
215
216         ldy     #.sizeof (MOUSE_BOX)-1
217 @L1:    lda     XMin,y
218         sta     (ptr1),y
219         dey
220         bpl     @L1
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     #0
256         and     #$1F
257         rts
258
259 ;----------------------------------------------------------------------------
260 ; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
261 ; No return code required.
262
263 POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
264
265         sei                             ; Disable interrupts
266         lda     XPos                    ; Transfer the position
267         sta     (ptr1),y
268         lda     XPos+1
269         iny
270         sta     (ptr1),y
271         lda     YPos
272         iny
273         sta     (ptr1),y
274         lda     YPos+1
275         cli                             ; Enable interrupts
276
277         iny
278         sta     (ptr1),y                ; Store last byte
279
280         rts                             ; Done
281
282 ;----------------------------------------------------------------------------
283 ; INFO: Returns mouse position and current button mask in the MOUSE_INFO
284 ; struct pointed to by ptr1. No return code required.
285 ;
286 ; We're cheating here to keep the code smaller: The first fields of the
287 ; mouse_info struct are identical to the mouse_pos struct, so we will just
288 ; call _mouse_pos to initialize the struct pointer and fill the position
289 ; fields.
290
291 INFO:   jsr     POS
292
293 ; Fill in the button state
294
295         jsr     BUTTONS                 ; Will not touch ptr1
296         ldy     #MOUSE_INFO::BUTTONS
297         sta     (ptr1),y
298
299         rts
300
301 ;----------------------------------------------------------------------------
302 ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
303 ; specific data in ptr1, and the ioctl code in A.
304 ; Must return an error code in a/x.
305 ;
306
307 IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
308         ldx     #>MOUSE_ERR_INV_IOCTL
309         rts
310
311 ;----------------------------------------------------------------------------
312 ; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
313 ; (so be careful). The routine MUST return carry set if the interrupt has been
314 ; 'handled' - which means that the interrupt source is gone. Otherwise it
315 ; MUST return carry clear.
316 ;
317
318 IRQ:
319
320 ; Record the state of the buttons.
321 ; Avoid crosstalk between the keyboard and the mouse.
322
323         ldy     #%00000000              ; Set ports A and B to input
324         sty     CIA1_DDRB
325         sty     CIA1_DDRA               ; Keyboard won't look like mouse
326         lda     CIA1_PRB                ; Read Control-Port 1
327         dec     CIA1_DDRA               ; Set port A back to output
328         eor     #%11111111              ; Bit goes up when button goes down
329         sta     Buttons
330         beq     @L0                     ;(bze)
331         dec     CIA1_DDRB               ; Mouse won't look like keyboard
332         sty     CIA1_PRB                ; Set "all keys pushed"
333
334 @L0:    lda     SID_ADConv1             ; Get mouse X movement
335         ldy     OldPotX
336         jsr     MoveCheck               ; Calculate movement vector
337
338 ; Skip processing if nothing has changed
339
340         bcc     @SkipX
341         sty     OldPotX
342
343 ; Calculate the new X coordinate (--> a/y)
344
345         add     XPos
346         tay                             ; Remember low byte
347         txa
348         adc     XPos+1
349         tax
350
351 ; Limit the X coordinate to the bounding box
352
353         cpy     XMin
354         sbc     XMin+1
355         bpl     @L1
356         ldy     XMin
357         ldx     XMin+1
358         jmp     @L2
359 @L1:    txa
360
361         cpy     XMax
362         sbc     XMax+1
363         bmi     @L2
364         ldy     XMax
365         ldx     XMax+1
366 @L2:    sty     XPos
367         stx     XPos+1
368
369 ; Move the mouse pointer to the new X pos
370
371         tya
372         jsr     CMOVEX
373
374 ; Calculate the Y movement vector
375
376 @SkipX: lda     SID_ADConv2             ; Get mouse Y movement
377         ldy     OldPotY
378         jsr     MoveCheck               ; Calculate movement
379
380 ; Skip processing if nothing has changed
381
382         bcc     @SkipY
383         sty     OldPotY
384
385 ; Calculate the new Y coordinate (--> a/y)
386
387         sta     OldValue
388         lda     YPos
389         sub     OldValue
390         tay
391         stx     OldValue
392         lda     YPos+1
393         sbc     OldValue
394         tax
395
396 ; Limit the Y coordinate to the bounding box
397
398         cpy     YMin
399         sbc     YMin+1
400         bpl     @L3
401         ldy     YMin
402         ldx     YMin+1
403         jmp     @L4
404 @L3:    txa
405
406         cpy     YMax
407         sbc     YMax+1
408         bmi     @L4
409         ldy     YMax
410         ldx     YMax+1
411 @L4:    sty     YPos
412         stx     YPos+1
413
414 ; Move the mouse pointer to the new Y pos
415
416         tya
417         jsr     CMOVEY
418
419 ; Done
420
421         clc                             ; Interrupt not "handled"
422 @SkipY: rts
423
424 ; --------------------------------------------------------------------------
425 ;
426 ; Move check routine, called for both coordinates.
427 ;
428 ; Entry:        y = old value of pot register
429 ;               a = current value of pot register
430 ; Exit:         y = value to use for old value
431 ;               x/a = delta value for position
432 ;
433
434 MoveCheck:
435         sty     OldValue
436         sta     NewValue
437         ldx     #$00
438
439         sub     OldValue                ; a = mod64 (new - old)
440         and     #%01111111
441         cmp     #%01000000              ; if (a > 0)
442         bcs     @L1                     ;
443         lsr     a                       ;   a /= 2;
444         beq     @L2                     ;   if (a != 0)
445         ldy     NewValue                ;     y = NewValue
446         sec
447         rts                             ;   return
448
449 @L1:    ora     #%11000000              ; else, "or" in high-order bits
450         cmp     #$FF                    ; if (a != -1)
451         beq     @L2
452         sec
453         ror     a                       ;   a /= 2
454         dex                             ;   high byte = -1 (X = $FF)
455         ldy     NewValue
456         sec
457         rts
458
459 @L2:    txa                             ; A = $00
460         clc
461         rts
462