TbltDrvr.a

*
*   Apple Macintosh Developer Technical Support
*
*   Sample 'ADBS' Resource
*
*   TbltDrvr
*
*   TbltDrvr.a  -   Assembler Source
*
*   Copyright © 1989 Apple Computer, Inc.
*   All rights reserved.
*
*   Versions:   
*               1.00            04/89
*
*   Components:
*               TbltDrvr.a      April 1, 1989
*
*   ******************************************************************* 
*   BUILD COMMANDS:
*   
*   asm TbltDrvr.a -lo TbltDrvr.a.lst -l
*   link TbltDrvr.a.o -o 'MrBootDisk:System Folder:System' -rt ADBS=4 -ra =16
*
*   *******************************************************************
*   ADBS resources are loaded and executed at boot time (before
*   INIT 31), and are made of two main parts, the installation/
*   initialization code and the the actual driver. 
*   
*   In this example, the installation portion allocates memory
*   in the system heap for the service routine and the "optional
*   data area". It installs the driver using the ADB Mgr call SetADBInfo.
*   
*   Generally speaking, ADB devices are intended to be user input devices.
*   The ADB Manager polls the bus every 11 milliseconds to see if a device
*   has new user input data. This is accomplished by sending a talk R0 command
*   to the last active device. The last active device is the last device that
*   had data to send to the host. If another device has data, it can request
*   a poll by sending a service request signal to the host.
*   
*   When a device has responded to a poll, the ADB Manager will call the driver
*   to process the data. This is done a interrupt time (level 1), and
*   The driver is passed the data, by getting a pointer to a pascal string
*   which contains the actual data.
*
*   In this example, the data is in the form of a pointing device's coordinates
*   and button(s) state(s). When the driver gets the data, it stores the 
*   coordinate information in RawMouse and MTemp. We stuff both RawMouse and MTemp,
*   because the tablet is an "absolute" device. It also checks the state of
*   the button, against MBState, and if there has been a change, it will update
*   MBState, and post either a mouseUp or mouseDown event, as appropriate.
*
*   NOTE:   This code demonstrate how to move the cursor position. This information
*           is meant for input device drivers only, this technique should not
*           be used by applications to move the cursor around. It's bad user
*           interface, and nobody likes a bad user interface, so Just Say No!
*
***********************************************************************
 
                    STRING  ASIS
                    PRINT   OFF
                    INCLUDE 'Traps.a'
                    INCLUDE 'SysEqu.a'
                    PRINT   ON
 
**************************** Driver Offsets ***************************
 
                                                ;this is the structure of the "optional data area"
StartDataArea       EQU     0
TabltID             EQU     StartDataArea       ;ID so Apps can find this data (should contain 'TBLT')
                                                ; this identifies the structure for this driver,
                                                ; if your structure is different, you should have 
                                                ; a different ID.
R0Count             EQU     TabltID+4           ;count byte of Pascal data string returned by ADB
R0Data              EQU     R0Count+1           ;data can be up to 8 bytes (but reference from R0Count
                                                ; for even address access)
R1Count             EQU     R0Data+8            ;count byte of Pascal data string returned by ADB
                                                ;data can be up to 8 bytes (reference from R1Data
R1Data              EQU     R1Count+1           ; for even address access)
MyFlags             EQU     R1Data+8            ;hi nibble are flags, bit 7 = update system cursor (zero = yes)
                                                ;bit 6 = new R0 data, bit 5 = new R1 data (1 = new)
                                                ;lo nibble is the address we ended up at (set by installer)
MyTalkR0            EQU     MyFlags+1           ;This byte used to build a talk R0 cmd byte with our new address
RsrvdPtr            EQU     MyTalkR0+1          ;Back door for Apple (you never knowÉ)
Scratch             EQU     RsrvdPtr+4          ;scratch area
ADBSvRt             EQU     Scratch+2           ;pointer to service routine
ADBDtAr             EQU     ADBSvRt+4           ;pointer to optional data area
NextjADBProc        EQU     ADBDtAr+4           ;a place to keep pointer to next guy in jADBProc chain
EndDataArea         EQU     NextjADBProc+4      ;this is my end marker & how big it is
 
******************************** Equates ******************************
 
