1;
2; Mouse driver for ST & Amiga mouses and Atari trakball.
3;
4; Original access routines: 05/07/2000 Freddy Offenga
5; Converted to driver: Christian Groessler, 2014-01-04
6;
7; Defines:
8;       AMIGA_MOUSE     -       builds Amiga mouse version
9;       TRAK_MOUSE      -       builds trakball version
10; If none of these defines are active, the ST mouse version
11; is being built.
12;
13
14;DEBUG           =       1
15
16DISABLE_TIMEOUT =       30              ; # of vertical blank interrupts after which, if
17                                        ; no mouse motion occurred, the polling IRQ gets
18                                        ; disabled.
19                                        ; VBI frequency is 50Hz for PAL and 60Hz for NTSC
20
21        .include        "zeropage.inc"
22        .include        "mouse-kernel.inc"
23        .include        "atari.inc"
24
25        .macpack        generic
26        .macpack        module
27
28.if .not ( .defined (AMIGA_MOUSE) .or .defined (TRAK_MOUSE))
29        ST_MOUSE = 1
30.endif
31
32; ------------------------------------------------------------------------
33; Header. Includes jump table
34
35.if .defined (ST_MOUSE)
36
37.ifdef __ATARIXL__
38        module_header   _atrxst_mou
39.else
40        module_header   _atrst_mou
41.endif
42
43.elseif .defined (AMIGA_MOUSE)
44
45.ifdef __ATARIXL__
46        module_header   _atrxami_mou
47.else
48        module_header   _atrami_mou
49.endif
50
51.elseif .defined (TRAK_MOUSE)
52
53.ifdef __ATARIXL__
54        module_header   _atrxtrk_mou
55.else
56        module_header   _atrtrk_mou
57.endif
58
59.endif
60
61HEADER:
62
63; Driver signature
64
65        .byte   $6d, $6f, $75           ; "mou"
66        .byte   MOUSE_API_VERSION       ; Mouse driver API version number
67
68; Library reference
69
70libref: .addr   $0000
71
72; Jump table
73
74        .addr   INSTALL
75        .addr   UNINSTALL
76        .addr   HIDE
77        .addr   SHOW
78        .addr   SETBOX
79        .addr   GETBOX
80        .addr   MOVE
81        .addr   BUTTONS
82        .addr   POS
83        .addr   INFO
84        .addr   IOCTL
85        .addr   IRQ
86
87; Mouse driver flags
88
89        .byte   MOUSE_FLAG_LATE_IRQ
90
91; Callback table, set by the kernel before INSTALL is called
92
93CHIDE:  jmp     $0000                   ; Hide the cursor
94CSHOW:  jmp     $0000                   ; Show the cursor
95CPREP:  jmp     $0000                   ; Prepare to move the cursor
96CDRAW:  jmp     $0000                   ; Draw the cursor
97CMOVEX: jmp     $0000                   ; Move the cursor to X coord
98CMOVEY: jmp     $0000                   ; Move the cursor to Y coord
99
100
101;----------------------------------------------------------------------------
102; Constants
103
104SCREEN_HEIGHT   = 191
105SCREEN_WIDTH    = 319
106
107.enum   JOY
108        UP      = $01
109        DOWN    = $02
110        LEFT    = $04
111        RIGHT   = $08
112.endenum
113
114;----------------------------------------------------------------------------
115; Global variables. The bounding box values are sorted so that they can be
116; written with the least effort in the SETBOX and GETBOX routines, so don't
117; reorder them.
118
119.bss
120
121Vars:
122YPos:           .res    2               ; Current mouse position, Y
123XPos:           .res    2               ; Current mouse position, X
124XMin:           .res    2               ; X1 value of bounding box
125YMin:           .res    2               ; Y1 value of bounding box
126XMax:           .res    2               ; X2 value of bounding box
127YMax:           .res    2               ; Y2 value of bounding box
128Buttons:        .res    1               ; Button mask
129OldButton:      .res    1               ; previous buttons
130
131XPosWrk:        .res    2
132YPosWrk:        .res    2
133
134irq_enabled:    .res    1               ; flag indicating that the high frequency polling interrupt is enabled
135old_porta_vbi:  .res    1               ; previous PORTA value of the VBI interrupt (IRQ)
136how_long:       .res    1               ; counter for how many VBI interrupts the mouse hasn't been moved
137in_irq:         .res    1               ; flag indicating high-frequency polling interrupt is active
138
139.if .defined (AMIGA_MOUSE) .or .defined (ST_MOUSE)
140dumx:           .res    1
141dumy:           .res    1
142.endif
143
144.ifdef TRAK_MOUSE
145oldval:         .res    1
146.endif
147
148.ifndef __ATARIXL__
149OldT2:          .res    2
150.else
151
152.data
153set_VTIMR2_handler:
154                .byte   $4C, 0, 0
155.endif
156
157.rodata
158
159; Default values for some of the above variables
160; (We use ".proc" because we want to define both a label and a scope.)
161
162.proc   DefVars
163        .word   (SCREEN_HEIGHT+1)/2     ; YPos
164        .word   (SCREEN_WIDTH+1)/2      ; XPos
165        .word   0                       ; XMin
166        .word   0                       ; YMin
167        .word   SCREEN_WIDTH            ; XMax
168        .word   SCREEN_HEIGHT           ; YMax
169        .byte   0                       ; Buttons
170.endproc
171
172.ifdef ST_MOUSE
173
174; ST mouse lookup table
175
176STTab:  .byte $FF,$01,$00,$01
177        .byte $00,$FF,$00,$01
178        .byte $01,$00,$FF,$00
179        .byte $01,$00,$01,$FF
180
181.endif
182
183.ifdef AMIGA_MOUSE
184
185; Amiga mouse lookup table
186
187AmiTab: .byte $FF,$01,$00,$FF
188        .byte $00,$FF,$FF,$01
189        .byte $01,$FF,$FF,$00
190        .byte $FF,$00,$01,$FF
191
192.endif
193
194.code
195
196;----------------------------------------------------------------------------
197; INSTALL routine. Is called after the driver is loaded into memory. If
198; possible, check if the hardware is present.
199; Must return an MOUSE_ERR_xx code in a/x.
200
201INSTALL:
202
203; Initialize variables. Just copy the default stuff over
204
205        ldx     #.sizeof(DefVars)-1
206@L1:    lda     DefVars,x
207        sta     Vars,x
208        dex
209        bpl     @L1
210
211; Make sure the mouse cursor is at the default location.
212
213        lda     XPos
214        sta     XPosWrk
215        ldx     XPos+1
216        stx     XPosWrk+1
217        jsr     CMOVEX
218        lda     YPos
219        sta     YPosWrk
220        ldx     YPos+1
221        stx     YPosWrk+1
222        jsr     CMOVEY
223
224; Install timer irq routine to poll mouse.
225
226.ifdef __ATARIXL__
227
228        ; Setup pointer to wrapper install/deinstall function.
229        lda     libref
230        sta     set_VTIMR2_handler+1
231        lda     libref+1
232        sta     set_VTIMR2_handler+2
233
234        ; Install my handler.
235        sec
236        lda     #<T2Han
237        ldx     #>T2Han
238        jsr     set_VTIMR2_handler
239
240.else
241
242        lda     VTIMR2
243        sta     OldT2
244        lda     VTIMR2+1
245        sta     OldT2+1
246
247        php
248        sei
249        lda     #<T2Han
250        sta     VTIMR2
251        lda     #>T2Han
252        sta     VTIMR2+1
253        plp
254
255.endif
256
257        lda     #%00000001
258        sta     AUDCTL
259
260        lda     #0
261        sta     AUDC2
262
263        lda     #15
264        sta     AUDF2
265        sta     STIMER
266
267        lda     PORTA
268        and     #$0f
269        sta     old_porta_vbi
270
271; Done, return zero (= MOUSE_ERR_OK)
272
273        ldx     #$00
274        txa
275        rts
276
277;----------------------------------------------------------------------------
278; UNINSTALL routine. Is called before the driver is removed from memory.
279; No return code required (the driver is removed from memory on return).
280
281UNINSTALL:
282
283; uninstall timer irq routine
284
285        lda     POKMSK
286        and     #%11111101              ; timer 2 disable
287        sta     IRQEN
288        sta     POKMSK
289
290.ifdef __ATARIXL__
291
292        clc
293        jsr     set_VTIMR2_handler
294
295.else
296
297        php
298        sei
299        lda     OldT2
300        sta     VTIMR2
301        lda     OldT2+1
302        sta     VTIMR2+1
303        plp
304
305.endif
306        ; fall thru...
307
308;----------------------------------------------------------------------------
309; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
310; a counter for calls to show/hide, and the driver entry point is only called
311; if the mouse is currently visible and should get hidden. For most drivers,
312; no special action is required besides hiding the mouse cursor.
313; No return code required.
314
315HIDE:   php
316        sei
317        jsr     CHIDE
318        plp
319        rts
320
321;----------------------------------------------------------------------------
322; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
323; a counter for calls to show/hide, and the driver entry point is only called
324; if the mouse is currently hidden and should become visible. For most drivers,
325; no special action is required besides enabling the mouse cursor.
326; No return code required.
327
328SHOW:   php
329        sei
330        jsr     CSHOW
331        plp
332        rts
333
334;----------------------------------------------------------------------------
335; SETBOX: Set the mouse bounding box. The parameters are passed as they come
336; from the C program, that is, a pointer to a mouse_box struct in a/x.
337; No checks are done if the mouse is currently inside the box, this is the job
338; of the caller. It is not necessary to validate the parameters, trust the
339; caller and save some code here. No return code required.
340
341SETBOX: sta     ptr1
342        stx     ptr1+1                  ; Save data pointer
343
344        ldy     #.sizeof (MOUSE_BOX)-1
345        php
346        sei
347
348@L1:    lda     (ptr1),y
349        sta     XMin,y
350        dey
351        bpl     @L1
352
353        plp
354        rts
355
356;----------------------------------------------------------------------------
357; GETBOX: Return the mouse bounding box. The parameters are passed as they
358; come from the C program, that is, a pointer to a mouse_box struct in a/x.
359
360GETBOX: sta     ptr1
361        stx     ptr1+1                  ; Save data pointer
362
363        ldy     #.sizeof (MOUSE_BOX)-1
364        php
365        sei
366
367@L1:    lda     XMin,y
368        sta     (ptr1),y
369        dey
370        bpl     @L1
371
372        plp
373        rts
374
375;----------------------------------------------------------------------------
376; MOVE: Move the mouse to a new position. The position is passed as it comes
377; from the C program, that is: X on the stack and Y in a/x. The C wrapper will
378; remove the parameter from the stack on return.
379; No checks are done if the new position is valid (within the bounding box or
380; the screen). No return code required.
381;
382
383MOVE:   php
384        sei                             ; No interrupts
385
386        pha
387        txa
388        pha
389        jsr     CPREP
390        pla
391        tax
392        pla
393
394        sta     YPos
395        sta     YPosWrk
396        stx     YPos+1                  ; New Y position
397        stx     YPosWrk+1
398        jsr     CMOVEY                  ; Set it
399
400        ldy     #$01
401        lda     (sp),y
402        sta     XPos+1
403        sta     XPosWrk+1
404        tax
405        dey
406        lda     (sp),y
407        sta     XPos                    ; New X position
408        sta     XPosWrk
409        jsr     CMOVEX                  ; Move the cursor
410
411        jsr     CDRAW
412
413        plp                             ; Restore interrupt flag
414        rts
415
416;----------------------------------------------------------------------------
417; BUTTONS: Return the button mask in a/x.
418
419BUTTONS:
420        lda     Buttons
421        ldx     #$00
422        rts
423
424;----------------------------------------------------------------------------
425; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
426; No return code required.
427
428POS:    ldy     #MOUSE_POS::XCOORD      ; Structure offset
429
430        php
431        sei                             ; Disable interrupts
432        lda     XPos                    ; Transfer the position
433        sta     (ptr1),y
434        lda     XPos+1
435        iny
436        sta     (ptr1),y
437        lda     YPos
438        iny
439        sta     (ptr1),y
440        lda     YPos+1
441        plp                             ; Restore interrupt flag
442
443        iny
444        sta     (ptr1),y                ; Store last byte
445
446        rts                             ; Done
447
448;----------------------------------------------------------------------------
449; INFO: Returns mouse position and current button mask in the MOUSE_INFO
450; struct pointed to by ptr1. No return code required.
451;
452; We're cheating here to keep the code smaller: The first fields of the
453; mouse_info struct are identical to the mouse_pos struct, so we will just
454; call _mouse_pos to initialize the struct pointer and fill the position
455; fields.
456
457INFO:   jsr     POS
458
459; Fill in the button state
460
461        lda     Buttons
462        ldy     #MOUSE_INFO::BUTTONS
463        sta     (ptr1),y
464
465        rts
466
467;----------------------------------------------------------------------------
468; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
469; specific data in ptr1, and the ioctl code in A.
470; Must return an error code in a/x.
471;
472
473IOCTL:  lda     #<MOUSE_ERR_INV_IOCTL     ; We don't support ioclts for now
474        ldx     #>MOUSE_ERR_INV_IOCTL
475        rts
476
477;----------------------------------------------------------------------------
478; IRQ: Irq handler entry point. Called as a subroutine but in IRQ context
479; (so be careful). The routine MUST return carry set if the interrupt has been
480; 'handled' - which means that the interrupt source is gone. Otherwise it
481; MUST return carry clear.
482;
483
484IRQ:    lda     PORTA                   ; mouse port contents
485        and     #$0f                    ; check port 1 only
486        ldx     irq_enabled
487        bne     @L1
488
489; IRQ is disabled, check for mouse motion and enable IRQ if mouse motion detected
490
491        cmp     old_porta_vbi
492        beq     @L3                     ; no motion
493        lda     #0
494        sta     ATRACT                  ; disable "attract mode"
495
496; Turn mouse polling IRQ back on
497
498        lda     POKMSK
499        ora     #%00000010              ; timer 2 enable
500        sta     POKMSK
501        sta     IRQEN
502        sta     irq_enabled
503        bne     @L3
504        ; not reached
505
506; IRQ is enabled
507
508@L1:    cmp     old_porta_vbi           ; mouse motion since last VBI?
509        sta     old_porta_vbi
510        beq     @L2                     ; no, increment timeout to disable IRQ
511
512        lda     #0
513        sta     how_long                ; yes, reinitialize wait counter
514        beq     @L3
515        ; not reached
516
517@L2:    inc     how_long                ; no motion, increment wait counter
518        lda     how_long
519        cmp     #DISABLE_TIMEOUT        ; timeout?
520        bcc     @L3                     ; no
521
522        lda     #0                      ; yes, turn off IRQ
523        sta     how_long
524
525; no mouse input -- turn IRQ off
526
527        sta     irq_enabled
528        lda     POKMSK
529        and     #%11111101              ; timer 2 disable
530        sta     IRQEN
531        sta     POKMSK
532
533; Check for a pressed button and place the result into Buttons
534
535@L3:    ldx     #0
536        lda     TRIG0                   ; joystick #0 trigger
537        bne     @L4                     ; not pressed
538        ldx     #MOUSE_BTN_LEFT
539@L4:    stx     Buttons
540
541        jsr     CPREP
542
543; Disable "attract mode" if button status has changed
544
545        lda     Buttons
546        cmp     OldButton
547        beq     @L5
548        sta     OldButton
549        lda     #0
550        sta     ATRACT
551
552; Limit the X coordinate to the bounding box
553
554@L5:    lda     XPosWrk+1
555        ldy     XPosWrk
556        tax
557        cpy     XMin
558        sbc     XMin+1
559        bpl     @L6
560        ldy     XMin
561        ldx     XMin+1
562        jmp     @L7
563
564@L6:    txa
565        cpy     XMax
566        sbc     XMax+1
567        bmi     @L7
568        ldy     XMax
569        ldx     XMax+1
570@L7:    sty     XPos
571        stx     XPos+1
572        tya
573        jsr     CMOVEX
574
575; Limit the Y coordinate to the bounding box
576
577        lda     YPosWrk+1
578        ldy     YPosWrk
579        tax
580        cpy     YMin
581        sbc     YMin+1
582        bpl     @L8
583        ldy     YMin
584        ldx     YMin+1
585        jmp     @L9
586
587@L8:    txa
588        cpy     YMax
589        sbc     YMax+1
590        bmi     @L9
591        ldy     YMax
592        ldx     YMax+1
593@L9:    sty     YPos
594        stx     YPos+1
595        tya
596        jsr     CMOVEY
597
598        jsr     CDRAW
599
600.ifdef  DEBUG
601        ; print on upper right corner 'E' or 'D', indicating the IRQ is enabled or disabled
602        ldy     irq_enabled
603        beq     @L10
604        lda     #37                     ; screen code for 'E'
605        .byte   $2c                     ; bit opcode, eats next 2 bytes
606@L10:   lda     #36                     ; screen code for 'D'
607        ldy     #39
608        sta     (SAVMSC),y
609.endif
610
611        clc
612        rts
613
614
615;----------------------------------------------------------------------------
616; T2Han: Local IRQ routine to poll mouse
617;
618
619T2Han:  lda     CRITIC                  ; if CRITIC flag is set, disable the
620        bne     disable_me              ; high frequency polling IRQ, in order
621                                        ; not to interfere with SIO I/O (e.g.
622                                        ; floppy access or serial I/O)
623
624        lda     in_irq                  ; handler entered again?
625        bne     skip                    ; yes, ignore this interrupt
626        inc     in_irq
627        cli                             ; enable IRQs so that we don't block them for too long
628
629        tya
630        pha
631        txa
632        pha
633
634.ifdef DEBUG
635        lda     RANDOM
636        sta     COLBK
637.endif
638
639        lda     PORTA
640        tay
641
642.ifdef ST_MOUSE
643
644; ST mouse version
645
646        and     #%00000011
647        ora     dumx
648        tax
649        lda     STTab,x
650        bmi     nxst
651
652        beq     xist
653
654        dec     XPosWrk
655        lda     XPosWrk
656        cmp     #255
657        bne     nxst
658        dec     XPosWrk+1
659        jmp     nxst
660
661xist:   inc     XPosWrk
662        bne     nxst
663        inc     XPosWrk+1
664
665nxst:   tya
666        and     #%00001100
667        ora     dumy
668        tax
669        lda     STTab,x
670        bmi     nyst
671
672        bne     yst
673
674        dec     YPosWrk
675        lda     YPosWrk
676        cmp     #255
677        bne     nyst
678        dec     YPosWrk+1
679        jmp     nyst
680
681yst:    inc     YPosWrk
682        bne     nyst
683        inc     YPosWrk+1
684
685; store old readings
686
687nyst:   tya
688        and     #%00000011
689        asl
690        asl
691        sta     dumx
692        tya
693        and     #%00001100
694        lsr
695        lsr
696        sta     dumy
697
698.elseif .defined (AMIGA_MOUSE)
699
700; Amiga mouse version
701
702        lsr
703        and     #%00000101
704        ora     dumx
705        tax
706        lda     AmiTab,x
707        bmi     nxami
708
709        bne     xiami
710
711        dec     XPosWrk
712        lda     XPosWrk
713        cmp     #255
714        bne     nxami
715        dec     XPosWrk+1
716        jmp     nxami
717
718xiami:  inc     XPosWrk
719        bne     nxami
720        inc     XPosWrk+1
721
722nxami:  tya
723
724        and     #%00000101
725        ora     dumy
726        tax
727        lda     AmiTab,x
728        bmi     nyami
729
730        bne     yiami
731
732        dec     YPosWrk
733        lda     YPosWrk
734        cmp     #255
735        bne     nyami
736        dec     YPosWrk+1
737        jmp     nyami
738
739yiami:  inc     YPosWrk
740        bne     nyami
741        inc     YPosWrk+1
742
743; store old readings
744
745nyami:  tya
746        and     #%00001010
747        sta     dumx
748        tya
749        and     #%00000101
750        asl
751        sta     dumy
752
753.elseif .defined (TRAK_MOUSE)
754
755; trakball version
756
757        eor     oldval
758        and     #%00001000
759        beq     horiz
760
761        tya
762        and     #%00000100
763        beq     mmup
764
765        inc     YPosWrk
766        bne     horiz
767        inc     YPosWrk+1
768        bne     horiz
769
770mmup:   dec     YPosWrk
771        lda     YPosWrk
772        cmp     #255
773        bne     horiz
774        dec     YPosWrk+1
775
776horiz:  tya
777        eor     oldval
778        and     #%00000010
779        beq     mmexit
780
781        tya
782        and     #%00000001
783        beq     mmleft
784
785        inc     XPosWrk
786        bne     mmexit
787        inc     XPosWrk+1
788        bne     mmexit
789
790mmleft: dec     XPosWrk
791        lda     XPosWrk
792        cmp     #255
793        bne     mmexit
794        dec     XPosWrk+1
795
796mmexit: sty     oldval
797
798.endif
799
800        pla
801        tax
802        pla
803        tay
804        dec     in_irq
805skip:
806.ifdef  __ATARIXL__
807        rts
808.else
809        pla
810        rti
811.endif
812
813
814; Disable the interrupt source which caused us to be called.
815; The interrupt will be enabled again by the "IRQ" routine.
816; The "IRQ" routine, despite its name, is called from the
817; vertical blank NMI interrupt *only* if the CRITIC flag has
818; been cleared.
819
820disable_me:
821        lda     POKMSK
822        and     #%11111101              ; timer 2 disable
823        sta     IRQEN
824        sta     POKMSK
825        lda     #0
826        sta     irq_enabled
827        lda     PORTA
828        and     #$0f
829        sta     old_porta_vbi
830.ifdef  __ATARIXL__
831        rts
832.else
833        pla
834        rti
835.endif
836