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