TalkR0              EQU     $0C                 ;ADB Talk register zero (installer fills in the device address in upper nibble)
TalkR1              EQU     $0D                 ;ADB Talk register one (installer fills in the device address in upper nibble)
DoSysCrsr           EQU     $80                 ;mask for "update system cursor?" flag (in MyFlags)
FreshR0             EQU     $40                 ;mask for new R0 data (in MyFlags)
FreshR1             EQU     $20                 ;mask for new R1 data (in MyFlags)
TabltType           EQU     $7F                 ;Device type of digitizing tablet
* jADBProc          EQU     $6B8                ;can't find this in MPW equ's!
 
************************ Entry and installation ***********************
***
***     When the ADBS is loaded and jumped to, it comes here and branches
***     around the actual driver to the installation code.
***
***     On Entry:   A0 = Pointer to this routine
***                 D0 = ADB device address (0-15)
***                 D1 = ADB Device Type
***     
***********************************************************************
 
Entry   MAIN
 
        ALIGN 2
        
        bra         Installer
        
Entry2                                  ;this is how we enter the relocated installer
        move.l      a2,a0
        _DisposHandle                   ;dispose the original ADBS
        move.l      #0,a2               ;clear this handle
        bra         noChoice            ;now do that installation thang!
        
************************ My ADB Service Routine ***********************
***
***     ADB service routine for digitizing tablets.
***     
***     This routine is called as a result of the successful completion
***     of a Talk R0 or Talk R1 command. The successful completion of Talk R0 means
***     that the tablet has either new coordinate data or a change in a
***     button state, or both. This routine is also able to handle the
***     completion of a Talk R1 command, and will update the data area
***     with R1's contents.
***     
***     On Entry:   A0 = Pointer to Device Data
***                 A1 = Pointer to this routine
***                 A2 = Pointer to optional data area
***                 D0 = ADB Command that got us called
***     
***********************************************************************
 
DataCount   EQU     0                           ;this is the format of the tablet data
SwitchWord  EQU     DataCount+1                 ;info about switch(es) state(s)
XCoord      EQU     SwitchWord+2                ;hi byte = LSB of X, lo byte = MSB of X
YCoord      EQU     XCoord+2                    ;hi byte = LSB of Y, lo byte = MSB of Y
ZCoord      EQU     YCoord+2                    ;hi byte = LSB of Z, lo byte = MSB of Z
 
SwitchMask  EQU     $80                         ;bit seven = any button down (if one)
 
MySrvcRt
 
        movem.l     a0-a3/d0-d2,-(a7)           ;save regs we will abuse
        cmp.b       MyTalkR0(a2),d0
        bne         DoOther                     ;if not a Talk R0, see if a Talk R1
                
        move.b      MyFlags(a2),d1
        andi.b      #DoSysCrsr,d1               ;see if current app wants us to update the system cursor
        bne.s       NoUpdate                    ;if bit 7 set, just update data area, and check button
        
        move.b      YCoord(a0),MTemp            ;move coordinate data into cursor low mem locations
        move.b      YCoord+1(a0),MTemp+1
        move.b      XCoord(a0),MTemp+2
        move.b      XCoord+1(a0),MTemp+3
 
        move.l      MTemp,RawMouse              ;cursor guy likes to see this twice
                                                ;to make the position absolute. If RawMouse were not
                                                ;updated, jCrsrTask would attempt to scale the move
        MOVE.B      CrsrCouple,CrsrNew          ; note the change
NoUpdate
        lea         R0Count(a2),a3              ;set up the destination in a3, source is a0
        bsr.s       MoveData                    ;go update the data area
        ori.b       #FreshR0,MyFlags(a2)        ;flag to say the data is new
                                                ;  The following code with capatalized mnemonics (can
                                                ;  you say that?) is stolen from the Mac II ROM 
                                                ;  source. Thanks to the 4th floor, DA3 (or wherever
                                                ;  they are nowÉ).
        MOVEQ       #1, D0                      ; D0 holds the event (1 = mouse button down)
        move.b      #$00,d2                     ;our new MBState, assume mouse down
        move.b      SwitchWord+1(a0),d1         ;get switch state
        andi.b      #SwitchMask,d1              ;mask for button(s) down
        bne.s       Down                        ;if zero, no button down
        
        MOVEQ       #2, D0                      ; set D0 to 2 if mouse button is up
        move.b      #$80,d2                     ;well, turns out the button's up
 
