    include defs.asm

;  Copyright, 1988-9, 1990, Russell Nelson
;  Copyright, 1991, Roger F. James

;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, version 1.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

code    segment word public
    assume  cs:code, ds:code

    org 2ch
phd_environ dw  ?

    org 80h
phd_dioa    label   byte

    org 100h
start:
    jmp start_1

    even

;Debugging stuff
;total_pkts dw  0
;gvm_pkts   dw  0
;bvm_pkts   dw  0

npacket_int_no  db  ?,?,?,?     ; interrupt to communicate.

per_handle  struc
in_use      db  0       ;non-zero if this handle is in use.
their_handle    dw  0       ;lower layer handle
recv_handler    dd  0       ;receiver upcall.
vm_id       dw  0       ;VM_ID for this handler
per_handle  ends

handles     per_handle MAX_HANDLE dup(<>)
end_handles label   byte


regs    struc               ; stack offsets of incoming regs
_ES dw  ?
_DS dw  ?
_BP dw  ?
_DI dw  ?
_SI dw  ?
_DX dw  ?
_CX dw  ?
_BX dw  ?
_AX dw  ?
_IP dw  ?
_CS dw  ?
_F  dw  ?           ; flags, Carry flag is bit 0
regs    ends

CY  equ 0001h
EI  equ 0200h


bytes   struc               ; stack offsets of incoming regs
    dw  ?           ; es, ds, bp, di, si are 16 bits
    dw  ?
    dw  ?
    dw  ?
    dw  ?
_DL db  ?
_DH db  ?
_CL db  ?
_CH db  ?
_BL db  ?
_BH db  ?
_AL db  ?
_AH db  ?
bytes   ends

their_isr   dd  0       ; original owner of pkt driver int
old_isr     dd  0       ; old pkt driver int
their_2f_isr    dd  0       ; original int 2f ISR

; The following are globals that assume that the code that access them
; single threads.
MAX_BUFFER_LEN  equ 1520
our_buffer  db  MAX_BUFFER_LEN dup (0)
buffer_flag db  0
buffer_len  dw  0
their_handler   dd  0       ; Recv handler to call
their_bx    dw  0
;
;

vmm_running db  0       ; The 386 virtual machine manager is
                    ; running
our_handle  dw  0       ; Current top level handle

our_isr:
    jmp our_isr_0       ;the required signature.
    db  'PKT DRVR',0

our_isr_0:
    assume  ds:nothing
    cmp ah,2            ;Check if it one of the calls we
    je  our_isr_2       ;want to intercept.
    cmp ah,3
    je  our_isr_2
    cmp ah,5
    je  our_isr_2
    cmp ah,8
    je  our_isr_2
our_isr_1:
    jmp old_isr         ;Just chain onto the original driver.

our_isr_2:
    push    ax          ;We are interested in this one
    push    bx          ;so save some registers and off we go.
    push    cx
    push    dx
    push    si
    push    di
    push    bp
    push    ds
    push    es
    cld
    mov bx,cs           ;set up ds.
    mov ds,bx
    assume  ds:code
    mov bp,sp           ;we use bp to access the original regs.
    and _F[bp],not CY       ;start by clearing the carry flag.

    cmp ah,2            ;Jump to the function requested.
    jne our_isr_3
    jmp f_access_type
our_isr_3:
    cmp ah,3
    jne our_isr_4
    jmp f_release_type
our_isr_4:
    cmp ah,5
    jne our_isr_5
    jmp f_terminate
our_isr_5:
    jmp f_stop          ;Must be this one!

our_isr_error:
    mov _DH[bp],dh
    or  _F[bp],CY       ;return their carry flag.
our_isr_return:
    pop es
    pop ds
    pop bp
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret


our_2f_isr:
    assume  ds:nothing
    cmp ax,1608h
    jne our_2f_1
    mov vmm_running,1   ;Windows has started.
    jmp their_2f_isr    ;Pass it on down the chain.
our_2f_1:
    cmp ax,1609h
    jne our_2f_2
    mov vmm_running,0   ;Windows has stopped.
our_2f_2:
    jmp their_2f_isr    ;Pass it on down the chain.

