; snoop.asm
;
; Written By Ray Smith
; Bix Name: rjsmith
; Copyright (c) 1987, Smith, Brittain & Associates. All rights reserved.
;
;
;*****************************************************************************
; Usage: snoop [/d] [/e] [/i] [/m] [/a] [/x] [/n]
;
;        where d = resident and installed device drivers
;              e = system equipment and configuration
;              i = Invars Table
;              m = Memory Control Blocks
;              a = all of the above
;              x = all of the above except /e
;              n = do Not pause the display, allowing the output to be 
;                  redirected to a file in a continuous stream.
;
;*****************************************************************************
; 
;
.8087
cseg	segment para public 'code'
	org     100h                     ; this will be a .com file
	assume cs:cseg,ds:cseg           ; all seg regs point to start of PSP
main	proc far
;
begin:	jmp	entry	                 ; go around the data
;
;***********************************************************************
;* Explanation of bit settings in byte "config_flag"
;***********************************************************************
;    
; Value      8   4   2   1     8   4   2   1
;        +-----------------+-----------------+
;  Bit   |                 |                 |
; Number |  07  06  05  04 |  03  02  01  00 |
;        |                 |                 |
;        +-----------------+----------------------------------------+
;     01 |   |   |   |   |     |   |   |   1 = PC/AT Class Machine  |
;     02 |   |   |   |   |     |   |   1 = Enhanced BIOS Present    |         
;     04 |   |   |   |   |     |   1 = Enhanced Keyboard Attached   |     
;     08 |   |   |   |   |     1 = Compaq DeskPro 386 or klone      | 
;     10 |   |   |   |   1 = PC/XT Class Machine                    | 
;     20 |   |   |   1 = PC Class Machine                           |
;     40 |   |   1 = PC Convertible                                 |
;     80 |   1 = PC/XT 089                                          |
;        +----------------------------------------------------------+
;
;
display_flag db 0h
config_flag  db 0h
equ_flag     db 0h
inv_flag     db 0h
drv_flag     db 0h
pse_flag     db 0h
mem_flag     db 0h
cpu_flag     db 0h
ps2_flag     db 0h
sys_flag     db 0h
env_flag     db 0h
psp_flag     db 0h
tsr_flag     db 0h
mt_flag      db 0h
data_flag    db 0h
switchar     db 2Fh   ; "/" for command line switches
pathchar     db 5Ch   ; "\" for path name delimiter
switch_err   db 0h 
scan_code    db 44h    
loop_count   dw 0h
arg_cnt      dw 0h
argc         db 0h
optcnt       db 0h
portcnt      db 0h
total_pages  dw 0h
avail_pages  dw 0h
firstenv     db 01h
firstpsp     db 01h
firstmcb     db 01h
firstsys     db 01h
one          db 01h
linecnt      db 0h
de_allocmsg  db 'Deallocated$'  
systype      db 'DOS $'
psptype      db 'PSP $'
envtype      db 'ENV $'
datatype     db 'DATA$'
mttype       db 'Unkn$'
lastdpb      db 'Chain Ends$'
notread      db 'nr$'     ; drive was not read
dos_vers     dw 0h
emm_vers     db 0h
rom_flag db     0FFh
get_key  db     0h        ; default BIOS Int 16h function to read a key
avail_c  db     01h       ; default BIOS Int 16h function to check avail char
get_sts  db     02h       ; default BIOS Int 16h function to check shift status
control  dw     0h        ; save 80x87 control word
sav_code db     0h
sav_addr dw     0h
sav_off  dw     0h
invar_seg dw    0h
invar_off dw    0h
dpb_seg  dw     0h
dpb_off  dw     0h
sav_name db     '             '
         db     '$'
type_drv dw     0h
devh_seg dw     0h        ; segment address NUL device driver
devh_off dw     0h        ; offset  address NUL device driver
next_seg dw     0h        ; next seg addr
next_off dw     0h        ; next off addr
colon    db     ':'
emm_name db     'EMMXXXX0' ; Guaranteed emm device driver file name
blank    db     20h
ourcopy  db     0Dh,0Ah,' Copyright (c) 1987 Smith, Brittain & Associates. '
         db     'All Rights Reserved.$'
mcbseg   dw     0h
filename db '        $'
dataname db '        $'
         db     'Written By Raymond J. Smith'
msg1	 db	0Dh,0Ah,' Machine is an IBM PC/AT or AT klone $'
msg1A	 db	0Dh,0Ah,' Machine is an IBM PC/AT Model 339   $'
msg1B	 db	0Dh,0Ah,' Machine is an IBM PC/AT Model 239   $'
msg1C	 db	0Dh,0Ah,' Machine is an IBM PC/AT Model 099   $'
msg1D	 db	0Dh,0Ah,' Machine is a Compaq Dekspro 386 or compatible$'
msg1E	 db	0Dh,0Ah,' Machine is an IBM Personal System/2 Model 30$'
msg1F	 db	0Dh,0Ah,' Machine is an IBM Personal System/2 Model 50$'
msg1G	 db	0Dh,0Ah,' Machine is an IBM Personal System/2 Model 60$'
msg1H	 db	0Dh,0Ah,' Machine is an IBM Personal System/2 Model 80$'
msg2	 db	0Dh,0Ah,'         Enhanced BIOS is present $'
msg3	 db	0Dh,0Ah,'         Enhanced Keyboard is attached  $'
msg4	 db	0Dh,0Ah,' Machine ID byte contains code ==> $'
msg5	 db	0Dh,0Ah,'         No Enhanced BIOS is present $'
msg6	 db	0Dh,0Ah,'         No Enhanced Keyboard is attached  $'
msg7	 db	0Dh,0Ah,'                           Press any key to continue... $'
msg9	 db	0Dh,0Ah,'         EGA is active adapter $'
msg10	 db	0Dh,0Ah,'         EGA is active and is monochrome $'
msg11	 db	0Dh,0Ah,'         EGA is not active, a MDA or Hercules card is active $'
msg12	 db	0Dh,0Ah,'         EGA is not active, a CGA is active $'
msg14    db	0Dh,0Ah,'         EGA has 64kb graphics memory installed  $'
msg15    db	0Dh,0Ah,'         EGA has 128kb graphics memory installed  $'
msg16    db	0Dh,0Ah,'         EGA has 192kb graphics memory installed  $'
msg17    db	0Dh,0Ah,'         EGA has 256kb graphics memory installed  $'
msg18    db	0Dh,0Ah,'         Enhanced Color Display is attached $'
msg18A   db	0Dh,0Ah,'         Enhanced Color Display in Color Graphics mode is attached $'
msg19    db	0Dh,0Ah,'         Normal Color Display is attached $'
msg19A   db	0Dh,0Ah,'         CGA is active$'
msg20    db	0Dh,0Ah,'         Monochrome Display is active$'
msg21	 db	0Dh,0Ah,' Machine is an IBM PC/XT or XT klone $'
msg22	 db	0Dh,0Ah,' Machine is an IBM PC or PC klone $'
msg23	 db	0Dh,0Ah,' Machine is an IBM PC convertible $'
msg24	 db	0Dh,0Ah,' Machine is a PC/XT Model 089 $'
msg25	 db	0Dh,0Ah,' Machine is a PC/XT Model 286 $'	
msg26	 db	0Dh,0Ah,'         EGA switches improperly set $'
msg27    db	0Dh,0Ah,'         8087 is installed $'
msg28    db	0Dh,0Ah,'         80287 is installed $'
msg29    db	0Dh,0Ah,'         no NDP is installed$'
msg30    db	' exTEnded memory installed$',0Dh,0Ah
msg31    db	' exPAnded memory installed$'
msg31A   db     ' total EMS pages$'
msg31B   db     ' available EMS pages$'
msg31C   db     0Dh,0Ah,'         EMM Version: $'
msg32    db	0Dh,0Ah,'         no$'
msg33    db	0Dh,0Ah,'         System is operating under DOS Version $'
msg33A   db     0Dh,0Ah,'         Switch character currently in effect is $'
msg33B   db     0Dh,0Ah,'         DOS Int 21h Function 37h not responding$'
msg34    db	' is installed$'
msg35    db	'3.00$'
msg36    db	'3.10$'
msg37    db	'3.20$'
msg37A   db	'3.30$'
msg37B   db	'3.40$'
msg38    db	'2.00$'
msg39    db	'2.10$'
msg40    db	'is unknown$'
msg41    db	'kb$'
msg42    db	' base memory installed$',0Dh,0Ah
msg43    db	' OR the EMM is not installed$'
msg44    db	'LPT1: $'
msg45    db	'LPT2: $'
msg46    db	'LPT3: $'
msg47    db	'COM1: $'
msg48    db	'COM2: $'
msg48A   db	'COM3: $'
msg48B   db	'COM4: $'
msg49    db	'No Parallel Ports active$'
msg50    db	'No Serial Ports active$'
msg98    db	'2.x$'
msg99    db	'3.x$'
copyrite db     'ROM BIOS Copyright ==> $'
biosdate db     'ROM BIOS dated $'
egacopy  db     'EGA BIOS Copyright ==> $'
egadate  db     'EGA BIOS dated $'
baddrv   db	'Corrupted EMM Driver $'
badbrd   db	'Malfunction in exPAnded memory hardware$'
romid    db	'Adapter ROM signature found at segment $'
noroms   db	'No adapter ROMs detected $'
newline  db	0Dh,0Ah,'$'
indent   db     '         $'
rom_sig  dw     0AA55h
tbuff    db     255 dup('$')
char_dev db     'Char   $'
blk_dev  db     'Block  $'
clk_dev  db     'Clock  $'
cmndmsg  db     'COMMAND $'
sysmsg   db     'System  $'
freemsg  db     'Free    $'
mtblkmsg db     '<Empty> $'
unknmsg  db     'Unknown $'
envmsg   db     'Process Env  $'
mastenv  db     'Master Env   $'
tsrmsg   db     'TSR          $'
resdmsg  db     'Resident part$'
datamsg  db     'Process Data $'
thismsg  db     'Transient    $'
drvmsg   db     'Dev Drvs, etc$'
space1   db     ' $'
space2   db     '  $'
space3   db     '   $'
space4   db     '    $'   
space5   db     '     $'
space6   db     '      $'
space7   db     '       $'
space8   db     '        $'
space9   db     '         $'
space10  db     '          $'
space11  db     '           $'
space12  db     '            $'
space13  db     '             $'
space14  db     '              $'
scr1     db 0Dh,0Ah,'                     Partial Contents of DOS "Invar" Table'
         db 0Dh,0Ah,' '  
         db 0Dh,0Ah,'   Invar        First      1st Dir      Size of    Drive/Directory  DOS "Busy"'
         db 0Dh,0Ah,'   Table      DPB Table     Buffer      1 Cache         Table         Flag'
         db 0Dh,0Ah,' Seg : Off    Seg : Off    Seg : Off    Buffer        Seg : Off     Seg : Off'
         db 0Dh,0Ah,' ---------    ---------    ---------    -------       ---------     --------- $'
