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