
;/************************************************************************/
;/*                                                                      */
;/* Driver Name: IBM2SCSI.ADD - Adapter Driver for ABIOS SCB Devices     */
;/*              ---------------------------------------------------     */
;/*                                                                      */
;/* Source File Name: IO.ASM                                             */
;/*                                                                      */
;/* Descriptive Name: I/O routines for accessing the adpaters.           */
;/*                                                                      */
;/* Function:                                                            */
;/*                                                                      */
;/*                                                                      */
;/*----------------------------------------------------------------------*/
;/*                                                                      */
;/* Copyright (C) 1992 IBM Corporation                                   */
;/*                                                                      */
;/* DISCLAIMER OF WARRANTIES.  The following [enclosed] code is          */
;/* provided to you solely for the purpose of assisting you in           */
;/* the development of your applications. The code is provided           */
;/* "AS IS", without warranty of any kind. IBM shall not be liable       */
;/* for any damages arising out of your use of this code, even if        */
;/* they have been advised of the possibility of such damages.           */
;/*                                                                      */
;/*----------------------------------------------------------------------*/
;/*                                                                      */
;/* Change Log                                                           */
;/*                                                                      */
;/* Mark    Date      Programmer  Comment                                */
;/* ----    ----      ----------  -------                                */
;/* @nnnn   mm/dd/yy  NNN                                                */
;/*                                                                      */
;/* @0415   04/15/96  Andy Mcneill - changed ResetToggle routine to      */
;/*                   improve error recovery by determining if the       */
;/*                   adapter is completely broken (or POS has been      */
;/*                   corrupted) and returning status to the caller.     */
;/*                   Also, unmask interrupt level 14 in case it was     */
;/*                   masked by the spurious interrupt handler.  These   */
;/*                   changes were made as a result of extensive debug   */
;/*                   on a problem related to noise on the Micro Channel */
;/*                   reset signal in a system driving an SSD Optical    */
;/*                   Library system.                                    */
;/*                                                                      */
;/************************************************************************/

;
; Some local equates

BPTR    EQU     <byte ptr>
WPTR    EQU     <word ptr>
DPTR    EQU     <dword ptr>

CIR0    EQU     0   ; command interface register 0
CIR1    EQU     1   ; command interface register 1
CIR2    EQU     2   ; command interface register 2
CIR3    EQU     3   ; command interface register 3
AR      EQU     4   ; attention register
BCR     EQU     5   ; basic control reg
ISR     EQU     6   ; interrupt status register
BSR     EQU     7   ; basic status register

LONGSCB EQU   40h   ; long SCB attention request code
NORMSCB EQU   30h   ; normal SCB attention request code
IMMDCMD EQU   10h   ; immediate command



WAIT400 EQU    27   ; 27 15.625 intervals for 400 us wait (a little longer)
WAIT50  EQU     4   ; 4 intervals for 50 us wait



BUSY    EQU   01h   ; busy bit




        public  __StartIO
        public  _ReadReg
        public  _WriteReg
        public  _WriteAttn
        public  _ResetToggle


_TEXT   segment word public 'code'
        assume  cs:_TEXT

.286p


;/*************************************************************************/
;/*                                                                       */
;/* StartIO - Start an SCB to the specified device.                       */
;/*                                                                       */
;/*    This routine will start the SCB on the device passed.              */
;/*                                                                       */
;/*       Entry: StartSCB(USHORT IOAddr,ULONG ppSCB, USHORT cmd);         */
;/*                                                                       */
;/*                 IOAddr - I/O base address of the card                 */
;/*                 ppSCB  - phys ptr to SCB or the immediate command     */
;/*                 cmd    - command to write to the attention register.  */
;/*                          It includes the logical device number and    */
;/*                          the request code.  Only the low byte is used */
;/*                                                                       */
;/*       Exit:  AX = 0 if no error, <> 0 if busy never cleared.          */
;/*              ints disabled if started (AX = 0)                        */
;/*                                                                       */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr      EQU     <(WPTR [bp+4])>      ; IO base address
ppSCB       EQU     <(DPTR [bp+6])>      ; physical ptr to the SCB
cmd         EQU     <(WPTR [bp+10])>     ; command to the adapter.


