PAGE    60,132

TITLE   "NETIO" -- NETWORK I/O DRIVER
CSEG    SEGMENT PARA PUBLIC 'CODE'
        ASSUME CS:CSEG,SS:STACK

;------------------------------------------------;
;       DRIVER HEADER                            ;
;------------------------------------------------;
                DD      -1             ; Ptr to next device driver
                DW      1100000000000000B ; Attribute byte, char dev.
                DW      OFFSET STRATEGY ; OFFSET to STRATEGY routine
                DW      OFFSET IRPT    ; OFFSET to INTERRUPT routine
                DB      'NETIO   '     ; Character device name

RH_PTR  LABEL   DWORD                  ; Storage of PTR to request header
RH_PTRO DW      0                      ; OFFSET (filled by strat. rout)
RH_PTRS DW      0                      ; SEGMENT  "

;------------------------------------------------;
;       REQUEST HEADER, COMMON PORTION           ;
;------------------------------------------------;
RH              EQU     DS:[BX]

RHC             STRUC
RHC_LEN         DB      ?              ; Length of request header + data
                DB      ?              ; unit code (Ignored)
RHC_CMD         DB      ?              ; Command code
RHC_STA         DW      ?              ; Status word
                DQ      ?              ; Reserved for DOS
RHC             ENDS

;------------------------------------------------;
;       REQUEST HEADER FOR INIT COMMAND          ;
;------------------------------------------------;
RH0             STRUC
                DB      (TYPE RHC) DUP (?)      ;Common portion
RH0_NUM         DB      ?              ; Number of units (Ignored)
RH0_ENDO        DW      ?              ; OFFSET of ending address of res. code
RH0_ENDS        DW      ?              ; SEGMENT of ending address of res. code
RH0_BPB         DD      ?              ; Address of BPB (Ignored)
RH0_DRIV        DB      ?              ; Drive code (Ignored)
RH0             ENDS

;------------------------------------------------;
;       REQUEST HEADER FOR I/O                   ;
;------------------------------------------------;
RH1             STRUC
                DB      (TYPE RHC) DUP (?)      ;Common portion
                DB      ?              ; Media descriptor byte (Ignored)
RH1_BUFO        DW      ?              ; OFFSET to buffer address
RH1_BUFS        DW      ?              ; SEGMENT of buffer address
RH1_CNT         DB      ?              ; Bytes transferred count
RH1             ENDS

RH1_BUF         EQU     DWORD PTR RH1_BUFO ; OFFSET/SEG of buffer

;------------------------------------------------;
;       REQUEST HEADER FOR INPUT NO WAIT         ;
;------------------------------------------------;
RH2             STRUC
                DB      (TYPE RHC) DUP (?)      ;Common portion
RH2_DATA        DB      ?              ; Data from driver service routine
RH2             ENDS


;------------------------------------------------;
;       FLAGS FOR REQUEST HEADER STATUS WORD     ;
;------------------------------------------------;
STAT_DONE       EQU     01H            ; Done flag
STAT_CMDERR     EQU     8003H          ; Command error flag
STAT_BUSY       EQU     0200H          ; Busy -- I/O in progress

CTRL_C          EQU     03H
BELL            EQU     07H
CR              EQU     0DH
LF              EQU     0AH

;------------------------------------------------;
; NETWORK CONTROL BLOCK ( GENERIC )              ;
;------------------------------------------------;
NCB             EQU     $
NCB_COMMAND     DB      00H               ; NCB Command field
NCB_RETCODE     DB      00H               ; NCB Return code
NCB_LSN         DB      00H               ; NCB Local session number
NCB_NUM         DB      00H               ; NCB Number of your name
NCB_BUFFER@     DW      OFFSET CHAR       ; NCB Ptr to message buffer
                DW      CSEG
NCB_LENGTH      DW      0001H             ; NCB Message buffer length
NCB_CALLNAME    DB      'HUNTER THOMPSON '; NCB Name on remote adapter
NCB_NAME        DB      'RAOUL DUKE      '; NCB Name on local adapter
NCB_RTO         DB      00H               ; NCB Receive timeout
NCB_STO         DB      00H               ; NCB Send timeout
NCB_POST@       DD      00000000H         ; NCB Ptr to POST routine
NCB_LANA_NUM    DB      00H               ; NCB Adapter number (00H = 1st Adapter)
NCB_CMD_CPLT    DB      00H               ; NCB Command status
NCB_RESERVE     DB      14 DUP (0)        ; Reserved

