home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
420.lha
/
Example_device
/
Ex_UnitMgr.asm
< prev
next >
Wrap
Assembly Source File
|
1990-09-29
|
16KB
|
323 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 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