[net.micro.amiga] Demo RAM disk device driver

erickson@cbmvax.cbm.UUCP (Lee Erickson) (10/21/86)

The following source is intended to serve as a demo disk device driver.
Since I can't email you "demo" hardware, it functions as a RAM disk.
I don't really recommend that you USE it as a RAM disk, but it should be
very helpful to those of you trying to implement hard disk drivers....

Lee Erickson

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	asmsupp.i
#	messages.i
#	mydev.i
#	mydev.asm
#	testdev.asm
#	makefile
# This archive created: Mon Oct 20 17:20:51 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'asmsupp.i'" '(1056 characters)'
if test -f 'asmsupp.i'
then
	echo shar: will not over-write existing file "'asmsupp.i'"
else
sed 's/^	X//' << \SHAR_EOF > 'asmsupp.i'
	X
	X*************************************************************************
	X*									*
	X*	Copyright (C) 1985, Commodore Amiga Inc.  All rights reserved.	*
	X*	Permission granted for non-commercial use			*								*
	X*									*
	X*************************************************************************
	X
	X
	X*************************************************************************
	X*
	X* asmsupp.i -- random low level assembly support routines
	X*
	X* Source Control
	X* ------ -------
	X* 
	X* $Header: asmsupp.i,v 31.1 85/10/13 23:12:33 neil Exp $
	X*
	X* $Locker:  $
	X*
	X*************************************************************************
	X
	XCLEAR	MACRO		; quick way to clear a D register on 68000
	X	MOVEQ	#0,\1
	X	ENDM
	X
	XBHS	MACRO
	X	BCC.\0	\1
	X	ENDM
	X
	XBLO	MACRO
	X	BCS.\0	\1
	X	ENDM
	X
	XEVEN	MACRO		; word align code stream
	X	DS.W	0
	X	ENDM
	X
	XLINKSYS	MACRO		; link to a library without having to see a _LVO
	X	LINKLIB	_LVO\1,\2
	X	ENDM
	X
	XCALLSYS	MACRO		; call a library without having to see _LVO
	X	CALLLIB	_LVO\1
	X	ENDM
	X
	XXLIB	MACRO		; define a library reference without the _LVO
	X	XREF	_LVO\1
	X	ENDM
	X
SHAR_EOF
if test 1056 -ne "`wc -c < 'asmsupp.i'`"
then
	echo shar: error transmitting "'asmsupp.i'" '(should have been 1056 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'messages.i'" '(853 characters)'
if test -f 'messages.i'
then
	echo shar: will not over-write existing file "'messages.i'"
else
sed 's/^	X//' << \SHAR_EOF > 'messages.i'
	X
	X*************************************************************************
	X*									*
	X*	Copyright (C) 1985, Commodore Amiga Inc.  All rights reserved.	*
	X*	Permission granted for non-commercial use			*
	X*									*
	X*************************************************************************
	X
	X
	X*************************************************************************
	X*
	X* messages.i
	X*
	X* Source Control
	X* ------ -------
	X* 
	X* $Header: messages.i,v 31.1 85/10/13 23:12:48 neil Exp $
	X*
	X* $Locker:  $
	X*
	X*************************************************************************
	X
	X		XREF	KPutFmt
	X
	XPUTMSG:		MACRO	* level,msg
	X		
	X		IFGE	INFO_LEVEL-\1
	X
	X		PEA	subSysName(PC)
	X		MOVEM.L	A0/A1/D0/D1,-(SP)
	X		LEA	msg\@,A0
	X		LEA	4*4(SP),A1
	X		JSR	KPutFmt
	X		MOVEM.L (SP)+,D0/D1/A0/A1
	X		ADDQ.L	#4,SP
	X		BRA.S	end\@
	X
	Xmsg\@		DC.B	\2
	X		DC.B	10
	X		DC.B	0
	X		DS.W	0
	Xend\@
	X		ENDC
	X		ENDM
SHAR_EOF
if test 853 -ne "`wc -c < 'messages.i'`"
then
	echo shar: error transmitting "'messages.i'" '(should have been 853 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mydev.i'" '(4411 characters)'
if test -f 'mydev.i'
then
	echo shar: will not over-write existing file "'mydev.i'"
else
sed 's/^	X//' << \SHAR_EOF > 'mydev.i'
	X
	X******************************************************************
	X*                                                                *
	X* Copyright (C) 1986, Commodore Amiga Inc.  All rights reserved. *
	X* Permission granted for non-commercial use			 *								*
	X*                                                                *
	X******************************************************************
	X
	X
	X*****************************************************************
	X*
	X* mydev.i -- external declarations for skeleton device
	X*
	X* SOURCE CONTROL
	X* ------ -------
	X* $Header: ramlib.i,v 31.1 85/10/13 23:12:51 neil Exp $
	X*
	X* $Locker: neil $
	X*
	X*****************************************************************
	XINFO_LEVEL	EQU	0	; Specify amount of debugging info desired
	X*INTRRUPT	SET	1	; Remove "*" to enable fake interrupt code
	X
	X; stack size and priority for the process we will create
	XMYPROCSTACKSIZE	EQU	$800
	XMYPROCPRI	EQU	0
	X
	XSECTOR		EQU	512	; # bytes per sector
	XSECSHIFT	EQU	9	; Shift count to convert byte # to sector #
	XRAMSIZE		EQU	512*200	; Use this much RAM per unit
	XIAMPULLING	EQU	7	; "I am pulling the interrupt" bit of INTCRL1
	XINTENABLE	EQU	4	; "Interrupt Enable" bit of INTCRL2
	XINTCTRL1	EQU	$40	; Interrupt control register offset on board
	XINTCTRL2	EQU	$42	; Interrupt control register offset on board
	XINTACK		EQU	$50	; My board's interrupt reset address
	X;-----------------------------------------------------------------------
	X;
	X; device command definitions
	X;
	X;-----------------------------------------------------------------------
	X
	X	DEVINIT
	X	DEVCMD	CMD_MOTOR	; control the disk's motor (NO-OP)
	X	DEVCMD	CMD_SEEK	; explicit seek (NO-OP)
	X	DEVCMD	CMD_FORMAT	; format disk - equated to WRITE for RAMDISK
	X	DEVCMD	CMD_REMOVE	; notify when disk changes (NO-OP)
	X	DEVCMD	CMD_CHANGENUM	; number of disk changes (always 0)
	X	DEVCMD	CMD_CHANGESTATE	; is there a disk in the drive? (always TRUE)
	X	DEVCMD	CMD_PROTSTATUS	; is the disk write protected? (always FALSE)
	X	DEVCMD	CMD_RAWREAD	; Not supported
	X	DEVCMD	CMD_RAWWRITE	; Not supported
	X	DEVCMD	CMD_GETDRIVETYPE; Get drive type
	X	DEVCMD	CMD_GETNUMTRACKS; Get number of tracks
	X	DEVCMD	CMD_ADDCHANGEINT; Add disk change interrupt (NO-OP)
	X	DEVCMD	CMD_REMCHANGEINT; Remove disk change interrupt ( NO-OP)
	X	DEVCMD	MYDEV_END	; place marker -- first illegal command #
	X
	X;-----------------------------------------------------------------------
	X;
	X; Layout of parameter packet for MakeDosNode
	X;
	X;-----------------------------------------------------------------------
	X
	X    STRUCTURE MkDosNodePkt,0
	X	APTR	mdn_dosName	; Pointer to DOS file handler name
	X	APTR	mdn_execName	; Pointer to device driver name
	X	ULONG	mdn_unit	; Unit number
	X	ULONG	mdn_flags	; OpenDevice flags
	X	ULONG	mdn_tableSize	; Environment size
	X	ULONG	mdn_sizeBlock	; # longwords in a block
	X	ULONG	mdn_secOrg	; sector origin -- unused
	X	ULONG	mdn_numHeads	; number of surfaces
	X	ULONG	mdn_secsPerBlk	; secs per logical block -- unused
	X	ULONG	mdn_blkTrack	; secs per track
	X	ULONG	mdn_resBlks	; reserved blocks -- MUST be at least 1!
	X	ULONG	mdn_prefac	; unused
	X	ULONG	mdn_interleave	; interleave
	X	ULONG	mdn_lowCyl	; lower cylinder
	X	ULONG	mdn_upperCyl	; upper cylinder
	X	ULONG	mdn_numBuffers	; number of buffers
	X	ULONG	mdn_memBufType	; Type of memory for AmigaDOS buffers
	X	STRUCT	mdn_dName,5	; DOS file handler name "RAM0"
	X	LABEL   mdn_Sizeof	; Size of this structure
	X
	X;-----------------------------------------------------------------------
	X;
	X; device data structures
	X;
	X;-----------------------------------------------------------------------
	X
	X; maximum number of units in this device
	XMD_NUMUNITS	EQU	4
	X
	X    STRUCTURE MyDev,LIB_SIZE
	X	ULONG	md_SysLib
	X	ULONG	md_SegList
	X	ULONG	md_Base		; Base address of this device's expansion board
	X	UBYTE	md_Flags
	X	UBYTE	md_pad
	X	STRUCT	md_Units,MD_NUMUNITS*4
	X	LABEL   MyDev_Sizeof
	X
	X    STRUCTURE MyDevMsg,MN_SIZE
	X	APTR	mdm_Device
	X	APTR	mdm_Unit
	X	LABEL	MyDevMsg_Sizeof
	X
	X    STRUCTURE MyDevUnit,UNIT_SIZE
	X	UBYTE	mdu_UnitNum
	X	UBYTE	mdu_SigBit		; Signal bit allocated for interrupts
	X	APTR	mdu_Device
	X	STRUCT	mdu_stack,MYPROCSTACKSIZE
	X 	STRUCT	mdu_is,IS_SIZE		; Interrupt structure
	X	STRUCT	mdu_tcb,TC_SIZE		; TCB for disk task
	X	STRUCT	mdu_Msg,MyDevMsg_Sizeof
	X	ULONG	mdu_SigMask		; Signal these bits on interrupt
	X	STRUCT	mdu_RAM,RAMSIZE		; RAM used to simulate disk
	X	LABEL	MyDevUnit_Sizeof
	X
	X	;------ state bit for unit stopped
	X	BITDEF	MDU,STOPPED,2
	X
	XMYDEVNAME	MACRO
	X		DC.B	'mydev.device',0
	X		ENDM
	X
