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