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