scr1A    db 0Dh,0Ah,'                     Contents of DPB Table By Drive'
	 db 0Dh,0Ah,'                                                        Dev Drvr      Next DPB'
	 db 0Dh,0Ah,'Drv DU BPC  SPC   RS  CF  MDE   FUS   TCC   SIF  FDS    Hdr Addr  MD   Address'
	 db 0Dh,0Ah,'--  -- ---- ---  ---- --  ----  ----  ----  ---  ----  ---------  --  ---------$'
scr2     db 0Dh,0Ah,'                       Installed Device Drivers'
         db 0Dh,0Ah,'       Name or      Header Address   Entry Points               DOS'
         db 0Dh,0Ah,'Type   # Units        Seg : Off      Strat  Interpt   Attrib  Version'
         db 0Dh,0Ah,'----------------------------------------------------------------------------$'
scr3     db 0Dh,0Ah,'                             Memory  Allocation '
         db 0Dh,0Ah,' '  
         db 0Dh,0Ah,'MCB   Points Paras                                         Hooked'
         db 0Dh,0Ah,'Addr  To Blk Alloc Type Owner     Comment       Cmnd Line  Vectors'
         db 0Dh,0Ah,'----  ----   ----  ---- --------  -----------   ---------  -------------------$'
cnt      dw 00h
vectcnt  dw 00h
blk_beg  dw 00h
blk_end  dw 00h
usemsg   db 0Dh,0Ah,' Usage: snoop [/d] [/e] [/i] [/m] [/a] [/n]',0Dh,0Ah
         db 0Dh,0Ah
         db 0Dh,0Ah,'        where d = list device drivers'
         db 0Dh,0Ah,'              e = list equipment'
         db 0Dh,0Ah,'              i = list Invars Table and DPBs'
         db 0Dh,0Ah,'              m = list Memory Control Blocks'
         db 0Dh,0Ah,'              a = list all of the above'
         db 0Dh,0Ah,'              n = do Not pause the display'
         db 0Dh,0Ah,'              x = list all of the above except e'
         db 0Dh,0Ah,'                  (for non-IBM BIOS compatible machines)$'
;
;***********************************************************************
;* Beginning of Code Seg
;***********************************************************************
entry:
        call switch_char                 ; go see what they're using for paths
        mov  switch_err,al               ; check for error and store for later 
        call chk_switch                  ; get our command line switches
        mov  ah,30h			 ; get dos ver num
	int  21h			 ; go do it
       	xchg ah,al			 ; turn it around
        mov  dos_vers,ax                 ; need for NUL device driver location
	cmp  ax,0200h		         ; 2.00 or greater?
	jae  oz_ok
bad_oz:
	jmp  quit                        ; need 2.0 or greater
oz_ok:
	mov  dx,offset ourcopy		 ; display our copyright message
        call dostty
	mov  dx,offset newline		 
        call dostty
        cmp  equ_flag,0FFh               ; switch set for equipment list?
        je   equip_tests                 ; yes
        jmp  invar_chk                   ; no - bypass equipment list

equip_tests:
        clc                              ; clear the carry flag for error rtn
        mov  ah,0C0h                     ; get configuration parameters
        int  15h                         ; avaialable on AT class machines only
        sti                              ; int 15h does not reenable interrupts
        jc   is_old                      ; it's a PC
        cmp  ah,80h                      ; return value if original PC
        je   is_old                      ; 86h is returned on all PS/2 models
        cmp  ah,86h                      ; return value if not new machine
        jne  is_new                      ; 86h is returned on all PS/2 models
is_old:
        jmp  test_more                   ; it's either a PS/2 or PC PC/XT class
is_new:                                  ; determine machine type
        cmp  ax,00FFh                    ; return value if  PS/2
        jne  not_new_mod                 ; 
        call chk_ps2
        cmp  ps2_flag,0h
        je   not_new_mod                 ; 
        jmp  at_tests
not_new_mod:
        cmp  byte ptr es:[bx+2],0FCh     ; AT class machine ID
        je   chk_339
        jmp  test_more  

chk_339:
        cmp  byte ptr es:[bx+3],01h      ; check sub-model type byte
        je   is_339                      ; 1 means IBM AT/339 or Compaq 386
        cmp  byte ptr es:[bx+3],02h      ; check sub-model type byte
        jne  next_one
        jmp  xt_286                      ; a 2 means it's an IBM XT/286
next_one:
        cmp  byte ptr es:[bx+3],0h       ; check sub-model type byte
        je   is_239                      ; 0 means it's an IBM AT/239 or 099
        jmp  test_more                   ; (note:AT/239 has rom 06/10/85)

is_339:                                  ; mach id byte is FCh - means little
        call chk_cpu                     ; see if 386 or 286 cpu
	cmp  byte ptr cs:cpu_flag,03h    ; is it a 386 cpu?
        je   is_386                      ; 
is_286:                                  ; it's a 286 cpu
	or   byte ptr cs:config_flag,01h ; indicate PC/AT
	mov  dx,offset msg1A		 ; AT class message
        call dostty
        jmp  at_tests
is_386:
	or   byte ptr cs:config_flag,09h ; indicate PC/AT class & 386 class
	mov  dx,offset msg1D		 ; Compaq DeskPro 386 Class message
        call dostty
        jmp  at_tests

is_239:
	or   byte ptr cs:config_flag,01h ; indicate PC/AT
        cmp  byte ptr es:[bx+4],0h       ; check bios level byte
        je   is_099                      ; 0 means it's an IBM AT/099
        cmp  byte ptr es:[bx+4],01       ; check bios level byte
        jne  test_more                   ; 1 means it's an IBM AT/239
	mov  dx,offset msg1B		 ; Einen Klonen Machinen message
        call dostty                      ; if all tests fail, assume klone
        jmp  at_tests                    ; test for AT type stuff
is_099:
	mov  dx,offset msg1C		 ; original 6 mHz AT
        call dostty
        jmp  at_tests

;***********************************************************************
;* Determine Machine Type for non_AT type machines.                    *
;***********************************************************************
test_more:        
	mov  ax,0F000h		         ; seg address of rom bios
	mov  es,ax                       ; establish addressibility
	mov  ax,word ptr es:[0FFFEh]	 ; offset of rom bios machine id byte
        call chk_mod30                   ; see if PS/2 Model 30 first 
        cmp  ps2_flag,30h                ; is it a PS/2 model 30?
        jne  not_a_30                    ; if greater, is model 50/60/80
        jmp  chk_mem
not_a_30:
	cmp  al,0FCh  		         ; not PS/2: PC/AT? Compaq 386? XT/286?
        je   at_ok     	                 ; 
	jmp  not_at			 ; it's not PS/2 or AT class
at_ok:
	or   byte ptr cs:config_flag,01h ; indicate PC/AT
	mov  dx,offset msg1		 ; 
        call dostty
        jmp  at_tests

xt_286:
	or   byte ptr cs:config_flag,01h ; indicate PC/AT class machine
	mov  dx,offset msg25		 ; 
        call dostty

;***********************************************************************
;* These tests are applicable to AT class machines only.               *
;***********************************************************************
;
at_tests:
                                         ; test for base memory 
        int  12h                         ; BIOS int 12h returns memory size
        push ax                          ; this is safe on an AT
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
        pop  dx
        call dec16out
	mov  dx,offset msg41		 ; kb
        call dostty
	mov  dx,offset msg42		 ; bytes
        call dostty
;
;****************************************************************************
;* Test for presence and amount of exTEnded memory (AT class machines only) *
;****************************************************************************
        mov  ax,8800h
        int  15h
        cmp  ax,0h
        je   noexten
        push ax
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
        pop  dx
        call dec16out                    ; convert binary to decimal & display
	mov  dx,offset msg41		 ; kb
        call dostty
        jmp  extend
noexten:
	mov  dx,offset msg32		 ; no exTEnded memory installed
        call dostty
	mov  dx,offset msg30
        call dostty
        jmp  chk_emm                     ; go see if exPAnded memory installed

extend:
	mov  dx,offset msg30		 ; exTEnded memory is installed
        call dostty
        jmp  chk_emm                     ; go see if exPAnded memory installed
;
;***********************************************************************
;* Test for machine type if not AT class.                              *
;***********************************************************************
not_at:
        cmp  ps2_flag,30h                ; is this a PS/2 Model 30?
        je   chk_mem                     ; if yes, go right to mem chk
	cmp  al,0FEh  		         ; is this a PC/XT ?
	jne   not_xt			 ; set flag if it is, branch if not
	or   byte ptr cs:config_flag,10h ; indicate PC/XT
	mov  dx,offset msg21		 ; 
        call dostty
        jmp  chk_mem
not_xt:
	cmp  al,0FFh  		         ; is this a PC ?
	jne   not_pc			 ; set flag if it is, branch if not
	or   byte ptr cs:config_flag,20h ; indicate PC
	mov  dx,offset msg22		 ; 
        call dostty
        jmp  chk_mem
not_pc:
	cmp  al,0F9h  		         ; is this a PC Convertible?
	jne   xt_089			 ; set flag if it is, branch if not
	or   byte ptr cs:config_flag,40h ; indicate PC Convertible
	mov  dx,offset msg23		 ; 
        call dostty
        jmp  chk_mem

xt_089:
	cmp  al,0FBh  		         ; is this a new PC/XT-089? 
	jne   unk_id			 ; set flag if it is, branch if not
	or   byte ptr cs:config_flag,80h ; indicate PC/XT-089 
	mov  dx,offset msg24		 ; 
        call dostty
        jmp  chk_mem

unk_id:
        mov  dx,offset msg4		 ; don't recognize this Klone 
        call dostty
        mov  sav_code,al                 ; display the actual machine ID byte
	mov  cx,1	                 ; count of bytes to display
	lea  bx,[sav_code]
        call show_bytes