__StartIO       proc    near

        enter   0,0

;
;       First wait for busy to be cleared

        push    dx
        push    cx

        mov     cx, WAIT400     ; 400 us wait
        mov     dx, IOAddr      ; card IO address
        call    wbsy            ; go wait

        jc      bsy             ; card is busy, something wrong

;
;       Card is not busy and ints are off now.  Program the Command interface
;       registers with the SCB address.

.386p
        mov     eax, ppSCB      ; get the physical address
        mov     cx, 4           ; loop 4 times
outl:   out     dx, al          ; out the low byte
        inc     dx              ; next CIR
        shr     eax, 8          ; shift next byte into al
        loop    outl            ; go loop

.286p

;
;       Now get the command code from the stack and write the low byte to
;       the attention register.

        mov     ax, cmd         ; get logical device number from stack
        mov     dx, IOAddr      ; get base I/O address
        add     dx, AR          ; attention register offset
        out     dx, al          ; start the I/0
        mov     ax, 0           ; no error
        jmp     exit            ; leave

bsy:    mov     ax, 1           ; signal some error

exit:   pop     cx              ; restore stack
        pop     dx

        leave
        ret

__StartIO       endp


;/*************************************************************************/
;/*                                                                       */
;/* ReadReg - read the specified adapter register.                        */
;/*                                                                       */
;/*    This routine reads the register pointed to by IOAddr and returns   */
;/*    the value in AX.                                                   */
;/*                                                                       */
;/*       Entry: ReadReg(USHORT IOAddr);                                  */
;/*                                                                       */
;/*       Exit:  AL = register value                                      */
;/*                                                                       */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr      EQU     <(WPTR [bp+4])>      ; IO reg address

_ReadReg  proc  near

        enter   0,0

        push    dx             ; save DX
        mov     dx, IOAddr     ; get register address
        in      al, dx         ; read it
        pop     dx             ; restore dx
        leave                  ; clear stack frame
        ret                    ; return to caller

_ReadReg  endp


;/*************************************************************************/
;/*                                                                       */
;/* WriteReg - write adapter register.                                    */
;/*                                                                       */
;/*    This routine writes the low byte of the value on the stack to the  */
;/*    specified register.  This should not be used for writing to the    */
;/*    attention register.                                                */
;/*                                                                       */
;/*       Entry: WriteReg(USHORT IOAddr, USHORT val);                     */
;/*                                                                       */
;/*       Exit:  None.                                                    */
;/*                                                                       */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr      EQU     <(WPTR [bp+4])>      ; IO reg address
val         EQU     <(WPTR [bp+6])>      ; value

_WriteReg  proc  near

        enter   0,0

        push    dx             ; save DX
        push    ax             ; save AX
        mov     ax, val        ; get value to write out (low byte only)
        mov     dx, IOAddr     ; get register address
        out     dx, al         ; write the low byte out
        pop     ax             ; restore ax
        pop     dx             ; restore dx
        leave                  ; clear stack frame
        ret                    ; return to caller

_WriteReg  endp


;/*************************************************************************/
;/*                                                                       */
;/* WriteAttn - write the attention register.                             */
;/*                                                                       */
;/*    This routine writes the low byte of the value on the stack to the  */
;/*    attention register of the specified adapter.  It checks to make    */
;/*    sure that busy is clear before doing the write.                    */
;/*                                                                       */
;/*       Entry: WriteReg(USHORT IOAddr, USHORT val);                     */
;/*                                                                       */
;/*       Exit:  0 = no error, <>0 = error                                */
;/*                                                                       */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr      EQU     <(WPTR [bp+4])>      ; base IO address
val         EQU     <(WPTR [bp+6])>      ; value

_WriteAttn  proc  near

        enter   0,0

;
;       First wait for busy to be cleared

        push    dx
        push    cx

        mov     cx, WAIT400     ; 400 us wait
        mov     dx, IOAddr      ; card IO address
        call    wbsy            ; go wait

        jc      hosed           ; card is busy, something wrong

        mov     ax, val         ; get value to write out (low byte only)
        mov     dx, IOAddr      ; get the base IO address
        add     dx, AR          ; get to the attention register
        out     dx, al          ; write the low byte out
        mov     ax, 0           ; no error
        jmp     allok

