home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 377b.lha / devices / sampledevice / ramdev.device.asm < prev    next >
Encoding:
Assembly Source File  |  1980-02-03  |  46.5 KB  |  1,444 lines

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