;
;***********************************************************************
; Test for amount of conventional memory.                              *
;***********************************************************************
chk_mem:                                 ; test for main memory if not AT class 
                                         ;  machine
        int  12h                         ; BIOS int 12h returns memory size
        push ax                          ; risky: only returns switch settings
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
        pop  dx
        call dec16out
	mov  dx,offset msg41		 ; kb
        call dostty
	mov  dx,offset msg42		 ; bytes
        call dostty
;
;***********************************************************************
;* Test for presence and amount of LIM/EMS (exPAnded) memory
;***********************************************************************
;
chk_emm:
        mov  ah,35h
        mov  al,67h
        int  21h
        mov  di,0Ah
        push cs
        pop  ds
        mov  si,offset emm_name          ; guaranteed device driver name
        mov  cx,8                        ; length of file name
        cld
        repz cmpsb
        jz   chk_ems_hardware
        jmp  no_emm
;***********************************************************************
;* Find out if hardware is functional using Int 67h fcn 40h
;***********************************************************************
chk_ems_hardware:
        mov  ah,40h                      ;
        int  67h
        cmp  ah,0h                       ; zero return code means all ok
        je   emm_ok
        cmp  ah,80h                      ;
        je   bad_drv
bad_hard:
	mov  dx,offset newline		 ;
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset badbrd            ; bad emm hardware
        call dostty
        jmp  chk_bios
bad_drv:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset baddrv            ; bad emm driver image
        call dostty
        jmp  chk_bios
;
;***********************************************************************
;* Find out how many pages present/available using Int 67h fcn 03h
;***********************************************************************
emm_ok:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
        mov  ah,42h                      ; get number of pages
        int  67h
        or   ah,ah
        jz   no_err
                                         ; put error messages here
        jmp  chk_bios
no_err:
        mov  word ptr total_pages,dx     ; dx holds total number of 16kb pages
        mov  word ptr avail_pages,bx     ; bx holds number of available pages
        call dec16out                    ; display it
	mov  dx,offset msg31A            ; 
        call dostty
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
        mov  dx,word ptr avail_pages
        call dec16out                    ; display it
	mov  dx,offset msg31B            ; 
        call dostty
;
;***********************************************************************
;* Find out the EMS version using Int 67h fcn 07h
;***********************************************************************
;
        mov  ah,46h                      ; get emm version
        int  67h                         ; al = version # in BCD
        mov  byte ptr emm_vers,al        ; Major revision in upper 4 bits
                                         ; Minor revision in lower 4 bits
	mov  dx,offset msg31C            ; 
        call dostty
        mov  al,byte ptr emm_vers
        and  ax,00F0h                    ; isolate major ver #
        mov  cl,4                        ; shift to low bits
        shr  ax,cl                 
        add  al,30h                      ; make it ascii
        mov  dl,al                       ; set it up for stdout
        call stdout                      ; display major version #
        mov  dl,2Eh
        call stdout                      ; display a period
        mov  al,byte ptr emm_vers        ; get emm version
        and  ax,000Fh                    ; isolate minor version
        add  al,30h                      ; make it ascii
        mov  dl,al                       ; set it up for stdout
        call stdout                      ; display minor version #
        jmp  chk_bios

no_emm:
	mov  dx,offset msg32 		 ; no exPAnded memory found...
        call dostty
	mov  dx,offset msg31
        call dostty
	mov  dx,offset msg43	         ; ...or the emm not installed
        call dostty
;
;***********************************************************************
;* display copyright and rom release date and test for enhanced BIOS   *
;***********************************************************************
chk_bios:
                                         ; display copyright slug
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset copyrite		 ; copyright header string
        call dostty
        cld
        push es
        push ds
        push ds
        pop  es                          ; destination string address in es:di
        mov  ax,0F000h                   ; source string address in ds:si
        mov  ds,ax                       ; ...location of IBM AT copyright
        xor  ax,ax
        mov  cx,10h                      ; count of words in copyright
	test byte ptr cs:config_flag,10h ; is this an older PC/XT?
        jz   getcopy
        mov  ax,0E000h                   ; IBM XT copyright is higher up in rom
        mov  cx,0Bh                      ; count of words (XT is shorter)
getcopy:
        mov  bx,cx                       ;
        shl  bx,1                        ; multiply by 2 to get offset of 
        mov  si,ax                       ; terminator character in dest string
        mov  di,offset es:tbuff
        rep  movsw
        pop  ds
        pop  es
        mov  byte ptr tbuff+[bx],'$'    ; insert terminator
        mov  bx, offset tbuff
scan:
        cmp  byte ptr[bx],'$'           ; are we done?
        je   end_scan                   ; go display it
        cmp  byte ptr[bx],' '           ; less than a space?
        jb   hide_it                    ;
        cmp  byte ptr[bx],'~'           ; greater than a tilde
        jna  bump_bx                    ; if not it's printable
        
hide_it:
        mov  byte ptr[bx],20h           ; make nonsense characters a space
bump_bx:
        inc  bx                         ; increment the address
        jmp  scan                       ; go do it again
end_scan:
        mov  dx,offset tbuff	        ; 
        call dostty
        push es
        push ds
        push ds
        pop  es                          ; destination string address in es:di
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset biosdate		 ; date header
        call dostty
        mov  ax,0F000h                   ; source string address in ds:si
        mov  ds,ax
        mov  ax,0FFF5h                    ;offset of rom release date
        mov  si,ax
        mov  di,offset es:tbuff
        mov  cx,04h                      ; count of words
        rep  movsw
        pop  ds
        pop  es
        mov  byte ptr tbuff[08h],'$'     ; insert string terminator
        mov  dx,offset tbuff	         
        call dostty
                                         ; check for enhanced bios
        mov  ah,2                        ; get shift status (old function)
        int  16h                         ;  
        mov  bl,al                       ; Save shift status
        not  al                          ; change al (invert all bits)
        mov  ah,12h                      ; new BIOS extended shift status
        int  16h                         ; 
        cmp  al,bl                       ; is result the same from both calls?
        jne  next1                       ; if not, this is not enhanced BIOS
        or   byte ptr cs:config_flag,02h ; indicate enhanced BIOS present
	mov  ax,40h		         ; seg address of BIOS data area
	mov  es,ax                       ; establish addressibility
	mov  al,byte ptr es:[96h]	 ; offset of BIOS @KB_FLAG_3 byte
	test al,10h  		         ; is enhanced keyboard attached?
	jz   next1  			 ; set flag if it is, branch if not
        or   byte ptr cs:config_flag,04h ; indicate enhanced keyboard attached
        test byte ptr cs:config_flag,06h ; could be new XTs with enhanced BIOS 
	jz   next1                       ; branch if not
	add  get_key,10h                 ; if new bios...
	add  avail_c,10h                 ; ...and new keyboard...
	add  get_sts,10h                 ; ...add 10h to get extended functions

next1:
        test byte ptr cs:config_flag,02h ; is enhanced BIOS present?
	jz   old_bios			 ; branch if not
	mov  dx,offset msg2
        call dostty
        jmp  new_bios
old_bios:
	mov  dx,offset msg5
        call dostty
new_bios:
        test byte ptr cs:config_flag,04h ; is enhanced keyboard present?
	jz   not_enhanced       	 ; branch if not
	mov  dx,offset msg3		 ; 
        call dostty
        mov  scan_code,86h               ; ascii code for F12
	jmp  scan_rom
not_enhanced:
	mov  dx,offset msg6
        call dostty

;***********************************************************************
;* Scan ROM for BIOS IDs...
;***********************************************************************
scan_rom:
        mov  dx,0C000h                   ; start point for rom signature scan
        mov  di,word ptr rom_sig         ; rom signature is 55 AA hex
scan_loop:
        mov  es,dx 
        xor  bx,bx                       ; offset zero
        mov  ax,es:[bx]                  ; 
        cmp  ax,di                       ; equal to 55 AA ?
        jnz  next_blk                    ; if no, go increment block address
        mov  ax,es                       ; get segment address for display
        push dx
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset romid             ; ROM detected message
        call dostty
        call disp_hex
        mov  al,'h'
        call stdout
        mov  rom_flag,0h                 ; indicate an adapter ROM was found
        pop  dx
next_blk:
        add  dx,80h                      ; point to next 2k address
chk_addr:
        cmp  dx,0F600h                   ; end of addresses where ROMs can be?
        jl   scan_loop                   ; search for more if not
no_roms:
        cmp  rom_flag,0h                 ; was an adapter ROM found?
        je   end_rom                     ; yes? bypass message
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset noroms            ; no ROMs detected
        call dostty
end_rom:
;
;***********************************************************************
;* Check active display and adapter
;***********************************************************************
;
chkdisp:                                 ; check for presence of an EGA first
        mov  bx, 0FF10h                  ; see if EGA responds
        mov  ah, 12h
        int  10h
        test bh, 0FEh                    ; bl has mem size
        jnz  notega                      ; cl has switch settings
        jmp  IsEga

notega:                                  ; see if MDA or CGA or HGC    
	mov  ax,40h		         ; seg address of BIOS data area
	mov  es,ax                       ; establish addressibility
	mov  al,byte ptr es:[10h]	 ; offset of video status byte
	and  al,00110000b                ; isolate bits 4 & 5
	cmp  al,30h                      ; is it monochrome card?
	je   is_mono                     ; assume color if not mono
	mov  dx,offset msg19A    	 ; CGA is active adapter
        call dostty
        jmp     chk_ndp

is_mono:
	mov  dx,offset msg20		 ; MDA is active adapter
        call dostty
        jmp     chk_ndp
IsEga:
        call ega_copy                    ; bl and cl get clobbered
	mov  al,byte ptr es:[87h]	 ; offset of EGA BIOS data byte INFO
	shl  al,1
	shl  al,1
	shl  al,1
	shl  al,1
	test al,0h  		         ; look for EGA with color
	jnz  nocolor			 ; if not, see if mono attached
	mov  dx,offset msg9		 ; EGA is active adapter
        call dostty
	mov  al,byte ptr es:[88h]	 ; offset of EGA BIOS data byte INFO_3
	shl  al,1
	shl  al,1
	shl  al,1
	shl  al,1
	test al,09h  		         ; EGA with ECD in hi res mode
	jnz  nothires			 ; set flag if it is, branch if not
	mov  dx,offset msg18		 ; 
        call dostty
        jmp  chkmem
