;*********************************************************************
;*                                                                   *
;* All changes I made are hereby in the Public Domain.  Commodore    *
;* retains its copyright, if any, of the original code in the Rom    *
;* Kernel manuals since this is a derivative work.  -Jeff Rush       *
;*                                                                   *
;*********************************************************************
;*                           History                                 *
;*                                                                   *
;* 20Sep89 jrr Original crib from Amiga Rom Kernel Manual.           *
;* 23Jul90 jrr Extensive descriptive prose added.                    *
;*                                                                   *
;*********************************************************************
        TITLE   Example Device Driver for Tutorial Use
        SMALLOBJ    ; Use PC-Relative Addressing
        OPTIMON     ; Enable C.A.P.E. 68K Optimizations

        SECTION TheOnlySection

        ;************
        ;* Includes *
        ;************
        NOLIST
        INCLUDE "exec/types.i"
        INCLUDE "exec/devices.i"
        INCLUDE "exec/initializers.i"
        INCLUDE "exec/memory.i"
        INCLUDE "exec/resident.i"
        INCLUDE "exec/io.i"
        INCLUDE "exec/ables.i"
        INCLUDE "exec/errors.i"
        INCLUDE "exec/tasks.i"
        INCLUDE "exec/semaphores.i"
        INCLUDE "hardware/intbits.i"

        INCLUDE "asmsupp.i"

        INCLUDE "Example.i"     ; Device-Specific Definitions
        LIST

        ;*************
        ;* Constants *
        ;*************

        ;******************
        ;* Public Symbols *
        ;******************
        XDEF    EndCode         ; Label at End of Device Code
        XDEF    LockGlobal      ; Obtain Exclusive Access to Device Global Data
        XDEF    UnlockGlobal    ; Release Exclusive Access to Device Global Data
        XDEF    PerformIO       ; Common Fork or Dispatch to Handle Device Commands
        XDEF    TermIO          ; Common Join from Device Commands to Do Common Cleanup

        ;********************
        ;* External Symbols *
        ;********************
        ; This Name is for Debugging Use, to Annotate Serial-Debug Output
        IFNE    INFO_LEVEL      ; If Any Debugging Enabled at All
        XREF    subSysName
        ENDC

        ; Defined in Ex_Main.Asm
        XREF    CmdTable    ; Device Global Command Dispatch Table

;*********************************************************************
;*        LockGlobal Helper Routine (for Open, Close, etc.)          *
;*                                                                   *
;* This functions uses a semaphore defined in the device global data *
;* to obtain exclusive access to the device global data area.  It is *
;* used by DevOpen and DevClose to setup and takedown items that may *
;* break a Forbid().  Its use is optional and is not required if you *
;* have very simple opens/closes, *and* multiple copies of your unit *
;* tasks are not attempting to alter the device global area          *
;* concurrently.  They may all read it however, as long as no one is *
;* actually trying to -change- it while others are reading from it.  *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*                                                                   *
;* Outputs:                                                          *
;*   None                                                            *
;*                                                                   *
;*      * All Registers are Preserved, Including D0-D1/A0-A1 *       *
;*                                                                   *
;* Notes:                                                            *
;*   Calling task blocks until exclusive access can be granted.      *
;*                                                                   *
;*********************************************************************
LockGlobal:
        ;
        ; Lock Device Global Data for Exclusive Access
        MOVEM.L D0-D1/A0-A1,-(SP)   ; Preserve Registers During Lock
        LEA.L   md_SemLock(A6),A0   ; Pickup Ptr to the Arbitration Semaphore
        LINKSYS ObtainSemaphore,md_SysLib(A6)   ; Get Control of Device Node
        MOVEM.L (SP)+,D0-D1/A0-A1   ; Restore Registers
        RTS

;*********************************************************************
;*       UnlockGlobal Helper Routine (for Open, Close, etc.)         *
;*                                                                   *
;* This functions uses a semaphore defined in the device global data *
;* to release exclusive access to the device global data area.  It   *
;* is used by DevOpen and DevClose to setup and takedown items that  *
;* may break a Forbid().  Its use is optional and is not required if *
;* you have very simple opens/closes, *and* multiple copies of your  *
;* unit tasks are not attempting to alter the device global area     *
;* concurrently.  They may all read it however, as long as no one is *
;* actually trying to -change- it while others are reading from it.  *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*                                                                   *
;* Outputs:                                                          *
;*   None                                                            *
;*                                                                   *
;*      * All Registers are Preserved, Including D0-D1/A0-A1 *       *
;*                                                                   *
;* Notes:                                                            *
;*   Calling task blocks until exclusive access can be granted.      *
;*                                                                   *
;*********************************************************************
UnlockGlobal:
        ;
        ; Unlock Device Global Data from Exclusive Access
        MOVEM.L D0-D1/A0-A1,-(SP)   ; Preserve Registers During Unlock
        LEA.L   md_SemLock(A6),A0   ; Pickup Ptr to the Arbitration Semaphore
        LINKSYS ReleaseSemaphore,md_SysLib(A6)   ; Release Control of Device Node
        MOVEM.L (SP)+,D0-D1/A0-A1   ; Restore Registers
        RTS

