home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
420.lha
/
Example_device
/
Ex_EntryPts.asm
< prev
next >
Wrap
Assembly Source File
|
1990-09-29
|
32KB
|
567 lines
;*********************************************************************
;* *
;* 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 LoadInitRoutine ; Initialization when Device First Loads into Memory
XDEF DevOpen ; Unit Allocation for Each Time Device is Opened
XDEF DevClose ; Unit Deallocation for Each Time Device is Opened
XDEF DevExpunge ; Remove Device from Memory if No One is Using It
XDEF DevNull ; Reserved for Future Expansion
XDEF DevBeginIO ; Initiate an I/O Request either Now or Put in Queue
XDEF DevAbortIO ; Abort an I/O Request either Active or in Queue
;********************
;* 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_Support.Asm
XREF LockGlobal ; Obtain Exclusive Access to Device Global Data
XREF UnlockGlobal ; Release Exclusive Access to Device Global Data
XREF PerformIO ; Common Fork or Dispatch to Handle Device Commands
; Defined in Ex_UnitMgr.Asm
XREF InitUnit ; Initialize a Unit Descriptor and Fire Up a Task
XREF ExpungeUnit ; Terminate a Unit Task and Deallocate the Unit Descriptor
;*********************************************************************
;* Device-Just-Loaded-Into-Ram Initialization Routine *
;* *
;* This routine gets called immediately after the device has been *
;* loaded into Ram, in order to allow it to set up its environment. *
;* *
;* Inputs: *
;* D0 - Ptr to base of the newly allocated device node structure. *
;* A0 - BPtr to the segment list for the device's code. *
;* A6 - Ptr to the Exec Library. *
;* *
;* Outputs: *
;* D0 - Ptr to base of where the device has been loaded or NULL *
;* if initialization failed and the device is to be unloaded. *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* The call to this routine is single-threaded by Exec, providing *
;* it does not perform a Wait(), either directly or indirectly. *
;* This means that it cannot perform any DOS calls, even to open *
;* a disk-based library. Libraries which are -known- to be in *
;* ram can be opened however, such as Exec, Expansion, etc. *
;* *
;* This function is called once per time the device is loaded into *
;* ram, unlike DevOpen() which is called each time the device is *
;* opened by a caller. An Exec device can be loaded once and *
;* opened multiple times, either for different units or in *
;* succession without an intervening expunge. *
;* *
;* At the time this function is called, the device node is -NOT- *
;* yet linked into the system lists. *
;* *
;* If you don't use the "RTF_AUTOINIT" feature, there is an *
;* additional caveat. If you allocate memory in your Open *
;* function, remember that allocating memory can cause an Expunge, *
;* including an expunge of your device. This must not be fatal. *
;* The easy solution is to avoid adding your device to the public *
;* list until after it is ready for action, to prevent your being *
;* on the list of expunge candidates. *
;* *
;*********************************************************************
LoadInitRoutine:
PUTMSG 5,<'%s/Init: called'>
MOVEM.L D1-D7/A0-A5,-(SP) ; Preserve ALL modified registers
;Debug CALLSYS Debug ; Enter ROMWack Debugger to Determine My Address
MOVE.L D0,A5 ; Set A5 to Base of Our Device
MOVE.L A6,md_SysLib(A5) ; Keep a Ptr to the Exec Library
MOVE.L A0,md_SegList(A5) ; Keep a Ptr to our Seglist
;
; Initialize the Device Global Data Arbitration Semaphore
LEA.L md_SemLock(A5),A0 ; Pickup Ptr to the Arbitration Semaphore
CALLSYS InitSemaphore ; Obtain Control of the Device Node
MOVE.L A5,D0 ; Signal Success via Device Ptr in D0
MOVEM.L (SP)+,D1-D7/A0-A5 ; Restore ALL modified registers
RTS
;*********************************************************************
;* Open Device Routine *
;* *
;* When the user calls OpenDevice(), this eventually gets translated *
;* into a call to this routine. By the time this routine gets *
;* control, the device node has already been linked into the system *
;* list of known devices. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* A1 - Ptr to the caller's I/O request block. *
;* D0 - Unit number desired. *
;* D1 - Device-Specific Flags indicating desired. *
;* *
;* Outputs: *
;* D0 - Error code of operation (0 if open succeeded). *
;* IO_ERROR - Error code of operation (another copy as a byte). *
;* IO_UNIT - Some unit identification information (if success). *
;* IO_DEVICE - Set up by Exec on behalf of device. *
;* LN_TYPE - Signal end of operation (unclear on requirement) *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* The call to this routine is single-threaded by Exec, providing *
;* it does not perform a Wait(), either directly or indirectly. *
;* Because of this, do not take very much time. *
;* *
;*********************************************************************
DevOpen:
PUTMSG 20,<'%s/Open: called'>
MOVEM.L D2/A2/A3/A4,-(SP) ; Preserve Original Registers Across Call
MOVE.L A1,A2 ; Save Ptr to the I/O Block in A2
ADDQ.W #1,LIB_OPENCNT(A6) ; Add Fake Opener to Prevent Expunge
BSR LockGlobal ; Obtain Exclusive Access to Device Node
;
; Insure the Specified Unit Number is In-Range
CMP.L #MD_NUMUNITS,D0
BCC.S Open_Range_Error ; Unit Number is Out of Range!!
;
; See if the Specified Unit is Already Initialized
MOVE.L D0,D2 ; Save Unit Number in D2
LSL.L #2,D0 ; Multiply Unit Number by 4
LEA.L md_Units(A6,D0.L),A4 ; Index into Unit Table
MOVE.L (A4),D0 ; Pickup Ptr to Unit Descriptor
BNE.S Open_UnitOK ; Descriptor is Non-Null, Unit is Ready!
;
; Unit is Uninitialized, Attempt to Conjure One Up
BSR InitUnit ; Scratch:A3, Unit Number:D2, Ptr to Device:A6
MOVE.L (A4),D0 ; Did It Initialize Ok?
BEQ.S Open_Error ; Nope, Error!
;
; Unit is Located and Ready for Us
Open_UnitOK:
MOVE.L D0,A3 ; Put Ptr to Unit Descriptor into A3
MOVE.L D0,IO_UNIT(A2)
;
; Adjust Reference Counts to Track Number of Openers
ADDQ.W #1,LIB_OPENCNT(A6) ; One More User of the Device
ADDQ.W #1,UNIT_OPENCNT(A3) ; One More User of the Specific Unit Too
BCLR #LIBB_DELEXP,md_Flags(A6) ; Prevent Delayed Expunges
MOVEQ.L #0,D0 ; Return Error Code in D0 and in IO_ERROR
CLR.B IO_ERROR(A2) ; Signal "No Error Occurred During Open"
MOVE.B #NT_REPLYMSG,LN_TYPE(A2) ; IMPORTANT: Mark IORequest as "Complete"
Open_End:
SUBQ.W #1,LIB_OPENCNT(A6) ; Remove Fake Opened Now¨the Open is Done
BSR UnlockGlobal ; Release Exclusive Access to Device Node
MOVEM.L (SP)+,D2/A2/A3/A4 ; Restore Original Registers
RTS
Open_Range_Error:
Open_Error:
MOVEQ #IOERR_OPENFAIL,D0 ; Return Error Code in D0 and in IO_ERROR
MOVE.B D0,IO_ERROR(A2)
MOVE.L D0,IO_DEVICE(A2) ; IMPORTANT: Trash IO_DEVICE on Open Failure
PUTMSG 2,<'%s/Open: failed'>
BRA.S Open_End
;*********************************************************************
;* Close Device Routine *
;* *
;* When the user calls CloseDevice(), this eventually gets changed *
;* into a call to this routine which terminates communication with *
;* the device. Upon closing, the device's input buffers are freed *
;* and the memory occupied by the device (code and data) may or may *
;* not be freed. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* A1 - Ptr to the caller's I/O request block as initialized by *
;* OpenDevice(). *
;* *
;* Outputs: *
;* D0 - Device unload indicator: *
;* device seglist as given to Init (causes unload) *
;* zero (causes device to stay around in memory) *
;* IO_ERROR - Error code of operation (another copy as a byte). *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* The call to this routine is single-threaded by Exec, providing *
;* it does not perform a Wait(), either directly or indirectly. *
;* Because of this, do not take very much time. *
;* *
;* All I/O requests -must- be complete before closing. If any are *
;* pending, the user's program must AbortIO() then WaitIO() for *
;* each one to complete it. *
;* *
;*********************************************************************
DevClose:
MOVEM.L D1/A2-A3,-(SP) ; Preserve Registers D1, A2 and A3 Across Call
BSR LockGlobal ; Obtain Exclusive Access to Device Node
PUTMSG 20,<'%s/Close: called'>
;
; Obtain a Ptr to the Appropriate Unit Descriptor
MOVE.L A1,A2 ; Move IOB Ptr to A2
MOVE.L IO_UNIT(A2),A3 ; Pickup a Ptr to the Unit Descriptor in A3
;
; Mark the IOB Closed to Prevent Accidental Reuse
MOVEQ.L #-1,D0
MOVE.L D0,IO_UNIT(A2) ; Scribble Over Ptr to Unit Descriptor
MOVE.L D0,IO_DEVICE(A2) ; Scribble Over Ptr to Device Descriptor
;
; Decrement Unit Reference Count and Optionally Expunge Unit if No Users
SUBQ.W #1,UNIT_OPENCNT(A3)
BNE.S Close_Device ; Reference Count -not- Zero, Do Not Release Unit
BSR ExpungeUnit ; A3:UnitPtr, A6:DevicePtr
NOP ; Required to Handle a Null Jump to the Next Byte
; Do Not Expunge Ramdisks or the Contents Will Vanish!
Close_Device:
CLEAR D0
;
; Decrement Device Reference Count and Optionally Expunge Device if Asked
SUBQ.W #1,LIB_OPENCNT(A6) ; Decrement Device Reference Count
BNE.S Close_End ; Reference Count -not- Zero, Do Not Release Device
;
; See If There is a Delayed Expunge Pending
BTST #LIBB_DELEXP,md_Flags(A6) ; Check for a Delayed Expunge Pending...
BEQ.S Close_End ; Nope, No Request for Us to Unload is Pending
BSR DevExpunge ; A6:DevicePtr
Close_End:
BSR UnlockGlobal ; Release Exclusive Access to Device Node
MOVEM.L (SP)+,D1/A2-A3 ; Restore Original Registers D1, A2 and A3
RTS ; Must Return Either Zero or Device Seglist in D0
;*********************************************************************
;* Expunge Device Routine *
;* *
;* This routine frees all system resources and dependencies used by *
;* the device by any unit. If the device is currently closed, the *
;* expunge takes place immediately. Otherwise, this routine sets a *
;* Delayed Expunge bit and returns a device unload indicator of zero *
;* (do not unload device at this time). *
;* *
;* Expunge() is called by the memory allocator when the system is *
;* low on memory. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* A1 - Ptr to the caller's I/O request block as initialized by *
;* OpenDevice(). *
;* *
;* Outputs: *
;* D0 - Device unload indicator: *
;* device seglist as given to Init (causes unload) *
;* zero (causes device to stay around in memory) *
;* IO_ERROR - Error code of operation (zero means success). *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* The call to this routine is single-threaded by Exec, providing *
;* it does not perform a Wait(), either directly or indirectly. *
;* Because of this, do not take very much time. *
;* *
;* Because Expunge() is called from the memory allocator, it may *
;* -never- Wait() or otherwise take a long time to complete. *
;* *
;*********************************************************************
DevExpunge:
PUTMSG 10,<'%s/Expunge: called'>
MOVEM.L D1/D2/A5/A6,-(SP) ; Preserve ALL Modified Registers Across Call
MOVE.L A6,A5 ; Move Device Ptr into A5
MOVE.L md_SysLib(A5),A6 ; Pickup the Stashed Ptr to the Exec Library
TST.W LIB_OPENCNT(A5) ; See if Device Reference Count is Zero (No Users)
BEQ 1$ ; No Users, Device Can Be Unloaded
;
; Device is Still Open by Someone, Request Expunge in the Future
BSET #LIBB_DELEXP,md_Flags(A5) ; Set the Delayed Expunge Flag
CLEAR D0 ; Set up to Return Zero to Prevent Device Unload
BRA.S Expunge_End ; Jump to Function Exit
1$:
;
; Device has No Users, It Can Be Unloaded
MOVE.L md_SegList(A5),D2 ; Pickup the Device Seglist in D2
MOVE.L A5,A1 ; Get Ptr to Device Node into A1 for Remove()
CALLSYS Remove ; Remove Device Node from Global Linked List of Devices
;
; Device-Specific Closing Operations Here...
; (Do Not Break Forbid!)
;
;
; Free the Memory Occupied by the Device Node
; (Size Depends Upon Size of Jump Table and Device-Global Data)
; (The Code and Data Part will be Freed as a Seglist)
;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE)
MOVE.L A5,A1 ; Get Ptr to Device Node into A1
CLEAR D0 ; Clear Calculation Accumulator
MOVE.W LIB_NEGSIZE(A5),D0 ; Pickup Negative Extent of Device Node
SUBA.L D0,A1 ; Adjust Ptr to Device Node Down To Base of Device Node
ADD.W LIB_POSSIZE(A5),D0 ; Add in Positive Extent of Device Node (Functions + Data)
CALLSYS FreeMem ; Free Memory Occuppied by Device Node
MOVE.L D2,D0 ; Set up to Return Our Device Seglist to Cause Unload
Expunge_End:
MOVEM.L (SP)+,D1/D2/A5/A6 ; Restore Original Registers
RTS ; Must Return Either Zero or Device Seglist in D0
;*********************************************************************
;* Null Device Routine *
;* *
;* This routine handles a null entry point into the device provided *
;* for future expansion. It performs no function. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* *
;* Outputs: *
;* D0 - Must return zero (NULL) for future compatibility. *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;*********************************************************************
DevNull:
PUTMSG 1,<'%s/Null: called'>
CLEAR D0
RTS
;*********************************************************************
;* BeginIO Device Routine *
;* *
;* BeginIO starts all incoming I/O. The I/O is either queued up for *
;* the unit task or processed immediately. *
;* *
;* BeginIO is often given the responsibility of making devices *
;* single threaded, so that two tasks sending commands at the same *
;* time don't cause a problem. Once this has been done, the command *
;* is dispatched via PerformIO to the actual implementation code. *
;* *
;* There are many ways to do the threading. This example uses the *
;* UNITB_ACTIVE bit. Be sure this method is good enough for your *
;* device before using. Any method is ok. If immediate access can *
;* not be obtained, the request is queued for later processing. *
;* *
;* Some I/O requests do not need single threading. These can be *
;* performed immediately. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* A1 - Ptr to the caller's I/O request block as initialized by *
;* OpenDevice(). *
;* *
;* Within the I/O request block: *
;* IO_COMMAND The specific command desired by the caller. *
;* IO_LENGTH Depends upon the particular command issued. *
;* IO_DATA Depends upon the particular command issued. *
;* IO_MESSAGE An mn_ReplyPort is required. *
;* IO_DEVICE Set by the OpenDevice() function. *
;* IO_UNIT Set by the OpenDevice() function. *
;* IO_FLAGS If the IOB_QUICK bit is set, the device will *
;* -attempt- to handle the command on the caller's *
;* thread and avoid the overhead of a context *
;* switch when the Exec Message System is used. *
;* *
;* If the command had to be queued (and therefore *
;* the Exec Message System must be used), then *
;* IOB_QUICK bit is cleared in the request block. *
;* This means that the caller must use CheckIO() *
;* and WaitIO() calls to detect when the command *
;* is finished. *
;* *
;* Outputs: *
;* D0 - ????? *
;* IO_ERROR - Error code of operation (zero means success). *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* The call to this routine is *NOT* single-threaded by Exec. *
;* *
;* The Exec WaitIO() function uses the IORequest node type *
;* (LN_TYPE) as a flag. If set to NT_MESSAGE, it assumes the *
;* request is still pending and will wait. If set to NT_REPLYMSG, *
;* it assumes the request is finished. It's the responsibility of *
;* the device to set the node type to NT_MESSAGE before returning *
;* to the user. *
;* *
;*********************************************************************
DevBeginIO:
IFGE INFO_LEVEL-1
BCHG.B #1,$BFE001 ; Blink the Power LED for Debugging Purposes
ENDC
IFGE INFO_LEVEL-3
CLR.L -(SP) ; Allocate and Clear a 32-bit Scratch Word on the Stack
MOVE.W IO_COMMAND(A1),2(SP) ; Pickup the 16-bit Command Word
PUTMSG 3,<'%s/BeginIO -- %ld'> ; Display a Debugging Message w/Command Code
ADDQ.L #4,SP ; Deallocate the 32-bit Scratch Word from the Stack
ENDC
MOVEM.L D1/A0/A3,-(SP) ; Preserve Original Registers
;
; Perform Basic Operation Setup
MOVE.B #NT_MESSAGE,LN_TYPE(A1) ; Set Node Type so WaitIO() is Happy
MOVE.L IO_UNIT(a1),A3 ; Pick up Ptr to Unit Descriptor in A3
MOVE.W IO_COMMAND(A1),D0 ; Pick up Desired Command Code in D0
;
; Validate the Range of the Command Code
CMP.W #CMDRANGE_END,D0 ; Is It In Range?
BCC BeginIO_NoCmd ; Nope, Reject It and Signal Error
;
; Check For an Immediate Command and Process Them Now
MOVE.L #IMMEDIATES,D1 ; Pick up the Immediate Command Mask
DISABLE A0 ; Disable Interrupts (Also Prevents Task Switches)
BTST.L D0,D1 ; Test Command Against Classification Mask
BNE BeginIO_Immediate ; Yes, It is an Immediate Command!
;
; Check For a Never-Immediate Command and Queue Them
MOVE.L #NEVERIMMED,D1 ; Pick up the Never-Immediate Command Mask
BTST.L D0,D1 ; Test Command Against Classification Mask
BNE.S BeginIO_QueueMsg ; Yes, It is a Never-Immediate Command!
;
; See if the Unit is Stopped and Queue the Command If So
BTST #MDUB_STOPPED,UNIT_FLAGS(A3) ; Test Unit-Stopped Flag
BNE BeginIO_QueueMsg ; Yes, Go Queue the Command for Later
;
; This is not an immediate command. See if the device is busy.
; If the device is not, do the command on the user's thread,
; else fire up the unit task. This type of arbitration is not
; really needed for a ram disk, but it is essential for a device
; to reliably work with shared hardware.
;
; When the lines below are ";" commented out, the task gets a
; better workout. When the lines are active, the calling process
; is usually used for the operation.
;
; REMEMBER: Never Wait() on the user's thread in BeginIO()! The
; only exception is when the user has indicated it is ok by
; setting the "quick" bit.
BSET #UNITB_ACTIVE,UNIT_FLAGS(A3) ; Set the Unit-Active Flag
BEQ.S BeginIO_Immediate
;
; Queue I/O Request for Unit Task for Pick Up and Execute
BeginIO_QueueMsg:
BSET #UNITB_INTASK,UNIT_FLAGS(A3)
BCLR #IOB_QUICK,IO_FLAGS(A1) ; Clear the I/O Quick Flag
ENABLE A0 ; Enable Interrupts (and Task Switching)
IFGE INFO_LEVEL-250
MOVE.L A1,-(SP) ; Allocate and Store into a 32-bit Scratch Word
MOVE.L A3,-(SP) ; Allocate and Store into a 32-bit Scratch Word
PUTMSG 250,<'%s/PutMsg: Port=%lx Message=%lx'>
ADDQ.L #8,SP ; Deallocate Scratch Words from Stack
ENDC
;
; Send I/O Request Message to the Unit Task's Message Port
MOVE.L A3,A0 ; Put Ptr to Unit Descriptor into A0 for PutMsg()
LINKSYS PutMsg,md_SysLib(A6) ; Ptr to Port:A0, Ptr to Message:A1
BRA.S BeginIO_End ; Command Queued for Later, Jump to Function Exit
;
; Perform the Command on the Thread of the Calling Process
BeginIO_Immediate:
ENABLE A0 ; Enable Interrupts (and Task Switches)
BSR.S PerformIO
BeginIO_End:
PUTMSG 200,<'%s/BeginIO_End'>
MOVEM.L (SP)+,D1/A0/A3 ; Restore Original Registers
RTS
BeginIO_NoCmd:
MOVE.B #IOERR_NOCMD,IO_ERROR(A1) ; Signal Error - No Such Command
BRA.S BeginIO_End ; Jump to Function Exit
;*********************************************************************
;* AbortIO Device Routine *
;* *
;* This function aborts an I/O request in progress. If the request *
;* is active, it is stopped immediately. If the request is still *
;* queued, it is removed from the queue. The request is returned *
;* in the same way as if it had normally completed. *
;* *
;* Inputs: *
;* A6 - Ptr to our Device node. *
;* A1 - Ptr to the caller's I/O request block as initialized by *
;* OpenDevice(). *
;* *
;* Outputs: *
;* D0 - Set to #IOERR_ABORTED *
;* IO_ERROR - ????? *
;* *
;* All Original Registers Must Be Preserved Across Function Call!!!! *
;* *
;* Notes: *
;* An AbortIO() is a -request- to "hurry up" processing of an I/O *
;* request. If the request was already complete, nothing happens. *
;* The message must be replied with ReplyMsg() as normal. *
;* *
;*********************************************************************
DevAbortIO:
MOVEQ #IOERR_NOCMD,D0 ; Return "AbortIO() Request Failed"
RTS ; (Can't Abort I/O to Our Ramdisk)
;*********************************************************************
;* *
;*********************************************************************
END