nothires:	
	test al,01h  		         ; EGA with ECD, EGA in CGA mode
	jnz  noecd			 ; set flag if it is, branch if not
	mov  dx,offset msg18A   	 ; ECD is in CGD emulation mode
        call dostty
        jmp  chkmem
noecd:
	test al,0Eh  		         ; EGA with Normal Color Display
	jnz  nocolor			 ; set flag if it is, branch if not
	mov  dx,offset msg19		 ; 
        call dostty
        jmp  chkmem

nocolor:
	test al,0Dh  		         ; look for EGA with monochrome display
	jnz  mono2			 ; set flag if it is, branch if not
	mov  dx,offset msg20		 ; 
        call dostty
        jmp  chkmem
mono2:
	test al,05h  		         ; look for EGA with monochrome display
	jnz  badswitch			 ; set flag if it is, branch if not
	mov  dx,offset msg20		 ; 
        call dostty
        jmp  chkmem
badswitch:                               ; switches must be set incorrectly
	mov  dx,offset msg25		 ; 
        call dostty

;
;***********************************************************************
;* Test for amount of graphics memory on EGA
;***********************************************************************
;
chkmem:
        mov  ax,40h
        mov  es,ax
	mov  al,byte ptr es:[87h]	 ; offset of EGA BIOS data byte INFO
	and  al,60h                      ; get rid of unwanted bits
	cmp  al,60h                      ; is 256kb onboard?
	jne  not256                      ; no - check for 192kb
	mov  dx,offset msg17		 ; 
        call dostty
        jmp  chk_ndp

not256:
	cmp  al,40h                      ; is 192kb onboard? (is IBM EGA)
	jne  not192                      ; no - check for 128kb
	mov  dx,offset msg16		 ; 
        call dostty
        jmp  chk_ndp

not192:
	cmp  al,20h                      ; is 128kb onboard? (is IBM EGA)
	jne  not128                      ; no - check for 64kb
	mov  dx,offset msg15		 ; 
        call dostty
        jmp  chk_ndp

not128:                                  ; *must* be 64kb memory (is IBM EGA)
	mov  dx,offset msg14		 ; 
        call dostty
        jmp  chk_ndp

;
;***********************************************************************
;* Test for presence of 80x87.                                         * 
;***********************************************************************
;
chk_ndp:
	fninit      			; finit - see if 8087 installed
	sub  ax,ax  			; fn means no wait in case no ndp
	mov  word ptr control,ax	; move two zeros to cntrl reg save area
	fnstcw control 		        ; get 8087 control word - no wait
	mov  ax,word ptr control	; put control word in ax
	and  ax,037Fh		        ; default initialization state
	cmp  ax,037Fh		        ; equal to 8087 here?
	jne  no_ndp			; no - go around
	cmp  ps2_flag,30h               ; is it a PS/2 model 50 or above?
        ja   is_80287                   ; if so it's an 80287
	test byte ptr config_flag,01h   ; is this an 80286 class machine ?
        jz   is_8087                    ; if not, NDP is 8087
is_80287:
	mov  dx,offset msg28            ; else it's an 80287
	call dostty
        jmp  getvers
is_8087:
	mov  dx,offset msg27
	call dostty
        jmp  getvers
no_ndp:	
	mov  dx,offset msg29
	call dostty
;
;***********************************************************************
;* Display DOS Version
;***********************************************************************
;
getvers:
	mov  dx,offset msg33
	call dostty
        mov  ax,dos_vers
	cmp  ax,0200h		         ; 2.00 or greater?
	ja   isver3
	mov  dx,offset msg38             ; it's 2.00
	call dostty
	jmp  endvers

isver3:
	cmp  ax,0300h		         ; 3.00 or greater?
        jae  which3
	mov  dx,offset msg39             ; no, must be 2.10
	call dostty
	jmp  endvers

which3:
	cmp  al,0Ah		         ; 3.10 ?
	jne  mustbe32
	mov  dx,offset msg36
	call dostty
	jmp  endvers
mustbe32:
	cmp  al,14h		         ; 3.20 ?
	jne  mustbe33
	mov  dx,offset msg37             ; 3.20
	call dostty
	jmp  endvers
mustbe33:
	cmp  al,1Eh		         ; 3.30 ?
	jne  unk_dos
	mov  dx,offset msg37A            ; 3.30
	call dostty
	jmp  endvers
unk_dos:
	mov  dx,offset msg40             ; unknown version number
	call dostty
	jmp  quit
endvers:
;
;***********************************************************************
;* Display the active switch character
;***********************************************************************
        cmp  switch_err,0h               ; was there a switchar error?
        je   fcn_37_ok
	mov  dx,offset msg33B
	call dostty
fcn_37_ok:
	mov  dx,offset msg33A
	call dostty
        mov  al,switchar                 ; display the *current* switchar
        call stdout
;
;***********************************************************************
;* Display the active Lpt: ports
;***********************************************************************
;
chklpt:
        mov  ax,40h                      ; ROM BIOS data area segment address
        mov  es,ax
        mov  ax,word ptr es:[8h]         ; offset of lpt I/O addresses
        cmp  ax,0h
        je   no_lpt1
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg44             ; lpt1:
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_lpt1:
        mov  ax,word ptr es:[0Ah]
        cmp  ax,0h
        je   no_lpt2
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg45             ; lpt2:
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_lpt2:
        mov  ax,word ptr es:[0Ch]
        cmp  ax,0h
        je   no_lpt3
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg46             ; lpt3:
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_lpt3:
        cmp  portcnt,0h
        je   no_prns
        jmp  chkcom
no_prns:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg49             ; no lpt ports found
	call dostty
;
;***********************************************************************
;* Display the active Com: ports
;***********************************************************************
;

chkcom:
        and  portcnt,0h
        mov  ax,word ptr es:[0h]         ; offset of com I/O addresses
        cmp  ax,0h
        je   no_com1
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg47             ; com1:
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_com1:
        mov  ax,word ptr es:[02h]
        cmp  ax,0h
        je   no_com2
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg48             ; com2:
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_com2:
        mov  ax,word ptr es:[04h]
        cmp  ax,0h
        je   no_com3
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg48A            ; com3: 
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_com3:
        mov  ax,word ptr es:[06h]
        cmp  ax,0h
        je   no_com4
        inc  portcnt
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg48B            ; com4: 
	call dostty
        call disp_hex
        mov  al,'h'
        call stdout
no_com4:
        cmp  portcnt,0h
        je   no_coms
        jmp  next_chk
no_coms:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset msg50             ; no com ports found
	call dostty
next_chk:
        dec  optcnt
	call pause

;***********************************************************************
;* Display DOS Invar Table Contents.                                   *
;***********************************************************************
invar_chk:
        cmp  inv_flag,0FFh               ; switch set for invar list?
        je   disp_invar                  ; yes
        jmp  chk_dev_drv                 ; no - bypass it.
disp_invar:                              ; list "Invars" table contents
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset scr1
	call dostty
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset space1
        call dostty
        mov  ah,52h                      ; undocumented function DOS 2.x or...
        int  21h                         ; ...greater. Returns pointer to a...
        push es                          ; ... DOS internal structure in es:bx
        push bx                          ; save for later
        push bx                          ;
        pop  sav_off                     ;
        mov  ax,es                       ; get Invars seg address for display
        call disp_hex                    ; display it.
        call split                       ; print the colon.
        mov  bx,sav_off                  ; restore offset
        mov  ax,bx                       ; get Invars off address for display
        call disp_hex
	mov  dx,offset space4
        call dostty
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+2]                ; get DPB segment address for display
        mov  dpb_seg,ax
        call disp_hex
        call split
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx]                  ; get DPB offset address for display
        mov  dpb_off,ax
        call disp_hex
	mov  dx,offset space4
        call dostty
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+6]                ; get cache buffer seg addr 
        call disp_hex
        call split
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+4]                ; get cache buffer offset addr 
        call disp_hex
	mov  dx,offset space6
        call dostty
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+10h]              ; get cache buffer size for display
        add  ax,10h                      ; add in size of overhead bytes (16)
        call disp_hex
	mov  dx,offset space8
        call dostty
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+18h]              ; get drive/directory table seg addr 
        call disp_hex
        call split
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+16h]              ; get drive/directory table off addr 
        call disp_hex
	mov  dx,offset space5
        call dostty
        mov  ah,34h                      ; svc 34h = get DOS "Busy" flag addr
        int  21h
        push bx
        pop  sav_off
        mov  ax,es                       ; get DOS busy seg address for display
        call disp_hex
        call split
        mov  bx,sav_off                  ; restore offset
        mov  ax,bx                       ; get DOS busy off address for display
        call disp_hex
;
;***********************************************************************
;* Display Disk Paramter Blocks.
;***********************************************************************
dpb:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset scr1A
	call dostty
	mov  ax,dpb_seg
	mov  es,ax
	mov  bx,dpb_off
dpb_loop:
	mov  dx,offset newline		 
        call dostty
        mov  al,byte ptr es:[bx]         ; get drive id
        add  al,41h                      ; convert to ascii letter
        call stdout
        call split
	mov  dx,offset space2
        call dostty
        mov  al,byte ptr es:[bx+01h]     ; get unit number (watch VDISKs)
                                         ; media descriptor FEh indicates vdisk?
        add  al,31h                      ; convert to ascii numeral
        call stdout
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+02h]     ; bytes per sector
        mov  cx,word ptr es:[bx+05h]     ; num of bits to shift BPS to get BPC
        shl  ax,cl                       ; multiply it
        call disp_hex
	mov  dx,offset space2		 ; 
        call dostty
        mov  al,byte ptr es:[bx+04h]     ; get Sectors Per Cluster minus 1
        cmp  al,0FEh                     ; this value indicates that ...
        je   not_read                    ; ... the drive was never accessed
        jmp  was_read
not_read:
	mov  dx,offset notread
        call dostty
	mov  dx,offset space2
        call dostty
        jmp  res_sect
was_read:
        inc  al                          ; make SPC-1 SPC
        add  al,30h                      ; make ascii char
        call stdout
	mov  dx,offset space3
        call dostty