Down    MOVE.B      MBState, D1                 ; get copy of last state
        EOR.B       D2, D1                      ; did it change?
        BPL.S       MyExit                      ; if it didn't, no event
 
; the mouse button changed, do debounce it , only generate event if it wasn't a bounce
        MOVE.L      TICKS,D1                    ; get current system ticks
        SUB.L       MBTICKS,D1                  ; and subtract ticks when the button changed
        CMP.L       #1,D1                       ; was it long enough?
        BLT.S       MyExit                      ; if not, don't post an event
 
        ADD.L       D1,RndSeed                  ; randomize our seed
        MOVE.L      TICKS,MBTICKS               ; since it changed, update the ticks        
 
        AND.B       #$80, D2                    ; just store high bit
        MOVE.B      D2, MBState                 ; also update the state
 
; post the mouse button event
;**********************************************************************************************
                                                ;add call to jCrsrTask!!!!!
;**********************************************************************************************                                             
        MOVE.L      D0, A0                      ; get event number where it belongs
        MOVEQ       #0, D0                      ; no message
        _PostEvent                              ; post the event
        
MyExit  movem.l     (a7)+,a0-a3/d0-d2           ;restore regs we abused
        rts
        
DoOther
 
        move.b      Myflags(a2),d2
        lsl.b       #4,d2                       ;move address up to hi nibble
        ori.b       #TalkR1,d2                  ;now we've built a Talk R1 at our address
        cmp.b       d2,d0                       ;so we can see if that's why we're here
        bne.s       MyExit                      ;wasn't a Talk R0, or a Talk R1, so get out
        lea         R1Count(a2),a3              ;we got a Talk R1, so point to R1 data area for move
        bsr.s       MoveData                    ;go put the R1 data in the data area
        ori.b       #FreshR1,MyFlags(a2)        ;flag that says data is new
        bra.s       MyExit                      ;all done
        
MoveData                                        ;A0 points to source, A3 points to destination
        movem.l     a0/a3/d0,-(a7)              ;save the regs
        move.w      #$08,d0                     ;get the count (never more than 9 bytes)
MD1     move.b      (a0)+,(a3)+                 ;move a byte at a timeÉ
        dbra        d0,MD1                      ;until low word of d0 = -1
        movem.l     (a7)+,a0/a3/d0              ;restore the regs
        rts
        
************************ My ADBReInit  Procedure **********************
***
***     ADBReInit Proc for deallocating memory before re-initializing
***     the ADB Mgr
***
***     On Entry:   D0 = 0 for pre processing, 1 for post processing
***                 A0 = Pointer to this routine
***     
***********************************************************************
 
jProc   
        cmpi.b      #1,d0
        beq.s       outtaHere                   ;only do the pre-processing
        move.b      d0,d2
 
        lea         MySrvcRtEnd,a2              ;get a pointer to the data area
        move.l      NextjADBProc(a2),-(a7)      ;push address of next guy in the chain
        move.l      ADBSvRt(a2),a0
        _DisposPtr                              ;dispose the service routine and data area pointer
        move.b      d2,d0                       ;restore d0
        rts
        
outtaHere
        move.l      NextjADBProc(a2),-(a7)      ;push address of next guy in the chain
        rts
        
MySrvcRtEnd                                     ;and begin optional data area
 
******************************* Installer *****************************
***     On Entry:   A0 = Pointer to this routine
***                 D0 = ADB device address (0-15)
***                 D1 = ADB Device Type
***********************************************************************
******************************** Equates ******************************
 
DataSiz             EQU     EndDataArea-StartDataArea   ;this is how big my optional data area is
RoutineSiz          EQU     MySrvcRtEnd-MySrvcRt        ;this is how big my service routine is
NewPtrSiz           EQU     RoutineSiz+DataSiz
jADBProcOffset      EQU     jProc-MySrvcRt              ;offset from NewPtr to my jADBProc (after blockmove)
jmpOffset           EQU     entry2-entry
 
