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