; eject.asm
;   eject a tape (or CD) from a device
;
; written on Tue  01-17-1995  by Ed Beroset
; and placed into the public domain by the author
;
; note that this is a VERY terse code sample!  It is assumed that the
; reader is already familiar with SCSI and ASPI to some degree.
;
        IDEAL
        MODEL small
        STACK 1000h

;/****************************************************************************
;*                               structures                                  *
;****************************************************************************/
STRUC SCSIRequestBlock
        CommandCode     db      2               ; SCSI request
        Status          db      0               ; returned after command
        HostAdapterNum  db      0               ; default is 0 (first adapter)
        SCSIReqFlags    db      0               ;
        Reserved1       db      4 dup (0)       ;
        TargetID        db      0               ; set to device target ID
        LUN             db      0               ; defaults to 0
        DataLength      dd      0               ;
        SenseLength     db      16              ; usu. sufficient length
        DataPointer     dd      0               ; no data
        SRBLinkPointer  dd      0               ; no linking
        CDBLength       db      10              ; always sufficient length
        AdapterStatus   db      0               ;
        TargetStatus    db      0               ;
        PostRoutinePtr  dd      0               ; no post routine is default
        ASPIWorkspace   db      34 dup (0)      ; req'd but not used
        CDB             db      10 dup (0)      ; SCSI Command Descriptor Blk
        SenseData       db      16 dup (0)      ;
ENDS  SCSIRequestBlock

        DATASEG

DOS_OPEN_HANDLE  = 03dh
DOS_CLOSE_HANDLE = 03eh
DOS_IOCTL        = 044h
IOCTL_RX_CTL_DATA = 02h
DOS_INT         = 21h

ASPI_Entry      dd      ?
SRB SCSIRequestBlock   <>
crlf    equ   13,10
ErrMsg          db      "ERROR:  no ASPI manager detected. ",crlf,'$'
OKMsg           db      "All is well.",crlf,'$'
SCSIMgrString   db      "SCSIMGR$",0

        CODESEG
PROC    begin
        STARTUPCODE
        call    GetASPIAddress          ;
        jnb     @@AllOK                 ;
        mov     dx, OFFSET ErrMsg       ;
        jmp     @@ErrorExit             ;
@@AllOK:
;
; here's the eject sequence
;
        mov     [(SRB.CDB) + 0],01Bh    ; load/unload command
        mov     [(SRB.CDB) + 4],0       ; 00 = unload, 01=load, 02=retension
        mov     [(SRB.TargetID)], 2     ; SCSI ID of target device
        push    SEG SRB                 ;
        push    OFFSET SRB              ;
        call    [ASPI_Entry]            ; sometimes it takes 2 requests
        call    [ASPI_Entry]            ;
        add     sp,4                    ;
        mov     dx,OFFSET OKMsg         ;
        xor     ah,ah                   ;
        mov     al,[(SRB.Status)]       ;
@@ErrorExit:                            ; ds:dx ==> ASCIIZ error string
        push    ax
        mov     ah,9                    ;
        int     21h                     ;
        pop     ax                      ;
@@NoError:
        EXITCODE
ENDP    begin

PROC    GetASPIAddress C
        USES    bx, cx, ds              ;
        xor     ax,ax                   ;
        mov     [WORD ASPI_Entry],ax    ;
        mov     [WORD ASPI_Entry+2],ax  ;
        lea     dx,[SCSIMgrString]      ;ds:dx ==> 'SCSIMGR' string
        mov     ah,DOS_OPEN_HANDLE      ; open request
        int     DOS_INT                 ;
        jb      @@exit                  ;
        mov     dx,ss                   ;
        mov     ds,dx                   ;
        lea     dx,[ASPI_Entry]         ;
        mov     cx,4                    ;
        mov     bx,ax                   ;
        mov     ax,DOS_IOCTL SHL 8 OR IOCTL_RX_CTL_DATA
        int     DOS_INT                 ;
        jb      @@exit                  ;
        mov     ah,DOS_CLOSE_HANDLE     ;
        int     DOS_INT                 ;
@@exit:
        ret                             ;
ENDP    GetASPIAddress

        END
-+--- eject.asm ends -----

A few notes are probably in order here.  First, if you're already familiar
with the ASPI and SCSI stuff, it may be sufficient to tell you that the key
part to this is to send the SCSI load/unload command (a 6-byte sequence) to
the target device.  Here are the bytes of that command:

1B 00 00 00 0? 00

The 0? can be 00 (unload) or 01 (load), 02 (unload with tape retension), or 03
(load with tape retension).  Note that some devices will allow either 00 or
02, but some (like my CD-ROM) only accept one form.

Also, you may wonder why the call [ASPI_Entry] appears twice.   This is
because some devices, in my experience, will essentially ignore the first
request even though they return with a status that indicates all was well!
The double call is a workaround for this problem and won't adversely affect
anything (at least any device I've worked with) even if the first command is
heeded.  Of course, in a real application, you'd add more error checking to
all of this...

-> Ed <-