our_handler:
    cmp ax,1
    je  our_handler_2   ;Second upcall
                ;Must be first upcall
                ;So check if our buffer is free
    cmp buffer_flag,1
    je  our_handler_1   ;Our buffer is busy
    cmp cx,MAX_BUFFER_LEN
    jg  our_handler_1   ;Frame to big for our buffer
    mov buffer_flag,1   ;Mark our buffer busy
    mov buffer_len,cx   ;Store frame length for later use
    mov ax,cs
    mov es,ax       ;Segment of our buffer
    mov di,offset our_buffer
    retf

our_handler_1:
    mov ax,0        ;Tell the driver we dont want the frame
    mov es,ax
    mov di,0
    retf

our_handler_2:          ;Second upcall
    mov their_bx,bx ;Save their registers
    mov dx,bx
    mov bx,cs       ;Get our data segment
    mov ds,bx
    assume  ds:code
    mov bx,offset handles
our_handler_3:
    cmp [bx].their_handle,dx
    je  our_handler_4       ;Found their handle.
    add bx,(size per_handle)    ;go to the next handle.
    cmp bx,offset end_handles   ;examined all handles?
    jb  our_handler_3       ;no, continue.
                    ;Fall through if we cannot find it.
    mov buffer_flag,0       ;Mark our buffer free
    retf

our_handler_4:              ;Found the handle.
;   inc total_pkts
    mov ax,[bx].recv_handler.segm
    mov their_handler.segm,ax
    mov ax,[bx].recv_handler.offs
    mov their_handler.offs,ax
    push    bx
    mov ax,1683h        ;Check current VM_ID
    int 2fh
    mov ax,bx
    pop bx
    cmp ax,[bx].vm_id
    je  our_handler_5       ;Correct VM is running
    jmp our_handler_7       ;Wrong VM running so switch it
our_handler_5:
;   inc gvm_pkts
    call    pass_to_app
    retf

pass_to_app:
    mov ax,0            ;Set up registers for first upcall
                    ;to application
    mov bx,their_bx
    mov cx,buffer_len
    push    ds
    call    their_handler
    pop ds
    mov ax,es
    cmp ax,0
    jne pass_to_app_1
    cmp di,0
    jne pass_to_app_1
    mov buffer_flag,0   ;They dont want the frame
    ret
pass_to_app_1:
    push    di
    mov cx,buffer_len
    mov si,offset our_buffer
    rep movsb       ;Copy frame into apps buffer
    mov ax,1        ;Set up regs for second upcall
    mov bx,their_bx
    mov cx,buffer_len
    pop si
    mov dx,es
    push    ds
    mov ds,dx
    assume  ds:nothing
    call    their_handler
    pop ds
    assume  ds:code
    mov buffer_flag,0
    ret

our_handler_7:
;   inc bvm_pkts
    mov ax,1685h        ;Request switch VMs and callback
    mov bx,[bx].vm_id
    mov cx,0
    mov dx,40h
    mov si,0
    push    cs
    pop es
    mov di,offset our_callback
    int 2fh
    retf

    assume ds:nothing
our_callback:
    push    ax
    push    bx
    push    cx
    push    dx
    push    si
    push    di
    push    ds
    push    es
    push    bp
    mov ax,cs
    mov ds,ax       ;Set up our ds
    assume  ds:code
    call    pass_to_app
    pop bp
    pop es
    pop ds
    assume  ds:nothing
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret

    assume  ds:code
;register caller of pkt TYPE
f_access_type:

    mov bx,offset handles
access_type_1:
    cmp [bx].in_use,0       ;is this handle in use?
    je  access_type_2       ;found free one
    add bx,(size per_handle)    ;go to the next handle.
    cmp bx,offset end_handles   ;examined all handles?
    jb  access_type_1       ;no, continue.
    jmp access_type_space   ;no - return error.
access_type_2:
    mov our_handle,bx       ;save our handle
    mov [bx].in_use,1       ;remember that we're using it.
    cmp vmm_running,0       ;Is VMM running
    je  access_type_3       ;No, so dont bother to redirect
    mov [bx].recv_handler.segm,es
    mov [bx].recv_handler.offs,di
    mov bx,cs
    mov es,bx
    mov di,offset our_handler