Installer
 
        movem.l     a0-a3/d0-d2,-(a7)           ;protect the virtue of the registers we use
        move.b      d0,d2                       ;save device address for later
        move.l      #0,a2                       ;zero our handle regs (a2 for original ADBS and a3 the new one)
        move.l      #0,a3
        lea         Entry,a0                    ;get a handle on the ADBS
        _RecoverHandle
        move.l      a0,a2                       ;keep this handle around, we need to dispose is later
        cmp.b       #TabltType,d1               ;see if it's our kind of device
        bne         exit                        ;if not, quit
        
        _GetHandleSize                          ;this code relocates the ADBS hi in the heap, to
                                                ;try and cause the least amount of heap fragmentation
        bmi         exit                        ;something's wrong, let's get outta here
        move.l      d0,d1                       ;we'll need the size later for BlockMove
        _NewHandle  ,SYS                        ;get a new handle of our same size
        bne.s       noChoice                    ;if we can't get memory, go ahead and fragment the sys heap
        move.l      a0,a3                       ;remember the new handle
        _MoveHHi                                ;move this new handle up in the heap (error shouldn't be possible)
        _Hlock                                  ;lock it down (error shouldn't occur)
        move.l      d1,d0                       ;get our size
        move.l      (a0),a1                     ;set up destination ptr
        move.l      (a2),a0                     ;and source
        _BlockMove                              ;move entire ADBS up hi in the heap
                                                ;do this so that the NewPtr will be as low as
                                                ;possible in the heap, avoiding fragmentation due to
                                                ;ADBS being below NewPtr
        adda.l      #jmpOffset,a1               ;a0 now points to Entry2 in the copy of this ADBS
        jmp         (a1)                        ;now we start execution in our MoveHHi'd ADBS 
        
noChoice
        move.l      #NewPtrSiz,d0               ;make a block for the driver and data area
        _NewPtr     ,SYS,CLEAR                  ;we likes our bytes clean, we does
        bne.s       exit                        ;abort if error
        move.l      a0,a1                       ;set up destination for BlockMove
        lea         MySrvcRt,a0                 ;and source pointer
        move.l      #RoutineSiz,d0              ;and how many bytes to move
        _BlockMove                              ;now move the driver into our new ptr (no errors)
        move.l      a1,a0                       ;now create pointer to the data area
        adda.l      #RoutineSiz,a0              ;a0 now points to area past the service routine
        move.l      a0,ADBDtAr(a0)              ;keep pointer to data area in data area
        move.l      a1,ADBSvRt(a0)              ;keep pointer to service routine in data area
                                                ;initialize the data area
        move.l      #'TBLT',TabltID(a0)         ;set up the drivers data area
        andi.b      #$0F,d2                     ;clear hi nibble of device address
        or.b        d2,MyFlags(a0)              ;fill in our address for the driver's sake
        move.b      d2,d0                       ;set the device addrs for our tablet (for _SetADBInfo)
        lsl.b       #4,d2                       ;now move the address into the hi nibble
        move.b      d2,MyTalkR0(a0)             ;Create a TalkR0 command byte for the drvr to compare with
        ori.b       #TalkR0,MyTalkR0(a0)        ;now we've built a Talk R0 at our address
        move.l      a0,d2                       ;keep pointer to data area for later
                
        lea         ADBSvRt(a0),a0              ;set up for installing my stuff
        _SetADBInfo                             ;install it!
        tst.w       d0                          ;any error?
        bne.s       badExit                     ;if so, abort
 
        move.l      d2,a0                       ;get pointer to data area back
        move.l      jADBProc,NextjADBProc(a0)   ;get the next guy's address
        lea         jADBProcOffset(a1),a0       ;set up the jADBProc for _ADBReInt (Just in caseÉ)
        move.l      a0,jADBProc                 ;all done
        
exit    move.l      a2,a0                       ;the handle of the original ADBS
        cmpa.l      #0,a0                       ;make sure it's a real handle
        beq.s       exit2
        _DisposHandle                           ;nuke it!
 
exit2   move.l      a3,a0                       ;enter here with handle to dispose in a3
        cmpa.l      #0,a0                       ;if handle is nil, don't dispose
        beq.s       around
        _DisposHandle                           ;
around  movem.l     (a7)+,a0-a3/d0-d2           ;restore the registers we use
        rts                                     ;Return To Sender
 
badExit                                         ;error occured after allocating new ptr
        move.l      a1,a0
        _DisposPtr                              ;get rid of our pointer
        bra.s       exit
                
        END