res_sect:
        mov  ax,word ptr es:[bx+06h]     ; Reserved Sectors
        call disp_hex
	mov  dx,offset space2
        call dostty
        mov  al,byte ptr es:[bx+08h]     ; get Copies of FAT
        add  al,30h                      ; make ascii char
        call stdout
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+09h]     ; Maximum Directory Entries
        call disp_hex
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+0Bh]     ; First Usable Sector
        call disp_hex
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+0Dh]     ; Total Cluster Count plus 1
        dec  ax                          ; make it TCC
        call disp_hex
	mov  dx,offset space3
        call dostty
        mov  al,byte ptr es:[bx+0Fh]     ; get Sectors in FAT
        mov  sav_code,al                 ; display low order byte
	mov  cx,1	                 ; count of bytes to display
        push bx
	lea  bx,[sav_code]
        call show_bytes
        pop  bx
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+10h]     ; get First Directory Sector
        call disp_hex
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+14h]     ; get dev drv seg addr 
        call disp_hex
        call split
        mov  ax,word ptr es:[bx+12h]     ; get dev drv off addr 
        call disp_hex
	mov  dx,offset space2
        call dostty
        mov  ax,word ptr es:[bx+16h]     ; get Media Descriptor
        cmp  al,00h                      ; if zero, drive was never accessed
        jne  good_md
	mov  dx,offset notread
        call dostty
	mov  dx,offset space2
        call dostty
        jmp  next_link
good_md:
        mov  sav_code,al                 ; display low order byte
	mov  cx,1	                 ; count of bytes to display
        push bx
	lea  bx,[sav_code]
        call show_bytes
        pop  bx
	mov  dx,offset space2
        call dostty
next_link:
        mov  dx,word ptr es:[bx+18h]     ; get off of next dpb
        cmp  dx,0FFFFh                   ; last dpb in chain?
        jne  next_dpb
        jmp  end_dpb
next_dpb:
        mov  ax,word ptr es:[bx+1Ah]     ; get next DPB seg addr 
        call disp_hex
        call split
        mov  ax,word ptr es:[bx+18h]     ; get next DPB off addr 
        call disp_hex
        mov  ax,word ptr es:[bx+1Ah]     ; get seg of next dpb
        mov  dx,word ptr es:[bx+18h]     ; get off of next dpb
        mov  es,ax                       ; establish addressibility
        mov  bx,dx
        jmp  dpb_loop
end_dpb:
	mov  dx,offset lastdpb
        call dostty
	mov  dx,offset newline
        call dostty
        dec  optcnt
        call pause
        pop  bx
        pop  es
;
;***********************************************************************
;* Display Device Driver Info.
;***********************************************************************
chk_dev_drv:
        cmp  drv_flag,0FFh               ; switch set for device driver list?
        je   nul_dev                     ; yes
        jmp  chk_tsr                     ; no - bypass driver list

nul_dev:                                 ; list device driver section
        mov  ah,52h                      ; undocumented function DOS 2.x or...
        int  21h                         ; ...greater. Returns pointer to a...
        push es                          ; ... DOS internal structure in es:bx
        push bx                          ; save for later
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset scr2              
	call dostty
	pop  bx
	pop  es
        cmp  dos_vers+1,03h              ; Version dependent...
        je   add_34                      ;
        add  bx,17h                      ; add 23 byte offset if DOS 2.x
        jmp  is_two
add_34:
        add  bx,22h                      ; add 34 byte offset if DOS 3.x
is_two:
        mov  devh_seg,es                 ; segment of device header chain in es
        mov  devh_off,bx                 ; offset of NUL device now in bx
        mov  ax,es:[bx+2]                ; get seg of next driver in chain
        mov  next_seg,ax                 ; save it
        mov  ax,es:[bx]                  ; get offset of next driver in chain       
        mov  next_off,ax                 ; save it

drv_loop:
        cmp  ax,0FFFFh                   ; end of chain?
        jne  more_drv
        jmp  end_drv
more_drv:
        mov  ax,es:[bx+2]                ; get seg of next driver in chain
        mov  next_seg,ax                 ; save it
        mov  ax,es:[bx]                  ; get off of next driver in chain       
        mov  next_off,ax                 ; save it
	mov  dx,offset newline		 
        call dostty
        mov  sav_off,bx                  ; save offset
        mov  ax,es:[bx+04h]              ; attribute word
        mov  type_drv,ax                 ; put it where I can get it
        test ax,8000h                    ; what kind of device: blk or char?
        jz   is_block
        mov  dx,offset char_dev          ; is character device
        call dostty                      ; display the word "Char"
        jmp  is_char
is_block:
        mov  dx,offset blk_dev           ; is block device
        call dostty                      ; display the word "Block"
        call blk_name                    ; format the block device name
        jmp  clean_up_name
is_char:
        mov  ax,word ptr es:[bx+0Ah]     ; move name to my buffer
        mov  word ptr sav_name+0,ax
        mov  ax,word ptr es:[bx+0Ch]
        mov  word ptr sav_name+2,ax
        mov  ax,word ptr es:[bx+0Eh]
        mov  word ptr sav_name+4,ax
        mov  ax,word ptr es:[bx+10h]
        mov  word ptr sav_name+6,ax
        mov  ax,2020h                    ; pad name to 12 chars
        mov  word ptr sav_name+8,ax
        mov  word ptr sav_name+10,ax
        mov  word ptr sav_name+12,ax
        mov  byte ptr sav_name+13,0FFh   ; the field termination character
clean_up_name:
        push bx
        mov  bx, offset sav_name
scan2:
        cmp  byte ptr[bx],0FFh           ; are we done? 
        je   end_scan2                   ; go display it
        cmp  byte ptr[bx],20h            ; less than a space?
        jb   hide_it2
        cmp  byte ptr[bx],'z'            ; greater than lower case z?
        jna  bump_bx2                    ; if not it's printable
        
hide_it2:
        mov  byte ptr[bx],20h            ; make nonsense characters a space
bump_bx2:
        inc  bx                          ; increment the address
        jmp  scan2                       ; go do it again
end_scan2:
        pop  bx
        mov  dx,offset sav_name          ; print dev name even if block dev
        call disp_dev_drv
        mov  dx,offset space2
        call dostty
        mov  ax,es                       ; get this driver's seg addr
        call disp_hex
        call split

        mov  bx,sav_off                  ; restore offset
        mov  ax,bx                       ; get this driver's off addr
        call disp_hex
	mov  dx,offset space7		 ; 
        call dostty

                                         ; strategy offset
        mov  bx,sav_off                  ; restore offset
        mov  ax,es:[bx+06h]              ; get strategy offset
        call disp_hex
	mov  dx,offset space3		 ; 
        call dostty

                                         ; interrupt offset
        mov  bx,sav_off                  ; restore offset
        mov  ax,word ptr es:[bx+08h]     ; get interrupt offset
        call disp_hex
	mov  dx,offset space6		 ; 
        call dostty

        mov  bx,sav_off                  ; restore offset
        mov  ax,word ptr es:[bx+04h]     ; get device attribute word
        call disp_hex

	mov  dx,offset space5		 ; 
        call dostty
        mov  ax,type_drv                 ; check DOS version this driver
        and  ah,08h                      ; ...requires
        cmp  ah,08h                      ;
        jne  not_rm                      ; OCREM not supported - a 2.x driver
        mov  dx,offset msg99             ; OCREM is  supported - a 3.x driver
        jmp  is_rm
not_rm:
        mov  dx,offset msg98
is_rm:
        call dostty
        mov  bx,sav_off                  ; restore offset
        mov  es,next_seg                 ; get seg of next driver in chain
        mov  bx,next_off                 ; get off of next driver in chain       
        mov  ax,bx                       ; set up check for end of chain
        jmp  drv_loop
end_drv:
        dec  optcnt
	call pause

;***********************************************************************
;* Display Memory Control Blocks
;***********************************************************************
chk_tsr:                                 ; list MCBs
        cmp  mem_flag,0FFh               ; switch set for MCB list?
        je   mem_alloc                   ; yes - go do it
        jmp  quit                        ; no - quit

mem_alloc:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset scr3              ; Memory Allocation Screen
	call dostty
        mov  ah,52h
        int  21h
	mov  ax,es:[bx-2]                ; contains seg address of 1st MCB
	mov  es,ax                       ; establish addressibility
	xor  bx,bx                       ; offset will always be zero

mcb_loop:
        and  env_flag,0h                 ; set environment found flag "off"
        and  psp_flag,0h                 ; set PSP found flag "off"
        and  sys_flag,0h                 ; set system block found flag "off"
        and  tsr_flag,0h                 ; set TSR flag "off"
        and  mt_flag,0h                  ; set empty block found flag "off"

        cmp  byte ptr es:[bx],5Ah        ; hex 5A is last MCB
        jne  mcb_2
        jmp  last_mcb                    ; process last one differently
mcb_2:
        mov  mcbseg,ax                   ; save for later
	mov  dx,offset newline		 
        call dostty
	xor  bx,bx                       ; offset will always be zero
        call disp_hex
        mov  dx, offset space2           ; will change as logic is inserted
        call dostty
	xor  bx,bx                       
chk_sys:
        mov  ax,word ptr es:[bx+1]       ; get seg addr this MCB allocates for
        cmp  ax,0008h                    ; DOS block always has 0008h here
        jne  chk_dealloc
        or   sys_flag,0FFh
        and  firstsys,0h
chk_dealloc:                             ; was this block deallocated?
        push ax
        mov  ax,word ptr es:[bx+1]       ; get seg addr this MCB allocates for
        call disp_hex
        mov  dx, offset space3
        call dostty
	xor  bx,bx                       ; offset will always be zero
        pop  ax
        cmp  ax,0000h                    ; is block begin zero?
        jne  paras                       ; if yes, this is empty block
        or   mt_flag,0FFh                ; set flag (block was deallocated)
paras:
        mov  ax,word ptr es:[bx+3]       ; get number of paragraphs allocated
        call disp_hex
        mov  dx, offset space2
        call dostty
	xor  bx,bx                       ; 
search_for_env:                          ; does this MCB allocate for an Env?
        and  env_flag,0h                 ; clear flag
        cmp  byte ptr es:[bx+10h],43h    ; looking for "C" in "COMSPEC="
        jne  no_autoexec              ; yes = last MCB controls environment
        cmp  byte ptr es:[bx+11h],4Fh    ; looking for "O" in "COMSPEC="
        jne  no_autoexec                 ; if not, maybe no autoexec.bat
        jmp  env_found
no_autoexec:                             ; if no autoexec, 1st string is PATH=
        cmp  byte ptr es:[bx+10h],50h    ; looking for "P" in "PATH="
        jne  search_for_psp              ; yes = last MCB controls environment
        cmp  byte ptr es:[bx+11h],41h    ; looking for "A" in "PATH="
        jne  search_for_psp              ; if not, no environment
