home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Extras / Development / RKM_Companion_v2.04 / SampleDevice / ramdev.device.asm < prev    next >
Encoding:
Assembly Source File  |  1999-10-27  |  44.7 KB  |  1,416 lines

  1. *************************************************************************
  2. *
  3. *   Copyright (C) 1986-1999 Amiga, Inc.  All rights reserved.
  4. *   Permission granted for non-commercial use.
  5. *
  6. *************************************************************************
  7. *
  8. * ramdev.asm -- Skeleton device code.
  9. *
  10. * A sample 4 unit ramdisk that can be bound to an expansion slot device,
  11. * or used without.  Works with the Fast File System.
  12. * This code is required reading for device driver writers.  It contains
  13. * information not found elsewhere.  This code is somewhat old; you probably
  14. * don't want to copy it directly.
  15. *
  16. * This example includes a task, though a task is not actually needed for
  17. * a simple ram disk.  Unlike a single set of hardware registers that
  18. * may need to be shared by multiple tasks, ram can be freely shared.
  19. * This example does not show arbitration of hardware resources.
  20. *
  21. * Tested with CAPE and Metacomco
  22. *
  23. *       Based on mydev.asm
  24. *       10/07/86 Modified by Lee Erickson to be a simple disk device
  25. *            using RAM to simulate a disk.
  26. *       02/02/88 Modified by C. Scheppner, renamed ramdev
  27. *       09/28/88 Repaired by Bryce Nesbitt for new release
  28. *       11/02/88 More clarifications
  29. *       02/01/89 Even more clarifications & warnings
  30. *       02/22/89 START/STOP fix from Marco Papa
  31. *
  32. * Bugs: If RTF_AUTOINIT fails, library base still left in memory.
  33. *
  34. *************************************************************************
  35.  
  36.    SECTION firstsection,code
  37.  
  38.    NOLIST
  39.    include "exec/types.i"
  40.    include "exec/devices.i"
  41.    include "exec/initializers.i"
  42.    include "exec/memory.i"
  43.    include "exec/resident.i"
  44.    include "exec/io.i"
  45.    include "exec/ables.i"
  46.    include "exec/errors.i"
  47.    include "exec/tasks.i"
  48.    include "hardware/intbits.i"
  49.  
  50.    include "asmsupp.i"  ;standard asmsupp.i, same as used for library
  51.    include "ramdev.i"
  52.  
  53.    IFNE AUTOMOUNT
  54.    include "libraries/expansion.i"
  55.    include "libraries/configvars.i"
  56.    include "libraries/configregs.i"
  57.    ENDC
  58.    LIST
  59.  
  60.  
  61. ABSEXECBASE equ 4   ;Absolute location of the pointer to exec.library base
  62.  
  63.  
  64.    ;------ These don't have to be external, but it helps some
  65.    ;------ debuggers to have them globally visible
  66.    XDEF   Init
  67.    XDEF   Open
  68.    XDEF   Close
  69.    XDEF   Expunge
  70.    XDEF   Null
  71.    XDEF   myName
  72.    XDEF   BeginIO
  73.    XDEF   AbortIO
  74.  
  75.    ;Pull these _LVOs in from amiga.lib
  76.    XLIB   AddIntServer
  77.    XLIB   RemIntServer
  78.    XLIB   Debug
  79.    XLIB   InitStruct
  80.    XLIB   OpenLibrary
  81.    XLIB   CloseLibrary
  82.    XLIB   Alert
  83.    XLIB   FreeMem
  84.    XLIB   Remove
  85.    XLIB   AddPort
  86.    XLIB   AllocMem
  87.    XLIB   AddTask
  88.    XLIB   PutMsg
  89.    XLIB   RemTask
  90.    XLIB   ReplyMsg
  91.    XLIB   Signal
  92.    XLIB   GetMsg
  93.    XLIB   Wait
  94.    XLIB   WaitPort
  95.    XLIB   AllocSignal
  96.    XLIB   SetTaskPri
  97.    XLIB   GetCurrentBinding    ;Use to get list of boards for this driver
  98.    XLIB   MakeDosNode
  99.    XLIB   AddDosNode
  100.    XLIB   CopyMemQuick    ;Highly optimized copy function from exec.library
  101.  
  102.    INT_ABLES        ;Macro from exec/ables.i
  103.  
  104.  
  105. ;-----------------------------------------------------------------------
  106. ; The first executable location.  This should return an error
  107. ; in case someone tried to run you as a program (instead of
  108. ; loading you as a device).
  109.  
  110. FirstAddress:
  111.         moveq    #-1,d0
  112.         rts
  113.  
  114. ;-----------------------------------------------------------------------
  115. ; A romtag structure.  After your driver is brought in from disk, the
  116. ; disk image will be scanned for this structure to discover magic constants
  117. ; about you (such as where to start running you from...).
  118. ;-----------------------------------------------------------------------
  119.  
  120.    ; Most people will not need a priority and should leave it at zero.
  121.    ; the RT_PRI field is used for configuring the roms.  Use "mods" from
  122.    ; wack to look at the other romtags in the system
  123. MYPRI    EQU   0
  124.  
  125. initDDescrip:
  126.                 ;STRUCTURE RT,0
  127.      DC.W    RTC_MATCHWORD    ; UWORD RT_MATCHWORD (Magic cookie)
  128.      DC.L    initDDescrip    ; APTR    RT_MATCHTAG  (Back pointer)
  129.      DC.L    EndCode        ; APTR    RT_ENDSKIP   (To end of this hunk)
  130.      DC.B    RTF_AUTOINIT    ; UBYTE RT_FLAGS     (magic-see "Init:")
  131.      DC.B    VERSION        ; UBYTE RT_VERSION
  132.      DC.B    NT_DEVICE        ; UBYTE RT_TYPE      (must be correct)
  133.      DC.B    MYPRI        ; BYTE    RT_PRI
  134.      DC.L    myName        ; APTR    RT_NAME      (exec name)
  135.      DC.L    idString        ; APTR    RT_IDSTRING  (text string)
  136.      DC.L    Init        ; APTR    RT_INIT
  137.            ; LABEL RT_SIZE
  138.  
  139.  
  140.    ;This name for debugging use
  141.    IFNE INFO_LEVEL  ;If any debugging enabled at all
  142. subSysName:
  143.     dc.b    "ramdev",0
  144.    ENDC
  145.  
  146.    ; this is the name that the device will have
  147. myName:      MYDEVNAME
  148.  
  149.  IFNE  AUTOMOUNT
  150. ExLibName    dc.b 'expansion.library',0   ; Expansion Library Name
  151.  ENDC
  152.  
  153.    ; a major version number.
  154. VERSION:    EQU   37
  155.  
  156.    ; A particular revision.  This should uniquely identify the bits in the
  157.    ; device.  I use a script that advances the revision number each time
  158.    ; I recompile.  That way there is never a question of which device
  159.    ; that really is.
  160. REVISION:   EQU   1
  161.  
  162.    ; this is an identifier tag to help in supporting the device
  163.    ; format is 'name version.revision (d.m.yy)',<cr>,<lf>,<null>
  164. idString:   dc.b   'ramdev 37.1 (28.8.91)',13,10,0
  165.  
  166.    ; force word alignment
  167.    ds.w   0
  168.  
  169.  
  170.    ; The romtag specified that we were "RTF_AUTOINIT".  This means
  171.    ; that the RT_INIT structure member points to one of these
  172.    ; tables below.  If the AUTOINIT bit was not set then RT_INIT
  173.    ; would point to a routine to run.
  174.  
  175. Init:
  176.    DC.L   MyDev_Sizeof        ; data space size
  177.    DC.L   funcTable        ; pointer to function initializers
  178.    DC.L   dataTable        ; pointer to data initializers
  179.    DC.L   initRoutine        ; routine to run
  180.  
  181.  
  182. funcTable:
  183.    ;------ standard system routines
  184.    dc.l   Open
  185.    dc.l   Close
  186.    dc.l   Expunge
  187.    dc.l   Null        ;Reserved for future use!
  188.  
  189.    ;------ my device definitions
  190.    dc.l   BeginIO
  191.    dc.l   AbortIO
  192.  
  193.    ;------ custom extended functions
  194.    dc.l   FunctionA
  195.    dc.l   FunctionB
  196.  
  197.    ;------ function table end marker
  198.    dc.l   -1
  199.  
  200.  
  201.    ;The data table initializes static data structures. The format is
  202.    ;specified in exec/InitStruct routine's manual pages.  The
  203.    ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  204.    ;The first argument is the offset from the device base for this
  205.    ;byte/word/long. The second argument is the value to put in that cell.
  206.    ;The table is null terminated
  207.    ;
  208. dataTable:
  209.    INITBYTE   LN_TYPE,NT_DEVICE       ;Must be LN_TYPE!
  210.    INITLONG   LN_NAME,myName
  211.    INITBYTE   LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
  212.    INITWORD   LIB_VERSION,VERSION
  213.    INITWORD   LIB_REVISION,REVISION
  214.    INITLONG   LIB_IDSTRING,idString
  215.    DC.W   0   ;terminate list
  216.  
  217.  
  218. ;-------- initRoutine -------------------------------------------------------
  219. ;
  220. ; FOR RTF_AUTOINIT:
  221. ;   This routine gets called after the device has been allocated.
  222. ;   The device pointer is in D0.  The AmigaDOS segment list is in a0.
  223. ;   If it returns the device pointer, then the device will be linked
  224. ;   into the device list.  If it returns NULL, then the device
  225. ;   will be unloaded.
  226. ;
  227. ; IMPORTANT:
  228. ;   If you don't use the "RTF_AUTOINIT" feature, there is an additional
  229. ;   caveat.  If you allocate memory in your Open function, remember that
  230. ;   allocating memory can cause an Expunge... including an expunge of your
  231. ;   device.  This must not be fatal.  The easy solution is don't add your
  232. ;   device to the list until after it is ready for action.
  233. ;
  234. ; This call is single-threaded by exec; please read the description for
  235. ; "Open" below.
  236. ;
  237. ; Register Usage
  238. ; ==============
  239. ; a3 -- Points to temporary RAM
  240. ; a4 -- Expansion library base
  241. ; a5 -- device pointer
  242. ; a6 -- Exec base
  243. ;----------------------------------------------------------------------
  244. initRoutine:
  245.    ;------ get the device pointer into a convenient A register
  246.    PUTMSG   5,<'%s/Init: called'>
  247.    movem.l  d1-d7/a0-a5,-(sp)   ; Preserve ALL modified registers
  248.    move.l   d0,a5
  249.  
  250.    ;------ save a pointer to exec
  251.    move.l   a6,md_SysLib(a5)    ;faster access than move.l 4,a6
  252.  
  253.    ;------ save pointer to our loaded code (the SegList)
  254.    move.l   a0,md_SegList(a5)
  255.  
  256.  IFNE  AUTOMOUNT
  257. **************************************************************************
  258. *
  259. * Here starts the AutoConfig stuff.  If this driver was to be tied to
  260. * an expansion board, you would put this driver in the expansion drawer,
  261. * and be called when BindDrivers finds a board that matches this driver.
  262. * The Amiga assigned product number of your board must be
  263. * specified in the "PRODUCT=" field in the TOOLTYPES of this driver's icon.
  264. * GetCurrentBinding() returns your (first) board.
  265. *
  266.    lea.l     ExLibName,A1    ; Get expansion lib. name
  267.    moveq.l   #0,D0
  268.    CALLSYS   OpenLibrary    ; Open the expansion library
  269.    tst.l     D0
  270.    beq         Init_Error
  271.  
  272.    ;------ init_OpSuccess:
  273.    move.l    D0,A4        ;[expansionbase to A4]
  274.    moveq     #0,D3
  275.    lea         md_Base(A5),A0   ; Get the Current Bindings
  276.    moveq     #4,D0          ; Just get address (length = 4 bytes)
  277.    LINKLIB   _LVOGetCurrentBinding,A4
  278.    move.l    md_Base(A5),D0      ; Get start of list
  279.    tst.l     D0        ; If controller not found
  280.    beq         Init_End       ; Exit and unload driver
  281.  
  282.    PUTMSG    10,<'%s/Init: GetCurrentBinding returned non-zero'>
  283.    move.l    D0,A0       ; Get config structure address
  284.    move.l    cd_BoardAddr(A0),md_Base(A5); Save board base address
  285.    bclr.b    #CDB_CONFIGME,cd_Flags(A0); Mark board as configured
  286.  
  287. ;----------------------------------------------------------------------
  288. ;
  289. ; Here we build a packet describing the characteristics of our disk to
  290. ; pass to AmigaDOS.  This serves the same purpose as a "mount" command
  291. ; of this device would.  For disks, it might be useful to actually
  292. ; get this information right from the disk itself.  Just as mount,
  293. ; it could be for multiple partitions on the single physical device.
  294. ; For this example, we will simply hard code the appropriate parameters.
  295. ;
  296. ; The AddDosNode call adds things to dos's list without needing to
  297. ; use mount.  We'll mount all 4 of our units whenever we are
  298. ; started.
  299. ;
  300. ;-----------------------------------------------------------------------
  301.  
  302. ;!!! If your card was successfully configured, you can mount the
  303. ;!!! units as DOS nodes
  304.  
  305.    ;------   Allocate temporary RAM to build MakeDosNode parameter packet
  306.    move.l    #MEMF_CLEAR!MEMF_PUBLIC,d1
  307.    move.l    #mdn_Sizeof,d0   ; Enough room for our parameter packet
  308.    CALLSYS   AllocMem
  309.    move.l    d0,a3        ;:BUG: AllocMem error not checked here.
  310.  
  311.    ;-----   Use InitStruct to initialize the constant portion of packet
  312.    move.l    d0,a2       ; Point to memory to initialize
  313.    moveq.l   #0,d0       ; Don't need to re-zero it
  314.    lea.l     mdn_Init(pc),A1
  315.    CALLSYS   InitStruct
  316.  
  317.    lea         mdn_dName(a3),a0     ; Get addr of Device name
  318.    move.l    a0,mdn_dosName(a3)   ;   and save in environment
  319.  
  320.    moveq     #0,d6          ; Now tell AmigaDOS about all units UNITNUM
  321. Uloop:
  322.    move.b    d6,d0         ; Get unit number
  323.    add.b     #$30,d0         ; Make ASCII, minus 1
  324.    move.b    d0,mdn_dName+2(a3)   ;   and store in name
  325.    move.l    d6,mdn_unit(a3)      ; Store unit # in environment
  326.  
  327. ;
  328. ;! Before adding to the dos list, you should really check if you
  329. ;! are about to cause a name collision.  This example does not.
  330. ;
  331.  
  332.    move.l    a3,a0
  333.    LINKLIB   _LVOMakeDosNode,a4   ; Build AmigaDOS structures
  334.    ;This can fail, but so what?
  335.    move.l    d0,a0          ; Get deviceNode address
  336.    moveq.l   #0,d0          ; Set device priority to 0
  337.    moveq.l   #0,d1
  338. *  moveq.l   #ADNF_STARTPROC,d1     ; See note below
  339.    ;It's ok to pass a zero in here
  340.    LINKLIB   _LVOAddDosNode,a4
  341.  
  342.  
  343. ; ADNF_STARTPROC will work, but only if dn_SegList is filled in
  344. ; in the SegPtr of the handler task.
  345.  
  346.  
  347.    addq     #1,d6      ; Bump unit number
  348.    cmp.b    #MD_NUMUNITS,d6
  349.    bls.s    Uloop      ; Loop until all units installed
  350.  
  351.    move.l   a3,a1      ; Return RAM to system
  352.    move.l   #mdn_Sizeof,d0
  353.    CALLSYS  FreeMem
  354.  
  355. Init_End:
  356.  
  357.    move.l   a4,a1      ; Now close expansion library
  358.    CALLSYS  CloseLibrary
  359. *
  360. *   You would normally set d0 to a NULL if your initialization failed,
  361. *   but I'm not doing that for this demo, since it is unlikely
  362. *   you actually have a board with any particular manufacturer ID
  363. *   installed when running this demo.
  364. *************************************************************************
  365.   ENDC
  366.  
  367.    move.l   a5,d0
  368. Init_Error:
  369.    movem.l  (sp)+,d1-d7/a0-a5
  370.    rts
  371.  
  372.  
  373. ;----------------------------------------------------------------------
  374. ;
  375. ; Here begins the system interface commands.  When the user calls
  376. ; OpenDevice/CloseDevice/RemDevice, this eventually gets translated
  377. ; into a call to the following routines (Open/Close/Expunge).
  378. ; Exec has already put our device pointer in a6 for us.
  379. ;
  380. ; IMPORTANT:
  381. ;   These calls are guaranteed to be single-threaded; only one task
  382. ;   will execute your Open/Close/Expunge at a time.
  383. ;
  384. ;   For Kickstart V33/34, the single-threading method involves "Forbid".
  385. ;   There is a good chance this will change.  Anything inside your
  386. ;   Open/Close/Expunge that causes a direct or indirect Wait() will break
  387. ;   the Forbid().  If the Forbid() is broken, some other task might
  388. ;   manage to enter your Open/Close/Expunge code at the same time.
  389. ;   Take care!
  390. ;
  391. ; Since exec has turned off task switching while in these routines
  392. ; (via Forbid/Permit), we should not take too long in them.
  393. ;
  394. ;----------------------------------------------------------------------
  395.  
  396.    ; Open sets the IO_ERROR field on an error.    If it was successfull,
  397.    ; we should also set up the IO_UNIT and LN_TYPE fields.
  398.    ; exec takes care of setting up IO_DEVICE.
  399.  
  400. Open:       ; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
  401.  
  402. ;** Subtle point: any AllocMem() call can cause a call to this device's
  403. ;** expunge vector.  If LIB_OPENCNT is zero, the device might get expunged.
  404.    addq.w   #1,LIB_OPENCNT(a6)  ;Fake an opener for duration of call <|>
  405.  
  406.    PUTMSG   20,<'%s/Open: called'>
  407.    movem.l  d2/a2/a3/a4,-(sp)
  408.  
  409.    move.l   a1,a2      ; save the iob
  410.  
  411.    ;------ see if the unit number is in range    *!* UNIT 0 to 3 *!*
  412.    cmp.l   #MD_NUMUNITS,d0
  413.    bcc.s   Open_Range_Error   ; unit number out of range (BHS)
  414.  
  415.    ;------ see if the unit is already initialized
  416.    move.l   d0,d2      ; save unit number
  417.    lsl.l    #2,d0
  418.    lea.l    md_Units(a6,d0.l),a4
  419.    move.l   (a4),d0
  420.    bne.s    Open_UnitOK
  421.  
  422.    ;------ try and conjure up a unit
  423.    bsr        InitUnit    ;scratch:a3 unitnum:d2 devpoint:a6
  424.  
  425.    ;------ see if it initialized OK
  426.    move.l   (a4),d0
  427.    beq.s    Open_Error
  428.  
  429. Open_UnitOK:
  430.    move.l   d0,a3      ; unit pointer in a3
  431.    move.l   d0,IO_UNIT(a2)
  432.  
  433.    ;------ mark us as having another opener
  434.    addq.w   #1,LIB_OPENCNT(a6)
  435.    addq.w   #1,UNIT_OPENCNT(a3)     ;Internal bookkeeping
  436.  
  437.    ;------ prevent delayed expunges
  438.    bclr     #LIBB_DELEXP,md_Flags(a6)
  439.  
  440.    CLEAR    d0
  441.    move.b   d0,IO_ERROR(a2)
  442.    move.b   #NT_REPLYMSG,LN_TYPE(a2) ;IMPORTANT: Mark IORequest as "complete"
  443.  
  444. Open_End:
  445.  
  446.    subq.w   #1,LIB_OPENCNT(a6) ;** End of expunge protection <|>
  447.    movem.l  (sp)+,d2/a2/a3/a4
  448.    rts
  449.  
  450. Open_Range_Error:
  451. Open_Error:
  452.    moveq    #IOERR_OPENFAIL,d0
  453.    move.b   d0,IO_ERROR(a2)
  454.    move.l   d0,IO_DEVICE(a2)    ;IMPORTANT: trash IO_DEVICE on open failure
  455.    PUTMSG   2,<'%s/Open: failed'>
  456.    bra.s    Open_End
  457.  
  458.  
  459. ;----------------------------------------------------------------------------
  460. ; There are two different things that might be returned from the Close
  461. ; routine.  If the device wishes to be unloaded, then Close must return
  462. ; the segment list (as given to Init).  Otherwise close MUST return NULL.
  463.  
  464. Close:        ; ( device:a6, iob:a1 )
  465.    movem.l  d1/a2-a3,-(sp)
  466.    PUTMSG   20,<'%s/Close: called'>
  467.  
  468.    move.l   a1,a2
  469.  
  470.    move.l   IO_UNIT(a2),a3
  471.  
  472.    ;------ IMPORTANT: make sure the IORequest is not used again
  473.    ;------ with a -1 in IO_DEVICE, any BeginIO() attempt will
  474.    ;------ immediatly halt (which is better than a subtle corruption
  475.    ;------ that will lead to hard-to-trace crashes!!!!!!!!!!!!!!!!!!
  476.    moveq.l  #-1,d0
  477.    move.l   d0,IO_UNIT(a2)      ;We're closed...
  478.    move.l   d0,IO_DEVICE(a2)    ;customers not welcome at this IORequest!!
  479.  
  480.    ;------ see if the unit is still in use
  481.    subq.w   #1,UNIT_OPENCNT(a3)
  482.  
  483. ;!!!!!! Since this example is a RAM disk (and we don't want the contents to
  484. ;!!!!!! disappear between opens, ExpungeUnit will be skipped here.  It would
  485. ;!!!!!! be used for drivers of "real" devices
  486. ;!!!!!!   bne.s   Close_Device
  487. ;!!!!!!   bsr      ExpungeUnit
  488.  
  489. Close_Device:
  490.    CLEAR   d0
  491.    ;------ mark us as having one fewer openers
  492.    subq.w  #1,LIB_OPENCNT(a6)
  493.  
  494.    ;------ see if there is anyone left with us open
  495.    bne.s   Close_End
  496.  
  497.    ;------ see if we have a delayed expunge pending
  498.    btst    #LIBB_DELEXP,md_Flags(a6)
  499.    beq.s   Close_End
  500.  
  501.    ;------ do the expunge
  502.    bsr       Expunge
  503.  
  504. Close_End:
  505.    movem.l   (sp)+,d1/a2-a3
  506.    rts                ;MUST return either zero or the SegList!!!
  507.  
  508.  
  509. ;------- Expunge -----------------------------------------------------------
  510. ;
  511. ; Expunge is called by the memory allocator when the system is low on
  512. ; memory.
  513. ;
  514. ; There are two different things that might be returned from the Expunge
  515. ; routine.  If the device is no longer open then Expunge may return the
  516. ; segment list (as given to Init).  Otherwise Expunge may set the
  517. ; delayed expunge flag and return NULL.
  518. ;
  519. ; One other important note: because Expunge is called from the memory
  520. ; allocator, it may NEVER Wait() or otherwise take long time to complete.
  521. ;
  522. ;    A6        - library base (scratch)
  523. ;    D0-D1/A0-A1 - scratch
  524. ;
  525. Expunge:   ; ( device: a6 )
  526.    PUTMSG   10,<'%s/Expunge: called'>
  527.  
  528.    movem.l  d1/d2/a5/a6,-(sp)   ; Save ALL modified registers
  529.    move.l   a6,a5
  530.    move.l   md_SysLib(a5),a6
  531.  
  532.    ;------ see if anyone has us open
  533.    tst.w   LIB_OPENCNT(a5)
  534. ;!!!!!    The following line is commented out for this RAM disk demo, since
  535. ;!!!!!    we don't want the RAM to be freed after FORMAT, for example.
  536. ;   beq    1$
  537.  
  538.    ;------ it is still open.  set the delayed expunge flag
  539.    bset    #LIBB_DELEXP,md_Flags(a5)
  540.    CLEAR   d0
  541.    bra.s   Expunge_End
  542.  
  543. 1$:
  544.    ;------ go ahead and get rid of us.    Store our seglist in d2
  545.    move.l   md_SegList(a5),d2
  546.  
  547.    ;------ unlink from device list
  548.    move.l    a5,a1
  549.    CALLSYS   Remove        ;Remove first (before FreeMem)
  550.  
  551.    ;
  552.    ; device specific closings here...
  553.    ;
  554.  
  555.    ;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE)
  556.    move.l   a5,a1        ;Devicebase
  557.    CLEAR    d0
  558.    move.w   LIB_NEGSIZE(a5),d0
  559.    suba.l   d0,a1        ;Calculate base of functions
  560.    add.w    LIB_POSSIZE(a5),d0  ;Calculate size of functions + data area
  561.    CALLSYS  FreeMem
  562.  
  563.    ;------ set up our return value
  564.    move.l   d2,d0
  565.  
  566. Expunge_End:
  567.    movem.l  (sp)+,d1/d2/a5/a6
  568.    rts
  569.  
  570.  
  571. ;------- Null ---------------------------------------------------------------
  572. Null:
  573.    PUTMSG  1,<'%s/Null: called'>
  574.    CLEAR   d0
  575.    rts        ;The "Null" function MUST return NULL.
  576.  
  577.  
  578. ;------- Custom ------------------------------------------------------------
  579. ;
  580. ;Two "do nothing" device-specific functions
  581. ;
  582. FunctionA:
  583.     add.l   d1,d0   ;Add
  584.     rts
  585. FunctionB:
  586.     add.l   d0,d0   ;Double
  587.     rts
  588.  
  589.  
  590. ****************************************************************************
  591.  
  592. InitUnit:   ; ( d2:unit number, a3:scratch, a6:devptr )
  593.    PUTMSG   30,<'%s/InitUnit: called'>
  594.    movem.l  d2-d4/a2,-(sp)
  595.  
  596.    ;------ allocate unit memory
  597.    move.l   #MyDevUnit_Sizeof,d0
  598.    move.l   #MEMF_PUBLIC!MEMF_CLEAR,d1
  599.    LINKSYS  AllocMem,md_SysLib(a6)
  600.    tst.l    d0
  601.    beq        InitUnit_End
  602.    move.l   d0,a3
  603.  
  604.    moveq.l  #0,d0       ; Don't need to re-zero it
  605.    move.l   a3,a2       ; InitStruct is initializing the UNIT
  606.    lea.l    mdu_Init(pc),A1
  607.    LINKSYS  InitStruct,md_SysLib(a6)
  608.  
  609.    ;!! IMPORTANT !!
  610.    move.l   #42414400,mdu_RAM(a3)   ;Mark offset zero as ASCII "BAD "
  611.    ;!! IMPORTANT !!
  612.  
  613.    move.b   d2,mdu_UnitNum(a3)      ;initialize unit number
  614.    move.l   a6,mdu_Device(a3)       ;initialize device pointer
  615.  
  616.    ;------ start up the unit task.  We do a trick here --
  617.    ;------ we set his message port to PA_IGNORE until the
  618.    ;------ new task has a change to set it up.
  619.    ;------ We cannot go to sleep here: it would be very nasty
  620.    ;------ if someone else tried to open the unit
  621.    ;------ (exec's OpenDevice has done a Forbid() for us --
  622.    ;------ we depend on this to become single threaded).
  623.  
  624.    ;------ Initialize the stack information
  625.    lea        mdu_stack(a3),a0          ; Low end of stack
  626.    move.l   a0,mdu_tcb+TC_SPLOWER(a3)
  627.    lea        MYPROCSTACKSIZE(a0),a0    ; High end of stack
  628.    move.l   a0,mdu_tcb+TC_SPUPPER(a3)
  629.    move.l   a3,-(A0)                  ; argument -- unit ptr (send on stack)
  630.    move.l   a0,mdu_tcb+TC_SPREG(a3)
  631.    lea        mdu_tcb(a3),a0
  632.    move.l   a0,MP_SIGTASK(a3)
  633.  
  634.    IFGE INFO_LEVEL-30
  635.        move.l    a0,-(SP)
  636.        move.l    a3,-(SP)
  637.        PUTMSG    30,<'%s/InitUnit, unit= %lx, task=%lx'>
  638.        addq.l    #8,sp
  639.    ENDC
  640.  
  641.    ;------ initialize the unit's message port's list
  642.    lea        MP_MSGLIST(a3),a0
  643.    NEWLIST  a0            ;<- IMPORTANT! Lists MUST! have NEWLIST
  644.                 ;work magic on them before use.  (AddPort()
  645.                 ;can do this for you)
  646.  
  647.    IFD     INTRRUPT
  648.    move.l   a3,mdu_is+IS_DATA(a3)   ; Pass unit addr to interrupt server
  649.    ENDC
  650.  
  651. ;   Startup the task
  652.    lea        mdu_tcb(a3),a1
  653.    lea        Task_Begin(PC),a2
  654.    move.l   a3,-(sp)      ; Preserve UNIT pointer
  655.    lea        -1,a3      ; generate address error
  656.               ; if task ever "returns" (we RemTask() it
  657.               ; to get rid of it...)
  658.    CLEAR   d0
  659.    PUTMSG   30,<'%s/About to add task'>
  660.    LINKSYS AddTask,md_SysLib(a6)
  661.    move.l   (sp)+,a3      ; restore UNIT pointer
  662.  
  663.    ;------ mark us as ready to go
  664.    move.l   d2,d0      ; unit number
  665.    lsl.l    #2,d0
  666.    move.l   a3,md_Units(a6,d0.l)   ; set unit table
  667.    PUTMSG   30,<'%s/InitUnit: ok'>
  668.  
  669. InitUnit_End:
  670.    movem.l   (sp)+,d2-d4/a2
  671.    rts
  672.  
  673.  
  674. ;---------------------------------------------------------------------------
  675. FreeUnit:   ; ( a3:unitptr, a6:deviceptr )
  676.    move.l   a3,a1
  677.    move.l   #MyDevUnit_Sizeof,d0
  678.    LINKSYS  FreeMem,md_SysLib(a6)
  679.    rts
  680.  
  681. ;---------------------------------------------------------------------------
  682. ExpungeUnit:   ; ( a3:unitptr, a6:deviceptr )
  683.    PUTMSG   10,<'%s/ExpungeUnit: called'>
  684.    move.l   d2,-(sp)
  685.  
  686. ;
  687. ; If you can expunge you unit, and each unit has it's own interrupts,
  688. ; you must remember to remove its interrupt server
  689. ;
  690.  
  691.    IFD     INTRRUPT
  692.    lea.l   mdu_is(a3),a1              ; Point to interrupt structure
  693.    moveq   #INTB_PORTS,d0          ; Portia interrupt bit 3
  694.    LINKSYS RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
  695.    ENDC
  696.  
  697.    ;------ get rid of the unit's task.  We know this is safe
  698.    ;------ because the unit has an open count of zero, so it
  699.    ;------ is 'guaranteed' not in use.
  700.    lea     mdu_tcb(a3),a1
  701.    LINKSYS RemTask,md_SysLib(a6)
  702.  
  703.    ;------ save the unit number
  704.    CLEAR   d2
  705.    move.b  mdu_UnitNum(a3),d2
  706.  
  707.    ;------ free the unit structure.
  708.    bsr       FreeUnit
  709.  
  710.    ;------ clear out the unit vector in the device
  711.    lsl.l   #2,d2
  712.    clr.l   md_Units(a6,d2.l)
  713.  
  714.    move.l  (sp)+,d2
  715.    rts
  716.  
  717.  
  718.  
  719. *****************************************************************************
  720. ;
  721. ; here begins the device functions
  722. ;
  723. ;----------------------------------------------------------------------------
  724. ; cmdtable is used to look up the address of a routine that will
  725. ; implement the device command.
  726. ;
  727. ; NOTE: the "extended" commands (ETD_READ/ETD_WRITE) have bit 15 set!
  728. ; We deliberately refuse to operate on such commands.  However a driver
  729. ; that supports removable media may want to implement this.  One
  730. ; open issue is the handling of the "seclabel" area. It is probably
  731. ; best to reject any command with a non-null "seclabel" pointer.
  732. ;
  733. cmdtable:
  734.    DC.L   Invalid    ;$00000001  ;0    CMD_INVALID
  735.    DC.L   MyReset    ;$00000002  ;1    CMD_RESET
  736.    DC.L   RdWrt     ;$00000004  ;2    CMD_READ    (\/common)
  737.    DC.L   RdWrt     ;$00000008  ;3    CMD_WRITE    (/\common)  ETD_
  738.    DC.L   Update    ;$00000010  ;4    CMD_UPDATE    (NO-OP)     ETD_
  739.    DC.L   Clear     ;$00000020  ;5    CMD_CLEAR    (NO-OP)     ETD_
  740.    DC.L   MyStop    ;$00000040  ;6    CMD_STOP            ETD_
  741.    DC.L   Start     ;$00000080  ;7    CMD_START
  742.    DC.L   Flush     ;$00000100  ;8    CMD_FLUSH
  743.    DC.L   Motor     ;$00000200  ;9    TD_MOTOR    (NO-OP)     ETD_
  744.    DC.L   Seek        ;$00000400  ;A    TD_SEEK     (NO-OP)     ETD_
  745.    DC.L   RdWrt     ;$00000800  ;B    TD_FORMAT    (Same as write)
  746.    DC.L   MyRemove    ;$00001000  ;C    TD_REMOVE    (NO-OP)
  747.    DC.L   ChangeNum    ;$00002000  ;D    TD_CHANGENUM    (returns 0)
  748.    DC.L   ChangeState    ;$00004000  ;E    TD_CHANGESTATE    (returns 0)
  749.    DC.L   ProtStatus    ;$00008000  ;F    TD_PROTSTATUS    (returns 0)
  750.    DC.L   RawRead    ;$00010000  ;10 TD_RAWREAD    (INVALID)
  751.    DC.L   RawWrite    ;$00020000  ;11 TD_RAWWRITE    (INVALID)
  752.    DC.L   GetDriveType    ;$00040000  ;12 TD_GETDRIVETYPE (Returns 1)
  753.    DC.L   GetNumTracks    ;$00080000  ;13 TD_GETNUMTRACKS (Returns NUMTRKS)
  754.    DC.L   AddChangeInt    ;$00100000  ;14 TD_ADDCHANGEINT (NO-OP)
  755.    DC.L   RemChangeInt    ;$00200000  ;15 TD_REMCHANGEINT (NO-OP)
  756. cmdtable_end:
  757.  
  758. ; this define is used to tell which commands should be handled
  759. ; immediately (on the caller's schedule).
  760. ;
  761. ; The immediate commands are Invalid, Reset, Stop, Start, Flush
  762. ;
  763. ; Note that this method limits you to just 32 device specific commands,
  764. ; which may not be enough.
  765. ;IMMEDIATES   EQU   %00000000000000000000000111000011
  766. ;;             --------========--------========
  767. ;;             FEDCBA9876543210FEDCBA9876543210
  768.  
  769. ;;An alternate version.  All commands that are trivially short
  770. ;;and %100 reentrant are included.  This way you won't get the
  771. ;;task switch overhead for these commands.
  772. ;;
  773. IMMEDIATES   EQU   %11111111111111111111011111110011
  774. ;            --------========--------========
  775. ;            FEDCBA9876543210FEDCBA9876543210
  776.  
  777.     IFD   INTRRUPT   ; if using interrupts,
  778. ; These commands can NEVER be done "immediately" if using interrupts,
  779. ; since they would "wait" for the interrupt forever!
  780. ; Read, Write, Format
  781. NEVERIMMED   EQU   $0000080C
  782.     ENDC
  783.  
  784.  
  785. ;--------------------------------
  786. ; BeginIO starts all incoming io.  The IO is either queued up for the
  787. ; unit task or processed immediately.
  788. ;
  789. ;
  790. ; BeginIO often is given the responsibility of making devices single
  791. ; threaded... so two tasks sending commands at the same time don't cause
  792. ; a problem.  Once this has been done, the command is dispatched via
  793. ; PerformIO.
  794. ;
  795. ; There are many ways to do the threading.  This example uses the
  796. ; UNITB_ACTIVE bit.  Be sure this is good enough for your device before
  797. ; using!  Any method is ok.  If immediate access can not be obtained, the
  798. ; request is queued for later processing.
  799. ;
  800. ; Some IO requests do not need single threading, these can be performed
  801. ; immediatley.
  802. ;
  803. ; IMPORTANT:
  804. ;   The exec WaitIO() function uses the IORequest node type (LN_TYPE)
  805. ;   as a flag.    If set to NT_MESSAGE, it assumes the request is
  806. ;   still pending and will wait.  If set to NT_REPLYMSG, it assumes the
  807. ;   request is finished.  It's the responsibility of the device driver
  808. ;   to set the node type to NT_MESSAGE before returning to the user.
  809. ;
  810. BeginIO:   ; ( iob: a1, device:a6 )
  811.  
  812.     IFGE INFO_LEVEL-1
  813.     bchg.b    #1,$bfe001  ;Blink the power LED
  814.     ENDC
  815.     IFGE INFO_LEVEL-3
  816.      clr.l    -(sp)
  817.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  818.      PUTMSG   3,<'%s/BeginIO  -- $%lx'>
  819.      addq.l   #4,sp
  820.     ENDC
  821.  
  822.     movem.l   d1/a0/a3,-(sp)
  823.  
  824.     move.b  #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work
  825.     move.l  IO_UNIT(a1),a3          ;bookkeeping -> what unit to play with
  826.     move.w  IO_COMMAND(a1),d0
  827.  
  828.     ;Do a range check & make sure ETD_XXX type requests are rejected
  829.     cmp.w   #MYDEV_END,d0    ;Compare all 16 bits
  830.     bcc     BeginIO_NoCmd    ;no, reject it.  (bcc=bhs - unsigned)
  831.  
  832.     ;------ process all immediate commands no matter what
  833.     move.l  #IMMEDIATES,d1
  834.     DISABLE a0            ;<-- Ick, nasty stuff, but needed here.
  835.     btst.l  d0,d1
  836.     bne     BeginIO_Immediate
  837.  
  838.     IFD   INTRRUPT   ; if using interrupts,
  839.      ;------ queue all NEVERIMMED commands no matter what
  840.      move.w  #NEVERIMMED,d1
  841.      btst    d0,d1
  842.      bne.s   BeginIO_QueueMsg
  843.     ENDC
  844.  
  845.  
  846.     ;------ see if the unit is STOPPED.  If so, queue the msg.
  847.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  848.     bne     BeginIO_QueueMsg
  849.  
  850.  
  851.     ;------ This is not an immediate command.  See if the device is
  852.     ;------ busy.  If the device is not, do the command on the
  853.     ;------ user schedule.  Else fire up the task.
  854.     ;------ This type of arbitration is not really needed for a ram
  855.     ;------ disk, but is essential for a device to reliably work
  856.     ;------ with shared hardware
  857.     ;------
  858.     ;------ When the lines below are ";" commented out, the task gets
  859.     ;------ a better workout.  When the lines are active, the calling
  860.     ;------ process is usually used for the operation.
  861.     ;------
  862.     ;------ REMEMBER:::: Never Wait() on the user's schedule in BeginIO()!
  863.     ;------ The only exception is when the user has indicated it is ok
  864.     ;------ by setting the "quick" bit.  Since this device copies from
  865.     ;------ ram that never needs to be waited for, this subtlely may not
  866.     ;------ be clear.
  867.     ;------
  868.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)   ;<---- comment out these
  869.     beq.s   BeginIO_Immediate           ;<---- lines to test task.
  870.  
  871.  
  872.     ;------ we need to queue the device.  mark us as needing
  873.     ;------ task attention.  Clear the quick flag
  874. BeginIO_QueueMsg:
  875.     bset    #UNITB_INTASK,UNIT_FLAGS(a3)
  876.     bclr    #IOB_QUICK,IO_FLAGS(a1)   ;We did NOT complete this quickly
  877.     ENABLE  a0
  878.  
  879.  
  880.     IFGE INFO_LEVEL-250
  881.      move.l  a1,-(sp)
  882.      move.l  a3,-(sp)
  883.      PUTMSG  250,<'%s/PutMsg: Port=%lx Message=%lx'>
  884.      addq.l  #8,sp
  885.     ENDC
  886.  
  887.     move.l  a3,a0
  888.     LINKSYS  PutMsg,md_SysLib(a6)   ;Port=a0, Message=a1
  889.     bra.s   BeginIO_End
  890.     ;----- return to caller before completing
  891.  
  892.  
  893.     ;------ Do it on the schedule of the calling process
  894.     ;------
  895. BeginIO_Immediate:
  896.     ENABLE  a0
  897.     bsr.s   PerformIO
  898.  
  899. BeginIO_End:
  900.     PUTMSG  200,<'%s/BeginIO_End'>
  901.     movem.l (sp)+,d1/a0/a3
  902.     rts
  903.  
  904. BeginIO_NoCmd:
  905.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  906.     bra.s   BeginIO_End
  907.  
  908.  
  909. ;
  910. ; PerformIO actually dispatches an io request.    It might be called from
  911. ; the task, or directly from BeginIO (thus on the callers's schedule)
  912. ;
  913. ; It expects a3 to already
  914. ; have the unit pointer in it.    a6 has the device pointer (as always).
  915. ; a1 has the io request.  Bounds checking has already been done on
  916. ; the I/O Request.
  917. ;
  918.  
  919. PerformIO:   ; ( iob:a1, unitptr:a3, devptr:a6 )
  920.     IFGE INFO_LEVEL-150
  921.      clr.l    -(sp)
  922.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  923.      PUTMSG   150,<'%s/PerformIO -- $%lx'>
  924.      addq.l   #4,sp
  925.     ENDC
  926.  
  927.     moveq   #0,d0
  928.     move.b  d0,IO_ERROR(A1)     ; No error so far
  929.     move.b  IO_COMMAND+1(a1),d0 ;Look only at low byte
  930.     lsl.w   #2,d0        ; Multiply by 4 to get table offset
  931.     lea.l   cmdtable(pc),a0
  932.     move.l  0(a0,d0.w),a0
  933.  
  934.     jmp     (a0)    ;iob:a1  unit:a3  devprt:a6
  935.  
  936.  
  937.  
  938. ;
  939. ; TermIO sends the IO request back to the user.  It knows not to mark
  940. ; the device as inactive if this was an immediate request or if the
  941. ; request was started from the server task.
  942. ;
  943.  
  944. TermIO:      ; ( iob:a1, unitptr:a3, devptr:a6 )
  945.     PUTMSG  160,<'%s/TermIO'>
  946.     move.w  IO_COMMAND(a1),d0
  947.  
  948.     move.w  #IMMEDIATES,d1
  949.     btst    d0,d1
  950.     bne.s   TermIO_Immediate    ;IO was immediate, don't do task stuff...
  951.  
  952.     ;------ we may need to turn the active bit off.
  953.     btst    #UNITB_INTASK,UNIT_FLAGS(a3)
  954.     bne.s   TermIO_Immediate    ;IO was came from task, don't clear ACTIVE...
  955.  
  956.     ;------ the task does not have more work to do
  957.     bclr    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  958.  
  959. TermIO_Immediate:
  960.     ;------ if the quick bit is still set then we don't need to reply
  961.     ;------ msg -- just return to the user.
  962.     btst    #IOB_QUICK,IO_FLAGS(a1)
  963.     bne.s   TermIO_End
  964.     LINKSYS ReplyMsg,md_SysLib(a6)      ;a1-message
  965.     ;(ReplyMsg sets the LN_TYPE to NT_REPLYMSG)
  966.  
  967. TermIO_End:
  968.     rts
  969.  
  970.  
  971. *****************************************************************************
  972. ;
  973. ; Here begins the functions that implement the device commands
  974. ; all functions are called with:
  975. ;   a1 -- a pointer to the io request block
  976. ;   a3 -- a pointer to the unit
  977. ;   a6 -- a pointer to the device
  978. ;
  979. ; Commands that conflict with 68000 instructions have a "My" prepended
  980. ; to them.
  981. ;----------------------------------------------------------------------
  982.  
  983. ;We can't AbortIO anything, so don't touch the IORequest!
  984. ;
  985. ;AbortIO() is a REQUEST to "hurry up" processing of an IORequest.
  986. ;If the IORequest was already complete, nothing happens (if an IORequest
  987. ;is quick or LN_TYPE=NT_REPLYMSG, the IORequest is complete).
  988. ;The message must be replied with ReplyMsg(), as normal.
  989. ;
  990. AbortIO:    ; ( iob: a1, device:a6 )
  991.     moveq   #IOERR_NOCMD,d0 ;return "AbortIO() request failed"
  992.     rts
  993.  
  994. RawRead:    ; 10 Not supported   (INVALID)
  995. RawWrite:    ; 11 Not supported   (INVALID)
  996. Invalid:
  997.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  998.     bra.s   TermIO
  999.  
  1000. ;
  1001. ; Update and Clear are internal buffering commands.  Update forces all
  1002. ; io out to its final resting spot, and does not return until this is
  1003. ; totally done.  Since this is automatic in a ramdisk, we simply return "Ok".
  1004. ;
  1005. ; Clear invalidates all internal buffers.  Since this device
  1006. ; has no internal buffers, these commands do not apply.
  1007. ;
  1008. Update:
  1009. Clear:
  1010. MyReset:            ;Do nothing (nothing reasonable to do)
  1011. AddChangeInt:            ;Do nothing
  1012. RemChangeInt:            ;Do nothing
  1013. MyRemove:            ;Do nothing
  1014. Seek:                ;Do nothing
  1015. Motor:                ;Do nothing
  1016. ChangeNum:            ;Return zero (changecount =0)
  1017. ChangeState:            ;Zero indicates disk inserted
  1018. ProtStatus:            ;Zero indicates unprotected
  1019.     clr.l   IO_ACTUAL(a1)
  1020.     bra.s   TermIO
  1021.  
  1022.  
  1023. GetDriveType:            ;make it look like 3.5" (90mm) drive
  1024.     moveq   #DRIVE3_5,d0
  1025.     move.l  d0,IO_ACTUAL(a1)
  1026.     bra.s   TermIO
  1027.  
  1028.  
  1029. GetNumTracks:
  1030.     move.l  #RAMSIZE/BYTESPERTRACK,IO_ACTUAL(a1) ;Number of tracks
  1031.     bra.s   TermIO
  1032.  
  1033. ;
  1034. ; Foo and Bar are two device specific commands that are provided just
  1035. ; to show you how commands are added.  They currently return that
  1036. ; no work was done.
  1037. ;
  1038. Foo:
  1039. Bar:
  1040.     clr.l   IO_ACTUAL(a1)
  1041.     bra     TermIO
  1042.  
  1043.  
  1044. ;---------------------------------------------------------------------------
  1045. ; This device is designed so that no combination of bad
  1046. ; inputs can ever cause the device driver to crash.
  1047. ;---------------------------------------------------------------------------
  1048. RdWrt:
  1049.     IFGE INFO_LEVEL-200
  1050.     move.l    IO_DATA(a1),-(sp)
  1051.     move.l    IO_OFFSET(a1),-(sp)
  1052.     move.l    IO_LENGTH(a1),-(sp)
  1053.     PUTMSG    200,<'%s/RdWrt len %ld offset %ld data $%lx'>
  1054.     addq.l    #8,sp
  1055.     addq.l    #4,sp
  1056.     ENDC
  1057.  
  1058.     movem.l a2/a3,-(sp)
  1059.     move.l  a1,a2        ;Copy iob
  1060.     move.l  IO_UNIT(a2),a3      ;Get unit pointer
  1061.  
  1062. *      check operation for legality
  1063.     btst.b  #0,IO_DATA+3(a2)    ;check if user's pointer is ODD
  1064.     bne.s   IO_LenErr        ;bad...
  1065.     ;[D0=offset]
  1066.  
  1067.     move.l  IO_OFFSET(a2),d0
  1068.     move.l  d0,d1
  1069.     and.l   #SECTOR-1,d1    ;Bad sector boundary or alignment?
  1070.     bne.s   IO_LenErr        ;bad...
  1071.     ;[D0=offset]
  1072.  
  1073. *      check for IO within disc range
  1074.     ;[D0=offset]
  1075.     add.l   IO_LENGTH(a2),d0    ;Add length to offset
  1076.     bcs.s   IO_LenErr        ;overflow... (important test)
  1077.     cmp.l   #RAMSIZE,d0     ;Last byte is highest acceptable total
  1078.     bhi.s   IO_LenErr        ;bad... (unsigned compare)
  1079.     and.l   #SECTOR-1,d0    ;Even sector boundary?
  1080.     bne.s   IO_LenErr        ;bad...
  1081.  
  1082. *      We've gotten this far, it must be a valid request.
  1083.  
  1084.     IFD   INTRRUPT
  1085.      move.l   mdu_SigMask(a3),d0  ; Get signals to wait for
  1086.      LINKSYS  Wait,md_SysLib(a6)  ; Wait for interrupt before proceeding
  1087.     ENDC
  1088.  
  1089.  
  1090.     lea.l   mdu_RAM(a3),a0      ; Point to RAMDISK "sector" for I/O
  1091.     add.l   IO_OFFSET(a2),a0    ; Add offset to ram base
  1092.     move.l  IO_LENGTH(a2),d0
  1093.     move.l  d0,IO_ACTUAL(a2)    ; Indicate we've moved all bytes
  1094.     beq.s   RdWrt_end        ;---deal with zero length I/O
  1095.     move.l  IO_DATA(a2),a1      ; Point to data buffer
  1096. ;
  1097. ;A0=ramdisk index
  1098. ;A1=user buffer
  1099. ;D0=length
  1100. ;
  1101.     cmp.b   #CMD_READ,IO_COMMAND+1(a2)  ; Decide on direction
  1102.     BEQ.S   CopyTheBlock
  1103.     EXG     A0,A1        ; For Write and Format, swap source & dest
  1104. CopyTheBlock:
  1105.     LINKSYS CopyMemQuick,md_SysLib(a6)  ;A0=source A1=dest D0=size
  1106.     ;CopyMemQuick is very fast
  1107.  
  1108. RdWrt_end:
  1109.     move.l  a2,a1
  1110.     movem.l (sp)+,a2/a3
  1111.     bra     TermIO    ;END
  1112.  
  1113.  
  1114.  
  1115. IO_LenErr:
  1116.     PUTMSG  10,<'bad length'>
  1117.     move.b  #IOERR_BADLENGTH,IO_ERROR(a2)
  1118. IO_End:
  1119.     clr.l   IO_ACTUAL(a2)       ;Initially, no data moved
  1120.     bra.s   RdWrt_end
  1121.  
  1122.  
  1123.  
  1124. ;
  1125. ; the Stop command stop all future io requests from being
  1126. ; processed until a Start command is received.    The Stop
  1127. ; command is NOT stackable: e.g. no matter how many stops
  1128. ; have been issued, it only takes one Start to restart
  1129. ; processing.
  1130. ;
  1131. ;Stop is rather silly for a ramdisk
  1132. MyStop:
  1133.    PUTMSG   30,<'%s/MyStop: called'>
  1134.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1135.    bra     TermIO
  1136.  
  1137.  
  1138. Start:
  1139.     PUTMSG   30,<'%s/Start: called'>
  1140.     bsr.s  InternalStart
  1141.     bra   TermIO
  1142.  
  1143.        ;[A3=unit A6=device]
  1144. InternalStart:
  1145.     move.l  a1,-(sp)
  1146.     ;------ turn processing back on
  1147.     bclr   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1148.     ;------ kick the task to start it moving
  1149.     move.b  MP_SIGBIT(a3),d1
  1150.     CLEAR   d0
  1151.     bset    d1,d0            ;prepared signal mask
  1152.     move.l  MP_SIGTASK(a3),a1       ;:FIXED:marco-task to signal
  1153.     LINKSYS Signal,md_SysLib(a6)    ;:FIXED:marco-a6 not a3
  1154.     move.l  (sp)+,a1
  1155.     rts
  1156.  
  1157.  
  1158. ;
  1159. ; Flush pulls all I/O requests off the queue and sends them back.
  1160. ; We must be careful not to destroy work in progress, and also
  1161. ; that we do not let some io requests slip by.
  1162. ;
  1163. ; Some funny magic goes on with the STOPPED bit in here.  Stop is
  1164. ; defined as not being reentrant.  We therefore save the old state
  1165. ; of the bit and then restore it later.  This keeps us from
  1166. ; needing to DISABLE in flush.    It also fails miserably if someone
  1167. ; does a start in the middle of a flush. (A semaphore might help...)
  1168. ;
  1169.  
  1170. Flush:
  1171.    PUTMSG   30,<'%s/Flush: called'>
  1172.    movem.l   d2/a1/a6,-(sp)
  1173.  
  1174.    move.l   md_SysLib(a6),a6
  1175.  
  1176.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1177.    sne     d2
  1178.  
  1179. Flush_Loop:
  1180.    move.l   a3,a0
  1181.    CALLSYS   GetMsg    ;Steal messages from task's port
  1182.  
  1183.    tst.l   d0
  1184.    beq.s   Flush_End
  1185.  
  1186.    move.l   d0,a1
  1187.    move.b   #IOERR_ABORTED,IO_ERROR(a1)
  1188.    CALLSYS   ReplyMsg
  1189.  
  1190.    bra.s   Flush_Loop
  1191.  
  1192. Flush_End:
  1193.    move.l   d2,d0
  1194.    movem.l   (sp)+,d2/a1/a6
  1195.  
  1196.    tst.b   d0
  1197.    beq.s   1$
  1198.  
  1199.    bsr     InternalStart
  1200. 1$:
  1201.    bra       TermIO
  1202.  
  1203.  
  1204. *****************************************************************************
  1205. ;
  1206. ; Here begins the task related routines
  1207. ;
  1208. ; A Task is provided so that queued requests may be processed at
  1209. ; a later time.  This is not very justifiable for a ram disk, but
  1210. ; is very useful for "real" hardware devices.  Take care with
  1211. ; your arbitration of shared hardware with all the multitasking
  1212. ; programs that might call you at once.
  1213. ;
  1214. ; Register Usage
  1215. ; ==============
  1216. ; a3 -- unit pointer
  1217. ; a6 -- syslib pointer
  1218. ; a5 -- device pointer
  1219. ; a4 -- task (NOT process) pointer
  1220. ; d7 -- wait mask
  1221. ;----------------------------------------------------------------------
  1222.  
  1223. ; some dos magic, useful for Processes (not us).  A process is started at
  1224. ; the first  executable address  after a segment list.    We hand craft a
  1225. ; segment list here.  See the the DOS technical reference if you really
  1226. ; need to know more about this.
  1227. ; The next instruction after the segment list is the first executable address
  1228.  
  1229.     cnop    0,4     ; long word align
  1230.     DC.L    16        ; segment length -- any number will do (this is 4
  1231.             ; bytes back from the segment pointer)
  1232. myproc_seglist:
  1233.     DC.L    0        ; pointer to next segment
  1234.  
  1235. Task_Begin:
  1236.     PUTMSG  35,<'%s/Task_Begin'>
  1237.     move.l  ABSEXECBASE,a6
  1238.  
  1239.     ;------ Grab the argument passed down from our parent
  1240.     move.l  4(sp),a3           ; Unit pointer
  1241.     move.l  mdu_Device(a3),a5  ; Point to device structure
  1242.  
  1243.     IFD   INTRRUPT
  1244.      ;------ Allocate a signal for "I/O Complete" interrupts
  1245.      moveq   #-1,d0        ; -1 is any signal at all
  1246.      CALLSYS   AllocSignal
  1247.      move.b   d0,mdu_SigBit(A3)   ; Save in unit structure
  1248.      moveq   #0,d7       ; Convert bit number signal mask
  1249.      bset   d0,d7
  1250.      move.l   d7,mdu_SigMask(A3)   ; Save in unit structure
  1251.      lea.l   mdu_is(a3),a1      ; Point to interrupt structure
  1252.      moveq   #INTB_PORTS,d0    ; Portia interrupt bit 3
  1253.      CALLSYS AddIntServer    ; Now install the server
  1254.      move.l   md_Base(a5),a0      ; Get board base address
  1255. *    bset.b   #INTENABLE,INTCTRL2(a0)   ; Enable interrupts
  1256.     ENDC
  1257.  
  1258.     ;------ Allocate a signal
  1259.     moveq   #-1,d0        ; -1 is any signal at all
  1260.     CALLSYS AllocSignal
  1261.     move.b  d0,MP_SIGBIT(a3)
  1262.     move.b  #PA_SIGNAL,MP_FLAGS(a3) ;Make message port "live"
  1263.     ;------ change the bit number into a mask, and save in d7
  1264.     moveq   #0,d7    ;Clear D7
  1265.     bset    d0,d7
  1266.  
  1267.     IFGE INFO_LEVEL-40
  1268.      move.l  $114(a6),-(sp)
  1269.      move.l  a5,-(sp)
  1270.      move.l  a3,-(sp)
  1271.      move.l  d0,-(sp)
  1272.      PUTMSG  40,<'%s/Signal=%ld, Unit=%lx Device=%lx Task=%lx'>
  1273.      add.l   #4*4,sp
  1274.     ENDC
  1275.  
  1276.     bra.s   Task_StartHere
  1277.  
  1278. ; OK, kids, we are done with initialization.  We now can start the main loop
  1279. ; of the driver.  It goes like this.  Because we had the port marked
  1280. ; PA_IGNORE for a while (in InitUnit) we jump to the getmsg code on entry.
  1281. ; (The first message will probably be posted BEFORE our task gets a chance
  1282. ; to run)
  1283. ;------     wait for a message
  1284. ;------     lock the device
  1285. ;------     get a message.  If no message, unlock device and loop
  1286. ;------     dispatch the message
  1287. ;------     loop back to get a message
  1288.  
  1289.     ;------ no more messages.  back ourselves out.
  1290. Task_Unlock:
  1291.     and.b   #$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
  1292.     ;------ main loop: wait for a new message
  1293.  
  1294. Task_MainLoop:
  1295.     PUTMSG   75,<'%s/++Sleep'>
  1296.     move.l  d7,d0
  1297.     CALLSYS Wait
  1298.     IFGE INFO_LEVEL-5
  1299.     bchg.b    #1,$bfe001  ;Blink the power LED
  1300.     ENDC
  1301. Task_StartHere:
  1302.     PUTMSG   75,<'%s/++Wakeup'>
  1303.     ;------ see if we are stopped
  1304.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  1305.     bne.s   Task_MainLoop    ; device is stopped, ignore messages
  1306.     ;------ lock the device
  1307.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  1308.     bne     Task_MainLoop    ; device in use (immediate command?)
  1309.  
  1310.  
  1311.    ;------ get the next request
  1312. Task_NextMessage:
  1313.     move.l  a3,a0
  1314.     CALLSYS GetMsg
  1315.     PUTMSG  1,<'%s/GotMsg'>
  1316.     tst.l   d0
  1317.     beq     Task_Unlock ; no message?
  1318.  
  1319.     ;------ do this request
  1320.     move.l  d0,a1
  1321.     exg     a5,a6    ; put device ptr in right place
  1322.     bsr     PerformIO
  1323.     exg     a5,a6    ; get syslib back in a6
  1324.  
  1325.     bra.s   Task_NextMessage
  1326.  
  1327. *****************************************************************************
  1328. ;
  1329. ; Here is a dummy interrupt handler, with some crucial components commented
  1330. ; out.    If the IFD INTRRUPT is enabled, this code will cause the device to
  1331. ; wait for a level two interrupt before it will process each request
  1332. ; (pressing RETURN on the keyboard will do it).  This code is normally
  1333. ; disabled, and must fake or omit certain operations since there  isn't
  1334. ; really any hardware for this driver.    Similar code has been used
  1335. ; successfully in other, "REAL" device drivers.
  1336. ;
  1337.  
  1338.    IFD     INTRRUPT
  1339.  
  1340. ;   A1 should be pointing to the unit structure upon entry! (IS_DATA)
  1341. myintr:
  1342. *      move.l    md_Base(a0),a0      ; point to board base address
  1343. *      btst.b    #IAMPULLING,INTCTRL1(a0);See if I'm interrupting
  1344. *      beq.s   myexnm          ; if not set, exit, not mine
  1345. *      move.b    #0,INTACK(a0)      ; toggle controller's int2 bit
  1346.  
  1347. ;      ------ signal the task that an interrupt has occurred
  1348.  
  1349.     move.l    mdu_Device(a1),a0   ; Get device pointer
  1350.     move.l    mdu_SigMask(a1),d0
  1351.     lea.l    mdu_tcb(a1),a1
  1352.     move.l    md_SysLib(a0),a6   ; Get pointer to system
  1353.     CALLSYS Signal
  1354.  
  1355. ;      now clear the zero condition code so that
  1356. ;      the interrupt handler doesn't call the next
  1357. ;      interrupt server.
  1358. ;
  1359. *      moveq   #1,d0         clear zero flag
  1360. *      bra.s   myexit          now exit
  1361. ;
  1362. ;      this exit point sets the zero condition code
  1363. ;      so the interrupt handler will try the next server
  1364. ;      in the interrupt chain
  1365. ;
  1366. myexnm        moveq   #0,d0      set zero condition code
  1367. ;
  1368. myexit        rts
  1369.    ENDC
  1370.  
  1371.  
  1372. *****************************************************************************
  1373.  
  1374. mdu_Init:
  1375. ;   ------ Initialize the device
  1376.  
  1377.     INITBYTE    MP_FLAGS,PA_IGNORE  ;Unit starts with a message port
  1378.     INITBYTE    LN_TYPE,NT_MSGPORT  ;
  1379.     INITLONG    LN_NAME,myName        ;
  1380.     INITLONG    mdu_tcb+LN_NAME,myName
  1381.     INITBYTE    mdu_tcb+LN_TYPE,NT_TASK
  1382.     INITBYTE    mdu_tcb+LN_PRI,5
  1383.     IFD   INTRRUPT
  1384.      INITBYTE     mdu_is+LN_PRI,4      ; Int priority 4
  1385.      INITLONG     mdu_is+IS_CODE,myintr    ; Interrupt routine addr
  1386.      INITLONG     mdu_is+LN_NAME,myName
  1387.     ENDC
  1388.     DC.W   0
  1389.  
  1390.  IFNE  AUTOMOUNT
  1391. mdn_Init:
  1392. *   ;------ Initialize packet for MakeDosNode
  1393.  
  1394.     INITLONG    mdn_execName,myName    ; Address of driver name
  1395.     INITLONG    mdn_tableSize,12    ; # long words in AmigaDOS env.
  1396.     INITLONG    mdn_dName,$524d0000    ; Store 'RM' in name
  1397.     INITLONG    mdn_sizeBlock,SECTOR/4    ; # longwords in a block
  1398.     INITLONG    mdn_numHeads,1        ; RAM disk has only one "head"
  1399.     INITLONG    mdn_secsPerBlk,1    ; secs/logical block, must = "1"
  1400.     INITLONG    mdn_blkTrack,SECTORSPER ; secs/track (must be reasonable)
  1401.     INITLONG    mdn_resBlks,1        ; reserved blocks, MUST > 0!
  1402.     INITLONG    mdn_upperCyl,(RAMSIZE/BYTESPERTRACK)-1 ; upper cylinder
  1403.     INITLONG    mdn_numBuffers,1    ; # AmigaDOS buffers to start
  1404.     DC.W   0
  1405.  ENDC
  1406.  
  1407. ;----------------------------------------------------------------------
  1408. ; EndCode is a marker that shows the end of your code.    Make sure it does not
  1409. ; span hunks, and is not before the rom tag!  It is ok to put it right after
  1410. ; the rom tag -- that way you are always safe.    I put it here because it
  1411. ; happens to be the "right" thing to do, and I know that it is safe in this
  1412. ; case (this program has only a single code hunk).
  1413. ;----------------------------------------------------------------------
  1414. EndCode
  1415.     END
  1416.