CHAR            DB      0              ; Our 1 Character 'BUFFER'
INSTALLED       DB      0              ; Network session started flag
BUFF            DB      16 DUP(0)
BUFF_STAT       DB      0
NEXT_TO_READ    DW      0
NEXT_TO_ADD     DW      0
BUFF_PTR        DW      0
OUT_BUFF        DB      64 DUP(0)

;------------------------------------------------;
;       STRATEGY ROUTINE, STORES HEADER ADDRESS  ;
;------------------------------------------------;
STRATEGY        PROC    FAR
                MOV     CS:RH_PTRS,ES
                MOV     CS:RH_PTRO,BX
                RET
STRATEGY        ENDP

;------------------------------------------------;
;       JUMP TABLE FOR PROCESSING DRIVER COMMANDS;
;------------------------------------------------;
CMD_TABLE       LABEL   WORD
                DW      OFFSET INIT             ; 0 - Initialization
                DW      OFFSET MEDIA_CHECK      ; 1 - Media Check
                DW      OFFSET BLD_BPB          ; 2 - Build BPB
                DW      OFFSET INPUT_IOCTL      ; 3 - IOCTL Input
                DW      OFFSET INPUT            ; 4 - Input
                DW      OFFSET INPUT_NDINW      ; 5 - Non-Destr. Input
                DW      OFFSET INPUT_STATUS     ; 6 - Input Status
                DW      OFFSET INPUT_FLUSH      ; 7 - Input Flush
                DW      OFFSET OUTPUT           ; 8 - Output
                DW      OFFSET OUTPUT_VER       ; 9 - Output w/Verify
                DW      OFFSET OUTPUT_STATUS    ;10 - Output Status
                DW      OFFSET OUTPUT_FLUSH     ;11 - Output Flush
                DW      OFFSET OUTPUT_IOCTL     ;12 - IOCTL Output
                DW      OFFSET DEVICE_OPEN      ;13 - Device OPEN
                DW      OFFSET DEVICE_CLOSE     ;14 - Device CLOSE
MAX_CMD         EQU     ($ - CMD_TABLE)/2       ;Highest Valid Command
                DW      OFFSET REMOVABLE_MED    ;15 - Removeable Media

;------------------------------------------------;
;       DEVICE INTERRUPT ENTRY POINT             ;
;------------------------------------------------;
IRPT            PROC    FAR
                PUSH    DS             ; Save registers
                PUSH    ES
                PUSH    AX
                PUSH    BX
                PUSH    CX
                PUSH    DX
                PUSH    DI
                PUSH    SI

                CLD                    ; All moves forward
                LDS     BX,CS:RH_PTR   ; Get req header address
                MOV     AL,RH.RHC_CMD  ; Command code from request header
                CBW
                CMP     AL,MAX_CMD     ; Valid command?
                JA      IRPT_CMD_HIGH  ;  Jump to error routine
                MOV     DI,OFFSET IRPT_CMD_EXIT ; Return addr from cmd proc.
                PUSH    DI             ; Save it
                ADD     AX,AX          ; Double command for jump table
                MOV     DI,AX          ; Move into index register
                XOR     AX,AX
                JMP     CS:CMD_TABLE[DI] ; Go to proper processing rout.

IRPT_CMD_ERROR:                        ; Called for unsupported commands
MEDIA_CHECK:
BLD_BPB:
DEVICE_OPEN:
DEVICE_CLOSE:
REMOVABLE_MED:
                POP     AX             ; POP Off return address
IRPT_CMD_HIGH:
                MOV     AX,STAT_CMDERR ; Set Command error flag in status
IRPT_CMD_EXIT:
                LDS     BX,CS:RH_PTR   ; Restore DS:BX as req head. ptr
                OR      AH,STAT_DONE   ; Set DONE flag
                MOV     RH.RHC_STA,AX  ; Store STATUS in header
                POP     SI             ; Restore Registers
                POP     DI
                POP     DX
                POP     CX
                POP     BX
                POP     AX
                POP     ES
                POP     DS
                RET
IRPT            ENDP
;------------------------------------------------;
;       COMMAND HANDLER ROUTINES                 ;
;------------------------------------------------;
INPUT           PROC
                PUSH    ES
                PUSH    BX             ; Store OFFSET of header
                CMP     INSTALLED,00H  ; Is NET session started?
                JNE     INPUT0
                CALL    INSTALL