env_found:
        cmp  firstenv,01h                ; first environment? 
        je   first_env
        call get_name                    ; get the name of the process
        or   tsr_flag,0FFh
first_env:
        and  psp_flag,0h                 ; can't be both
        or   env_flag,0FFh               ; set flag
        jmp  type_blk

search_for_psp:                          ; does this MCB allocate for a PSP?
        and  psp_flag,0h                 ; clear flag
        cmp  byte ptr es:[bx+10h],0CDh   ; looking for an Int 20h in PSP
        jne  type_blk
        cmp  byte ptr es:[bx+11h],20h    ; 
        jne  type_blk
        and  env_flag,0h                 ; can't be both
        or   psp_flag,0FFh               ; set flag for others
        xor  bx,bx
        cmp  firstpsp,01h
        je   type_blk
        or   tsr_flag,0FFh
type_blk:
        cmp  psp_flag,0FFh               ; PSP?
        jne  type2 
        mov  dx,offset psptype
        jmp  end_type
type2:
        cmp  env_flag,0FFh               ; environment? 
        jne  type3 
        mov  dx,offset envtype
        jmp  end_type

type3:
        cmp  mt_flag,0FFh                ; empty block?
        jne  type4 
        mov  dx,offset mttype
        jmp  end_type
type4:
        cmp  sys_flag,0FFh               ; system block?
        jne  type5 
        mov  dx,offset systype
        jmp  end_type
type5:
        call data_name                   ; save the filename if any
        mov  dx,offset datatype          ; it's a data block 
end_type:
        call dostty
        xor  bx,bx

owner:
        mov  dx,offset space1
        call dostty
        cmp  sys_flag,0FFh               ; is this THE DOS block?
        jne  owner1  
        mov  dx,offset sysmsg            ; assume system block 
        jmp  end_owner
owner1:                                  ; 
        cmp  psp_flag,0FFh               ; if it's a psp and...
        jne  owner2                      ; 
        cmp  firstpsp,01h                ; ...not the first PSP...
        jne  is_tsr                      ; 
        mov  dx,offset cmndmsg           ; it's command.com
        jmp  end_owner
owner2:
        cmp  env_flag,0FFh               ; is it an environment block?
        jne  owner3
        cmp  firstenv,01h                ; is it the DOS environment block?
        jne  is_tsr
        mov  dx,offset cmndmsg           ; yes, it belongs to COMMAND.COM
        jmp  end_owner
owner3:
        cmp  mt_flag,0FFh                ; is this block empty?
        jne  owner4
        mov  dx,offset mtblkmsg          ; assume that this block is empty
        jmp  end_owner
owner4:                                  ; must assume this is a data block
        mov  dx,offset dataname          ; display last env name
        jmp  end_owner
is_tsr:                                  ; this block is a TSR   
        mov  dx,offset filename
end_owner:
        call dostty                      ; 
        xor  bx,bx

comments:
        mov  dx,offset space2
        call dostty
comment1:
        cmp  env_flag,0FFh               ; environment? 
        jne  comment2 
        cmp  firstenv,01h                ; first environment? 
        je   comment4
        mov  dx,offset envmsg            ; process environment
        jmp  end_comment
comment2:
        cmp  sys_flag,0FFh               ; is this a system owned block?
        jne  comment3
        mov  dx,offset drvmsg            ; dev drvs, etc.
        jmp  end_comment
comment3:
        cmp  mt_flag,0FFh                ; is this block empty?
        jne  comment5
        mov  dx,offset de_allocmsg       ; assume that this block is empty
        jmp  end_comment
comment4:
        mov  dx,offset mastenv           ; this is the DOS master environment
        and  firstenv,0h                 ; there can be only one
        jmp  end_comment
comment5:
        cmp  psp_flag,0FFh               ; is this a tsr?
        jne  comment7
        cmp  firstpsp,01h                ; first PSP?
        je   comment6
        mov  dx,offset tsrmsg            ; TSR
        jmp  end_comment
comment6:
        mov  dx,offset resdmsg           ; resident part of command.com
        and  firstpsp,0h                 ; there can be only one
        jmp  end_comment
comment7:
        mov  dx,offset datamsg           ; must assume process data
        jmp  end_comment
end_comment:
        call dostty
        xor  bx,bx
;***********************************************************************
;* Display the command line arguments for this TSR
;***********************************************************************
args:
        call chk_args
;***********************************************************************
;* Find and display any interrupt vectors hooked by this TSR.          *
;***********************************************************************
chk_vec:                                 ; print hooked vectors here
        mov  dx,word ptr es:[bx+3]       ; get number of paragraphs allocated
        mov  ax,es                       ; get mcb seg
        inc  ax                          ; account for length of mcb
        mov  blk_beg,ax                  ; this is where the blk begins
        add  ax,dx                       ; add the length of the blk
        mov  blk_end,ax                  ; this is where the blk ends
        push es
        mov  ax,0h
        mov  es,ax
        and  vectcnt,0h
        mov  dx,vectcnt
        mov  cl,4
        mov  cnt,0h
vect_loop:
        shl  dx,1                        ; multiply the interrupt number...
        shl  dx,1                        ; ...by 4 to get offset of vector...
        mov  bx,dx                       ; .....in table.
        mov  ax,word ptr es:[bx+2]       ; get the vector segment
        mov  dx,word ptr es:[bx]         ; get the vector offset
        shr  dx,cl                       ; divide by 16 to get paragraphs
        add  ax,dx                       ; add them to get seg addr of entry
        cmp  ax,blk_beg                  ; compare the mcb seg and int seg
        jnb  chk_end                     ; int seg is greater...chk end seg
        jmp  not_hooked
chk_end:
        cmp  ax,blk_end                  ; compare the ending seg and int seg
        jna  is_hooked                   ; if int seg less, vector is hooked
        jmp  not_hooked                  ; ...otherwise it's not.
is_hooked:
        mov  ax,vectcnt                  ; print the interrupt number
        mov  sav_addr,ax                 ; put it where display routine wants
	mov  cx,1	                 ; count of bytes to display
	lea  bx,[sav_addr]
        call show_bytes                  
        mov  dx, offset space1
        call dostty
        inc  cnt
        cmp  cnt,05h
        jbe  not_hooked                  ; see if we have to wrap the line
        and  cnt,0h
	mov  dx,offset newline
        call dostty
        call chk_screen
        mov  dx,offset space12
        call dostty                      ; align the wrap line
        mov  dx,offset space12
        call dostty
        mov  dx,offset space12
        call dostty
        mov  dx,offset space12
        call dostty
        mov  dx,offset space11
        call dostty

not_hooked:
        mov  cl,4
        inc  vectcnt                     ; increment interrupt type number
        cmp  vectcnt,078h                ; not interested in types > 78h
        ja   done_vect
        mov  dx,vectcnt
        jmp  vect_loop
done_vect:
        call chk_screen
        pop  es
end_mcb_loop:                              
	xor  bx,bx                       ; offset will always be zero
        mov  ax,mcbseg                   ; seg addr of MCB
        mov  dx,word ptr es:[bx+3]       ; get number of paragraphs allocated
        add  ax,dx                       ; add paragraphs allocated to seg addr
        inc  ax                          ; +16 to account for the MCB length
        mov  es,ax                       ; this is the new seg addr
        jmp  mcb_loop

;***********************************************************************
;* This is the MCB for snoop.com                                       *
;***********************************************************************
last_mcb:
        or   psp_flag,0FFh
	mov  dx,offset newline		 
        call dostty
        xor  bx,bx
        call disp_hex
        mov  dx, offset space2           
        call dostty
        xor  bx,bx
        mov  ax,word ptr es:[bx+1]       ; get seg addr this MCB allocates for
        call disp_hex
        mov  dx, offset space3
        call dostty
        xor  bx,bx
        mov  ax,word ptr es:[bx+3]       ; get number of paragraphs allocated
        call disp_hex
        mov  dx, offset space2
        call dostty
        mov  dx, offset psptype
        call dostty
        mov  dx,offset space1
        call dostty
        mov  dx,offset filename
        call dostty
        mov  dx,offset space2
        call dostty
        mov  dx,offset thismsg           ; assume that this block is free
        call dostty
        call chk_args
	mov  dx,offset newline		 
        call dostty
;***********************************************************************
;* End of Job.                                                         *
;***********************************************************************
quit:
	mov  ax,4C00h		         ; Return code for normal completion
	int  21h			 ; return to DOS
main	endp
;
;
;***********************************************************************
;
;  Called procedures (Near)
;
;***********************************************************************
;
;***********************************************************************
;* DOS TTY Write
;***********************************************************************
dostty  proc near
        push ax
	mov  ah,09h			 ; ah=09h: display string at ds:dx
	int  21h
        pop  ax
	ret
dostty  endp

;***********************************************************************
;* DOS Display Single Character
;***********************************************************************

dos_char proc near
        push ax
        mov  ah,2                        ; ah=02h: display char in dl
        int  21h
        pop  ax
        ret
dos_char endp

;***********************************************************************
;* Prints a colon between seg and offset addresses
;***********************************************************************
split	proc near
	push ax
	mov  al,colon
	call show_ascii
	pop  ax
	ret
split	endp

;***********************************************************************
;* Prints a single space
;***********************************************************************
space	proc near
	push ax
	mov  al,blank
	call show_ascii
	pop  ax
	ret
space	endp

;***********************************************************************
;* Converts Hex numbers for display.
;***********************************************************************
show_bytes proc	near
	push ax
	push bx
	push cx
sbs_loop:
	mov  al,[bx]
	call show_byte
	inc  bx
	loop sbs_loop
	pop  cx
	pop  bx
	pop  ax
	ret
show_bytes endp

;***********************************************************************
;* Convert the hex character.
;***********************************************************************
show_byte proc near
	pushf
	push cx
	push ax
	mov  cl,4h	;shift rt 4 bits
	shr  al,cl	;to isolate m.s. nybble
    call testx  ;display hex contents
	pop  ax	        ;retrieve al
	and  al,0fh	;isolate l.s. nybble
    call testx  ;display hex contents
	pop  cx
	popf
	ret
show_byte endp

;***********************************************************************
;* Determines if hex digit is a letter.
;***********************************************************************
testx   proc near
	cmp  al,9h	;is nybble letter?
	ja   letter	;if so convert to letter
	or   al,30h	;if not add '30' code
    jmp  disply
