home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_progs / miscutil / driver.lzh / DRIVER / MYDEV.ASM < prev    next >
Encoding:
Assembly Source File  |  1991-11-01  |  29.5 KB  |  1,177 lines

  1.  
  2. *************************************************************************
  3. *                                    *
  4. *    Copyright (C) 1986, Commodore Amiga Inc.  All rights reserved.    *
  5. *    Permission granted for non-commercial use            *                                *
  6. *                                    *
  7. *************************************************************************
  8.  
  9.  
  10. *************************************************************************
  11. *
  12. * mydev.asm -- Skeleton device code.  Modified by Lee Erickson to be a 
  13. *           simple disk device, using RAM to simulate a disk.
  14. *           10/7/86
  15. *
  16. ************************************************************************/
  17.     SECTION    section
  18.  
  19.     NOLIST
  20.     include "exec/types.i"
  21.     include "exec/nodes.i"
  22.     include "exec/lists.i"
  23.     include "exec/libraries.i"
  24.     include "exec/devices.i"
  25.     include "exec/io.i"
  26.     include "exec/alerts.i"
  27.     include "exec/initializers.i"
  28.     include "exec/memory.i"
  29.     include "exec/resident.i"
  30.     include "exec/ables.i"
  31.     include "exec/errors.i"
  32.     include "exec/tasks.i"
  33.     include    'libraries/expansion.i'
  34.     include 'libraries/configvars.i'
  35.     include 'libraries/configregs.i'
  36.  
  37.     include "asmsupp.i"
  38.     include "messages.i"
  39.  
  40.     include "mydev.i"
  41.  
  42.     LIST
  43.  
  44.     ;------ These don't have to be external, but it helps some
  45.     ;------ debuggers to have them globally visible
  46.     XDEF    Init
  47.     XDEF    Open
  48.     XDEF    Close
  49.     XDEF    Expunge
  50.     XDEF    Null
  51.     XDEF    myName
  52.     XDEF    BeginIO
  53.     XDEF    AbortIO
  54.  
  55.     XREF    _AbsExecBase
  56.  
  57.     XLIB    AddIntServer
  58.     XLIB    RemIntServer
  59.     XLIB    Debug
  60.     XLIB    InitStruct
  61.     XLIB    OpenLibrary
  62.     XLIB    CloseLibrary
  63.     XLIB    Alert
  64.     XLIB    FreeMem
  65.     XLIB    Remove
  66.     XLIB    AllocMem
  67.     XLIB    AddTask
  68.     XLIB    PutMsg
  69.     XLIB    RemTask
  70.     XLIB    ReplyMsg
  71.     XLIB    Signal
  72.     XLIB    GetMsg
  73.     XLIB    Wait
  74.     XLIB    WaitPort
  75.     XLIB    AllocSignal
  76.     XLIB    SetTaskPri
  77.     XLIB    GetCurrentBinding    ; Get list of boards for this driver
  78.     XLIB    MakeDosNode
  79.     XLIB    AddDosNode
  80.  
  81.     INT_ABLES
  82.  
  83.  
  84.     ; The first executable location.  This should return an error
  85.     ; in case someone tried to run you as a program (instead of
  86.     ; loading you as a library).
  87. FirstAddress:
  88.     CLEAR    d0
  89.     rts
  90.  
  91. ;-----------------------------------------------------------------------
  92. ; A romtag structure.  Both "exec" and "ramlib" look for
  93. ; this structure to discover magic constants about you
  94. ; (such as where to start running you from...).
  95. ;-----------------------------------------------------------------------
  96.  
  97.     ; Most people will not need a priority and should leave it at zero.
  98.     ; the RT_PRI field is used for configuring the roms.  Use "mods" from
  99.     ; wack to look at the other romtags in the system
  100. MYPRI    EQU    0
  101.  
  102. initDDescrip:
  103.                     ;STRUCTURE RT,0
  104.       DC.W    RTC_MATCHWORD        ; UWORD RT_MATCHWORD
  105.       DC.L    initDDescrip        ; APTR  RT_MATCHTAG
  106.       DC.L    EndCode        ; APTR  RT_ENDSKIP
  107.       DC.B    RTF_AUTOINIT        ; UBYTE RT_FLAGS
  108.       DC.B    VERSION        ; UBYTE RT_VERSION
  109.       DC.B    NT_DEVICE        ; UBYTE RT_TYPE
  110.       DC.B    MYPRI            ; BYTE  RT_PRI
  111.       DC.L    myName        ; APTR  RT_NAME
  112.       DC.L    idString        ; APTR  RT_IDSTRING
  113.       DC.L    Init            ; APTR  RT_INIT
  114.                     ; LABEL RT_SIZE
  115.  
  116.  
  117.     ; this is the name that the device will have
  118. subSysName:
  119. myName:        MYDEVNAME
  120.  
  121. ExLibName    EXPANSIONNAME    ; Expansion Library Name
  122.  
  123.     ; a major version number.
  124. VERSION:    EQU    1
  125.  
  126.     ; A particular revision.  This should uniquely identify the bits in the
  127.     ; device.  I use a script that advances the revision number each time
  128.     ; I recompile.  That way there is never a question of which device
  129.     ; that really is.
  130. REVISION:    EQU    17
  131.  
  132.     ; this is an identifier tag to help in supporting the device
  133.     ; format is 'name version.revision (dd MON yyyy)',<cr>,<lf>,<null>
  134. idString:    dc.b    'mydev 1.0 (31 Oct 1985)',13,10,0
  135.  
  136.     ; force word allignment
  137.     ds.w    0
  138.  
  139.  
  140.     ; The romtag specified that we were "RTF_AUTOINIT".  This means
  141.     ; that the RT_INIT structure member points to one of these
  142.     ; tables below.  If the AUTOINIT bit was not set then RT_INIT
  143.     ; would point to a routine to run.
  144.  
  145. Init:
  146.     DC.L    MyDev_Sizeof        ; data space size
  147.     DC.L    funcTable        ; pointer to function initializers
  148.     DC.L    dataTable        ; pointer to data initializers
  149.     DC.L    initRoutine        ; routine to run
  150.  
  151.  
  152. funcTable:
  153.  
  154.     ;------ standard system routines
  155.     dc.l    Open
  156.     dc.l    Close
  157.     dc.l    Expunge
  158.     dc.l    Null
  159.  
  160.     ;------ my device definitions
  161.     dc.l    BeginIO
  162.     dc.l    AbortIO
  163.  
  164.     ;------ function table end marker
  165.     dc.l    -1
  166.  
  167.  
  168.     ; The data table initializes static data structures.
  169.     ; The format is specified in exec/InitStruct routine's
  170.     ; manual pages.  The INITBYTE/INITWORD/INITLONG routines
  171.     ; are in the file "exec/initializers.i".  The first argument
  172.     ; is the offset from the device base for this byte/word/long.
  173.     ; The second argument is the value to put in that cell.
  174.     ; The table is null terminated
  175. dataTable:
  176.     INITBYTE    LH_TYPE,NT_DEVICE
  177.     INITLONG    LN_NAME,myName
  178.     INITBYTE    LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
  179.     INITWORD    LIB_VERSION,VERSION
  180.     INITWORD    LIB_REVISION,REVISION
  181.     INITLONG    LIB_IDSTRING,idString
  182.     DC.L    0
  183.  
  184.  
  185.     ; This routine gets called after the device has been allocated.
  186.     ; The device pointer is in D0.  The segment list is in a0.
  187.     ; If it returns non-zero then the device will be linked into
  188.     ; the device list.
  189. initRoutine:
  190.  
  191. ; Register Usage
  192. ; ==============
  193. ; a3 -- Points to tempory RAM
  194. ; a4 -- Expansion library base
  195. ; a5 -- device pointer
  196. ; a6 -- Exec base
  197. ;
  198. ;----------------------------------------------------------------------
  199.     ;------ get the device pointer into a convenient A register
  200.     PUTMSG    30,<'%s/Init: called'>
  201.      movem.l    d1-d7/a0-a5,-(sp)    ; Preserve ALL modified registers
  202.     move.l    d0,a5
  203.  
  204.     ;------ save a pointer to exec
  205.     move.l    a6,md_SysLib(a5)
  206.  
  207.     ;------ save a pointer to our loaded code
  208.     move.l    a0,md_SegList(a5)
  209.  
  210.     lea.l    ExLibName,A1        ; Get expansion lib. name
  211.     moveq.l    #0,D0
  212.     CALLSYS    OpenLibrary        ; Open the expansion library
  213.     tst.l    D0
  214.     bne.s    init_OpSuccess
  215.  
  216. init_OpFail:
  217.     ALERT    AG_OpenLib!AO_ExpansionLib
  218.  
  219. init_OpSuccess:
  220.     move.l    D0,A4
  221.  
  222.     lea    md_Base(A5),A0    ; Get the Current Bindings
  223.     moveq    #4,D0        ; Just get address (length = 4 bytes)
  224.     LINKLIB    _LVOGetCurrentBinding,A4
  225.     move.l    md_Base(A5),D0        ; Get start of list
  226.     tst.l    D0            ; If controller not found
  227.     beq    Init_End        ;    Exit and unload driver
  228.     move.l    D0,A0            ; Get config structure address
  229.     move.l    cd_BoardAddr(A0),md_Base(A5); Save board base address
  230.     bclr.b    #CDB_CONFIGME,cd_Flags(A0); Mark board as configured
  231.  
  232. Init_End:
  233. ;----------------------------------------------------------------------
  234. ;
  235. ; Here we build a packet describing the characteristics of our disk to
  236. ; pass to AmigaDOS.  This serves the same purpose as a "mount" command
  237. ; of this device would.  For disks, it might be useful to actually
  238. ; get this information right from the disk itself.  Just as mount,
  239. ; it could be for multiple partitions on the single physical device.
  240. ; For this example, we will simply hard code the appropriate parameters.
  241. ;
  242. ;-----------------------------------------------------------------------
  243.  
  244. ;!!!! Normally you would only do this if your card was successfully configured
  245. ;!!!! up above.  For the demo, it will always be done.
  246.  
  247.     ;------    Allocate tempory RAM to build MakeDosNode parameter packet
  248.     move.l    #MEMF_CLEAR!MEMF_PUBLIC,d1
  249.     move.l    #mdn_Sizeof,d0    ; Enough room for our parameter packet
  250.     CALLSYS    AllocMem
  251.     move.l    d0,a3
  252.  
  253.     ;-----    Use InitStruct to initialize the constant portion of packet
  254.     move.l    d0,a2            ; Point to memory to initialize
  255.     moveq.l    #0,d0            ; Don't need to re-zero it
  256.     lea.l    mdn_Init(pc),A1
  257.     CALLSYS    InitStruct
  258.  
  259.     lea    mdn_dName(a3),a0    ; Get addr of Device name
  260.     move.l    a0,mdn_dosName(a3)    ;    and save in environment
  261.  
  262.     moveq    #1,d6            ; Now tell AmigaDOS about all units
  263. Uloop:
  264.     move.b  d6,d0            ; Get unit number
  265.     add.b    #$2F,d0            ; Make ASCII, minus 1
  266.     move.b    d0,mdn_dName+3(a3)    ;    and store in name
  267.     move.l    d6,mdn_unit(a3)        ; Store unit # in environment
  268.  
  269.     move.l    a3,a0
  270.     LINKLIB    _LVOMakeDosNode,a4    ; Build AmigaDOS structures
  271.     move.l    d0,a0            ; Get deviceNode address
  272.     moveq.l    #0,d0            ; Set device priority to 0
  273.     moveq.l    #0,d1
  274. *    moveq.l    #ADNF_STARTPROC,d1    ; Have handler started
  275.     LINKLIB    _LVOAddDosNode,a4
  276.  
  277.     addq    #1,d6            ; Bump unit number
  278.     cmp.b    #MD_NUMUNITS,d6
  279.     ble.s    Uloop            ; Loop until all units installed
  280.  
  281.     move.l    a3,a1        ; Return RAM to system
  282.     move.l    #mdn_Sizeof,d0
  283.     CALLSYS    FreeMem
  284.  
  285.     move.l    a4,a1        ; Now close expansion library
  286.     CALLSYS    CloseLibrary
  287.     ;
  288.     ; You would normally set d0 to a NULL if your initialization failed,
  289.     ; but I'm not doing that for this demo, since it is unlikely
  290.     ; you actually have a board with this particular manufacturer ID
  291.     ; installed when running this demo.
  292.     ;
  293.     move.l    a5,d0
  294.     movem.l    (sp)+,d1-d7/a0-a5
  295.  
  296.     rts
  297.  
  298. ;----------------------------------------------------------------------
  299. ;
  300. ; here begins the system interface commands.  When the user calls
  301. ; OpenLibrary/CloseLibrary/RemoveLibrary, this eventually gets translated
  302. ; into a call to the following routines (Open/Close/Expunge).  Exec
  303. ; has already put our device pointer in a6 for us.  Exec has turned
  304. ; off task switching while in these routines (via Forbid/Permit), so
  305. ; we should not take too long in them.
  306. ;
  307. ;----------------------------------------------------------------------
  308.  
  309.  
  310.     ; Open sets the IO_ERROR field on an error.  If it was successfull,
  311.     ; we should set up the IO_UNIT field.
  312.  
  313. Open:        ; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
  314.     PUTMSG    30,<'%s/Open: called'>
  315.     movem.l    d2/a2/a3/a4,-(sp)
  316.  
  317.     move.l    a1,a2        ; save the iob
  318.  
  319.     ;------ see if the unit number is in range
  320.     subq    #1,d0        ; Unit ZERO isn't allowed
  321.     cmp.l    #MD_NUMUNITS,d0
  322.     bcc.s    Open_Error    ; unit number out of range
  323.  
  324.     ;------ see if the unit is already initialized
  325.     move.l    d0,d2        ; save unit number
  326.     lsl.l    #2,d0
  327.     lea.l    md_Units(a6,d0.l),a4
  328.     move.l    (a4),d0
  329.     bne.s    Open_UnitOK
  330.  
  331.     ;------ try and conjure up a unit
  332.     bsr    InitUnit
  333.  
  334.     ;------ see if it initialized OK
  335.     move.l    (a4),d0
  336.     beq.s    Open_Error
  337.  
  338. Open_UnitOK:
  339.     move.l    d0,a3        ; unit pointer in a3
  340.  
  341.     move.l    d0,IO_UNIT(a2)
  342.  
  343.     ;------ mark us as having another opener
  344.     addq.w    #1,LIB_OPENCNT(a6)
  345.     addq.w    #1,UNIT_OPENCNT(a3)
  346.  
  347.     ;------ prevent delayed expunges
  348.     bclr    #LIBB_DELEXP,md_Flags(a6)
  349.     moveq.l    #0,d0
  350.  
  351. Open_End:
  352.  
  353.     movem.l    (sp)+,d2/a2/a3/a4
  354.     rts
  355.  
  356. Open_Error:
  357.     move.b    #IOERR_OPENFAIL,IO_ERROR(a2)
  358.     move.b    #IOERR_OPENFAIL,d0
  359.     bra.s    Open_End
  360.  
  361.     ; There are two different things that might be returned from
  362.     ; the Close routine.  If the device is no longer open and
  363.     ; there is a delayed expunge then Close should return the
  364.     ; segment list (as given to Init).  Otherwise close should
  365.     ; return NULL.
  366.  
  367. Close:        ; ( device:a6, iob:a1 )
  368.     movem.l    d1/a2-a3,-(sp)
  369.     PUTMSG    30,<'%s/Close: called'>
  370.  
  371.     move.l    a1,a2
  372.  
  373.     move.l    IO_UNIT(a2),a3
  374.  
  375.     ;------ make sure the iob is not used again
  376.     moveq.l    #-1,d0
  377.     move.l    d0,IO_UNIT(a2)
  378.     move.l    d0,IO_DEVICE(a2)
  379.  
  380.     ;------ see if the unit is still in use
  381.     subq.w    #1,UNIT_OPENCNT(a3)
  382.  
  383. ;!!!!!! Since this example is a RAM disk (and we don't want the contents to
  384. ;!!!!!! disappear between opens, ExpungeUnit will be skipped here.  It would
  385. ;!!!!!! be used for drivers of "real" devices
  386. ;!!!!!!    bne.s    Close_Device
  387. ;!!!!!!    bsr    ExpungeUnit
  388.  
  389. Close_Device:
  390.     ;------ mark us as having one fewer openers
  391.     moveq.l    #0,d0
  392.     subq.w    #1,LIB_OPENCNT(a6)
  393.  
  394.     ;------ see if there is anyone left with us open
  395.     bne.s    Close_End
  396.  
  397.     ;------ see if we have a delayed expunge pending
  398.     btst    #LIBB_DELEXP,md_Flags(a6)
  399.     beq.s    Close_End
  400.  
  401.     ;------ do the expunge
  402.     bsr    Expunge
  403.  
  404. Close_End:
  405.     movem.l    (sp)+,d1/a2-a3
  406.     rts
  407.  
  408.  
  409.     ; There are two different things that might be returned from
  410.     ; the Expunge routine.  If the device is no longer open
  411.     ; then Expunge should return the segment list (as given to
  412.     ; Init).  Otherwise Expunge should set the delayed expunge
  413.     ; flag and return NULL.
  414.     ;
  415.     ; One other important note: because Expunge is called from
  416.     ; the memory allocator, it may NEVER Wait() or otherwise
  417.     ; take long time to complete.
  418.  
  419. Expunge:    ; ( device: a6 )
  420.     PUTMSG    30,<'%s/Expunge: called'>
  421.  
  422.     movem.l    d1/d2/a5/a6,-(sp)    ; Best to save ALL modified registers
  423.     move.l    a6,a5
  424.     move.l    md_SysLib(a5),a6
  425.     
  426.     ;------ see if anyone has us open
  427.     tst.w    LIB_OPENCNT(a5)
  428. ;!!!!!  The following line is commented out for this RAM disk demo, since
  429. ;!!!!!  we don't want the RAM to be freed after FORMAT, for example.
  430. ;    beq    1$
  431.  
  432.     ;------ it is still open.  set the delayed expunge flag
  433.     bset    #LIBB_DELEXP,md_Flags(a5)
  434.     CLEAR    d0
  435.     bra.s    Expunge_End
  436.  
  437. 1$:
  438.     ;------ go ahead and get rid of us.  Store our seglist in d2
  439.     move.l    md_SegList(a5),d2
  440.  
  441.     ;------ unlink from device list
  442.     move.l    a5,a1
  443.     CALLSYS    Remove
  444.     
  445.     ;
  446.     ; device specific closings here...
  447.     ;
  448.  
  449.     ;------ free our memory
  450.     CLEAR    d0
  451.     CLEAR    d1
  452.     move.l    a5,a1
  453.     move.w    LIB_NEGSIZE(a5),d1
  454.  
  455.     sub.w    d1,a1
  456.     add.w    LIB_POSSIZE(a5),d0
  457.     add.l    d1,d0
  458.  
  459.     CALLSYS    FreeMem
  460.  
  461.     ;------ set up our return value
  462.     move.l    d2,d0
  463.  
  464. Expunge_End:
  465.     movem.l    (sp)+,d1/d2/a5/a6
  466.     rts
  467.  
  468.  
  469. Null:
  470.     PUTMSG    30,<'%s/Null: called'>
  471.     CLEAR    d0
  472.     rts
  473.  
  474.  
  475. InitUnit:    ; ( d2:unit number, a3:scratch, a6:devptr )
  476.     PUTMSG    30,<'%s/InitUnit: called'>
  477.     movem.l    d2-d4/a2,-(sp)
  478.  
  479.     ;------ allocate unit memory
  480.     move.l    #MyDevUnit_Sizeof,d0
  481.     move.l    #MEMF_PUBLIC!MEMF_CLEAR,d1
  482.     LINKSYS    AllocMem,md_SysLib(a6)
  483.  
  484.     tst.l    d0
  485.     beq    InitUnit_End
  486.  
  487.     move.l    d0,a3
  488.     move.b    d2,mdu_UnitNum(a3)    ; initialize unit number
  489.     move.l    a6,mdu_Device(a3)    ; initialize device pointer
  490.  
  491.     ;------ start up the unit process.  We do a trick here --
  492.     ;------ we set his message port to PA_IGNORE until the
  493.     ;------ new process has a change to set it up.
  494.     ;------ We cannot go to sleep here: it would be very nasty
  495.     ;------ if someone else tried to open the unit
  496.     ;------ (exec's OpenDevice has done a Forbid() for us --
  497.     ;------ we depend on this to become single threaded).
  498.  
  499.     ;------ Initialize the stack information
  500.     lea    mdu_stack(a3),a0    ; Low end of stack
  501.     move.l    a0,mdu_tcb+TC_SPLOWER(a3)
  502.     lea    MYPROCSTACKSIZE(a0),a0    ; High end of stack
  503.     move.l    a0,mdu_tcb+TC_SPUPPER(a3)
  504.     move.l    a3,-(A0)        ; argument -- unit ptr
  505.     move.l    a0,mdu_tcb+TC_SPREG(a3)
  506.     ;------ initialize the unit's list
  507.     lea    MP_MSGLIST(a3),a0
  508.     NEWLIST    a0
  509.     lea    mdu_tcb(a3),a0
  510.     move.l    a0,MP_SIGTASK(a3)
  511.     moveq.l    #0,d0            ; Don't need to re-zero it
  512.     move.l    a3,a2            ; InitStruct is initializing the UNIT
  513.     lea.l    mdu_Init,A1
  514.     LINKSYS    InitStruct,md_SysLib(a6)
  515.  
  516.     move.l    a3,mdu_is+IS_DATA(a3)    ; Pass int. server unit addr.
  517.  
  518. ;    Startup the task
  519.     lea    mdu_tcb(a3),a1
  520.     lea    Proc_Begin(PC),a2
  521.     move.l    a3,-(sp)        ; Preserve UNIT pointer
  522.     lea    -1,a3            ; generate address error
  523.                     ; if task ever "returns"
  524.     CLEAR    d0
  525.     LINKSYS AddTask,md_SysLib(a6)
  526.     move.l    (sp)+,a3        ; restore UNIT pointer
  527.  
  528.     ;------ mark us as ready to go
  529.     move.l    d2,d0            ; unit number
  530.     lsl.l    #2,d0
  531.     move.l    a3,md_Units(a6,d0.l)    ; set unit table
  532.  
  533.  
  534. InitUnit_End:
  535.     movem.l    (sp)+,d2-d4/a2
  536.     rts
  537.  
  538.     ;------ got an error.  free the unit structure that we allocated.
  539. InitUnit_FreeUnit:
  540.     bsr    FreeUnit
  541.     bra.s    InitUnit_End
  542.  
  543. FreeUnit:    ; ( a3:unitptr, a6:deviceptr )
  544.     move.l    a3,a1
  545.     move.l    #MyDevUnit_Sizeof,d0
  546.     LINKSYS    FreeMem,md_SysLib(a6)
  547.     rts
  548.  
  549.  
  550. ExpungeUnit:    ; ( a3:unitptr, a6:deviceptr )
  551.     PUTMSG    30,<'%s/ExpungeUnit: called'>
  552.     move.l    d2,-(sp)
  553.  
  554. ;
  555. ; If you can expunge you unit, and each unit has it's own interrups,
  556. ; you must remember to remove its interrupt server
  557. ;
  558.  
  559.     IFD    INTRRUPT
  560.     lea.l    mdu_is(a3),a1        ; Point to interrupt structure
  561.     moveq    #3,d0            ; Portia interrupt bit 3
  562.     LINKSYS    RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
  563.     ENDC
  564.  
  565.     ;------ get rid of the unit's task.  We know this is safe
  566.     ;------ because the unit has an open count of zero, so it
  567.     ;------ is 'guaranteed' not in use.
  568.     lea    mdu_tcb(a3),a1
  569.     LINKSYS    RemTask,md_SysLib(a6)
  570.  
  571.     ;------ save the unit number
  572.     CLEAR    d2
  573.     move.b    mdu_UnitNum(a3),d2
  574.  
  575.     ;------ free the unit structure.
  576.     bsr    FreeUnit
  577.  
  578.     ;------ clear out the unit vector in the device
  579.     lsl.l    #2,d2
  580.     clr.l    md_Units(a6,d2.l)
  581.  
  582.     move.l    (sp)+,d2
  583.  
  584.     rts
  585.  
  586. ;----------------------------------------------------------------------
  587. ;
  588. ; here begins the device specific functions
  589. ;
  590. ;----------------------------------------------------------------------
  591.  
  592. ; cmdtable is used to look up the address of a routine that will
  593. ; implement the device command.
  594. cmdtable:
  595.     DC.L    Invalid        ; $00000001
  596.     DC.L    MyReset        ; $00000002
  597.     DC.L    RdWrt        ; $00000004    Common routine for read/write
  598.     DC.L    RdWrt        ; $00000008
  599.     DC.L    Update        ; $00000010
  600.     DC.L    Clear        ; $00000020
  601.     DC.L    MyStop        ; $00000040
  602.     DC.L    Start        ; $00000080
  603.     DC.L    Flush        ; $00000100
  604.     DC.L    Motor        ; $00000200  motor    (NO-OP)
  605.     DC.L    Seek        ; $00000400  seek    (NO-OP)
  606.     DC.L    RdWrt        ; $00000800  format -> WRITE for RAMDISK
  607.     DC.L    MyRemove    ; $00001000  remove        (NO-OP)
  608.     DC.L    ChangeNum    ; $00002000  changenum        (Returns 0)
  609.     DC.L    ChangeState    ; $00004000  changestate    (Returns 0)
  610.     DC.L    ProtStatus    ; $00008000  protstatus        (Returns 0)
  611.     DC.L    RawRead        ; Not supported    (INVALID)
  612.     DC.L    RawWrite    ; Not supported    (INVALID)
  613.     DC.L    GetDriveType    ; Get drive type    (Returns 1)
  614.     DC.L    GetNumTracks    ; Get number of tracks (Returns NUMTRKS)
  615.     DC.L    AddChangeInt    ; Add disk change interrupt (NO-OP)
  616.     DC.L    RemChangeInt    ; Remove disk change interrupt ( NO-OP)
  617. cmdtable_end:
  618.  
  619. ; this define is used to tell which commands should not be queued
  620. ; command zero is bit zero.
  621. ; The immediate commands are Invalid, Reset, Stop, Start, Flush
  622. IMMEDIATES    EQU    $000001c3
  623.  
  624. ; These commands can NEVER be done "immediately" if using interrupts,
  625. ; since they would "wait" for the interrupt forever!
  626. ; Read, Write, Format
  627. NEVERIMMED    EQU    $0000080C
  628. ;
  629. ; BeginIO starts all incoming io.  The IO is either queued up for the
  630. ; unit task or processed immediately.
  631. ;
  632.  
  633. BeginIO:    ; ( iob: a1, device:a6 )
  634.     PUTMSG    30,<'%s/BeginIO: called'>
  635.     movem.l    d1/a0/a3,-(sp)
  636.  
  637.     ;------ bookkeeping
  638.     move.l    IO_UNIT(a1),a3
  639.  
  640.     ;------ see if the io command is within range
  641.     move.w    IO_COMMAND(a1),d0
  642.     cmp.w    #MYDEV_END,d0
  643.     bcc    BeginIO_NoCmd
  644.  
  645.     DISABLE    a0
  646.  
  647.     ;------ process all immediate commands no matter what
  648.     move.w    #IMMEDIATES,d1
  649.     btst    d0,d1
  650.     bne.s    BeginIO_Immediate
  651.  
  652.     IFD    INTRRUPT    ; if using interrupts,
  653.     ;------ queue all NEVERIMMED commands no matter what
  654.     move.w    #NEVERIMMED,d1
  655.     btst    d0,d1
  656.     bne.s    BeginIO_QueueMsg
  657.     ENDC
  658.  
  659.     ;------ see if the unit is STOPPED.  If so, queue the msg.
  660.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  661.     bne.s    BeginIO_QueueMsg
  662.  
  663.     ;------ this is not an immediate command.  see if the device is
  664.     ;------ busy.
  665.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  666.     beq.s    BeginIO_Immediate
  667.  
  668.     ;------ we need to queue the device.  mark us as needing
  669.     ;------ task attention.  Clear the quick flag
  670. BeginIO_QueueMsg:
  671.     BSET    #UNITB_INTASK,UNIT_FLAGS(a3)
  672.     bclr    #IOB_QUICK,IO_FLAGS(a1)
  673.  
  674.     ENABLE    a0
  675.  
  676.     move.l    a3,a0
  677.     LINKSYS    PutMsg,md_SysLib(a6)
  678.     bra    BeginIO_End
  679.  
  680. BeginIO_Immediate:
  681.     ENABLE    a0
  682.  
  683.     bsr    PerformIO
  684.  
  685. BeginIO_End:
  686.     movem.l    (sp)+,d1/a0/a3
  687.     rts
  688.  
  689. BeginIO_NoCmd:
  690.     move.b    #IOERR_NOCMD,IO_ERROR(a1)
  691.     bra.s    BeginIO_End
  692.  
  693.  
  694. ;
  695. ; PerformIO actually dispatches an io request.  It expects a3 to already
  696. ; have the unit pointer in it.  a6 has the device pointer (as always).
  697. ; a1 has the io request.  Bounds checking has already been done on
  698. ; the io request.
  699. ;
  700.  
  701. PerformIO:    ; ( iob:a1, unitptr:a3, devptr:a6 )
  702.     PUTMSG    30,<'%s/PerforIO: called'>
  703.     move.l    a2,-(sp)
  704.     move.l    a1,a2
  705.  
  706.     clr.b    IO_ERROR(A2)        ; No error so far
  707.     move.w    IO_COMMAND(a2),d0
  708.     lsl    #2,d0            ; Multiply by 4 to get table offset
  709.     lea    cmdtable(pc),a0
  710.     move.l    0(a0,d0.w),a0
  711.  
  712.     jsr    (a0)
  713.  
  714.     move.l    (sp)+,a2
  715.     rts
  716.  
  717. ;
  718. ; TermIO sends the IO request back to the user.  It knows not to mark
  719. ; the device as inactive if this was an immediate request or if the
  720. ; request was started from the server task.
  721. ;
  722.  
  723. TermIO:        ; ( iob:a1, unitptr:a3, devptr:a6 )
  724.     PUTMSG    30,<'%s/TermIO: called'>
  725.     move.w    IO_COMMAND(a1),d0
  726.     move.w    #IMMEDIATES,d1
  727.     btst    d0,d1
  728.     bne.s    TermIO_Immediate
  729.  
  730.     ;------ we may need to turn the active bit off.
  731.     btst    #UNITB_INTASK,UNIT_FLAGS(a3)
  732.     bne.s    TermIO_Immediate
  733.  
  734.     ;------ the task does not have more work to do
  735.     bclr    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  736.  
  737. TermIO_Immediate:
  738.     ;------ if the quick bit is still set then we don't need to reply
  739.     ;------ msg -- just return to the user.
  740.     btst    #IOB_QUICK,IO_FLAGS(a1)
  741.     bne.s    TermIO_End
  742.  
  743.     LINKSYS    ReplyMsg,md_SysLib(a6)
  744.  
  745. TermIO_End:
  746.     rts
  747.     
  748.  
  749. AbortIO:    ; ( iob: a1, device:a6 )
  750. ;----------------------------------------------------------------------
  751. ;
  752. ; here begins the functions that implement the device commands
  753. ; all functions are called with:
  754. ;    a1 -- a pointer to the io request block
  755. ;    a2 -- another pointer to the iob
  756. ;    a3 -- a pointer to the unit
  757. ;    a6 -- a pointer to the device
  758. ;
  759. ; Commands that conflict with 68000 instructions have a "My" prepended
  760. ; to them.
  761. ;----------------------------------------------------------------------
  762.  
  763. RawRead:        ; 10 Not supported    (INVALID)
  764. RawWrite:        ; 11 Not supported    (INVALID)
  765. Invalid:
  766.     move.b    #IOERR_NOCMD,IO_ERROR(a1)
  767.     bsr    TermIO
  768.     rts
  769.  
  770. MyReset:
  771. AddChangeInt:
  772. RemChangeInt:
  773. MyRemove:
  774. Seek:
  775. Motor:
  776. ChangeNum:
  777. ChangeState:
  778. ProtStatus:
  779.     clr.l    IO_ACTUAL(a1)    ; Indicate drive isn't protected
  780.     bsr    TermIO
  781.     rts
  782.  
  783. GetDriveType:
  784.     move.l    #1,IO_ACTUAL(a1)        ; Make it look like 3.5"
  785.     bsr    TermIO
  786.     rts
  787.  
  788. GetNumTracks:
  789.     move.l    #RAMSIZE/5120,IO_ACTUAL(a1)    ; Number of 10 sector tracks
  790.     bsr    TermIO
  791.     rts
  792.  
  793. RdWrt:
  794.     movem.l a2/a3,-(sp)
  795.     clr.l    IO_ACTUAL(a1)        ; Initially, no data moved
  796.     move.l    IO_DATA(a1),a0
  797.     move.l    IO_LENGTH(a1),d0
  798.  
  799.     ;------ deal with zero length I/O
  800.     beq.s    RdWrt_end
  801.  
  802.  
  803.     move.l    IO_UNIT(a1),a3        ; Get unit pointer
  804.     move.l    a1,a2
  805.  
  806. *        check operation for legality
  807.  
  808.     move.l    IO_OFFSET(a2),d0
  809.     move.l    d0,d1
  810.  
  811. *        check for being an even sector boundary
  812.     and.l    #SECTOR-1,d1
  813.     bne    IO_Err
  814.  
  815. *        check for IO within disc range
  816.     add.l    IO_LENGTH(a2),d0
  817.     cmp.l    #RAMSIZE,d0
  818.     bgt    IO_Err
  819.  
  820. * We've gotten this far, it must be a valid request.
  821.  
  822.     IFD    INTRRUPT
  823.     move.l    mdu_SigMask(a3),d0    ; Get signals to wait for
  824.     LINKSYS    Wait,md_SysLib(a6)    ; Wait for interrrupt before proceeding
  825.     ENDC
  826.  
  827.     move.l    IO_OFFSET(a2),d0
  828.     lea    mdu_RAM(a3),a0        ; Point to RAMDISK "sector" for I/O
  829.     add.l    d0,a0            ; Can't use index, "out of range"
  830.     move.l    IO_LENGTH(a2),d0
  831.     move.l    d0,IO_ACTUAL(a2)    ; Indicate we've moved all bytes
  832.     subq.w    #1,d0            ; Adjust byte count for DBRA loop
  833.     move.l    IO_DATA(a2),a1        ; Point to data buffer
  834.  
  835.     move.w    IO_COMMAND(a2),d1    ; Now go to correct loop for
  836.     cmp.b    #CMD_READ,d1        ; Read or Write commands
  837.     BEQ.S    RdLp
  838.  
  839. WrtLp:    move.b    (a1)+,(a0)+        ; Copy a byte
  840.     dbra    d0,WrtLp        ; Move all requested bytes
  841.     bra.s    RdWrt_end
  842.  
  843. RdLp:    move.b    (a0)+,(a1)+        ; Copy a byte
  844.     dbra    d0,RdLp            ; Move all requested bytes
  845.     bra.s    RdWrt_end
  846.  
  847. IO_Err:
  848.     move.b    #IOERR_BADLENGTH,IO_ERROR(a1)
  849.  
  850. RdWrt_end:
  851.     move.l    a2,a1
  852.     bsr    TermIO
  853.     movem.l (sp)+,a2/a3
  854.     rts
  855.  
  856. ;
  857. ; Update and Clear are internal buffering commands.  Update forces all
  858. ; io out to its final resting spot, and does not return until this is
  859. ; done.  Clear invalidates all internal buffers.  Since this device
  860. ; has no internal buffers, these commands do not apply.
  861. ;
  862.  
  863. Update:
  864.     PUTMSG    30,<'%s/Update: called'>
  865.     bra    Invalid
  866. Clear:
  867.     PUTMSG    30,<'%s/Clear: called'>
  868.     bra    Invalid
  869.  
  870. ;
  871. ; the Stop command stop all future io requests from being
  872. ; processed until a Start command is received.  The Stop
  873. ; command is NOT stackable: e.g. no matter how many stops
  874. ; have been issued, it only takes one Start to restart
  875. ; processing.
  876. ;
  877.  
  878. MyStop:
  879.     PUTMSG    30,<'%s/MyStop: called'>
  880.     bset    #MDUB_STOPPED,UNIT_FLAGS(a3)
  881.  
  882.     bsr    TermIO
  883.     rts
  884.     
  885. Start:
  886.     PUTMSG    30,<'%s/Start: called'>
  887.     bsr    InternalStart
  888.  
  889.     move.l    a2,a1
  890.     bsr    TermIO
  891.  
  892.     rts
  893.  
  894. InternalStart:
  895.     ;------ turn processing back on
  896.     bclr    #MDUB_STOPPED,UNIT_FLAGS(a3)
  897.  
  898.     ;------ kick the task to start it moving
  899.     move.l    a3,a1
  900.     CLEAR    d0
  901.     move.l    MP_SIGBIT(a3),d1
  902.     bset    d1,d0
  903.     LINKSYS    Signal,md_SysLib(a3)
  904.  
  905.     rts
  906.  
  907. ;
  908. ; Flush pulls all io requests off the queue and sends them back.
  909. ; We must be careful not to destroy work in progress, and also
  910. ; that we do not let some io requests slip by.
  911. ;
  912. ; Some funny magic goes on with the STOPPED bit in here.  Stop is
  913. ; defined as not being reentrant.  We therefore save the old state
  914. ; of the bit and then restore it later.  This keeps us from
  915. ; needing to DISABLE in flush.  It also fails miserably if someone
  916. ; does a start in the middle of a flush.
  917. ;
  918.  
  919. Flush:
  920.     PUTMSG    30,<'%s/Flush: called'>
  921.     movem.l    d2/a6,-(sp)
  922.  
  923.     move.l    md_SysLib(a6),a6
  924.  
  925.     bset    #MDUB_STOPPED,UNIT_FLAGS(a3)
  926.     sne    d2
  927.  
  928. Flush_Loop:
  929.     move.l    a3,a0
  930.     CALLSYS    GetMsg
  931.  
  932.     tst.l    d0
  933.     beq.s    Flush_End
  934.  
  935.     move.l    d0,a1
  936.     move.b    #IOERR_ABORTED,IO_ERROR(a1)
  937.     CALLSYS    ReplyMsg
  938.  
  939.     bra.s    Flush_Loop
  940.  
  941. Flush_End:
  942.  
  943.     move.l    d2,d0
  944.     movem.l    (sp)+,d2/a6
  945.  
  946.     tst.b    d0
  947.     beq.s    1$
  948.  
  949.     bsr    InternalStart
  950. 1$:
  951.  
  952.     move.l    a2,a1
  953.     bsr    TermIO
  954.  
  955.     rts
  956.  
  957. ;
  958. ; Foo and Bar are two device specific commands that are provided just
  959. ; to show you how to add your own commands.  The currently return that
  960. ; no work was done.
  961. ;
  962.  
  963. Foo:
  964. Bar:
  965.     CLEAR    d0
  966.     move.l    d0,IO_ACTUAL(a1)
  967.  
  968.     bsr    TermIO
  969.     rts
  970.  
  971. ;----------------------------------------------------------------------
  972. ;
  973. ; here begins the process related routines
  974. ;
  975. ; A Process is provided so that queued requests may be processed at
  976. ; a later time.
  977. ;
  978. ;
  979. ; Register Usage
  980. ; ==============
  981. ; a3 -- unit pointer
  982. ; a6 -- syslib pointer
  983. ; a5 -- device pointer
  984. ; a4 -- task (NOT process) pointer
  985. ; d7 -- wait mask
  986. ;
  987. ;----------------------------------------------------------------------
  988.  
  989. ; some dos magic.  A process is started at the first executable address
  990. ; after a segment list.  We hand craft a segment list here.  See the
  991. ; the DOS technical reference if you really need to know more about this.
  992.  
  993.     cnop    0,4            ; long word allign
  994.     DC.L    16            ; segment length -- any number will do
  995. myproc_seglist:
  996.     DC.L    0            ; pointer to next segment
  997.  
  998. ; the next instruction after the segment list is the first executable address
  999.  
  1000. Proc_Begin:
  1001.  
  1002.     move.l    _AbsExecBase,a6
  1003.  
  1004.     ;------ Grab the argument
  1005.     move.l    4(sp),a3        ; Unit pointer
  1006.  
  1007.     move.l    mdu_Device(a3),a5    ; Point to device structure
  1008.  
  1009.     IFD    INTRRUPT
  1010.     ;------ Allocate a signal for "I/O Complete" interrupts
  1011.     moveq    #-1,d0            ; -1 is any signal at all
  1012.     CALLSYS    AllocSignal
  1013.     move.b    d0,mdu_SigBit(A3)    ; Save in unit structure
  1014.  
  1015.     moveq    #0,d7            ; Convert bit number signal mask
  1016.     bset    d0,d7
  1017.     move.l    d7,mdu_SigMask(A3)    ; Save in unit structure
  1018.  
  1019.     lea.l    mdu_is(a3),a1        ; Point to interrupt structure
  1020.     moveq    #3,d0            ; Portia interrupt bit 3
  1021.     CALLSYS AddIntServer        ; Now install the server
  1022.  
  1023.     move.l    md_Base(a5),a0        ; Get board base address
  1024. *    bset.b    #INTENABLE,INTCTRL2(a0)    ; Enable interrupts
  1025.     ENDC
  1026.  
  1027.     ;------ Allocate the right signal
  1028.  
  1029.     moveq    #-1,d0            ; -1 is any signal at all
  1030.     CALLSYS    AllocSignal
  1031.  
  1032.     move.b    d0,MP_SIGBIT(a3)
  1033.     move.b    #PA_SIGNAL,MP_FLAGS(a3)
  1034.  
  1035.     ;------ change the bit number into a mask, and save in d7
  1036.  
  1037.     moveq    #0,d7
  1038.     bset    d0,d7
  1039.  
  1040.     ;------
  1041.     ;------ OK, kids, we are done with initialization.  We now
  1042.     ;------ can start the main loop of the driver.  It goes
  1043.     ;------ like this.  Because we had the port marked PA_IGNORE
  1044.     ;------ for a while (in InitUnit) we jump to the getmsg
  1045.     ;------ code on entry.
  1046.     ;------
  1047.     ;------        wait for a message
  1048.     ;------        lock the device
  1049.     ;------        get a message.  if no message unlock device and loop
  1050.     ;------        dispatch the message
  1051.     ;------        loop back to get a message
  1052.     ;------
  1053.  
  1054.     bra.s    Proc_CheckStatus
  1055.  
  1056.     ;------ main loop: wait for a new message
  1057. Proc_MainLoop:
  1058.     move.l    d7,d0
  1059.     CALLSYS    Wait
  1060.  
  1061. Proc_CheckStatus:
  1062.     ;------ see if we are stopped
  1063.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  1064.     bne.s    Proc_MainLoop        ; device is stopped
  1065.  
  1066.     ;------ lock the device
  1067.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  1068.     bne.s    Proc_MainLoop        ; device in use
  1069.  
  1070.     ;------ get the next request
  1071. Proc_NextMessage:
  1072.     move.l    a3,a0
  1073.     CALLSYS    GetMsg
  1074.     tst.l    d0
  1075.     beq.s    Proc_Unlock        ; no message?
  1076.  
  1077.     ;------ do this request
  1078.     move.l    d0,a1
  1079.     exg    a5,a6            ; put device ptr in right place
  1080.     bsr    PerformIO
  1081.     exg    a5,a6            ; get syslib back in a6
  1082.  
  1083.     bra.s    Proc_NextMessage
  1084.  
  1085.     ;------ no more messages.  back ourselves out.
  1086. Proc_Unlock:
  1087.     and.b    #$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
  1088.     bra    Proc_MainLoop
  1089.  
  1090. ;
  1091. ; Here is a dummy interrupt handler, with some crucial components commented
  1092. ; out.  If the IFD INTRRUPT is enabled, this code will cause the device to
  1093. ; wait for a level two interrupt before it will process each request
  1094. ; (pressing a key on the keyboard will do it).  This code is normally
  1095. ; disabled, and must fake or omit certain operations since there  isn't
  1096. ; really any hardware for this driver.  Similiar code has been used
  1097. ; successfully in other, "REAL" device drivers.
  1098. ;
  1099.  
  1100.     IFD    INTRRUPT
  1101. ;    A1 should be pointing to the unit structure upon entry!
  1102.  
  1103. myintr:        move.l    mdu_Device(a1),a0    ; Get device pointer
  1104.         move.l    md_SysLib(a0),a6    ; Get pointer to system
  1105.         move.l    md_Base(a0),a0        ; point to board base address
  1106. *        btst.b    #IAMPULLING,INTCTRL1(a0);See if I'm interrupting
  1107. *        beq.s    myexnm            ; if not set, exit, not mine
  1108. *        move.b    #0,INTACK(a0)        ; toggle controller's int2 bit
  1109.  
  1110. ;        ------ signal the task that an interrupt has occured
  1111.  
  1112.         move.l    mdu_SigMask(a1),d0
  1113.         lea    mdu_tcb(a1),a1
  1114.         CALLSYS    Signal
  1115.  
  1116. ;
  1117. ;        now clear the zero condition code so that
  1118. ;        the interrupt handler doesn't call the next
  1119. ;        interrupt server.
  1120. ;
  1121. *        moveq    #1,d0            clear zero flag
  1122. *        bra.s    myexit            now exit
  1123. ;
  1124. ;        this exit point sets the zero condition code
  1125. ;        so the interrupt handler will try the next server
  1126. ;        in the interrupt chain
  1127. ;
  1128. myexnm        moveq    #0,d0            set zero condition code
  1129. ;
  1130. myexit        rts
  1131.     ENDC
  1132.  
  1133. mdu_Init:
  1134. ;    ------ Initialize the device
  1135.  
  1136.     INITBYTE    MP_FLAGS,PA_IGNORE
  1137.     INITBYTE    LN_TYPE,NT_DEVICE
  1138.     INITLONG    LN_NAME,myName
  1139.     INITBYTE    mdu_Msg+LN_TYPE,NT_MSGPORT;Unit starts with MsgPort
  1140.     INITLONG    mdu_Msg+LN_NAME,myName        
  1141.     INITLONG    mdu_tcb+LN_NAME,myName
  1142.     INITBYTE    mdu_tcb+LN_TYPE,NT_TASK
  1143.     INITBYTE    mdu_tcb+LN_PRI,5
  1144.     INITBYTE    mdu_is+LN_PRI,4        ; Int priority 4
  1145.     IFD    INTRRUPT
  1146.     INITLONG    mdu_is+IS_CODE,myintr    ; Interrupt routine addr
  1147.     ENDC
  1148.     INITLONG    mdu_is+LN_NAME,myName
  1149.     DC.L    0
  1150.  
  1151. mdn_Init:
  1152. *    ;------ Initialize packet for MakeDosNode
  1153.  
  1154.     INITLONG    mdn_execName,myName    ; Address of driver name
  1155.     INITLONG    mdn_tableSize,11    ; # long words in AmigaDOS env.
  1156.     INITLONG    mdn_dName,$52414d00    ; Store 'RAM' in name
  1157.     INITLONG    mdn_sizeBlock,128    ; # longwords in a block
  1158.     INITLONG    mdn_numHeads,1        ; RAM disk has only one "head"
  1159.     INITLONG    mdn_secsPerBlk,1    ; secs/logical block, must = "1"
  1160.     INITLONG    mdn_blkTrack,10        ; secs/track (must be reasonable)
  1161.     INITLONG    mdn_resBlks,1        ; reserved blocks, MUST > 0!
  1162.     INITLONG    mdn_upperCyl,(RAMSIZE/5120)-1; upper cylinder
  1163.     INITLONG    mdn_numBuffers,1    ; # AmigaDOS buffers to start
  1164.     DC.L    0
  1165.  
  1166. ;----------------------------------------------------------------------
  1167. ; EndCode is a marker that show the end of your code.
  1168. ; Make sure it does not span sections nor is before the
  1169. ; rom tag in memory!  It is ok to put it right after
  1170. ; the rom tag -- that way you are always safe.  I put
  1171. ; it here because it happens to be the "right" thing
  1172. ; to do, and I know that it is safe in this case.
  1173. ;----------------------------------------------------------------------
  1174. EndCode:
  1175.  
  1176.     END
  1177.