hosed:  mov     ax, 1           ; error

allok:  pop     cx              ; restore ax
        pop     dx              ; restore dx
        sti
        leave                   ; clear stack frame
        ret                     ; return to caller

_WriteAttn  endp

;------ wbsy ---------------------------------------------------------------
; fixed time wait routine (memory refresh output used as reference)
;  as well as the busy indicator in the BSR
;
; entry:
;       cx      - count of 15.625 microsecond intervals to wait
;       dx      - base port value
;
; exit:
;       CFL     - 1, card busy, IFL unchanged
;                 0, card is idle and IFL disabled
;       cx      - undefined
;       INTs disabled
;---------------------------------------------------------------------------
wbsy    proc    near                    ;
        push    ax                      ; save reg
        push    dx                      ; save port base
        add     dx,BSR
wbsy1:                                  ;
        pushf                           ; save current IFL
        cli                             ; disable ints
        in      al,dx                   ; eyeball BSR for not busy condition
        test    al,BUSY                 ; is card still busy?
        jz      not_bsy                 ; nope - exit loop w/ IFL=0, CFL=0
        popf                            ; restore IFL
        in      al,61h                  ; read current refresh request output
        and     al,10h                  ; mask for refresh request bit
        cmp     al,ah                   ; did it just change
        je      wbsy1                   ; wait for a change
        mov     ah,al                   ; save new state
        loop    wbsy1                   ; decrement half cycles till count end
        stc                             ; time expired - CFL set means error
        jmp     short wb1               ; exit

not_bsy:                                ; (CFL=0)
        pop     ax                      ; balance stack (throw away flags)
wb1:
        pop     dx                      ; restore base port
        pop     ax                      ; restore ax
        ret                             ;  exit - CFL is clear

wbsy    endp


;/*************************************************************************/
;/*                                                                       */
;/* _ResetToggle                                                          */
;/*                                                                       */
;/*    This routine will reset the specified adapter by toggling the      */
;/*    sub-system reset bit in the basic control register.                */
;/*                                                                       */
;/*       Entry: _ResetToggle (USHORT baseIO)                             */
;/*                                                                       */
;/*       Exit:  0 = no error, <>0 = adapter hardware or POS reg error    */
;/*                                                                       */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


baseIO      EQU     <(WPTR [bp+4])>      ; base IO address

_ResetToggle  proc  near

        enter   0,0

;
;       Get current BCR value, mask some bits off and save it

        mov     dx, baseIO              ; base IO
        add     dx, BCR                 ; pt to basic control reg
        in      al, dx                  ; get current value
        and     al, 0fh                 ; mask off high nibble
        push    ax                      ; save value

;
;       Now write the reset bit on, wait 50 us and then turn it off.
;       Use memory refresh as a counter.

        mov     al, 80h
        out     dx, al                  ; reset bit on
        mov     cx, 4                   ; wait at least 50 us

@@:                                     ;
        in      al,61h                  ; read current refresh request output
        and     al,10h                  ; mask for refresh request bit
        cmp     al,ah                   ; did it just change
        je      @b                      ; wait for a change
        mov     ah,al                   ; save new state
        loop    @b                      ; decrement half cycles till count end

        mov     al, 00h
        out     dx, al                  ; now turn it off

;
;       Now program it back to the original value, minus a few bits.

        pop     ax                      ; original value

;@0415 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        or      al,03h                  ; make sure interrupts and bus master is enabled
;@0415 ----------------------------------------------------------------------
        out     dx, al                  ; write it back

;@0415 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        mov     ah,al                   ; save for compare later

        in      al,0a1h                 ; make sure system interrupt level 14
        and     al,0bfh                 ; is not masked off
        out     0a1h,al

        in      al,dx                   ; make sure BCR is OK
        sub     al,ah                   ; return code <> 0 if bad
        cbw
;@0415 ----------------------------------------------------------------------

        leave
        ret

_ResetToggle  endp


_TEXT   ends
        end
