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