access_type_3:
    push    ds
    mov bx,_DS[bp]
    mov ds,bx
    assume  ds:nothing
    mov bx,_BX[bp]      ;restore callers registers
    pushf
    call    old_isr
    pop ds
    assume  ds:code
    mov bx,our_handle
    jnc access_type_4       ;it worked
    mov [bx].in_use,0       ;it failed free our handle
    jmp our_isr_error
access_type_4:
    mov [bx].their_handle,ax
    mov _AX[bp],ax      ;save return handle
    cmp vmm_running,0
    je  access_type_5       ;Dont bother if VMM not running
    push    bx
    mov ax,1683h
    int 2fh         ;get current VM_ID
    mov ax,bx
    pop bx
    mov [bx].vm_id,ax
access_type_5:
    jmp our_isr_return

access_type_space:
    mov dh,NO_SPACE
    jmp our_isr_error

f_release_type:
    mov bx,_BX[bp]      ;restore callers registers
    pushf
    call    old_isr
    jnc release_type_1      ;it worked
    jmp our_isr_error
release_type_1:
    mov ax,_BX[bp]      ;just in case
    mov bx,offset handles
release_type_2:
    cmp [bx].their_handle,ax    ;compare handles
    je  release_type_3      ;found a match
    add bx,(size per_handle)    ;go to the next handle.
    cmp bx,offset end_handles   ;examined all handles?
    jb  release_type_2      ;no, continue.
    jmp err_bad_handle      ;no - return error.
release_type_3:
    mov [bx].in_use,0
    jmp our_isr_return

err_bad_handle:
    mov dh,BAD_HANDLE
    jmp our_isr_error

f_terminate:
    mov bx,_BX[bp]      ;restore callers registers
    pushf
    call    old_isr
    jnc terminate_1     ;it worked
    jmp our_isr_error
terminate_1:
    mov ax,_BX[bp]      ;just in case
    mov bx,offset handles
terminate_2:
    cmp [bx].their_handle,ax    ;compare handles
    je  terminate_3     ;found a match
    add bx,(size per_handle)    ;go to the next handle.
    cmp bx,offset end_handles   ;examined all handles?
    jb  terminate_2     ;no, continue.
    jmp err_bad_handle      ;no - return error.
terminate_3:
    mov [bx].in_use,0       ; mark handle as free
    mov bx,offset handles   ; check that all handles are free
terminate_4:
    cmp [bx].in_use,0       ; is this handle free?
    jne terminate_5     ; ne = no, so can't exit completely
    add bx,(size per_handle)    ; next handle
    cmp bx,offset end_handles   ; examined all handles?
    jb  terminate_4     ; b = no, continue examination

    mov al,npacket_int_no   ;release our_isr.
    mov ah,25h
    push    ds
    lds dx,their_isr
    int 21h
    pop ds

    mov al,2fh          ;Restore 2f int
    mov ah,25h          ;Just hope nobody has
    push    ds          ;chained on top of us.
    lds dx,their_2f_isr
    int 21h
    pop ds

;
; Now free our memory
;
    push    cs
    pop es
    mov ah,49h
    int 21h
    jmp our_isr_return
terminate_5:
    mov dh, CANT_TERMINATE
    jmp our_isr_error

; Stop the packet driver doing upcalls. Also a following terminate will
; always succed (no in use handles any longer).
f_stop:
    mov bx,_BX[bp]      ;restore callers registers
    pushf
    call    old_isr
    mov bx,offset handles
f_stop_2:
    mov [bx].in_use,0
    add bx,(size per_handle)    ; next handle
    cmp bx,offset end_handles
    jb  f_stop_2
    clc
    ret

end_resident    label   byte

    include printnum.asm
    include decout.asm
    include digout.asm
    include chrout.asm

;usage_msg is of the form "usage: driver [-d -n] <packet_int_no> <args>"
usage_msg   label   byte
 db "usage:vpkt <new_packet_int_no> <old_packet_int_no>",CR,LF,'$'

;copyright_msg is of the form:
;"Packet driver for the foobar",CR,LF
;"Portions Copyright 19xx, J. Random Hacker".
copyright_msg   label   byte
 db "Virtual packet driver for Windows 3",CR,LF
 db "Portions Copyright 1991 Roger F. James",CR,LF,'$'

