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