letter:
	sub  al,9h	;reduce letter nybble by 9
	or   al,40h	;add '40' code
disply:
	call show_ascii
	ret	
testx   endp

;***********************************************************************
;* Displays ASCII characters.
;***********************************************************************
show_ascii	proc 	near
	push ax
	push dx
	mov  dl,al
	mov  ah,02h			;ah=02: display char in dl 		
	int  21h
        pop  dx
	pop  ax
	ret
show_ascii endp
;
;*************************************************************************
;* dec16out - Routine to convert internal 16 bit binary to ASCII decimal *
;*************************************************************************
dec16out proc near
dec16out0:
	push ds
	push di
	push dx
	push cx
	push ax
	mov ax,cs
	mov ds,ax
                                         ; a binary number is in dx
                                         ; put the digits in a temporary buffer
	mov cx,0
	mov di, offset cs:tbuff          ; point to buffer
dec16out1:
	push cx
	mov  ax,dx		         ; ax has numerator
	mov  dx,0		
	mov  cx,10d		         ; divisor of 10
	div  cx
	xchg ax,dx		         ; get quotient
	add  al,30h
	mov  [di],al		         ; put in buffer
	inc  di			         ; point to next byte
	pop  cx
	inc  cx			         ; count the digit
	cmp  dx,0		         ; done?
	jnz  dec16out1
                                         ; dump the buffer out
dec16out2:
	dec  di			         ; back up through the buffer
	mov  al,[di]
	call stdout
	loop dec16out2
	pop  ax
	pop  cx
	pop  dx
	pop  di
	pop  ds
	ret
dec16out endp
;
;***********************************************************************
;* Display device driver names so embedded "$" while display.
;***********************************************************************
disp_dev_drv proc near
        push bx
        push si                          ; save the count
        xor  si,si
        mov  bx,dx
disp_drv_loop:
        cmp  byte ptr ds:[bx+si],0FFh    ; the field termination character
        je   end_drv_loop
        mov  al,byte ptr ds:[bx+si]      ; set it up for stdout
        call stdout
        inc  si
        cmp  si,0Dh                      ; paranoia: are we done yet?
        je   end_drv_loop
        jmp  disp_drv_loop
end_drv_loop:
        pop  si
        pop  bx
        ret
disp_dev_drv endp
;
;***********************************************************************
;* Sends a character to the std ouput device.
;***********************************************************************
stdout  proc near
	push dx
	mov  dl,al		;in dl for DOS call
	mov  ah,2		;send character to std output
	int  21h
	pop  dx
	ret
stdout  endp                
;***********************************************************************
;* Finds EGA Copyright String.
;***********************************************************************
ega_copy proc near
        cmp  ps2_flag,0h                 ; PS/2 series has onboard VGA
        je   not_vga                     ; and therefore no EGA copyright
        jmp  vga_exit
not_vga:
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset egacopy		 ; copyright header
        call dostty
        push es
        push ds
        push ds
        pop  es                          ; destination string address in es:di
        mov  ax,0C000h                   ; source string address in ds:si
        mov  ds,ax                       ; ...location of IBM EGA copyright
        mov  ax,05h                      ; 
        mov  cx,11h                      ; count of words in copyright
        mov  bx,cx                       ;
        shl  bx,1                        ; multiply by 2 then subtract 1...
        dec  bx                          ; ... to get offset of...
        mov  si,ax                       ; terminator character in dest string
        mov  di,offset es:tbuff
        rep  movsw
        pop  ds
        pop  es
        mov  byte ptr tbuff+[bx],'$'    ; insert terminator
        mov  bx, offset tbuff
scan_copy:
        cmp  byte ptr[bx],'$'           ; are we done?
        je   done_copy                  ; go display it
        cmp  byte ptr[bx],' '           ; less than a space?
        jb   mask_it                    ;
        cmp  byte ptr[bx],'~'           ; greater than a tilde
        jna  inc_bx                     ; if not it's printable
        
mask_it:
        mov  byte ptr[bx],20h           ; make nonsense characters a space
inc_bx:
        inc  bx                         ; increment the address
        jmp  scan_copy                  ; go do it again
done_copy:
        mov  dx,offset tbuff	        ; 
        call dostty
        push es
        push ds
        push ds
        pop  es                          ; destination string address in es:di
	mov  dx,offset newline		 
        call dostty
	mov  dx,offset indent 		 
        call dostty
	mov  dx,offset egadate		 ; date header
        call dostty
        mov  ax,0C000h                   ; source string address in ds:si
        mov  ds,ax
        mov  ax,26h                      ; offset of EGA rom release date
        mov  si,ax
        mov  di,offset es:tbuff
        mov  cx,04h                      ; count of words
        rep  movsw
        pop  ds
        pop  es
        mov  byte ptr tbuff[07h],'$'
        mov  dx,offset tbuff	         ; 
        call dostty
vga_exit:
        ret
ega_copy endp
;***********************************************************************
;* Division Routine.
;***********************************************************************
divide  proc near
        mov  ax,bx
        xor  dx,dx
        div  cx
        mov  bx,dx
        mov  dl,al
        cmp  al,0
        jz   chk_flag
        or   display_flag,al
chk_flag:
        cmp  display_flag,0
        ja   display_it
        ret
display_it:
        add  dl,30h
        call dos_char
        ret
divide  endp
;***********************************************************************
; Pause the scrolling display.
;***********************************************************************
pause   proc near
        cmp  byte ptr pse_flag,0h        ; are we to pause the display?
        jne  no_pause                    ; if pause is zero we will
        cmp  byte ptr optcnt,0h          ; last option?
        jbe  no_pause                    ; if so don't pause
        push dx
	mov  dx,offset msg7		 ; 
        call dostty
	mov  ah,get_key                  ; function number
	int  16h                         ; ascii code for key pressed in ah
        pop  dx
no_pause:
        ret                              ; yes - return to caller
pause   endp

;***********************************************************************
;* Get a block device driver's name into a buffer for display.
;***********************************************************************
blk_name proc near
        mov  al,byte ptr es:[bx+0Ah]     ; first byte is number of units
        add  al,30h                      ; make it ascii
        mov  ah,20h                      ; add a space
        mov  word ptr sav_name,ax        ; store it
        mov  ax,es:[bx+12h]              ; remainder of name...
        mov  word ptr sav_name+2,ax
        mov  ax,es:[bx+14h]              ; ...if any, follows units...
        mov  word ptr sav_name+4,ax
        mov  ax,es:[bx+16h]              ; ...and may include...
        mov  word ptr sav_name+6,ax
        mov  ax,es:[bx+18h]              ; ....some garbage characters...
        mov  word ptr sav_name+8,ax
        mov  ax,es:[bx+1Ah]              ; ...whic is unavoidable...
        mov  word ptr sav_name+10,ax
        mov  ax,es:[bx+1Ch]              ; ...because I want to see the name.
        mov  word ptr sav_name+12,ax
        mov  byte ptr sav_name+13,0FFh   ; the field termination character
        ret
blk_name endp
;

;***********************************************************************
;* Search for Task Name in Command Line in Environment.
;***********************************************************************
get_name proc near
        cmp  dos_vers+1,02h              ; name not available in DOS 2.x
        ja   vers_ok
        mov  arg_cnt,0h                  ; ignore switches
        jmp  quick_x                     ; display spaces for owner name
vers_ok:
        mov  dx,0000h
        mov  bx,10h
        mov  cx,532h                     ; env can't be longer than this
look_for:
        dec  cx
        cmp  cx,0h                       ; have we checked 1,330 chars?
        jne  not_1330                    ; no, continue the search
        mov  arg_cnt,0h                  ; yes, ignore switches
        jmp  quick_x                     ; display spaces for owner name
not_1330:
        inc  bx                          ; a byte at a time.....
        cmp  word ptr es:[bx],dx         ; ...look for back to back ascii nuls
        jne  look_for
        add  bx,2                        ; found them...
        mov  ax,word ptr es:[bx]         ; ... get count of command line args
        mov  arg_cnt,ax
        add  bx,3
        cmp  byte ptr es:[bx],':'         ; ...looking for :\
        je   not_netware
        mov  arg_cnt,0h                  ; ignore switches
        jmp  quick_x                     ; display spaces for owner name
not_netware:
        inc  bx                          ; step over ":"
        cmp  byte ptr es:[bx],5Ch        ; ...looking for "\" path delimiter
        je   not_novelle
        cmp  byte ptr es:[bx],2Fh        ; ...looking for "/" path delimiter
        je   not_novelle                 ; Novelle Netware does not use either
        mov  arg_cnt,0h                  ; won't find task name so ignore
        jmp  quick_x                     ; display spaces for owner name
not_novelle:
        inc  bx                          ; step over the "\" or "/"
        mov  di,offset filename
        mov  si,bx                       ; si is offset of beginning of name
        xor  cx,cx
mov_name:
        cmp  byte ptr es:[si],5Ch        ; looking for embedded '\' in path
        je   restart_scan
        cmp  byte ptr es:[si],2Fh        ; looking for embedded '/' in path
        jne  no_path
restart_scan:
        mov  di,offset filename          ; reset dest offset to beginning
        inc  si                          ; bump si past '\' or '/'
        xor  cx,cx
        jmp  mov_name                    ; go back and start again
no_path:
        cmp  byte ptr es:[si],2Eh        ; looking for '.' to exclude extension
        je   end_name
        cmp  cx,8h                       ; filename is 8 chars long max
        je   fini
        mov  al,byte ptr es:[si]
        mov  byte ptr ds:[di],al
        inc  si
        inc  di
        inc  cx
        jmp  mov_name
end_name:
        cmp  cx,8h                       ; filename is 8 chars long
        jnb  fini
        mov  byte ptr ds:[di],20h
        inc  di
        inc  cx
        jmp  end_name
fini:
        mov  byte ptr ds:[di],'$'
quick_x:
	xor  bx,bx                       ; offset will always be zero
        ret
get_name endp
;***********************************************************************
;* Save the filename for data blocks
;***********************************************************************
;
data_name proc near
        cmp  byte ptr filename,20h       ; will be a space first time around
        jne  not_first
	call get_owner			 ; find the owning task name