copyleft_msg    label   byte
 db "Packet driver skeleton copyright 1988-90, Russell Nelson.",CR,LF
 db "This program is free software; see the file COPYING for details.",CR,LF
 db "NO WARRANTY; see the file COPYING for details.",CR,LF
 db CR,LF
crlf_msg    db  CR,LF,'$'

location_msg    db  "Packet driver is at segment ",'$'

packet_int_no   db  ?
opacket_int_no  db  ?,?,?,?

already_msg db  CR,LF,"There is already a packet driver at ",'$'

not_found_msg   db  CR,LF,"There is no packet driver at ",'$'

new_int_msg db  CR,LF,"Virtual packet driver installed on new interrupt ",'$'

old_int_msg db  "Using old interrupt ",'$'

not_found_error:
    mov dx,offset not_found_msg
    mov di,offset opacket_int_no
    call    print_number
    mov ax,4c05h
    int 21h

already_error:
    mov dx,offset already_msg
    mov di,offset npacket_int_no
    call    print_number
    mov ax,4c05h        ; give errorlevel 5
    int 21h

usage_error:
    mov dx,offset usage_msg
error:
    mov ah,9
    int 21h
    mov ax,4c0ah        ; give errorlevel 10
    int 21h

start_1:
    cld

    mov dx,offset copyright_msg
    mov ah,9
    int 21h

    mov dx,offset copyleft_msg
    mov ah,9
    int 21h

    mov si,offset phd_dioa+1
    call    skip_blanks     ;end of line?
    cmp al,CR
    je  usage_error

;print the location we were loaded at.
    mov dx,offset location_msg
    mov ah,9
    int 21h

    mov ax,cs           ;print cs as a word.
    call    wordout

    mov dx,offset crlf_msg
    mov ah,9
    int 21h

chk_options:
    call    skip_blanks
    cmp al,'-'          ; any options?
    jne no_more_opt
usage_error_j_1:
    jmp usage_error
no_more_opt:

    mov di,offset npacket_int_no
    call    get_number

    call    skip_blanks
    cmp al,CR
    je  usage_error
    mov di,offset opacket_int_no
    call    get_number

    call    skip_blanks     ;end of line?
    cmp al,CR
    jne usage_error

    mov al,npacket_int_no
    mov packet_int_no,al
    call    verify_packet_int
    jnc packet_int_ok
    jmp error
packet_int_ok:
    jne packet_int_unused
    jmp already_error       ;give an error if there's one there.
packet_int_unused:

    mov al,opacket_int_no
    mov packet_int_no,al
    call    verify_packet_int
    jnc packet_int_ok_1
    jmp error
packet_int_ok_1:
    je  packet_int_used
    jmp not_found_error
packet_int_used:
    mov ah,35h          ;remember their packet interrupt.
    mov al,opacket_int_no
    int 21h
    mov old_isr.offs,bx
    mov old_isr.segm,es

    mov ah,35h          ;remember their 2f interrupt.
    mov al,2fh
    int 21h
    mov their_2f_isr.offs,bx
    mov their_2f_isr.segm,es

    mov ah,25h          ;install our 2f interrupt
    mov dx,offset our_2f_isr
    int 21h

    mov dx,offset new_int_msg
    mov di,offset npacket_int_no
    call    print_number
    mov dx,offset old_int_msg
    mov di,offset opacket_int_no
    call    print_number

    call    take_packet_int

    mov ah,49h          ;free our environment, because
    mov es,phd_environ      ;  we won't need it.
    int 21h

    mov bx,1            ;get the stdout handle.
    mov ah,3eh          ;close it in case they redirected it.
    int 21h

    mov dx, offset end_resident
    add dx,0fh          ;round up to next highest paragraph.
    mov cl,4
    shr dx,cl
    mov ah,31h          ;terminate, stay resident.
    mov al,0
    int 21h

take_packet_int:
    mov ah,35h          ;remember their packet interrupt.
    mov al,npacket_int_no
    int 21h
    mov their_isr.offs,bx
    mov their_isr.segm,es

    mov ah,25h          ;install our packet interrupt
    mov dx,offset our_isr
    int 21h
    ret

    include verifypi.asm
    include getnum.asm
    include getdig.asm
    include skipblk.asm
    include printea.asm

code    ends

    end start