;*********************************************************************
;*              PerformIO Helper Routine (for BeginIO)               *
;*                                                                   *
;* PerformIO actually dispatches an I/O request.  It might be called *
;* from within the unit task, or directly from BeginIO (and thus on  *
;* the callers's schedule).                                          *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block as initialized by    *
;*        OpenDevice().                                              *
;*   A3 - Ptr to the Unit Descriptor.                                *
;*                                                                   *
;*   (Bounds checking was done on the inputs by the caller)          *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation (zero means success).       *
;*                                                                   *
;*********************************************************************
PerformIO:
        IFGE    INFO_LEVEL-150
        CLR.L   -(SP)               ; Allocate and Clear a 32-bit Scratch Word
        MOVE.W  IO_COMMAND(A1),2(SP)  ; Pick up Command Code
        PUTMSG  150,<'%s/PerformIO -- %ld'> ; Output a Debugging Message
        ADDQ.L  #4,SP               ; Deallocate the 32-bit Scratch Word
        ENDC

        MOVEQ   #0,D0
        MOVE.B  D0,IO_ERROR(A1)     ; Initially Clear the Error Field¨to 'Ok'
        MOVE.B  IO_COMMAND+1(A1),D0 ; Pick up Command Byte
        LSL.W   #2,D0               ; Multiply by 4 to Get Table Offset
        LEA.L   CmdTable(PC),A0     ; Pick up Base of Command Table in A0
        MOVE.L  0(A0,D0.W),A0       ; Index into Table and Get Ptr to Handler Code
        ;
        ; Branch into Appropriate Handler Code for Desired Command
        ;   A1 - Ptr to I/O Request Block
        ;   A3 - Ptr to Unit Descriptor
        ;   A6 - Ptr to Device Node
        JMP     (A0)

;*********************************************************************
;*               TermIO Helper Routine (for BeginIO)                 *
;*                                                                   *
;* TermIO sends the I/O request back to the user.  It knows not to   *
;* mark the device as inactive if this was an immediate request or   *
;* if the request was started from the unit task.                    *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block as initialized by    *
;*        OpenDevice().                                              *
;*   A3 - Ptr to the Unit Descriptor.                                *
;*                                                                   *
;*   (Bounds checking was done on the inputs by the caller)          *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation (zero means success).       *
;*                                                                   *
;*********************************************************************
TermIO:
        PUTMSG  160,<'%s/TermIO'>
        MOVE.W  IO_COMMAND(A1),D0   ; Pick up Command Code in D0

        MOVE.L  #IMMEDIATES,D1      ; Pick up Immediate Command Classification Mask
        BTST    D0,D1               ; Test Command for an Immediate Type
        BNE.S   TermIO_Immediate    ; I/O was Immediate, Skip Task Handling Code

        BTST    #UNITB_INTASK,UNIT_FLAGS(A3)    ; Did I/O Come from Task?
        BNE.S   TermIO_Immediate    ; Yes, Don't Clear the Task-Active Flag

        BCLR    #UNITB_ACTIVE,UNIT_FLAGS(A3)    ; Clear the Task-Active Flag

TermIO_Immediate:
        BTST    #IOB_QUICK,IO_FLAGS(A1) ; Is the I/O Quick Bit Still Set?
        BNE.S   TermIO_End  ; Yes, Request Was Not Queued, Skip Reply Process
        ;
        ; Reply the Message Back to the Original Issuing Task
        LINKSYS ReplyMsg,md_SysLib(A6)      ; Ptr to Message:A1
        ; Note: The ReplyMsg() Function Sets the LN_TYPE to NT_REPLYMSG
TermIO_End:
        RTS

;*********************************************************************
;* 'EndCode' is a marker that shows the end of your code.  Make sure *
;* it does not span hunks and is not before the Romtag.              *
;*********************************************************************
EndCode:
        END

