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