INPUT0:         CMP     BUFF_STAT,0
                JE      INPUT0
                MOV     DI,NEXT_TO_READ
                MOV     AL,BUFF[DI]
                INC     DI
                CMP     DI,0FH
                JLE     INPUT1
                XOR     DI,DI
INPUT1:         MOV     NEXT_TO_READ,DI
                DEC     BUFF_STAT
                POP     BX
                POP     ES
                MOV     DI,RH.RH1_BUFO
                MOV     ES,RH.RH1_BUFS
                MOV     ES:[DI],AL
                MOV     RH.RH1_CNT,01H
                XOR     AX,AX
                RET
INPUT           ENDP

INPUT_STATUS    PROC
                CMP     BUFF_STAT,0H
                JE      BUFF_EMPTY
                XOR     AX,AX
                RET
BUFF_EMPTY:     MOV     AX,STAT_BUSY
                RET
INPUT_STATUS    ENDP

INPUT_NDINW     PROC
                PUSH    ES
                PUSH    BX             ; Store OFFSET of header
                CMP     INSTALLED,00H  ; Is NET session started?
                JNE     NDINW0
                CALL    INSTALL

NDINW0:         POP     BX
                POP     ES

                CMP     BUFF_PTR,00H   ; Is output buffer empty?
                JE      NDINW1

                MOV     DI,BUFF_PTR    ; if not, empty it...
                MOV     NCB_BUFFER@+2,CS
                MOV     NCB_BUFFER@,OFFSET OUT_BUFF
                MOV     NCB_COMMAND,014H
                MOV     NCB_LENGTH,DI
                MOV     BX,CS
                MOV     ES,BX
                MOV     BX,OFFSET NCB
                INT     5CH
                MOV     BUFF_PTR,00H   ; now buffer is empty
                CMP     NCB_RETCODE,00H
                JE      NDINW1
                JMP     IRPT_CMD_ERROR ; if anything went wrong, abort

NDINW1:         MOV     DI,NEXT_TO_READ ; get next char in buffer
                MOV     AL,BUFF[DI]
                MOV     RH.RH2_DATA,AL ; return it via req header
                CMP     BUFF_STAT,0H
                JE      NWBUFF_EMPTY

NDINW_LOOP:     CMP     AL,CTRL_C      ; scan active buffer for ^C
                JNE     NDINW2
                MOV     BUFF_PTR,04H   ; got one!
                MOV     OUT_BUFF,'^'   ; print ^C<CR><LF> on remote
                MOV     OUT_BUFF+1,'C'
                MOV     OUT_BUFF+2,CR
                MOV     OUT_BUFF+3,LF
                CALL    INPUT_FLUSH    ; flush input buffer
                INT     23H            ; tell DOS

NDINW2:         INC     DI             ; get next char in active buffer
                CMP     DI,0FH         ; check buffer rollover
                JLE     NDINW3
                XOR     DI,DI
NDINW3:         CMP     DI,NEXT_TO_ADD ; check end of active buffer
                JE      NDINW_EXIT
                MOV     AL,BUFF[DI]
                JMP     NDINW_LOOP

NDINW_EXIT:     XOR     AX,AX          ; clear error flag for return
                RET

NWBUFF_EMPTY:   MOV     AX,STAT_BUSY   ; set buffer empty flag
                RET
INPUT_NDINW     ENDP

INPUT_FLUSH     PROC
                MOV     BUFF_STAT,0H   ; 0 chars in buffer
                MOV     NEXT_TO_READ,0H ; offset for reading buffer
                MOV     NEXT_TO_ADD,0H ; offset for adding to buffer
                RET
INPUT_FLUSH     ENDP

INPUT_IOCTL     PROC
                RET
INPUT_IOCTL     ENDP

OUTPUT_IOCTL    PROC
                RET
OUTPUT_IOCTL    ENDP

OUTPUT          PROC
OUTPUT_VER:
                MOV     RH.RH1_CNT,01H
                MOV     DI,RH.RH1_BUFO ; Load DTA OFFSET
                MOV     ES,RH.RH1_BUFS ; Load DTA SEGMENT
                CMP     INSTALLED,0H   ; Is NETWORK session on?
                JNE     NET_OUT
                CALL    INSTALL        ;  If not, wait for CALL

