home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 206.lha / RamDev / ramdev.device.asm < prev    next >
Assembly Source File  |  1988-12-28  |  43KB  |  1,377 lines

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