not_first:
        cmp  data_flag,0FFh              ; if set, name is already in place
        je   data_exit                   ; exit if it is
        push es                          ; save MCB seg addr
        push ds                          ; establish addressibility
        pop  es                          ; destination string address in es:di
        mov  ax,offset filename          ; offset of filename 
        mov  si,ax                       ; set up source index
        mov  di,offset es:dataname       ; set up destination index
        mov  cx,04h                      ; count of words (8 bytes)
        rep  movsw                       ; do the move
        mov  byte ptr dataname[08h],'$'  ; string terminator for DOS
        or   data_flag,0FFh              ; set it for next time
        pop  es                          ; restore MCB seg addr
data_exit:
        ret
data_name endp
;
;***********************************************************************
;* Get name of task that owns this data block.
;***********************************************************************
get_owner proc near
	push es
	push bx
	xor  bx,bx
	mov  ax,word ptr es:[bx+1] 	 ; get seg addr of blk this MCB controls
	mov  es,ax                       ; es now points to PSP of owner
	mov  ax,word ptr es:[bx+2Ch]     ; get seg addr of environment
	mov  es,ax                       ; es now points to Env of owner
	call get_name                    ; call the normal get name routine	
	pop  bx
	pop  es
	ret
get_owner endp
;
;***********************************************************************
;* Display Hex Numbers.
;***********************************************************************
disp_hex proc near
        push bx
        xchg ah,al                       ; get it turned around
        mov  sav_addr,ax                 ; 
	mov  cx,2	                 ; count of bytes to display
	lea  bx,[sav_addr]
        call show_bytes
        pop  bx
        ret
disp_hex endp
;
;***********************************************************************
;*  Check for command line switches in the TSR's PSP.                  *
;***********************************************************************
chk_args proc near
        push bx
        cmp  psp_flag,0FFh               ; is this a PSP?
        jne  no_args
        cmp  arg_cnt,0h                  ; any command line switches?
        je   no_args
        mov  bx,90h                      ; offset of count (including MCB)
        mov  ax,es:[bx]                  ; PSP arg cnt
        cmp  ax,0h                       ; any args?
        je   end_args
        inc  bx                          ; point to tail
arg_loop:
        cmp  byte ptr es:[bx],0Dh        ; CR ends tail
        je   end_args
        mov  al,es:[bx]
        call stdout
        inc  bx                          ; bump offset
        cmp  bx,9Bh                      ; only room for 9 characters
        jb   arg_loop
end_args:
        sub  bx,91h                      ; get number of spaces to print
        cmp  bx,0h
        jbe  no_args                     ; if negative, no args        
        mov  cx,0Ch
        sub  cx,bx
fill_loop:
        mov  al,20h
        call stdout
        loop fill_loop
        jmp  exit_args
no_args:
        mov  dx,offset space12           ; change # of spaces for args print
        call dostty                      ; 
exit_args:
        pop  bx
        ret
chk_args endp
;***********************************************************************
;* See if 80286 or 80386 CPU using Intel approved method
;***********************************************************************
chk_cpu proc near
        push ax
        mov  cpu_flag,0h                 ; clear it
        pushf                            ; save original flags
        pushf                            ; make extra copy of flags register..
        pop  ax                          ; bits 12-14 can be set on a 386 only
        or   ax,0111000000000000b        ; turn on bits 12-14
        push ax                          ; 
        popf                             ; pop this value back into flags reg
        pushf                            ; 
        pop  ax                          ; look at results
        and  ax,0111000000000000b        ; see if bits 12-14 are set
        jnz  is_32_bit                   ; if they're set, it's a 386 cpu        
is_16_bit:
        popf                             ; restore flags
	or   byte ptr cs:cpu_flag,02h    ; indicate 286 cpu
        jmp  cpu_exit
is_32_bit:
        popf                             ; restore flags
	or   byte ptr cs:cpu_flag,03h    ; indicate 386 cpu
        jmp  cpu_exit
cpu_exit:
        pop  ax
        ret
chk_cpu endp
;
;***********************************************************************
;* Check to see if this is an IBM Personal System/2 Machine    *
;***********************************************************************
; Model Id Byte values and meanings:
;
; IBM Personal System/2 Model 30
; at address F000:FFFE FA 
;
; IBM Personal System/2 Model 50
; at address E000:7AF9 FC 04 <====Rom Revision Level 4.
;
; IBM Personal System/2 Model 60
; at address E000:7B03 FC 05 <====Rom Revision Level 5.
;
; IBM Personal System/2 Model 80
; at address E000:xxxx F8 00           F8 is an educated guess
;
chk_ps2 proc near
        and  ps2_flag,0h
        cmp  word ptr es:[bx+2],04FCh
        je   model_50
        cmp  word ptr es:[bx+2],05FCh
        je   model_60
        cmp  byte ptr es:[bx+2],0F8h
        je   need_386
        jmp  unkn_model
need_386:
        call chk_cpu
	cmp  byte ptr cs:cpu_flag,03h    ; is it a 386 cpu?
        je   model_80
        jmp  unkn_model                  ; it's an 80286, 8088, or 8086 
model_50:
	or   ps2_flag,50h
	mov  dx,offset msg1F
        call dostty
        jmp  end_ps2
model_60:
	or   ps2_flag,60h
	mov  dx,offset msg1G
        call dostty
        jmp  end_ps2
model_80:
	or   ps2_flag,80h
	mov  dx,offset msg1H
        call dostty
        jmp  end_ps2
unkn_model:                              ; probably a PC Convertible
end_ps2:
        ret                    
chk_ps2 endp
;
;***********************************************************************
;* chk_mod30 - Check if this is a PS/2 Model 30.
;***********************************************************************

chk_mod30 proc near
	cmp  al,0FAh 		         ; PS/2 Model 30?
        je   model_30
        jmp  end_30
model_30:
	or   ps2_flag,30h
	mov  dx,offset msg1E
        call dostty
end_30:
        ret
chk_mod30 endp

;
;***********************************************************************
;* chk_switch - Check command line switches for options requested.     *                           *
;***********************************************************************
chk_switch proc near
        mov  cl,ds:[0080h]               ; command line arg count in PSP
        cmp  cl,0h                       ; any args?
        ja   swt_set
        jmp  end_swt
swt_set:
        mov  bx,80h                      ; offset-1 of command tail in PSP
swt_char:
        inc  bx
        cmp  byte ptr ds:[bx],0Dh       ; CR ends command tail
        jne  more_swt
        jmp  end_swt
more_swt:
        cmp  byte ptr ds:[bx],'/'
        je   fnd_swt
        cmp  byte ptr ds:[bx],'\'
        je   fnd_swt
        cmp  byte ptr ds:[bx],' '
        je   fnd_swt
        cmp  byte ptr ds:[bx],'-'
        je   fnd_swt
        jmp  swt_char
fnd_swt:
        inc  bx
        or   byte ptr ds:[bx],20h
        cmp  byte ptr ds:[bx],'n'
        jne  chk_equ
        or   pse_flag,0FFh
        and  byte ptr one,0h             ; value to add to the line counter
        jmp  swt_char
chk_equ:
        cmp  byte ptr ds:[bx],'e'
        jne  chk_inv
        or   equ_flag,0FFh
        inc  optcnt
        jmp  swt_char
chk_inv:
        cmp  byte ptr ds:[bx],'i'
        jne  chk_drv
        or   inv_flag,0FFh
        inc  optcnt
        jmp  swt_char
chk_drv:
        cmp  byte ptr ds:[bx],'d'
        jne  chk_mcb
        or   drv_flag,0FFh
        inc  optcnt
        jmp  swt_char
chk_mcb:
        cmp  byte ptr ds:[bx],'m'
        jne  all_chk
        or   mem_flag,0FFh
        inc  optcnt
        jmp  swt_char
all_chk:
        cmp  byte ptr ds:[bx],'a'
        jne  klone_chk
        or   equ_flag,0FFh
        or   inv_flag,0FFh
        or   drv_flag,0FFh
        or   mem_flag,0FFh
        or   argc,0FFh
        jmp  swt_char
klone_chk:                               ; option for MS-DOS boxes
        cmp  byte ptr ds:[bx],'x'        ; which are not IBM ROM...
        jne  last_chk
        and  equ_flag,0h                 ; ... BIOS compatible. 
        or   inv_flag,0FFh
        or   drv_flag,0FFh
        or   mem_flag,0FFh
        or   argc,0FFh
        jmp  swt_char
last_chk:
        dec  bx
        jmp  swt_char
end_swt:
        cmp  argc,0FFh
        jne  not_all
        mov  optcnt,4
not_all:
        cmp  optcnt,1                    ; must specify one switch at least
        jnb  lv_swt
        mov  dx,offset usemsg
        call dostty
	mov  ax,4C01h		         ; Return code for abnormal completion
	int  21h			 ; return to DOS
lv_swt:
        ret
chk_switch endp
;
;***********************************************************************
;* Check the switch char for path names - is it "/" or "\"?
;***********************************************************************
switch_char proc near
        mov  ax,3700h                    ; request switchar be returned in dl
        int  21h
        cmp  dl,5CH                      ; are they using "\" for switches?
        jne  not_unix_lover              ; no, "/" for switches, "\" for paths
        cmp  dl,2FH                      ; are they using "/" for switches?
        jne  func_not_working            ; if no, the function is not working
        mov  switchar,5Ch                ; change it around for unix lovers
        mov  pathchar,2Fh                ; least they be troubled... :-)
        jmp  func_works
not_unix_lover:
        cmp  dl,2FH                      ; are they using "/" for switches?
        je   func_works                  ; the undocumented function works
func_not_working:
        mov  al,0FFh                     ; if no, the function is not working
                                         ; leave defaults in place, signal err
func_works:
        ret
switch_char endp
;
;***********************************************************************
;* Check to see if screen is full during MCB display.
;***********************************************************************
chk_screen proc near
        push ax
        push dx
        xor  ax,ax
        mov  al,byte ptr linecnt
        add  al,one                      ; will be 0 if pause, 1 if not
        cmp  al,14h                      ; 20 lines per screen
        jb   not_full
	mov  dx,offset newline
        call dostty
	mov  dx,offset msg7		 ; press any key message
        call dostty
	mov  ah,get_key                  ; function number (0h or 10h)
	int  16h                         ; ascii code for key pressed in ah
        xor  al,al
not_full:
        mov  byte ptr linecnt,al         ; store the updated counter
        pop  dx
        pop  ax
        ret
chk_screen endp
;
cseg	ends
	end  begin
;*****************************************************************************
;* End Of Source Code.
;*****************************************************************************
