home *** CD-ROM | disk | FTP | other *** search
-
- *************************************************************************
- * *
- * Copyright (C) 1986, Commodore Amiga Inc. All rights reserved. *
- * Permission granted for non-commercial use * *
- * *
- *************************************************************************
-
-
- *************************************************************************
- *
- * mydev.asm -- Skeleton device code. Modified by Lee Erickson to be a
- * simple disk device, using RAM to simulate a disk.
- * 10/7/86
- *
- ************************************************************************/
- SECTION section
-
- NOLIST
- include "exec/types.i"
- include "exec/nodes.i"
- include "exec/lists.i"
- include "exec/libraries.i"
- include "exec/devices.i"
- include "exec/io.i"
- include "exec/alerts.i"
- include "exec/initializers.i"
- include "exec/memory.i"
- include "exec/resident.i"
- include "exec/ables.i"
- include "exec/errors.i"
- include "exec/tasks.i"
- include 'libraries/expansion.i'
- include 'libraries/configvars.i'
- include 'libraries/configregs.i'
-
- include "asmsupp.i"
- include "messages.i"
-
- include "mydev.i"
-
- LIST
-
- ;------ These don't have to be external, but it helps some
- ;------ debuggers to have them globally visible
- XDEF Init
- XDEF Open
- XDEF Close
- XDEF Expunge
- XDEF Null
- XDEF myName
- XDEF BeginIO
- XDEF AbortIO
-
- XREF _AbsExecBase
-
- XLIB AddIntServer
- XLIB RemIntServer
- XLIB Debug
- XLIB InitStruct
- XLIB OpenLibrary
- XLIB CloseLibrary
- XLIB Alert
- XLIB FreeMem
- XLIB Remove
- XLIB AllocMem
- XLIB AddTask
- XLIB PutMsg
- XLIB RemTask
- XLIB ReplyMsg
- XLIB Signal
- XLIB GetMsg
- XLIB Wait
- XLIB WaitPort
- XLIB AllocSignal
- XLIB SetTaskPri
- XLIB GetCurrentBinding ; Get list of boards for this driver
- XLIB MakeDosNode
- XLIB AddDosNode
-
- INT_ABLES
-
-
- ; The first executable location. This should return an error
- ; in case someone tried to run you as a program (instead of
- ; loading you as a library).
- FirstAddress:
- CLEAR d0
- rts
-
- ;-----------------------------------------------------------------------
- ; A romtag structure. Both "exec" and "ramlib" look for
- ; this structure to discover magic constants about you
- ; (such as where to start running you from...).
- ;-----------------------------------------------------------------------
-
- ; Most people will not need a priority and should leave it at zero.
- ; the RT_PRI field is used for configuring the roms. Use "mods" from
- ; wack to look at the other romtags in the system
- MYPRI EQU 0
-
- initDDescrip:
- ;STRUCTURE RT,0
- DC.W RTC_MATCHWORD ; UWORD RT_MATCHWORD
- DC.L initDDescrip ; APTR RT_MATCHTAG
- DC.L EndCode ; APTR RT_ENDSKIP
- DC.B RTF_AUTOINIT ; UBYTE RT_FLAGS
- DC.B VERSION ; UBYTE RT_VERSION
- DC.B NT_DEVICE ; UBYTE RT_TYPE
- DC.B MYPRI ; BYTE RT_PRI
- DC.L myName ; APTR RT_NAME
- DC.L idString ; APTR RT_IDSTRING
- DC.L Init ; APTR RT_INIT
- ; LABEL RT_SIZE
-
-
- ; this is the name that the device will have
- subSysName:
- myName: MYDEVNAME
-
- ExLibName EXPANSIONNAME ; Expansion Library Name
-
- ; a major version number.
- VERSION: EQU 1
-
- ; A particular revision. This should uniquely identify the bits in the
- ; device. I use a script that advances the revision number each time
- ; I recompile. That way there is never a question of which device
- ; that really is.
- REVISION: EQU 17
-
- ; this is an identifier tag to help in supporting the device
- ; format is 'name version.revision (dd MON yyyy)',<cr>,<lf>,<null>
- idString: dc.b 'mydev 1.0 (31 Oct 1985)',13,10,0
-
- ; force word allignment
- ds.w 0
-
-
- ; The romtag specified that we were "RTF_AUTOINIT". This means
- ; that the RT_INIT structure member points to one of these
- ; tables below. If the AUTOINIT bit was not set then RT_INIT
- ; would point to a routine to run.
-
- Init:
- DC.L MyDev_Sizeof ; data space size
- DC.L funcTable ; pointer to function initializers
- DC.L dataTable ; pointer to data initializers
- DC.L initRoutine ; routine to run
-
-
- funcTable:
-
- ;------ standard system routines
- dc.l Open
- dc.l Close
- dc.l Expunge
- dc.l Null
-
- ;------ my device definitions
- dc.l BeginIO
- dc.l AbortIO
-
- ;------ function table end marker
- dc.l -1
-
-
- ; The data table initializes static data structures.
- ; The format is specified in exec/InitStruct routine's
- ; manual pages. The INITBYTE/INITWORD/INITLONG routines
- ; are in the file "exec/initializers.i". The first argument
- ; is the offset from the device base for this byte/word/long.
- ; The second argument is the value to put in that cell.
- ; The table is null terminated
- dataTable:
- INITBYTE LH_TYPE,NT_DEVICE
- INITLONG LN_NAME,myName
- INITBYTE LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
- INITWORD LIB_VERSION,VERSION
- INITWORD LIB_REVISION,REVISION
- INITLONG LIB_IDSTRING,idString
- DC.L 0
-
-
- ; This routine gets called after the device has been allocated.
- ; The device pointer is in D0. The segment list is in a0.
- ; If it returns non-zero then the device will be linked into
- ; the device list.
- initRoutine:
-
- ; Register Usage
- ; ==============
- ; a3 -- Points to tempory RAM
- ; a4 -- Expansion library base
- ; a5 -- device pointer
- ; a6 -- Exec base
- ;
- ;----------------------------------------------------------------------
- ;------ get the device pointer into a convenient A register
- PUTMSG 30,<'%s/Init: called'>
- movem.l d1-d7/a0-a5,-(sp) ; Preserve ALL modified registers
- move.l d0,a5
-
- ;------ save a pointer to exec
- move.l a6,md_SysLib(a5)
-
- ;------ save a pointer to our loaded code
- move.l a0,md_SegList(a5)
-
- lea.l ExLibName,A1 ; Get expansion lib. name
- moveq.l #0,D0
- CALLSYS OpenLibrary ; Open the expansion library
- tst.l D0
- bne.s init_OpSuccess
-
- init_OpFail:
- ALERT AG_OpenLib!AO_ExpansionLib
-
- init_OpSuccess:
- move.l D0,A4
-
- lea md_Base(A5),A0 ; Get the Current Bindings
- moveq #4,D0 ; Just get address (length = 4 bytes)
- LINKLIB _LVOGetCurrentBinding,A4
- move.l md_Base(A5),D0 ; Get start of list
- tst.l D0 ; If controller not found
- beq Init_End ; Exit and unload driver
- move.l D0,A0 ; Get config structure address
- move.l cd_BoardAddr(A0),md_Base(A5); Save board base address
- bclr.b #CDB_CONFIGME,cd_Flags(A0); Mark board as configured
-
- Init_End:
- ;----------------------------------------------------------------------
- ;
- ; Here we build a packet describing the characteristics of our disk to
- ; pass to AmigaDOS. This serves the same purpose as a "mount" command
- ; of this device would. For disks, it might be useful to actually
- ; get this information right from the disk itself. Just as mount,
- ; it could be for multiple partitions on the single physical device.
- ; For this example, we will simply hard code the appropriate parameters.
- ;
- ;-----------------------------------------------------------------------
-
- ;!!!! Normally you would only do this if your card was successfully configured
- ;!!!! up above. For the demo, it will always be done.
-
- ;------ Allocate tempory RAM to build MakeDosNode parameter packet
- move.l #MEMF_CLEAR!MEMF_PUBLIC,d1
- move.l #mdn_Sizeof,d0 ; Enough room for our parameter packet
- CALLSYS AllocMem
- move.l d0,a3
-
- ;----- Use InitStruct to initialize the constant portion of packet
- move.l d0,a2 ; Point to memory to initialize
- moveq.l #0,d0 ; Don't need to re-zero it
- lea.l mdn_Init(pc),A1
- CALLSYS InitStruct
-
- lea mdn_dName(a3),a0 ; Get addr of Device name
- move.l a0,mdn_dosName(a3) ; and save in environment
-
- moveq #1,d6 ; Now tell AmigaDOS about all units
- Uloop:
- move.b d6,d0 ; Get unit number
- add.b #$2F,d0 ; Make ASCII, minus 1
- move.b d0,mdn_dName+3(a3) ; and store in name
- move.l d6,mdn_unit(a3) ; Store unit # in environment
-
- move.l a3,a0
- LINKLIB _LVOMakeDosNode,a4 ; Build AmigaDOS structures
- move.l d0,a0 ; Get deviceNode address
- moveq.l #0,d0 ; Set device priority to 0
- moveq.l #0,d1
- * moveq.l #ADNF_STARTPROC,d1 ; Have handler started
- LINKLIB _LVOAddDosNode,a4
-
- addq #1,d6 ; Bump unit number
- cmp.b #MD_NUMUNITS,d6
- ble.s Uloop ; Loop until all units installed
-
- move.l a3,a1 ; Return RAM to system
- move.l #mdn_Sizeof,d0
- CALLSYS FreeMem
-
- move.l a4,a1 ; Now close expansion library
- CALLSYS CloseLibrary
- ;
- ; You would normally set d0 to a NULL if your initialization failed,
- ; but I'm not doing that for this demo, since it is unlikely
- ; you actually have a board with this particular manufacturer ID
- ; installed when running this demo.
- ;
- move.l a5,d0
- movem.l (sp)+,d1-d7/a0-a5
-
- rts
-
- ;----------------------------------------------------------------------
- ;
- ; here begins the system interface commands. When the user calls
- ; OpenLibrary/CloseLibrary/RemoveLibrary, this eventually gets translated
- ; into a call to the following routines (Open/Close/Expunge). Exec
- ; has already put our device pointer in a6 for us. Exec has turned
- ; off task switching while in these routines (via Forbid/Permit), so
- ; we should not take too long in them.
- ;
- ;----------------------------------------------------------------------
-
-
- ; Open sets the IO_ERROR field on an error. If it was successfull,
- ; we should set up the IO_UNIT field.
-
- Open: ; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
- PUTMSG 30,<'%s/Open: called'>
- movem.l d2/a2/a3/a4,-(sp)
-
- move.l a1,a2 ; save the iob
-
- ;------ see if the unit number is in range
- subq #1,d0 ; Unit ZERO isn't allowed
- cmp.l #MD_NUMUNITS,d0
- bcc.s Open_Error ; unit number out of range
-
- ;------ see if the unit is already initialized
- move.l d0,d2 ; save unit number
- lsl.l #2,d0
- lea.l md_Units(a6,d0.l),a4
- move.l (a4),d0
- bne.s Open_UnitOK
-
- ;------ try and conjure up a unit
- bsr InitUnit
-
- ;------ see if it initialized OK
- move.l (a4),d0
- beq.s Open_Error
-
- Open_UnitOK:
- move.l d0,a3 ; unit pointer in a3
-
- move.l d0,IO_UNIT(a2)
-
- ;------ mark us as having another opener
- addq.w #1,LIB_OPENCNT(a6)
- addq.w #1,UNIT_OPENCNT(a3)
-
- ;------ prevent delayed expunges
- bclr #LIBB_DELEXP,md_Flags(a6)
- moveq.l #0,d0
-
- Open_End:
-
- movem.l (sp)+,d2/a2/a3/a4
- rts
-
- Open_Error:
- move.b #IOERR_OPENFAIL,IO_ERROR(a2)
- move.b #IOERR_OPENFAIL,d0
- bra.s Open_End
-
- ; There are two different things that might be returned from
- ; the Close routine. If the device is no longer open and
- ; there is a delayed expunge then Close should return the
- ; segment list (as given to Init). Otherwise close should
- ; return NULL.
-
- Close: ; ( device:a6, iob:a1 )
- movem.l d1/a2-a3,-(sp)
- PUTMSG 30,<'%s/Close: called'>
-
- move.l a1,a2
-
- move.l IO_UNIT(a2),a3
-
- ;------ make sure the iob is not used again
- moveq.l #-1,d0
- move.l d0,IO_UNIT(a2)
- move.l d0,IO_DEVICE(a2)
-
- ;------ see if the unit is still in use
- subq.w #1,UNIT_OPENCNT(a3)
-
- ;!!!!!! Since this example is a RAM disk (and we don't want the contents to
- ;!!!!!! disappear between opens, ExpungeUnit will be skipped here. It would
- ;!!!!!! be used for drivers of "real" devices
- ;!!!!!! bne.s Close_Device
- ;!!!!!! bsr ExpungeUnit
-
- Close_Device:
- ;------ mark us as having one fewer openers
- moveq.l #0,d0
- subq.w #1,LIB_OPENCNT(a6)
-
- ;------ see if there is anyone left with us open
- bne.s Close_End
-
- ;------ see if we have a delayed expunge pending
- btst #LIBB_DELEXP,md_Flags(a6)
- beq.s Close_End
-
- ;------ do the expunge
- bsr Expunge
-
- Close_End:
- movem.l (sp)+,d1/a2-a3
- rts
-
-
- ; There are two different things that might be returned from
- ; the Expunge routine. If the device is no longer open
- ; then Expunge should return the segment list (as given to
- ; Init). Otherwise Expunge should set the delayed expunge
- ; flag and return NULL.
- ;
- ; One other important note: because Expunge is called from
- ; the memory allocator, it may NEVER Wait() or otherwise
- ; take long time to complete.
-
- Expunge: ; ( device: a6 )
- PUTMSG 30,<'%s/Expunge: called'>
-
- movem.l d1/d2/a5/a6,-(sp) ; Best to save ALL modified registers
- move.l a6,a5
- move.l md_SysLib(a5),a6
-
- ;------ see if anyone has us open
- tst.w LIB_OPENCNT(a5)
- ;!!!!! The following line is commented out for this RAM disk demo, since
- ;!!!!! we don't want the RAM to be freed after FORMAT, for example.
- ; beq 1$
-
- ;------ it is still open. set the delayed expunge flag
- bset #LIBB_DELEXP,md_Flags(a5)
- CLEAR d0
- bra.s Expunge_End
-
- 1$:
- ;------ go ahead and get rid of us. Store our seglist in d2
- move.l md_SegList(a5),d2
-
- ;------ unlink from device list
- move.l a5,a1
- CALLSYS Remove
-
- ;
- ; device specific closings here...
- ;
-
- ;------ free our memory
- CLEAR d0
- CLEAR d1
- move.l a5,a1
- move.w LIB_NEGSIZE(a5),d1
-
- sub.w d1,a1
- add.w LIB_POSSIZE(a5),d0
- add.l d1,d0
-
- CALLSYS FreeMem
-
- ;------ set up our return value
- move.l d2,d0
-
- Expunge_End:
- movem.l (sp)+,d1/d2/a5/a6
- rts
-
-
- Null:
- PUTMSG 30,<'%s/Null: called'>
- CLEAR d0
- rts
-
-
- InitUnit: ; ( d2:unit number, a3:scratch, a6:devptr )
- PUTMSG 30,<'%s/InitUnit: called'>
- movem.l d2-d4/a2,-(sp)
-
- ;------ allocate unit memory
- move.l #MyDevUnit_Sizeof,d0
- move.l #MEMF_PUBLIC!MEMF_CLEAR,d1
- LINKSYS AllocMem,md_SysLib(a6)
-
- tst.l d0
- beq InitUnit_End
-
- move.l d0,a3
- move.b d2,mdu_UnitNum(a3) ; initialize unit number
- move.l a6,mdu_Device(a3) ; initialize device pointer
-
- ;------ start up the unit process. We do a trick here --
- ;------ we set his message port to PA_IGNORE until the
- ;------ new process has a change 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 information
- lea mdu_stack(a3),a0 ; Low end of stack
- move.l a0,mdu_tcb+TC_SPLOWER(a3)
- lea MYPROCSTACKSIZE(a0),a0 ; High end of stack
- move.l a0,mdu_tcb+TC_SPUPPER(a3)
- move.l a3,-(A0) ; argument -- unit ptr
- move.l a0,mdu_tcb+TC_SPREG(a3)
- ;------ initialize the unit's list
- lea MP_MSGLIST(a3),a0
- NEWLIST a0
- lea mdu_tcb(a3),a0
- move.l a0,MP_SIGTASK(a3)
- moveq.l #0,d0 ; Don't need to re-zero it
- move.l a3,a2 ; InitStruct is initializing the UNIT
- lea.l mdu_Init,A1
- LINKSYS InitStruct,md_SysLib(a6)
-
- move.l a3,mdu_is+IS_DATA(a3) ; Pass int. server unit addr.
-
- ; Startup the task
- lea mdu_tcb(a3),a1
- lea Proc_Begin(PC),a2
- move.l a3,-(sp) ; Preserve UNIT pointer
- lea -1,a3 ; generate address error
- ; if task ever "returns"
- CLEAR d0
- LINKSYS AddTask,md_SysLib(a6)
- move.l (sp)+,a3 ; restore UNIT pointer
-
- ;------ mark us as ready to go
- move.l d2,d0 ; unit number
- lsl.l #2,d0
- move.l a3,md_Units(a6,d0.l) ; set unit table
-
-
- InitUnit_End:
- movem.l (sp)+,d2-d4/a2
- rts
-
- ;------ got an error. free the unit structure that we allocated.
- InitUnit_FreeUnit:
- bsr FreeUnit
- bra.s InitUnit_End
-
- FreeUnit: ; ( a3:unitptr, a6:deviceptr )
- move.l a3,a1
- move.l #MyDevUnit_Sizeof,d0
- LINKSYS FreeMem,md_SysLib(a6)
- rts
-
-
- ExpungeUnit: ; ( a3:unitptr, a6:deviceptr )
- PUTMSG 30,<'%s/ExpungeUnit: called'>
- move.l d2,-(sp)
-
- ;
- ; If you can expunge you unit, and each unit has it's own interrups,
- ; you must remember to remove its interrupt server
- ;
-
- IFD INTRRUPT
- lea.l mdu_is(a3),a1 ; Point to interrupt structure
- moveq #3,d0 ; Portia interrupt bit 3
- LINKSYS RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
- ENDC
-
- ;------ get rid of the unit's task. We know this is safe
- ;------ because the unit has an open count of zero, so it
- ;------ is 'guaranteed' not in use.
- lea mdu_tcb(a3),a1
- LINKSYS RemTask,md_SysLib(a6)
-
- ;------ save the unit number
- CLEAR d2
- move.b mdu_UnitNum(a3),d2
-
- ;------ free the unit structure.
- bsr FreeUnit
-
- ;------ clear out the unit vector in the device
- lsl.l #2,d2
- clr.l md_Units(a6,d2.l)
-
- move.l (sp)+,d2
-
- rts
-
- ;----------------------------------------------------------------------
- ;
- ; here begins the device specific functions
- ;
- ;----------------------------------------------------------------------
-
- ; cmdtable is used to look up the address of a routine that will
- ; implement the device command.
- cmdtable:
- DC.L Invalid ; $00000001
- DC.L MyReset ; $00000002
- DC.L RdWrt ; $00000004 Common routine for read/write
- DC.L RdWrt ; $00000008
- DC.L Update ; $00000010
- DC.L Clear ; $00000020
- DC.L MyStop ; $00000040
- DC.L Start ; $00000080
- DC.L Flush ; $00000100
- DC.L Motor ; $00000200 motor (NO-OP)
- DC.L Seek ; $00000400 seek (NO-OP)
- DC.L RdWrt ; $00000800 format -> WRITE for RAMDISK
- DC.L MyRemove ; $00001000 remove (NO-OP)
- DC.L ChangeNum ; $00002000 changenum (Returns 0)
- DC.L ChangeState ; $00004000 changestate (Returns 0)
- DC.L ProtStatus ; $00008000 protstatus (Returns 0)
- DC.L RawRead ; Not supported (INVALID)
- DC.L RawWrite ; Not supported (INVALID)
- DC.L GetDriveType ; Get drive type (Returns 1)
- DC.L GetNumTracks ; Get number of tracks (Returns NUMTRKS)
- DC.L AddChangeInt ; Add disk change interrupt (NO-OP)
- DC.L RemChangeInt ; Remove disk change interrupt ( NO-OP)
- cmdtable_end:
-
- ; this define is used to tell which commands should not be queued
- ; command zero is bit zero.
- ; The immediate commands are Invalid, Reset, Stop, Start, Flush
- IMMEDIATES EQU $000001c3
-
- ; These commands can NEVER be done "immediately" if using interrupts,
- ; since they would "wait" for the interrupt forever!
- ; Read, Write, Format
- NEVERIMMED EQU $0000080C
- ;
- ; BeginIO starts all incoming io. The IO is either queued up for the
- ; unit task or processed immediately.
- ;
-
- BeginIO: ; ( iob: a1, device:a6 )
- PUTMSG 30,<'%s/BeginIO: called'>
- movem.l d1/a0/a3,-(sp)
-
- ;------ bookkeeping
- move.l IO_UNIT(a1),a3
-
- ;------ see if the io command is within range
- move.w IO_COMMAND(a1),d0
- cmp.w #MYDEV_END,d0
- bcc BeginIO_NoCmd
-
- DISABLE a0
-
- ;------ process all immediate commands no matter what
- move.w #IMMEDIATES,d1
- btst d0,d1
- bne.s BeginIO_Immediate
-
- IFD INTRRUPT ; if using interrupts,
- ;------ queue all NEVERIMMED commands no matter what
- move.w #NEVERIMMED,d1
- btst d0,d1
- bne.s BeginIO_QueueMsg
- ENDC
-
- ;------ see if the unit is STOPPED. If so, queue the msg.
- btst #MDUB_STOPPED,UNIT_FLAGS(a3)
- bne.s BeginIO_QueueMsg
-
- ;------ this is not an immediate command. see if the device is
- ;------ busy.
- bset #UNITB_ACTIVE,UNIT_FLAGS(a3)
- beq.s BeginIO_Immediate
-
- ;------ we need to queue the device. mark us as needing
- ;------ task attention. Clear the quick flag
- BeginIO_QueueMsg:
- BSET #UNITB_INTASK,UNIT_FLAGS(a3)
- bclr #IOB_QUICK,IO_FLAGS(a1)
-
- ENABLE a0
-
- move.l a3,a0
- LINKSYS PutMsg,md_SysLib(a6)
- bra BeginIO_End
-
- BeginIO_Immediate:
- ENABLE a0
-
- bsr PerformIO
-
- BeginIO_End:
- movem.l (sp)+,d1/a0/a3
- rts
-
- BeginIO_NoCmd:
- move.b #IOERR_NOCMD,IO_ERROR(a1)
- bra.s BeginIO_End
-
-
- ;
- ; PerformIO actually dispatches an io request. It expects a3 to already
- ; have the unit pointer in it. a6 has the device pointer (as always).
- ; a1 has the io request. Bounds checking has already been done on
- ; the io request.
- ;
-
- PerformIO: ; ( iob:a1, unitptr:a3, devptr:a6 )
- PUTMSG 30,<'%s/PerforIO: called'>
- move.l a2,-(sp)
- move.l a1,a2
-
- clr.b IO_ERROR(A2) ; No error so far
- move.w IO_COMMAND(a2),d0
- lsl #2,d0 ; Multiply by 4 to get table offset
- lea cmdtable(pc),a0
- move.l 0(a0,d0.w),a0
-
- jsr (a0)
-
- move.l (sp)+,a2
- rts
-
- ;
- ; TermIO sends the IO 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 server task.
- ;
-
- TermIO: ; ( iob:a1, unitptr:a3, devptr:a6 )
- PUTMSG 30,<'%s/TermIO: called'>
- move.w IO_COMMAND(a1),d0
- move.w #IMMEDIATES,d1
- btst d0,d1
- bne.s TermIO_Immediate
-
- ;------ we may need to turn the active bit off.
- btst #UNITB_INTASK,UNIT_FLAGS(a3)
- bne.s TermIO_Immediate
-
- ;------ the task does not have more work to do
- bclr #UNITB_ACTIVE,UNIT_FLAGS(a3)
-
- TermIO_Immediate:
- ;------ if the quick bit is still set then we don't need to reply
- ;------ msg -- just return to the user.
- btst #IOB_QUICK,IO_FLAGS(a1)
- bne.s TermIO_End
-
- LINKSYS ReplyMsg,md_SysLib(a6)
-
- TermIO_End:
- rts
-
-
- AbortIO: ; ( iob: a1, device:a6 )
- ;----------------------------------------------------------------------
- ;
- ; here begins the functions that implement the device commands
- ; all functions are called with:
- ; a1 -- a pointer to the io request block
- ; a2 -- another pointer to the iob
- ; a3 -- a pointer to the unit
- ; a6 -- a pointer to the device
- ;
- ; Commands that conflict with 68000 instructions have a "My" prepended
- ; to them.
- ;----------------------------------------------------------------------
-
- RawRead: ; 10 Not supported (INVALID)
- RawWrite: ; 11 Not supported (INVALID)
- Invalid:
- move.b #IOERR_NOCMD,IO_ERROR(a1)
- bsr TermIO
- rts
-
- MyReset:
- AddChangeInt:
- RemChangeInt:
- MyRemove:
- Seek:
- Motor:
- ChangeNum:
- ChangeState:
- ProtStatus:
- clr.l IO_ACTUAL(a1) ; Indicate drive isn't protected
- bsr TermIO
- rts
-
- GetDriveType:
- move.l #1,IO_ACTUAL(a1) ; Make it look like 3.5"
- bsr TermIO
- rts
-
- GetNumTracks:
- move.l #RAMSIZE/5120,IO_ACTUAL(a1) ; Number of 10 sector tracks
- bsr TermIO
- rts
-
- RdWrt:
- movem.l a2/a3,-(sp)
- clr.l IO_ACTUAL(a1) ; Initially, no data moved
- move.l IO_DATA(a1),a0
- move.l IO_LENGTH(a1),d0
-
- ;------ deal with zero length I/O
- beq.s RdWrt_end
-
-
- move.l IO_UNIT(a1),a3 ; Get unit pointer
- move.l a1,a2
-
- * check operation for legality
-
- move.l IO_OFFSET(a2),d0
- move.l d0,d1
-
- * check for being an even sector boundary
- and.l #SECTOR-1,d1
- bne IO_Err
-
- * check for IO within disc range
- add.l IO_LENGTH(a2),d0
- cmp.l #RAMSIZE,d0
- bgt IO_Err
-
- * We've gotten this far, it must be a valid request.
-
- IFD INTRRUPT
- move.l mdu_SigMask(a3),d0 ; Get signals to wait for
- LINKSYS Wait,md_SysLib(a6) ; Wait for interrrupt before proceeding
- ENDC
-
- move.l IO_OFFSET(a2),d0
- lea mdu_RAM(a3),a0 ; Point to RAMDISK "sector" for I/O
- add.l d0,a0 ; Can't use index, "out of range"
- move.l IO_LENGTH(a2),d0
- move.l d0,IO_ACTUAL(a2) ; Indicate we've moved all bytes
- subq.w #1,d0 ; Adjust byte count for DBRA loop
- move.l IO_DATA(a2),a1 ; Point to data buffer
-
- move.w IO_COMMAND(a2),d1 ; Now go to correct loop for
- cmp.b #CMD_READ,d1 ; Read or Write commands
- BEQ.S RdLp
-
- WrtLp: move.b (a1)+,(a0)+ ; Copy a byte
- dbra d0,WrtLp ; Move all requested bytes
- bra.s RdWrt_end
-
- RdLp: move.b (a0)+,(a1)+ ; Copy a byte
- dbra d0,RdLp ; Move all requested bytes
- bra.s RdWrt_end
-
- IO_Err:
- move.b #IOERR_BADLENGTH,IO_ERROR(a1)
-
- RdWrt_end:
- move.l a2,a1
- bsr TermIO
- movem.l (sp)+,a2/a3
- rts
-
- ;
- ; Update and Clear are internal buffering commands. Update forces all
- ; io out to its final resting spot, and does not return until this is
- ; done. Clear invalidates all internal buffers. Since this device
- ; has no internal buffers, these commands do not apply.
- ;
-
- Update:
- PUTMSG 30,<'%s/Update: called'>
- bra Invalid
- Clear:
- PUTMSG 30,<'%s/Clear: called'>
- bra Invalid
-
- ;
- ; the Stop command stop all future io requests from being
- ; processed until a Start command is received. The Stop
- ; command is NOT stackable: e.g. no matter how many stops
- ; have been issued, it only takes one Start to restart
- ; processing.
- ;
-
- MyStop:
- PUTMSG 30,<'%s/MyStop: called'>
- bset #MDUB_STOPPED,UNIT_FLAGS(a3)
-
- bsr TermIO
- rts
-
- Start:
- PUTMSG 30,<'%s/Start: called'>
- bsr InternalStart
-
- move.l a2,a1
- bsr TermIO
-
- rts
-
- InternalStart:
- ;------ turn processing back on
- bclr #MDUB_STOPPED,UNIT_FLAGS(a3)
-
- ;------ kick the task to start it moving
- move.l a3,a1
- CLEAR d0
- move.l MP_SIGBIT(a3),d1
- bset d1,d0
- LINKSYS Signal,md_SysLib(a3)
-
- rts
-
- ;
- ; Flush pulls all io requests off the queue and sends them back.
- ; We must be careful not to destroy work in progress, and also
- ; that we do not let some io requests slip by.
- ;
- ; Some funny magic goes on with the STOPPED bit in here. Stop is
- ; defined as not being reentrant. We therefore save the old state
- ; of the bit and then restore it later. This keeps us from
- ; needing to DISABLE in flush. It also fails miserably if someone
- ; does a start in the middle of a flush.
- ;
-
- Flush:
- PUTMSG 30,<'%s/Flush: called'>
- movem.l d2/a6,-(sp)
-
- move.l md_SysLib(a6),a6
-
- bset #MDUB_STOPPED,UNIT_FLAGS(a3)
- sne d2
-
- Flush_Loop:
- move.l a3,a0
- CALLSYS GetMsg
-
- tst.l d0
- beq.s Flush_End
-
- move.l d0,a1
- move.b #IOERR_ABORTED,IO_ERROR(a1)
- CALLSYS ReplyMsg
-
- bra.s Flush_Loop
-
- Flush_End:
-
- move.l d2,d0
- movem.l (sp)+,d2/a6
-
- tst.b d0
- beq.s 1$
-
- bsr InternalStart
- 1$:
-
- move.l a2,a1
- bsr TermIO
-
- rts
-
- ;
- ; Foo and Bar are two device specific commands that are provided just
- ; to show you how to add your own commands. The currently return that
- ; no work was done.
- ;
-
- Foo:
- Bar:
- CLEAR d0
- move.l d0,IO_ACTUAL(a1)
-
- bsr TermIO
- rts
-
- ;----------------------------------------------------------------------
- ;
- ; here begins the process related routines
- ;
- ; A Process is provided so that queued requests may be processed at
- ; a later time.
- ;
- ;
- ; Register Usage
- ; ==============
- ; a3 -- unit pointer
- ; a6 -- syslib pointer
- ; a5 -- device pointer
- ; a4 -- task (NOT process) pointer
- ; d7 -- wait mask
- ;
- ;----------------------------------------------------------------------
-
- ; some dos magic. 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.
-
- cnop 0,4 ; long word allign
- DC.L 16 ; segment length -- any number will do
- myproc_seglist:
- DC.L 0 ; pointer to next segment
-
- ; the next instruction after the segment list is the first executable address
-
- Proc_Begin:
-
- move.l _AbsExecBase,a6
-
- ;------ Grab the argument
- move.l 4(sp),a3 ; Unit pointer
-
- move.l mdu_Device(a3),a5 ; Point to device structure
-
- IFD INTRRUPT
- ;------ Allocate a signal for "I/O Complete" interrupts
- moveq #-1,d0 ; -1 is any signal at all
- CALLSYS AllocSignal
- move.b d0,mdu_SigBit(A3) ; Save in unit structure
-
- moveq #0,d7 ; Convert bit number signal mask
- bset d0,d7
- move.l d7,mdu_SigMask(A3) ; Save in unit structure
-
- lea.l mdu_is(a3),a1 ; Point to interrupt structure
- moveq #3,d0 ; Portia interrupt bit 3
- CALLSYS AddIntServer ; Now install the server
-
- move.l md_Base(a5),a0 ; Get board base address
- * bset.b #INTENABLE,INTCTRL2(a0) ; Enable interrupts
- ENDC
-
- ;------ Allocate the right signal
-
- moveq #-1,d0 ; -1 is any signal at all
- CALLSYS AllocSignal
-
- move.b d0,MP_SIGBIT(a3)
- move.b #PA_SIGNAL,MP_FLAGS(a3)
-
- ;------ change the bit number into a mask, and save in d7
-
- moveq #0,d7
- bset d0,d7
-
- ;------
- ;------ OK, kids, we are done with initialization. We now
- ;------ can start the main loop of the driver. It goes
- ;------ like this. Because we had the port marked PA_IGNORE
- ;------ for a while (in InitUnit) we jump to the getmsg
- ;------ code on entry.
- ;------
- ;------ wait for a message
- ;------ lock the device
- ;------ get a message. if no message unlock device and loop
- ;------ dispatch the message
- ;------ loop back to get a message
- ;------
-
- bra.s Proc_CheckStatus
-
- ;------ main loop: wait for a new message
- Proc_MainLoop:
- move.l d7,d0
- CALLSYS Wait
-
- Proc_CheckStatus:
- ;------ see if we are stopped
- btst #MDUB_STOPPED,UNIT_FLAGS(a3)
- bne.s Proc_MainLoop ; device is stopped
-
- ;------ lock the device
- bset #UNITB_ACTIVE,UNIT_FLAGS(a3)
- bne.s Proc_MainLoop ; device in use
-
- ;------ get the next request
- Proc_NextMessage:
- move.l a3,a0
- CALLSYS GetMsg
- tst.l d0
- beq.s Proc_Unlock ; no message?
-
- ;------ do this request
- move.l d0,a1
- exg a5,a6 ; put device ptr in right place
- bsr PerformIO
- exg a5,a6 ; get syslib back in a6
-
- bra.s Proc_NextMessage
-
- ;------ no more messages. back ourselves out.
- Proc_Unlock:
- and.b #$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
- bra Proc_MainLoop
-
- ;
- ; Here is a dummy interrupt handler, with some crucial components commented
- ; out. If the IFD INTRRUPT is enabled, this code will cause the device to
- ; wait for a level two interrupt before it will process each request
- ; (pressing a key on the keyboard will do it). This code is normally
- ; disabled, and must fake or omit certain operations since there isn't
- ; really any hardware for this driver. Similiar code has been used
- ; successfully in other, "REAL" device drivers.
- ;
-
- IFD INTRRUPT
- ; A1 should be pointing to the unit structure upon entry!
-
- myintr: move.l mdu_Device(a1),a0 ; Get device pointer
- move.l md_SysLib(a0),a6 ; Get pointer to system
- move.l md_Base(a0),a0 ; point to board base address
- * btst.b #IAMPULLING,INTCTRL1(a0);See if I'm interrupting
- * beq.s myexnm ; if not set, exit, not mine
- * move.b #0,INTACK(a0) ; toggle controller's int2 bit
-
- ; ------ signal the task that an interrupt has occured
-
- move.l mdu_SigMask(a1),d0
- lea mdu_tcb(a1),a1
- CALLSYS Signal
-
- ;
- ; now clear the zero condition code so that
- ; the interrupt handler doesn't call the next
- ; interrupt server.
- ;
- * moveq #1,d0 clear zero flag
- * bra.s myexit now exit
- ;
- ; this exit point sets the zero condition code
- ; so the interrupt handler will try the next server
- ; in the interrupt chain
- ;
- myexnm moveq #0,d0 set zero condition code
- ;
- myexit rts
- ENDC
-
- mdu_Init:
- ; ------ Initialize the device
-
- INITBYTE MP_FLAGS,PA_IGNORE
- INITBYTE LN_TYPE,NT_DEVICE
- INITLONG LN_NAME,myName
- INITBYTE mdu_Msg+LN_TYPE,NT_MSGPORT;Unit starts with MsgPort
- INITLONG mdu_Msg+LN_NAME,myName
- INITLONG mdu_tcb+LN_NAME,myName
- INITBYTE mdu_tcb+LN_TYPE,NT_TASK
- INITBYTE mdu_tcb+LN_PRI,5
- INITBYTE mdu_is+LN_PRI,4 ; Int priority 4
- IFD INTRRUPT
- INITLONG mdu_is+IS_CODE,myintr ; Interrupt routine addr
- ENDC
- INITLONG mdu_is+LN_NAME,myName
- DC.L 0
-
- mdn_Init:
- * ;------ Initialize packet for MakeDosNode
-
- INITLONG mdn_execName,myName ; Address of driver name
- INITLONG mdn_tableSize,11 ; # long words in AmigaDOS env.
- INITLONG mdn_dName,$52414d00 ; Store 'RAM' in name
- INITLONG mdn_sizeBlock,128 ; # longwords in a block
- INITLONG mdn_numHeads,1 ; RAM disk has only one "head"
- INITLONG mdn_secsPerBlk,1 ; secs/logical block, must = "1"
- INITLONG mdn_blkTrack,10 ; secs/track (must be reasonable)
- INITLONG mdn_resBlks,1 ; reserved blocks, MUST > 0!
- INITLONG mdn_upperCyl,(RAMSIZE/5120)-1; upper cylinder
- INITLONG mdn_numBuffers,1 ; # AmigaDOS buffers to start
- DC.L 0
-
- ;----------------------------------------------------------------------
- ; EndCode is a marker that show the end of your code.
- ; Make sure it does not span sections nor is before the
- ; rom tag in memory! It is ok to put it right after
- ; the rom tag -- that way you are always safe. I put
- ; it here because it happens to be the "right" thing
- ; to do, and I know that it is safe in this case.
- ;----------------------------------------------------------------------
- EndCode:
-
- END
-