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