home *** CD-ROM | disk | FTP | other *** search
- ;*********************************************************************
- ;* *
- ;* 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 InitUnit ; Initialize a Unit Descriptor and Fire Up a Task
- XDEF FreeUnit ; Deallocate the Memory for the Unit Descriptor
- XDEF ExpungeUnit ; Terminate a Unit Task and Deallocate the Unit Descriptor
-
- ;********************
- ;* 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 PerformIO ; Beginning of I/O Dispatch Point
-
- ; Defined in Ex_Main.Asm
- XREF MyName ; Name of This Device (for Tagging Ports and Tasks)
-
- ;*********************************************************************
- ;* InitUnit Helper Routine *
- ;* *
- ;* InitUnit() is used to allocate a unit descriptor and optionally *
- ;* spawn a task to manage that unit. *
- ;* *
- ;* Inputs: *
- ;* A6 - Ptr to our Device node. *
- ;* A3 - Scratch Register. *
- ;* D2 - Unit Number Desired. *
- ;* *
- ;* Outputs: *
- ;* D0 - ????? *
- ;* *
- ;* All Original Registers Must Be Preserved Across Function Call!!!! *
- ;* *
- ;*********************************************************************
- InitUnit:
- PUTMSG 30,<'%s/InitUnit: called'>
- MOVEM.L D2-D4/A2,-(SP) ; Preserve Original Registgers
- ;
- ; Allocate Some Memory for a Unit Descriptor Structure
- MOVE.L #MyDevUnit_Sizeof,D0 ; Size of Structure --> D0
- MOVE.L #MEMF_PUBLIC!MEMF_CLEAR,D1 ; Type of Memory --> D1
- LINKSYS AllocMem,md_SysLib(A6) ; Allocate Memory
- TST.L D0 ; Did It Work?
- BEQ InitUnit_End ; Nope, Exit Function Early
- MOVE.L D0,A3 ; Save Ptr to Allocation in A3
- ;
- ; Initialize the Unit Descriptor
- MOVEQ.L #0,D0 ; Signal InitStruct() to Not Re-Zero Structure
- MOVE.L A3,A2 ; Ptr to Structure to Initialize --> A2
- LEA.L mdu_Init(PC),A1 ; Ptr to Initialization Definitions --> A1
- LINKSYS InitStruct,md_SysLib(A6) ; Initialize Unit Descriptor
-
- MOVE.B D2,mdu_UnitNum(A3) ; Initialize the Unit Number
- MOVE.L A6,mdu_Device(A3) ; Initialize the Ptr to the Device Node
-
- ;
- ; Start up the unit task. We do a trick here; we set his message
- ; port to PA_IGNORE until the new task has a chance to set it up.
- ; We cannot go to sleep here: it would be very nasty if someone
- ; else tried to open the unit. Exec's OpenDevice() has done a
- ; Forbid() for us, we depend on this to become single threaded.
-
- ;
- ; Initialize the Stack Boundaries
- LEA mdu_Stack(A3),A0 ; Define Low End of Task Stack
- MOVE.L A0,mdu_Tcb+TC_SPLOWER(A3)
- LEA TASKSTKSIZE(A0),A0 ; Define High End of Task Stack
- MOVE.L A0,mdu_Tcb+TC_SPUPPER(A3)
-
- MOVE.L A3,-(A0) ; Pass Ptr to Unit Descriptor on New Task's Stack
- MOVE.L A0,mdu_Tcb+TC_SPREG(A3) ; Define Position of New Task's Stack Pointer
- LEA mdu_Tcb(A3),A0 ; Ptr to Unit's Task Structure --> A0
- MOVE.L A0,MP_SIGTASK(A3) ; Set Message Port's Monitor Task to Unit's Task
-
- IFGE INFO_LEVEL-30
- MOVE.L A0,-(SP)
- MOVE.L A3,-(SP)
- PUTMSG 30,<'%s/InitUnit, unit= %lx, task=%lx'>
- ADDQ.L #8,SP
- ENDC
-
- LEA MP_MSGLIST(A3),A0
- NEWLIST A0 ; Initialize the Message Port's List
- ;
- ; Start the Unit Task
- LEA mdu_Tcb(A3),A1 ; Pickup a Ptr to the Task Control Block
- LEA UnitTask_Begin(PC),A2 ; Pickup Ptr to Task's Code Entry Point
- MOVE.L A3,-(SP) ; Preserve the Ptr to the Unit Descriptor
- ;
- ; If Unit Task ever Returns, Generate an Artificial Address Error
- LEA -1,A3 ; It Should Never Return, We RemTask() It when Done
- CLEAR D0
- PUTMSG 30,<'%s/About to add task'>
- LINKSYS AddTask,md_SysLib(A6)
- MOVE.L (SP)+,A3 ; Restore the Ptr to the Unit Descriptor
- ;
- ; Mark New Unit as Ready to Operate
- MOVE.L D2,D0 ; Pick up Unit Number in D0
- LSL.L #2,D0 ; Multiply by 4 Bytes Per Entry
- MOVE.L A3,md_Units(A6,D0.L) ; Set Ptr to Unit Descriptor into Unit Table
- PUTMSG 30,<'%s/InitUnit: ok'>
-
- InitUnit_End:
- MOVEM.L (SP)+,D2-D4/A2 ; Restore Original Registers
- RTS
-
- ;****************************************************
- ;* Initialization Definitions for a Unit Descriptor *
- ;****************************************************
- mdu_Init:
- INITBYTE MP_FLAGS,PA_IGNORE ;\
- INITBYTE LN_TYPE,NT_MSGPORT ; > Message Port
- INITLONG LN_NAME,MyName ;/
- INITLONG mdu_Tcb+LN_NAME,MyName ;\
- INITBYTE mdu_Tcb+LN_TYPE,NT_TASK ; > Task Control Block
- INITBYTE mdu_Tcb+LN_PRI,5 ;/
- DC.L 0
-
- ;*********************************************************************
- ;* FreeUnit Helper Routine *
- ;* *
- ;* FreeUnit() is used to deallocate the memory occupied by a unit *
- ;* descriptor. It assumes the task has already been stopped. *
- ;* *
- ;* Inputs: *
- ;* A6 - Ptr to our Device node. *
- ;* A3 - Ptr to Unit Descriptor. *
- ;* *
- ;* Outputs: *
- ;* None *
- ;* *
- ;*********************************************************************
- FreeUnit:
- MOVE.L A3,A1
- MOVE.L #MyDevUnit_Sizeof,D0
- LINKSYS FreeMem,md_SysLib(A6) ; Release the Memory
- RTS
-
- ;*********************************************************************
- ;* ExpungeUnit Helper Routine *
- ;* *
- ;* ExpungeUnit() is used to stop the unit task, free its descriptor *
- ;* and unlink it from the unit table. *
- ;* *
- ;* Inputs: *
- ;* A6 - Ptr to our Device node. *
- ;* A3 - Ptr to Unit Descriptor. *
- ;* *
- ;* Outputs: *
- ;* None *
- ;* *
- ;*********************************************************************
- ExpungeUnit:
- PUTMSG 10,<'%s/ExpungeUnit: called'>
- MOVE.L D2,-(SP) ; Preserve Original D2 Across Function
-
- LEA mdu_Tcb(A3),A1
- LINKSYS RemTask,md_SysLib(A6) ; Remove the Unit's Task
-
- CLEAR D2
- MOVE.B mdu_UnitNum(A3),D2 ; Save the Unit# in D2 for a Moment
-
- BSR FreeUnit ; Free the Unit Descriptor Structure Itself
-
- ;------ clear out the unit vector in the device
- LSL.L #2,D2 ; Compute an Index into the Unit Table
- CLR.L md_Units(A6,D2.L) ; Clear the Ptr to the Unit Descriptor
-
- MOVE.L (SP)+,D2 ; Restore Original D2
- RTS
-
- ;*********************************************************************
- ;* Unit Task Code (Reentrant) *
- ;* *
- ;* This block of code serves as the executable port of a spun-off *
- ;* that handles each unit. There may be multiple unit tasks using *
- ;* the same code in a reentrant fashion, therefore the device author *
- ;* must take steps to coordinate shared accesses. *
- ;* *
- ;* An alternative architecture is to create only a single task and *
- ;* let all unit request flow into it. The choice depends upon the *
- ;* specific hardware being managed and your design requirements. *
- ;* *
- ;* Register Usage: *
- ;* A3 - Ptr to the Unit Descriptor. *
- ;* A6 - Ptr to the Exec Library. *
- ;* A5 - Ptr to our Device node. *
- ;* A4 - Ptr to Task Control Block (-NOT- a DOS Process) *
- ;* D7 - Event Wait Mask *
- ;* *
- ;* Notes: *
- ;* This task never terminates, it is simply removed suddenly. *
- ;* *
- ;*********************************************************************
-
- ; Some DOS Magic, useful for Processes (not us). A process is
- ; started at the first executable address after a segment list.
- ; We hand craft a segment list here. See the the DOS technical
- ; reference if you really need to know more about this.
- ; The next instruction after the segment list is the first
- ; executable address.
-
- CNOP 0,4 ; Align the Following on a Long Word Boundary
- DC.L 16 ; Length of Segment (Dummy Value, Any will Do)
- UnitTask_Seglist:
- DC.L 0 ; NULL Pointer to the Next Segment
-
- UnitTask_Begin:
- PUTMSG 35,<'%s/Task_Begin'>
- MOVE.L ABSEXECBASE,A6 ; Pickup Ptr to Exec Library
- ;
- ; Pickup a Ptr to the Unit Descriptor as Passed to Us on
- ; our Initial Stack
- MOVE.L 4(SP),A3 ; Pickup Ptr to Unit Descriptor
- MOVE.L mdu_Device(A3),A5 ; Pickup Ptr to our Device node
- ;
- ; Allocate a Signal Bit for Use with Our Message Port
- MOVEQ #-1,D0 ; Any Signal Will Do (-1 = Any)
- CALLSYS AllocSignal ; Allocate a Signal Bit
- MOVE.B D0,MP_SIGBIT(A3) ; Note Signal Bit Allocated for Future
- MOVE.B #PA_SIGNAL,MP_FLAGS(A3) ; Make our Message Port 'Live'
-
- MOVEQ #0,D7 ; Change the Signal Bit# into a Mask
- BSET D0,D7 ; and Save in D7 for Future Reference
-
- ; Because our Message Port was Marked PA_IGNORE
- ; for a while (in InitUnit), We jump to the
- ; GetMsg() code upon entry. This is because
- ; our first message will probably be posted
- ; -before- our task gets a chance to run.
- BRA.S Task_StartHere ; Jump into Incoming-Message Waiting Loop
-
- ;***********************
- ;* Main Loop of Device *
- ;***********************
- Task_Unlock:
- AND.B #$FF&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(A3)
-
- Task_MainLoop:
- PUTMSG 75,<'%s/++Sleep'>
- MOVE.L D7,D0
- CALLSYS Wait ; Wait for an Incoming Message
-
- IFGE INFO_LEVEL-5
- BCHG.B #1,$BFE001 ; Blink the Power LED for Each Message Received
- ENDC
-
- Task_StartHere:
- PUTMSG 75,<'%s/++Wakeup'>
-
- BTST #MDUB_STOPPED,UNIT_FLAGS(A3) ; Is this Unit Suspended (Stopped)?
- BNE.S Task_MainLoop ; Yes, Ignore Messages
-
- BSET #UNITB_ACTIVE,UNIT_FLAGS(A3) ; Mark the Device as Active
- BNE Task_MainLoop ; If Device was Already In-Use, Ignore Message
- Task_NextMessage:
- MOVE.L A3,A0 ; Pickup a Ptr to the Incoming Message Port
- CALLSYS GetMsg ; Pull a Message from the Message Port
- PUTMSG 1,<'%s/GotMsg'>
- TST.L D0 ; Did We Get One?
- BEQ Task_Unlock ; No, Go Back to Waiting for One
-
- MOVE.L D0,A1 ; Ptr to I/O Request Block (Msg) --> A1
- EXG A5,A6 ; Ptr to Device node --> A6
- ; Ptr to Unit Descriptor --> A3
- BSR PerformIO ; Dispatch to I/O Handler Function
- EXG A5,A6 ; Get Ptr to Exec Library Back in A6
- BRA.S Task_NextMessage ; Go Process Next Message Already at Port
-
- ;*********************************************************************
- ;* *
- ;*********************************************************************
- END
-
-