NET_OUT:
                MOV     AL,ES:[DI]     ; Get char to send
                MOV     DI,BUFF_PTR    ; Put into output buffer
                MOV     OUT_BUFF[DI],AL
                INC     DI             ; Update output buffer pointer
                CMP     DI,040H        ; check for output buffer full
                JNE     OUT_EXIT

                MOV     DI,00H         ; it's full, send it on its way
                MOV     NCB_BUFFER@+2,CS
                MOV     NCB_BUFFER@,OFFSET OUT_BUFF
                MOV     NCB_LENGTH,040H
                MOV     NCB_COMMAND,014H
                MOV     AX,CS
                MOV     ES,AX
                MOV     BX,OFFSET NCB
                INT     5CH
                CMP     NCB_RETCODE,00H
                JE      OUT_EXIT
                JMP     IRPT_CMD_ERROR

OUT_EXIT:       XOR     AX,AX          ; Clear error code
                MOV     BUFF_PTR,DI
                RET
OUTPUT          ENDP

OUTPUT_STATUS   PROC
                RET
OUTPUT_STATUS   ENDP

OUTPUT_FLUSH    PROC
                RET
OUTPUT_FLUSH    ENDP

;------------------------------------------------;
;      DO A NETBIOS "ADD NAME"                   ;
;------------------------------------------------;
INSTALL         PROC
                MOV     BX,CS
                MOV     ES,BX
                MOV     NCB_COMMAND,030H ; ADDNAME Command code
                MOV     BX,OFFSET NCB   ; ES:BX points to NCB
                INT     5CH             ; NETWORK function call

;------------------------------------------------;
;      DO A NETBIOS "LISTEN"                     ;
;------------------------------------------------;
LISTEN:
                MOV     NCB_COMMAND,011H ; LISTEN Command code
                INT     5CH            ; NETWORK function call
                MOV     INSTALLED,01H
POST_REC:       MOV     NCB_COMMAND,095H
                MOV     NCB_POST@,OFFSET POST
                MOV     NCB_POST@+2,CS
                INT     5CH
                JMP     INST_EXIT
INST_ERR_EXIT:  POP     AX
INST_EXIT:
                RET
INSTALL         ENDP

INIT            PROC
                MOV     RH.RH0_ENDS,CS
                MOV     RH.RH0_ENDO,OFFSET HELLO
                RET
INIT            ENDP

POST            PROC    FAR
                PUSH    DI
                PUSH    AX
                PUSH    BX
                PUSH    CX
                PUSH    DX
                CLI

                CMP     CHAR,0FFH      ; Is session over?
                JNE     NORMAL_POST

                MOV     CHAR,00H       ; Yes, so reset everything
                MOV     NCB_COMMAND,012H
                MOV     BX,CS
                MOV     ES,BX
                MOV     BX,OFFSET NCB
                INT     5CH            ; RESET adapter
                MOV     INSTALLED,00H
                POP     DX
                POP     CX
                POP     BX
                POP     AX
                POP     DI
                IRET

NORMAL_POST:    CMP     BUFF_STAT,0FH  ; Is input buffer full?
                JL      XFER
                STI
                JMP     XEXIT

XFER:           MOV     DL,CHAR        ; transfer char to input buffer
                MOV     DI,NEXT_TO_ADD
                MOV     BUFF[DI],DL
                INC     DI             ; update next_to_add pointer
                CMP     DI,0FH         ; check for buffer rollover
                JLE     XFER2
                XOR     DI,DI
XFER2:          MOV     NEXT_TO_ADD,DI ; restore pointer
                INC     BUFF_STAT      ; increment # of chars in buffer

                MOV     AX,CS          ; POST another receive...
                MOV     ES,AX
                MOV     BX,OFFSET NCB
                MOV     NCB_BUFFER@,OFFSET CHAR
                MOV     NCB_BUFFER@+2,AX
                MOV     NCB_COMMAND,095H
                MOV     NCB_POST@,OFFSET POST
                MOV     NCB_POST@+2,AX
                STI
                INT     5CH
XEXIT:          POP     DX
                POP     CX
                POP     BX
                POP     AX
                POP     DI
                IRET
POST            ENDP

HELLO           EQU    $

CSEG            ENDS

STACK  SEGMENT PARA STACK 'STACK'
       DB     64 DUP('STACK   ')

STACK  ENDS
                END                    ; Eric W. DeSilva Nov 1985
