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