SHAR_EOF
if test 4411 -ne "`wc -c < 'mydev.i'`"
then
	echo shar: error transmitting "'mydev.i'" '(should have been 4411 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mydev.asm'" '(30251 characters)'
if test -f 'mydev.asm'
then
	echo shar: will not over-write existing file "'mydev.asm'"
else
sed 's/^	X//' << \SHAR_EOF > 'mydev.asm'
	X
	X*************************************************************************
	X*									*
	X*	Copyright (C) 1986, Commodore Amiga Inc.  All rights reserved.	*
	X*	Permission granted for non-commercial use			*								*
	X*									*
	X*************************************************************************
	X
	X
	X*************************************************************************
	X*
	X* mydev.asm -- Skeleton device code.  Modified by Lee Erickson to be a 
	X*	       simple disk device, using RAM to simulate a disk.
	X*	       10/7/86
	X*
	X************************************************************************/
	X	SECTION	section
	X
	X	NOLIST
	X	include "exec/types.i"
	X	include "exec/nodes.i"
	X	include "exec/lists.i"
	X	include "exec/libraries.i"
	X	include "exec/devices.i"
	X	include "exec/io.i"
	X	include "exec/alerts.i"
	X	include "exec/initializers.i"
	X	include "exec/memory.i"
	X	include "exec/resident.i"
	X	include "exec/ables.i"
	X	include "exec/errors.i"
	X	include "exec/tasks.i"
	X	include	'libraries/expansion.i'
	X	include 'libraries/configvars.i'
	X	include 'libraries/configregs.i'
	X
	X	include "asmsupp.i"
	X	include "messages.i"
	X
	X	include "mydev.i"
	X
	X	LIST
	X
	X	;------ These don't have to be external, but it helps some
	X	;------ debuggers to have them globally visible
	X	XDEF	Init
	X	XDEF	Open
	X	XDEF	Close
	X	XDEF	Expunge
	X	XDEF	Null
	X	XDEF	myName
	X	XDEF	BeginIO
	X	XDEF	AbortIO
	X
	X	XREF	_AbsExecBase
	X
	X	XLIB	AddIntServer
	X	XLIB	RemIntServer
	X	XLIB	Debug
	X	XLIB	InitStruct
	X	XLIB	OpenLibrary
	X	XLIB	CloseLibrary
	X	XLIB	Alert
	X	XLIB	FreeMem
	X	XLIB	Remove
	X	XLIB	AllocMem
	X	XLIB	AddTask
	X	XLIB	PutMsg
	X	XLIB	RemTask
	X	XLIB	ReplyMsg
	X	XLIB	Signal
	X	XLIB	GetMsg
	X	XLIB	Wait
	X	XLIB	WaitPort
	X	XLIB	AllocSignal
	X	XLIB	SetTaskPri
	X	XLIB	GetCurrentBinding	; Get list of boards for this driver
	X	XLIB	MakeDosNode
	X	XLIB	AddDosNode
	X
	X	INT_ABLES
	X
	X
	X	; The first executable location.  This should return an error
	X	; in case someone tried to run you as a program (instead of
	X	; loading you as a library).
	XFirstAddress:
	X	CLEAR	d0
	X	rts
	X
	X;-----------------------------------------------------------------------
	X; A romtag structure.  Both "exec" and "ramlib" look for
	X; this structure to discover magic constants about you
	X; (such as where to start running you from...).
	X;-----------------------------------------------------------------------
	X
	X	; Most people will not need a priority and should leave it at zero.
	X	; the RT_PRI field is used for configuring the roms.  Use "mods" from
	X	; wack to look at the other romtags in the system
	XMYPRI	EQU	0
	X
	XinitDDescrip:
	X					;STRUCTURE RT,0
	X	  DC.W    RTC_MATCHWORD		; UWORD RT_MATCHWORD
	X	  DC.L    initDDescrip		; APTR  RT_MATCHTAG
	X	  DC.L    EndCode		; APTR  RT_ENDSKIP
	X	  DC.B    RTF_AUTOINIT		; UBYTE RT_FLAGS
	X	  DC.B    VERSION		; UBYTE RT_VERSION
	X	  DC.B    NT_DEVICE		; UBYTE RT_TYPE
	X	  DC.B    MYPRI			; BYTE  RT_PRI
	X	  DC.L    myName		; APTR  RT_NAME
	X	  DC.L    idString		; APTR  RT_IDSTRING
	X	  DC.L    Init			; APTR  RT_INIT
	X					; LABEL RT_SIZE
	X
	X
	X	; this is the name that the device will have
	XsubSysName:
	XmyName:		MYDEVNAME
	X
	XExLibName	EXPANSIONNAME	; Expansion Library Name
	X
	X	; a major version number.
	XVERSION:	EQU	1
	X
	X	; A particular revision.  This should uniquely identify the bits in the
	X	; device.  I use a script that advances the revision number each time
	X	; I recompile.  That way there is never a question of which device
	X	; that really is.
	XREVISION:	EQU	17
	X
	X	; this is an identifier tag to help in supporting the device
	X	; format is 'name version.revision (dd MON yyyy)',<cr>,<lf>,<null>
	XidString:	dc.b	'mydev 1.0 (31 Oct 1985)',13,10,0
	X
	X	; force word allignment
	X	ds.w	0
	X
	X
	X	; The romtag specified that we were "RTF_AUTOINIT".  This means
	X	; that the RT_INIT structure member points to one of these
	X	; tables below.  If the AUTOINIT bit was not set then RT_INIT
	X	; would point to a routine to run.
	X
	XInit:
	X	DC.L	MyDev_Sizeof		; data space size
	X	DC.L	funcTable		; pointer to function initializers
	X	DC.L	dataTable		; pointer to data initializers
	X	DC.L	initRoutine		; routine to run
	X
	X
	XfuncTable:
	X
	X	;------ standard system routines
	X	dc.l	Open
	X	dc.l	Close
	X	dc.l	Expunge
	X	dc.l	Null
	X
	X	;------ my device definitions
	X	dc.l	BeginIO
	X	dc.l	AbortIO
	X
	X	;------ function table end marker
	X	dc.l	-1
	X
	X
	X	; The data table initializes static data structures.
	X	; The format is specified in exec/InitStruct routine's
	X	; manual pages.  The INITBYTE/INITWORD/INITLONG routines
	X	; are in the file "exec/initializers.i".  The first argument
	X	; is the offset from the device base for this byte/word/long.
	X	; The second argument is the value to put in that cell.
	X	; The table is null terminated
	XdataTable:
	X	INITBYTE	LH_TYPE,NT_DEVICE
	X	INITLONG	LN_NAME,myName
	X	INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
	X	INITWORD	LIB_VERSION,VERSION
	X	INITWORD	LIB_REVISION,REVISION
	X	INITLONG	LIB_IDSTRING,idString
	X	DC.L	0
	X
	X
	X	; This routine gets called after the device has been allocated.
	X	; The device pointer is in D0.  The segment list is in a0.
	X	; If it returns non-zero then the device will be linked into
	X	; the device list.
	XinitRoutine:
	X
	X; Register Usage
	X; ==============
	X; a3 -- Points to tempory RAM
	X; a4 -- Expansion library base
	X; a5 -- device pointer
	X; a6 -- Exec base
	X;
	X;----------------------------------------------------------------------
	X	;------ get the device pointer into a convenient A register
	X	PUTMSG	30,<'%s/Init: called'>
	X 	movem.l	d1-d7/a0-a5,-(sp)	; Preserve ALL modified registers
	X	move.l	d0,a5
	X
	X	;------ save a pointer to exec
	X	move.l	a6,md_SysLib(a5)
	X
	X	;------ save a pointer to our loaded code
	X	move.l	a0,md_SegList(a5)
	X
	X	lea.l	ExLibName,A1		; Get expansion lib. name
	X	moveq.l	#0,D0
	X	CALLSYS	OpenLibrary		; Open the expansion library
	X	tst.l	D0
	X	bne.s	init_OpSuccess
	X
	Xinit_OpFail:
	X	ALERT	AG_OpenLib!AO_ExpansionLib
	X
	Xinit_OpSuccess:
	X	move.l	D0,A4
	X
	X	lea	md_Base(A5),A0	; Get the Current Bindings
	X	moveq	#4,D0		; Just get address (length = 4 bytes)
	X	LINKLIB	_LVOGetCurrentBinding,A4
	X	move.l	md_Base(A5),D0		; Get start of list
	X	tst.l	D0			; If controller not found
	X	beq	Init_End		;	Exit and unload driver
	X	move.l	D0,A0			; Get config structure address
	X	move.l	cd_BoardAddr(A0),md_Base(A5); Save board base address
	X	bclr.b	#CDB_CONFIGME,cd_Flags(A0); Mark board as configured
	X
	XInit_End:
	X;----------------------------------------------------------------------
	X;
	X; Here we build a packet describing the characteristics of our disk to
	X; pass to AmigaDOS.  This serves the same purpose as a "mount" command
	X; of this device would.  For disks, it might be useful to actually
	X; get this information right from the disk itself.  Just as mount,
	X; it could be for multiple partitions on the single physical device.
	X; For this example, we will simply hard code the appropriate parameters.
	X;
	X;-----------------------------------------------------------------------
	X
	X;!!!! Normally you would only do this if your card was successfully configured
	X;!!!! up above.  For the demo, it will always be done.
	X
	X	;------	Allocate tempory RAM to build MakeDosNode parameter packet
	X	move.l	#MEMF_CLEAR!MEMF_PUBLIC,d1
	X	move.l	#mdn_Sizeof,d0	; Enough room for our parameter packet
	X	CALLSYS	AllocMem
	X	move.l	d0,a3
	X
	X	;-----	Use InitStruct to initialize the constant portion of packet
	X	move.l	d0,a2			; Point to memory to initialize
	X	moveq.l	#0,d0			; Don't need to re-zero it
	X	lea.l	mdn_Init(pc),A1
	X	CALLSYS	InitStruct
	X
	X	lea	mdn_dName(a3),a0	; Get addr of Device name
	X	move.l	a0,mdn_dosName(a3)	;	and save in environment
	X
	X	moveq	#1,d6			; Now tell AmigaDOS about all units
	XUloop:
	X	move.b  d6,d0			; Get unit number
	X	add.b	#$2F,d0			; Make ASCII, minus 1
	X	move.b	d0,mdn_dName+3(a3)	;	and store in name
	X	move.l	d6,mdn_unit(a3)		; Store unit # in environment
	X
	X	move.l	a3,a0
	X	LINKLIB	_LVOMakeDosNode,a4	; Build AmigaDOS structures
	X	move.l	d0,a0			; Get deviceNode address
	X	moveq.l	#0,d0			; Set device priority to 0
	X	moveq.l	#0,d1
	X*	moveq.l	#ADNF_STARTPROC,d1	; Have handler started
	X	LINKLIB	_LVOAddDosNode,a4
	X
	X	addq	#1,d6			; Bump unit number
	X	cmp.b	#MD_NUMUNITS,d6
	X	ble.s	Uloop			; Loop until all units installed
	X
	X	move.l	a3,a1		; Return RAM to system
	X	move.l	#mdn_Sizeof,d0
	X	CALLSYS	FreeMem
	X
	X	move.l	a4,a1		; Now close expansion library
	X	CALLSYS	CloseLibrary
	X	;
	X	; You would normally set d0 to a NULL if your initialization failed,
	X	; but I'm not doing that for this demo, since it is unlikely
	X	; you actually have a board with this particular manufacturer ID
	X	; installed when running this demo.
	X	;
	X	move.l	a5,d0
	X	movem.l	(sp)+,d1-d7/a0-a5
	X
	X	rts
	X
	X;----------------------------------------------------------------------
	X;
	X; here begins the system interface commands.  When the user calls
	X; OpenLibrary/CloseLibrary/RemoveLibrary, this eventually gets translated
	X; into a call to the following routines (Open/Close/Expunge).  Exec
	X; has already put our device pointer in a6 for us.  Exec has turned
	X; off task switching while in these routines (via Forbid/Permit), so
	X; we should not take too long in them.
	X;
	X;----------------------------------------------------------------------
	X
	X
	X	; Open sets the IO_ERROR field on an error.  If it was successfull,
	X	; we should set up the IO_UNIT field.
	X
	XOpen:		; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
	X	PUTMSG	30,<'%s/Open: called'>
	X	movem.l	d2/a2/a3/a4,-(sp)
	X
	X	move.l	a1,a2		; save the iob
	X
	X	;------ see if the unit number is in range
	X	subq	#1,d0		; Unit ZERO isn't allowed
	X	cmp.l	#MD_NUMUNITS,d0
	X	bcc.s	Open_Error	; unit number out of range
	X
	X	;------ see if the unit is already initialized
	X	move.l	d0,d2		; save unit number
	X	lsl.l	#2,d0
	X	lea.l	md_Units(a6,d0.l),a4
	X	move.l	(a4),d0
	X	bne.s	Open_UnitOK
	X
	X	;------ try and conjure up a unit
	X	bsr	InitUnit
	X
	X	;------ see if it initialized OK
	X	move.l	(a4),d0
	X	beq.s	Open_Error
	X
	XOpen_UnitOK:
	X	move.l	d0,a3		; unit pointer in a3
	X
	X	move.l	d0,IO_UNIT(a2)
	X
	X	;------ mark us as having another opener
	X	addq.w	#1,LIB_OPENCNT(a6)
	X	addq.w	#1,UNIT_OPENCNT(a3)
	X
	X	;------ prevent delayed expunges
	X	bclr	#LIBB_DELEXP,md_Flags(a6)
	X	moveq.l	#0,d0
	X
	XOpen_End:
	X
	X	movem.l	(sp)+,d2/a2/a3/a4
	X	rts
	X
	XOpen_Error:
	X	move.b	#IOERR_OPENFAIL,IO_ERROR(a2)
	X	move.b	#IOERR_OPENFAIL,d0
	X	bra.s	Open_End
	X
	X	; There are two different things that might be returned from
	X	; the Close routine.  If the device is no longer open and
	X	; there is a delayed expunge then Close should return the
	X	; segment list (as given to Init).  Otherwise close should
	X	; return NULL.
	X
	XClose:		; ( device:a6, iob:a1 )
	X	movem.l	d1/a2-a3,-(sp)
	X	PUTMSG	30,<'%s/Close: called'>
	X
	X	move.l	a1,a2
	X
	X	move.l	IO_UNIT(a2),a3
	X
	X	;------ make sure the iob is not used again
	X	moveq.l	#-1,d0
	X	move.l	d0,IO_UNIT(a2)
	X	move.l	d0,IO_DEVICE(a2)
	X
	X	;------ see if the unit is still in use
	X	subq.w	#1,UNIT_OPENCNT(a3)
	X
	X;!!!!!! Since this example is a RAM disk (and we don't want the contents to
	X;!!!!!! disappear between opens, ExpungeUnit will be skipped here.  It would
	X;!!!!!! be used for drivers of "real" devices
	X;!!!!!!	bne.s	Close_Device
	X;!!!!!!	bsr	ExpungeUnit
	X
	XClose_Device:
	X	;------ mark us as having one fewer openers
	X	moveq.l	#0,d0
	X	subq.w	#1,LIB_OPENCNT(a6)
	X
	X	;------ see if there is anyone left with us open
	X	bne.s	Close_End
	X
	X	;------ see if we have a delayed expunge pending
	X	btst	#LIBB_DELEXP,md_Flags(a6)
	X	beq.s	Close_End
	X
	X	;------ do the expunge
	X	bsr	Expunge
	X
	XClose_End:
	X	movem.l	(sp)+,d1/a2-a3
	X	rts
	X
	X
	X	; There are two different things that might be returned from
	X	; the Expunge routine.  If the device is no longer open
	X	; then Expunge should return the segment list (as given to
	X	; Init).  Otherwise Expunge should set the delayed expunge
	X	; flag and return NULL.
	X	;
	X	; One other important note: because Expunge is called from
	X	; the memory allocator, it may NEVER Wait() or otherwise
	X	; take long time to complete.
	X
	XExpunge:	; ( device: a6 )
	X	PUTMSG	30,<'%s/Expunge: called'>
	X
	X	movem.l	d1/d2/a5/a6,-(sp)	; Best to save ALL modified registers
	X	move.l	a6,a5
	X	move.l	md_SysLib(a5),a6
	X	
	X	;------ see if anyone has us open
	X	tst.w	LIB_OPENCNT(a5)
	X;!!!!!  The following line is commented out for this RAM disk demo, since
	X;!!!!!  we don't want the RAM to be freed after FORMAT, for example.
	X;	beq	1$
	X
	X	;------ it is still open.  set the delayed expunge flag
	X	bset	#LIBB_DELEXP,md_Flags(a5)
	X	CLEAR	d0
	X	bra.s	Expunge_End
	X
	X1$:
	X	;------ go ahead and get rid of us.  Store our seglist in d2
	X	move.l	md_SegList(a5),d2
	X
	X	;------ unlink from device list
	X	move.l	a5,a1
	X	CALLSYS	Remove
	X	
	X	;
	X	; device specific closings here...
	X	;
	X
	X	;------ free our memory
	X	CLEAR	d0
	X	CLEAR	d1
	X	move.l	a5,a1
	X	move.w	LIB_NEGSIZE(a5),d1
	X
	X	sub.w	d1,a1
	X	add.w	LIB_POSSIZE(a5),d0
	X	add.l	d1,d0
	X
	X	CALLSYS	FreeMem
	X
	X	;------ set up our return value
	X	move.l	d2,d0
	X
	XExpunge_End:
	X	movem.l	(sp)+,d1/d2/a5/a6
	X	rts
	X
	X
	XNull:
	X	PUTMSG	30,<'%s/Null: called'>
	X	CLEAR	d0
	X	rts
	X
	X
	XInitUnit:	; ( d2:unit number, a3:scratch, a6:devptr )
	X	PUTMSG	30,<'%s/InitUnit: called'>
	X	movem.l	d2-d4/a2,-(sp)
	X
	X	;------ allocate unit memory
	X	move.l	#MyDevUnit_Sizeof,d0
	X	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	X	LINKSYS	AllocMem,md_SysLib(a6)
	X
	X	tst.l	d0
	X	beq	InitUnit_End
	X
	X	move.l	d0,a3
	X	move.b	d2,mdu_UnitNum(a3)	; initialize unit number
	X	move.l	a6,mdu_Device(a3)	; initialize device pointer
	X
	X	;------ start up the unit process.  We do a trick here --
	X	;------ we set his message port to PA_IGNORE until the
	X	;------ new process has a change to set it up.
	X	;------ We cannot go to sleep here: it would be very nasty
	X	;------ if someone else tried to open the unit
	X	;------ (exec's OpenDevice has done a Forbid() for us --
	X	;------ we depend on this to become single threaded).
	X
	X	;------ Initialize the stack information
	X	lea	mdu_stack(a3),a0	; Low end of stack
	X	move.l	a0,mdu_tcb+TC_SPLOWER(a3)
	X	lea	MYPROCSTACKSIZE(a0),a0	; High end of stack
	X	move.l	a0,mdu_tcb+TC_SPUPPER(a3)
	X	move.l	a3,-(A0)		; argument -- unit ptr
	X	move.l	a0,mdu_tcb+TC_SPREG(a3)
	X	;------ initialize the unit's list
	X	lea	MP_MSGLIST(a3),a0
	X	NEWLIST	a0
	X	lea	mdu_tcb(a3),a0
	X	move.l	a0,MP_SIGTASK(a3)
	X	moveq.l	#0,d0			; Don't need to re-zero it
	X	move.l	a3,a2			; InitStruct is initializing the UNIT
	X	lea.l	mdu_Init,A1
	X	LINKSYS	InitStruct,md_SysLib(a6)
	X
	X	move.l	a3,mdu_is+IS_DATA(a3)	; Pass int. server unit addr.
	X
	X;	Startup the task
	X	lea	mdu_tcb(a3),a1
	X	lea	Proc_Begin(PC),a2
	X	move.l	a3,-(sp)		; Preserve UNIT pointer
	X	lea	-1,a3			; generate address error
	X					; if task ever "returns"
	X	CLEAR	d0
	X	LINKSYS AddTask,md_SysLib(a6)
	X	move.l	(sp)+,a3		; restore UNIT pointer
	X
	X	;------ mark us as ready to go
	X	move.l	d2,d0			; unit number
	X	lsl.l	#2,d0
	X	move.l	a3,md_Units(a6,d0.l)	; set unit table
	X
	X
	XInitUnit_End:
	X	movem.l	(sp)+,d2-d4/a2
	X	rts
	X
	X	;------ got an error.  free the unit structure that we allocated.
	XInitUnit_FreeUnit:
	X	bsr	FreeUnit
	X	bra.s	InitUnit_End
	X
	XFreeUnit:	; ( a3:unitptr, a6:deviceptr )
	X	move.l	a3,a1
	X	move.l	#MyDevUnit_Sizeof,d0
	X	LINKSYS	FreeMem,md_SysLib(a6)
	X	rts
	X
	X
	XExpungeUnit:	; ( a3:unitptr, a6:deviceptr )
	X	PUTMSG	30,<'%s/ExpungeUnit: called'>
	X	move.l	d2,-(sp)
	X
	X;
	X; If you can expunge you unit, and each unit has it's own interrups,
	X; you must remember to remove its interrupt server
	X;
	X
	X	IFD	INTRRUPT
	X	lea.l	mdu_is(a3),a1		; Point to interrupt structure
	X	moveq	#3,d0			; Portia interrupt bit 3
	X	LINKSYS	RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
	X	ENDC
	X
	X	;------ get rid of the unit's task.  We know this is safe
	X	;------ because the unit has an open count of zero, so it
	X	;------ is 'guaranteed' not in use.
	X	lea	mdu_tcb(a3),a1
	X	LINKSYS	RemTask,md_SysLib(a6)
	X
	X	;------ save the unit number
	X	CLEAR	d2
	X	move.b	mdu_UnitNum(a3),d2
	X
	X	;------ free the unit structure.
	X	bsr	FreeUnit
	X
	X	;------ clear out the unit vector in the device
	X	lsl.l	#2,d2
	X	clr.l	md_Units(a6,d2.l)
	X
	X	move.l	(sp)+,d2
	X
	X	rts
	X
	X;----------------------------------------------------------------------
	X;
	X; here begins the device specific functions
	X;
	X;----------------------------------------------------------------------
	X
	X; cmdtable is used to look up the address of a routine that will
	X; implement the device command.
	Xcmdtable:
	X	DC.L	Invalid		; $00000001
	X	DC.L	MyReset		; $00000002
	X	DC.L	RdWrt		; $00000004	Common routine for read/write
	X	DC.L	RdWrt		; $00000008
	X	DC.L	Update		; $00000010
	X	DC.L	Clear		; $00000020
	X	DC.L	MyStop		; $00000040
	X	DC.L	Start		; $00000080
	X	DC.L	Flush		; $00000100
	X	DC.L	Motor		; $00000200  motor	(NO-OP)
	X	DC.L	Seek		; $00000400  seek	(NO-OP)
	X	DC.L	RdWrt		; $00000800  format -> WRITE for RAMDISK
	X	DC.L	MyRemove	; $00001000  remove		(NO-OP)
	X	DC.L	ChangeNum	; $00002000  changenum		(Returns 0)
	X	DC.L	ChangeState	; $00004000  changestate	(Returns 0)
	X	DC.L	ProtStatus	; $00008000  protstatus		(Returns 0)
	X	DC.L	RawRead		; Not supported	(INVALID)
	X	DC.L	RawWrite	; Not supported	(INVALID)
	X	DC.L	GetDriveType	; Get drive type	(Returns 1)
	X	DC.L	GetNumTracks	; Get number of tracks (Returns NUMTRKS)
	X	DC.L	AddChangeInt	; Add disk change interrupt (NO-OP)
	X	DC.L	RemChangeInt	; Remove disk change interrupt ( NO-OP)
	Xcmdtable_end:
	X
	X; this define is used to tell which commands should not be queued
	X; command zero is bit zero.
	X; The immediate commands are Invalid, Reset, Stop, Start, Flush
	XIMMEDIATES	EQU	$000001c3
	X
	X; These commands can NEVER be done "immediately" if using interrupts,
	X; since they would "wait" for the interrupt forever!
	X; Read, Write, Format
	XNEVERIMMED	EQU	$0000080C
	X;
	X; BeginIO starts all incoming io.  The IO is either queued up for the
	X; unit task or processed immediately.
	X;
	X
	XBeginIO:	; ( iob: a1, device:a6 )
	X	PUTMSG	30,<'%s/BeginIO: called'>
	X	movem.l	d1/a0/a3,-(sp)
	X
	X	;------ bookkeeping
	X	move.l	IO_UNIT(a1),a3
	X
	X	;------ see if the io command is within range
	X	move.w	IO_COMMAND(a1),d0
	X	cmp.w	#MYDEV_END,d0
	X	bcc	BeginIO_NoCmd
	X
	X	DISABLE	a0
	X
	X	;------ process all immediate commands no matter what
	X	move.w	#IMMEDIATES,d1
	X	btst	d0,d1
	X	bne.s	BeginIO_Immediate
	X
	X	IFD	INTRRUPT	; if using interrupts,
	X	;------ queue all NEVERIMMED commands no matter what
	X	move.w	#NEVERIMMED,d1
	X	btst	d0,d1
	X	bne.s	BeginIO_QueueMsg
	X	ENDC
	X
	X	;------ see if the unit is STOPPED.  If so, queue the msg.
	X	btst	#MDUB_STOPPED,UNIT_FLAGS(a3)
	X	bne.s	BeginIO_QueueMsg
	X
	X	;------ this is not an immediate command.  see if the device is
	X	;------ busy.
	X	bset	#UNITB_ACTIVE,UNIT_FLAGS(a3)
	X	beq.s	BeginIO_Immediate
	X
	X	;------ we need to queue the device.  mark us as needing
	X	;------ task attention.  Clear the quick flag
	XBeginIO_QueueMsg:
	X	BSET	#UNITB_INTASK,UNIT_FLAGS(a3)
	X	bclr	#IOB_QUICK,IO_FLAGS(a1)
	X
	X	ENABLE	a0
	X
	X	move.l	a3,a0
	X	LINKSYS	PutMsg,md_SysLib(a6)
	X	bra	BeginIO_End
	X
	XBeginIO_Immediate:
	X	ENABLE	a0
	X
	X	bsr	PerformIO
	X
	XBeginIO_End:
	X	movem.l	(sp)+,d1/a0/a3
	X	rts
	X
	XBeginIO_NoCmd:
	X	move.b	#IOERR_NOCMD,IO_ERROR(a1)
	X	bra.s	BeginIO_End
	X
	X
	X;
	X; PerformIO actually dispatches an io request.  It expects a3 to already
	X; have the unit pointer in it.  a6 has the device pointer (as always).
	X; a1 has the io request.  Bounds checking has already been done on
	X; the io request.
	X;
	X
	XPerformIO:	; ( iob:a1, unitptr:a3, devptr:a6 )
	X	PUTMSG	30,<'%s/PerforIO: called'>
	X	move.l	a2,-(sp)
	X	move.l	a1,a2
	X
	X	clr.b	IO_ERROR(A2)		; No error so far
	X	move.w	IO_COMMAND(a2),d0
	X	lsl	#2,d0			; Multiply by 4 to get table offset
	X	lea	cmdtable(pc),a0
	X	move.l	0(a0,d0.w),a0
	X
	X	jsr	(a0)
	X
	X	move.l	(sp)+,a2
	X	rts
	X
	X;
	X; TermIO sends the IO request back to the user.  It knows not to mark
	X; the device as inactive if this was an immediate request or if the
	X; request was started from the server task.
	X;
	X
	XTermIO:		; ( iob:a1, unitptr:a3, devptr:a6 )
	X	PUTMSG	30,<'%s/TermIO: called'>
	X	move.w	IO_COMMAND(a1),d0
	X	move.w	#IMMEDIATES,d1
	X	btst	d0,d1
	X	bne.s	TermIO_Immediate
	X
	X	;------ we may need to turn the active bit off.
	X	btst	#UNITB_INTASK,UNIT_FLAGS(a3)
	X	bne.s	TermIO_Immediate
	X
	X	;------ the task does not have more work to do
	X	bclr	#UNITB_ACTIVE,UNIT_FLAGS(a3)
	X
	XTermIO_Immediate:
	X	;------ if the quick bit is still set then we don't need to reply
	X	;------ msg -- just return to the user.
	X	btst	#IOB_QUICK,IO_FLAGS(a1)
	X	bne.s	TermIO_End
	X
	X	LINKSYS	ReplyMsg,md_SysLib(a6)
	X
	XTermIO_End:
	X	rts
	X	
	X
	XAbortIO:	; ( iob: a1, device:a6 )
	X;----------------------------------------------------------------------
	X;
	X; here begins the functions that implement the device commands
	X; all functions are called with:
	X;	a1 -- a pointer to the io request block
	X;	a2 -- another pointer to the iob
	X;	a3 -- a pointer to the unit
	X;	a6 -- a pointer to the device
	X;
	X; Commands that conflict with 68000 instructions have a "My" prepended
	X; to them.
	X;----------------------------------------------------------------------
	X
	XRawRead:		; 10 Not supported	(INVALID)
	XRawWrite:		; 11 Not supported	(INVALID)
	XInvalid:
	X	move.b	#IOERR_NOCMD,IO_ERROR(a1)
	X	bsr	TermIO
	X	rts
	X
	XMyReset:
	XAddChangeInt:
	XRemChangeInt:
	XMyRemove:
	XSeek:
	XMotor:
	XChangeNum:
	XChangeState:
	XProtStatus:
	X	clr.l	IO_ACTUAL(a1)	; Indicate drive isn't protected
	X	bsr	TermIO
	X	rts
	X
	XGetDriveType:
	X	move.l	#1,IO_ACTUAL(a1)		; Make it look like 3.5"
	X	bsr	TermIO
	X	rts
	X
	XGetNumTracks:
	X	move.l	#RAMSIZE/5120,IO_ACTUAL(a1)	; Number of 10 sector tracks
	X	bsr	TermIO
	X	rts
	X
	XRdWrt:
	X	movem.l a2/a3,-(sp)
	X	clr.l	IO_ACTUAL(a1)		; Initially, no data moved
	X	move.l	IO_DATA(a1),a0
	X	move.l	IO_LENGTH(a1),d0
	X
	X	;------ deal with zero length I/O
	X	beq.s	RdWrt_end
	X
	X
	X	move.l	IO_UNIT(a1),a3		; Get unit pointer
	X	move.l	a1,a2
	X
	X*		check operation for legality
	X
	X	move.l	IO_OFFSET(a2),d0
	X	move.l	d0,d1
	X
	X*		check for being an even sector boundary
	X	and.l	#SECTOR-1,d1
	X	bne	IO_Err
	X
	X*		check for IO within disc range
	X	add.l	IO_LENGTH(a2),d0
	X	cmp.l	#RAMSIZE,d0
	X	bgt	IO_Err
	X
	X* We've gotten this far, it must be a valid request.
	X
	X	IFD	INTRRUPT
	X	move.l	mdu_SigMask(a3),d0	; Get signals to wait for
	X	LINKSYS	Wait,md_SysLib(a6)	; Wait for interrrupt before proceeding
	X	ENDC
	X
	X	move.l	IO_OFFSET(a2),d0
	X	lea	mdu_RAM(a3),a0		; Point to RAMDISK "sector" for I/O
	X	add.l	d0,a0			; Can't use index, "out of range"
	X	move.l	IO_LENGTH(a2),d0
	X	move.l	d0,IO_ACTUAL(a2)	; Indicate we've moved all bytes
	X	subq.w	#1,d0			; Adjust byte count for DBRA loop
	X	move.l	IO_DATA(a2),a1		; Point to data buffer
	X
	X	move.w	IO_COMMAND(a2),d1	; Now go to correct loop for
	X	cmp.b	#CMD_READ,d1		; Read or Write commands
	X	BEQ.S	RdLp
	X
	XWrtLp:	move.b	(a1)+,(a0)+		; Copy a byte
	X	dbra	d0,WrtLp		; Move all requested bytes
	X	bra.s	RdWrt_end
	X
	XRdLp:	move.b	(a0)+,(a1)+		; Copy a byte
	X	dbra	d0,RdLp			; Move all requested bytes
	X	bra.s	RdWrt_end
	X
	XIO_Err:
	X	move.b	#IOERR_BADLENGTH,IO_ERROR(a1)
	X
	XRdWrt_end:
	X	move.l	a2,a1
	X	bsr	TermIO
	X	movem.l (sp)+,a2/a3
	X	rts
	X
	X;
	X; Update and Clear are internal buffering commands.  Update forces all
	X; io out to its final resting spot, and does not return until this is
	X; done.  Clear invalidates all internal buffers.  Since this device
	X; has no internal buffers, these commands do not apply.
	X;
	X
	XUpdate:
	X	PUTMSG	30,<'%s/Update: called'>
	X	bra	Invalid
	XClear:
	X	PUTMSG	30,<'%s/Clear: called'>
	X	bra	Invalid
	X
	X;
	X; the Stop command stop all future io requests from being
	X; processed until a Start command is received.  The Stop
	X; command is NOT stackable: e.g. no matter how many stops
	X; have been issued, it only takes one Start to restart
	X; processing.
	X;
	X
	XMyStop:
	X	PUTMSG	30,<'%s/MyStop: called'>
	X	bset	#MDUB_STOPPED,UNIT_FLAGS(a3)
	X
	X	bsr	TermIO
	X	rts
	X	
	XStart:
	X	PUTMSG	30,<'%s/Start: called'>
	X	bsr	InternalStart
	X
	X	move.l	a2,a1
	X	bsr	TermIO
	X
	X	rts
	X
	XInternalStart:
	X	;------ turn processing back on
	X	bclr	#MDUB_STOPPED,UNIT_FLAGS(a3)
	X
	X	;------ kick the task to start it moving
	X	move.l	a3,a1
	X	CLEAR	d0
	X	move.l	MP_SIGBIT(a3),d1
	X	bset	d1,d0
	X	LINKSYS	Signal,md_SysLib(a3)
	X
	X	rts
	X
	X;
	X; Flush pulls all io requests off the queue and sends them back.
	X; We must be careful not to destroy work in progress, and also
	X; that we do not let some io requests slip by.
	X;
	X; Some funny magic goes on with the STOPPED bit in here.  Stop is
	X; defined as not being reentrant.  We therefore save the old state
	X; of the bit and then restore it later.  This keeps us from
	X; needing to DISABLE in flush.  It also fails miserably if someone
	X; does a start in the middle of a flush.
	X;
	X
	XFlush:
	X	PUTMSG	30,<'%s/Flush: called'>
	X	movem.l	d2/a6,-(sp)
	X
	X	move.l	md_SysLib(a6),a6
	X
	X	bset	#MDUB_STOPPED,UNIT_FLAGS(a3)
	X	sne	d2
	X
	XFlush_Loop:
	X	move.l	a3,a0
	X	CALLSYS	GetMsg
	X
	X	tst.l	d0
	X	beq.s	Flush_End
	X
	X	move.l	d0,a1
	X	move.b	#IOERR_ABORTED,IO_ERROR(a1)
	X	CALLSYS	ReplyMsg
	X
	X	bra.s	Flush_Loop
	X
	XFlush_End:
	X
	X	move.l	d2,d0
	X	movem.l	(sp)+,d2/a6
	X
	X	tst.b	d0
	X	beq.s	1$
	X
	X	bsr	InternalStart
	X1$:
	X
	X	move.l	a2,a1
	X	bsr	TermIO
	X
	X	rts
	X
	X;
	X; Foo and Bar are two device specific commands that are provided just
	X; to show you how to add your own commands.  The currently return that
	X; no work was done.
	X;
	X
	XFoo:
	XBar:
	X	CLEAR	d0
	X	move.l	d0,IO_ACTUAL(a1)
	X
	X	bsr	TermIO
	X	rts
	X
	X;----------------------------------------------------------------------
	X;
	X; here begins the process related routines
	X;
	X; A Process is provided so that queued requests may be processed at
	X; a later time.
	X;
	X;
	X; Register Usage
	X; ==============
	X; a3 -- unit pointer
	X; a6 -- syslib pointer
	X; a5 -- device pointer
	X; a4 -- task (NOT process) pointer
	X; d7 -- wait mask
	X;
	X;----------------------------------------------------------------------
	X
	X; some dos magic.  A process is started at the first executable address
	X; after a segment list.  We hand craft a segment list here.  See the
	X; the DOS technical reference if you really need to know more about this.
	X
	X	cnop	0,4			; long word allign
	X	DC.L	16			; segment length -- any number will do
	Xmyproc_seglist:
	X	DC.L	0			; pointer to next segment
	X
	X; the next instruction after the segment list is the first executable address
	X
	XProc_Begin:
	X
	X	move.l	_AbsExecBase,a6
	X
	X	;------ Grab the argument
	X	move.l	4(sp),a3		; Unit pointer
	X
	X	move.l	mdu_Device(a3),a5	; Point to device structure
	X
	X	IFD	INTRRUPT
	X	;------ Allocate a signal for "I/O Complete" interrupts
	X	moveq	#-1,d0			; -1 is any signal at all
	X	CALLSYS	AllocSignal
	X	move.b	d0,mdu_SigBit(A3)	; Save in unit structure
	X
	X	moveq	#0,d7			; Convert bit number signal mask
	X	bset	d0,d7
	X	move.l	d7,mdu_SigMask(A3)	; Save in unit structure
	X
	X	lea.l	mdu_is(a3),a1		; Point to interrupt structure
	X	moveq	#3,d0			; Portia interrupt bit 3
	X	CALLSYS AddIntServer		; Now install the server
	X
	X	move.l	md_Base(a5),a0		; Get board base address
	X*	bset.b	#INTENABLE,INTCTRL2(a0)	; Enable interrupts
	X	ENDC
	X
	X	;------ Allocate the right signal
	X
	X	moveq	#-1,d0			; -1 is any signal at all
	X	CALLSYS	AllocSignal
	X
	X	move.b	d0,MP_SIGBIT(a3)
	X	move.b	#PA_SIGNAL,MP_FLAGS(a3)
	X
	X	;------ change the bit number into a mask, and save in d7
	X
	X	moveq	#0,d7
	X	bset	d0,d7
	X
	X	;------
	X	;------ OK, kids, we are done with initialization.  We now
	X	;------ can start the main loop of the driver.  It goes
	X	;------ like this.  Because we had the port marked PA_IGNORE
	X	;------ for a while (in InitUnit) we jump to the getmsg
	X	;------ code on entry.
	X	;------
	X	;------		wait for a message
	X	;------		lock the device
	X	;------		get a message.  if no message unlock device and loop
	X	;------		dispatch the message
	X	;------		loop back to get a message
	X	;------
	X
	X	bra.s	Proc_CheckStatus
	X
	X	;------ main loop: wait for a new message
	XProc_MainLoop:
	X	move.l	d7,d0
	X	CALLSYS	Wait
	X
	XProc_CheckStatus:
	X	;------ see if we are stopped
	X	btst	#MDUB_STOPPED,UNIT_FLAGS(a3)
	X	bne.s	Proc_MainLoop		; device is stopped
	X
	X	;------ lock the device
	X	bset	#UNITB_ACTIVE,UNIT_FLAGS(a3)
	X	bne.s	Proc_MainLoop		; device in use
	X
	X	;------ get the next request
	XProc_NextMessage:
	X	move.l	a3,a0
	X	CALLSYS	GetMsg
	X	tst.l	d0
	X	beq.s	Proc_Unlock		; no message?
	X
	X	;------ do this request
	X	move.l	d0,a1
	X	exg	a5,a6			; put device ptr in right place
	X	bsr	PerformIO
	X	exg	a5,a6			; get syslib back in a6
	X
	X	bra.s	Proc_NextMessage
	X
	X	;------ no more messages.  back ourselves out.
	XProc_Unlock:
	X	and.b	#$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
	X	bra	Proc_MainLoop
	X
	X;
	X; Here is a dummy interrupt handler, with some crucial components commented
	X; out.  If the IFD INTRRUPT is enabled, this code will cause the device to
	X; wait for a level two interrupt before it will process each request
	X; (pressing a key on the keyboard will do it).  This code is normally
	X; disabled, and must fake or omit certain operations since there  isn't
	X; really any hardware for this driver.  Similiar code has been used
	X; successfully in other, "REAL" device drivers.
	X;
	X
	X	IFD	INTRRUPT
	X;	A1 should be pointing to the unit structure upon entry!
	X
	Xmyintr:		move.l	mdu_Device(a1),a0	; Get device pointer
	X		move.l	md_SysLib(a0),a6	; Get pointer to system
	X		move.l	md_Base(a0),a0		; point to board base address
	X*		btst.b	#IAMPULLING,INTCTRL1(a0);See if I'm interrupting
	X*		beq.s	myexnm			; if not set, exit, not mine
	X*		move.b	#0,INTACK(a0)		; toggle controller's int2 bit
	X
	X;		------ signal the task that an interrupt has occured
	X
	X		move.l	mdu_SigMask(a1),d0
	X		lea	mdu_tcb(a1),a1
	X		CALLSYS	Signal
	X
	X;
	X;		now clear the zero condition code so that
	X;		the interrupt handler doesn't call the next
	X;		interrupt server.
	X;
	X*		moveq	#1,d0			clear zero flag
	X*		bra.s	myexit			now exit
	X;
	X;		this exit point sets the zero condition code
	X;		so the interrupt handler will try the next server
	X;		in the interrupt chain
	X;
	Xmyexnm		moveq	#0,d0			set zero condition code
	X;
	Xmyexit		rts
	X	ENDC
	X
	Xmdu_Init:
	X;	------ Initialize the device
	X
	X	INITBYTE	MP_FLAGS,PA_IGNORE
	X	INITBYTE	LN_TYPE,NT_DEVICE
	X	INITLONG	LN_NAME,myName
	X	INITBYTE	mdu_Msg+LN_TYPE,NT_MSGPORT;Unit starts with MsgPort
	X	INITLONG	mdu_Msg+LN_NAME,myName		
	X	INITLONG	mdu_tcb+LN_NAME,myName
	X	INITBYTE	mdu_tcb+LN_TYPE,NT_TASK
	X	INITBYTE	mdu_tcb+LN_PRI,5
	X	INITBYTE	mdu_is+LN_PRI,4		; Int priority 4
	X	IFD	INTRRUPT
	X	INITLONG	mdu_is+IS_CODE,myintr	; Interrupt routine addr
	X	ENDC
	X	INITLONG	mdu_is+LN_NAME,myName
	X	DC.L	0
	X
	Xmdn_Init:
	X*	;------ Initialize packet for MakeDosNode
	X
	X	INITLONG	mdn_execName,myName	; Address of driver name
	X	INITLONG	mdn_tableSize,11	; # long words in AmigaDOS env.
	X	INITLONG	mdn_dName,$52414d00	; Store 'RAM' in name
	X	INITLONG	mdn_sizeBlock,128	; # longwords in a block
	X	INITLONG	mdn_numHeads,1		; RAM disk has only one "head"
	X	INITLONG	mdn_secsPerBlk,1	; secs/logical block, must = "1"
	X	INITLONG	mdn_blkTrack,10		; secs/track (must be reasonable)
	X	INITLONG	mdn_resBlks,1		; reserved blocks, MUST > 0!
	X	INITLONG	mdn_upperCyl,(RAMSIZE/5120)-1; upper cylinder
	X	INITLONG	mdn_numBuffers,1	; # AmigaDOS buffers to start
	X	DC.L	0
	X
	X;----------------------------------------------------------------------
	X; EndCode is a marker that show the end of your code.
	X; Make sure it does not span sections nor is before the
	X; rom tag in memory!  It is ok to put it right after
	X; the rom tag -- that way you are always safe.  I put
	X; it here because it happens to be the "right" thing
	X; to do, and I know that it is safe in this case.
	X;----------------------------------------------------------------------
	XEndCode:
	X
	X	END
SHAR_EOF
if test 30251 -ne "`wc -c < 'mydev.asm'`"
then
	echo shar: error transmitting "'mydev.asm'" '(should have been 30251 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'testdev.asm'" '(2021 characters)'
if test -f 'testdev.asm'
then
	echo shar: will not over-write existing file "'testdev.asm'"
else
sed 's/^	X//' << \SHAR_EOF > 'testdev.asm'
	X
	X*************************************************************************
	X*									*
	X*	Copyright (C) 1985, Commodore Amiga Inc.  All rights reserved.	*
	X*	Permission granted for non-commercial use			*								*
	X*									*
	X************************************************************************/
	X
	X
	X*************************************************************************
	X*
	X* testdev.asm -- test the mylib.asm code
	X*
	X* Source Control
	X* ------ -------
	X* 
	X* $Header: amain.asm,v 31.3 85/10/18 19:04:04 neil Exp $
	X*
	X* $Locker: neil $
	X*
	X* $Log:	amain.asm,v $
	X*
	X************************************************************************/
	X
	X	INCLUDE	'exec/types.i'
	X	INCLUDE	'exec/libraries.i'
	X	INCLUDE	'exec/devices.i'
	X	INCLUDE	'exec/io.i'
	X	INCLUDE	'exec/tasks.i'
	X	INCLUDE	'exec/interrupts.i'
	X
	X	INCLUDE	'asmsupp.i'
	X	INCLUDE	'mydev.i'
	X
	X
	X
	X	XDEF	_main
	X
	X	XREF	_printf
	X	XREF	_AbsExecBase
	X	XREF	_CreatePort
	X	XREF	_DeletePort
	X	XREF	_CreateStdIO
	X	XREF	_DeleteStdIO
	X
	X	XLIB	OpenDevice
	X	XLIB	CloseDevice
	X
	X
	X_main:
	X	move.l	_AbsExecBase,a6
	X
	X	;------ make a reply port
	X	pea	0
	X	pea	myName
	X	jsr	_CreatePort
	X	addq.l	#8,sp
	X
	X	move.l	d0,Port
	X	beq.s	main_end
	X
	X	;------ get an io request
	X	move.l	d0,-(sp)
	X	jsr	_CreateStdIO
	X	addq.l	#4,sp
	X
	X	move.l	d0,Iob
	X	beq	main_DeletePort
	X
	X	move.l	d0,a1
	X	move.l	#myName,LN_NAME(a1)
	X
	X	;------ open the test device: this will bring it in from disk
	X	lea	myDevName(pc),a0
	X	moveq.l	#1,d0
	X	moveq.l	#0,d1
	X	CALLSYS	OpenDevice
	X
	X	tst.l	d0
	X	beq.s	1$
	X
	X	;------ couldn't find the library
	X	pea	0
	X	move.l	d0,a0
	X	move.b	IO_ERROR(a0),3(sp)
	X	pea	myDevName(pc)
	X	pea	nodevmsg(pc)
	X	jsr	_printf
	X	addq.l	#8,sp
	X
	X	bra	main_DeleteIob
	X
	X1$:
	X
	X	;------ close the device
	X	move.l	Iob,a1
	X	CALLSYS	CloseDevice
	X
	Xmain_DeleteIob:
	X	move.l	Iob,-(sp)
	X	jsr	_DeleteStdIO
	X	addq.l	#4,sp
	X
	Xmain_DeletePort
	X	move.l	Port,-(sp)
	X	jsr	_DeletePort
	X	addq.l	#4,sp
	X
	Xmain_end:
	X	rts
	X
	XmyDevName:	MYDEVNAME
	XmyName:		dc.b	'testdev',0
	Xnodevmsg:	dc.b	'can not open device "%s": error %ld',10,0
	Xtestmsg:	dc.b	'function MYFUNC%ld returned %ld',10,0
	X
	XPort:	dc.l	0
	XIob:	dc.l	0
	X
	X	END
SHAR_EOF
if test 2021 -ne "`wc -c < 'testdev.asm'`"
then
	echo shar: error transmitting "'testdev.asm'" '(should have been 2021 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'makefile'" '(968 characters)'
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'makefile'
	X
	X#******************************************************************
	X#*                                                                *
	X#* Copyright (C) 1986, Commodore Amiga Inc.  All rights reserved. *
	X#* Permission granted for non-commercial use			 *								*
	X#*                                                                *
	X#******************************************************************
	X
	XOBJ = mydev.o testdev.o
	X
	X.asm.o:
	X	echo "Assembling $*.asm"
	X	Assem $*.asm -o $@ -i :include -c S -c W150000 -v $*.err
	X
	Xtdev: mydev.device testdev
	X	echo "DONE!"
	X
	Xtestdev: testdev.o
	X	echo "Linking $*"
	X	alink FROM startup.obj+testdev.o to testdev LIBRARY SYS:lib/amiga.lib ver tlink.err
	X
	X$(OBJ) : mydev.i asmsupp.i
	X
	Xmydev.device:	mydev.o
	X	echo "Linking $@"
	X	alink FROM mydev.o to mydev.device LIBRARY SYS:lib/amiga.lib+\
	Xsys:lib/debug.lib ver mlink.err
	X	echo "Copying $@ to devs:"
	X	copy $@ devs:$@
	X	echo "Copying $* to df0:expansion:"
	X	copy mydev.device df0:expansion/$*
SHAR_EOF
if test 968 -ne "`wc -c < 'makefile'`"
then
	echo shar: error transmitting "'makefile'" '(should have been 968 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Lee Erickson - now working with,       uucp: {ihnp4|seismo|caip}!cbmvax!erickson
but no way officially representing     arpa: cbmvax!erickson@seismo.css.GOV
Commodore, Engineering Department