;====================================================================== ; MAP 1.00 * Copyright (c) 1992, Robert L. Hummel ; PC Magazine Assembly Language Lab Notes ; ; MAP displays the contents of memory as reported by the DOS memory ; control block (MCB) chain. Interrupt vectors that point to each ; segment are also identified. ;====================================================================== CSEG SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG ORG 100H ;COM format ENTPT: JMP MAIN ;Jump over data ;====================================================================== ; Data for program use is stored here. ;---------------------------------------------------------------------- CR EQU 0DH ;Common equates LF EQU 0AH TAB EQU 09H COPYRIGHT$ DB CR,LF,"MAP 1.00 ",254," Copyright (c) 1992," DB " Robert L. Hummel",CR,LF DB "PC Magazine Assembly Language Lab Notes" CRLFLF$ DB CR,LF,LF,"$" COLHEDS LABEL BYTE DB "----------------------------------------------------------------" DB CR,LF DB "MCB Owner's Parent Num Size Hooked Interrupts",CR,LF DB "Addr Name Block Blks (paras)",CR,LF DB "----------------------------------------------------------------" CRLF$ DB CR,LF,"$" NOMEM$ DB "Not enough memory.",CR,LF,"$" SPC6$ DB " " SPC4$ DB " " SPC3$ DB " $" TABS$ DB CR,LF,5 DUP(9),"$" PSP$ DB "PSP $" ENV$ DB "Env $" DATA$ DB "Data$" CMD_NAME DB "COMMAND " NO_NAME DB "(n/a) " SYS_NAME DB "SYSTEM " FREE_NAME DB "FREE " VECTABLE DB 80H/8 DUP(0) ;1 bit per vector VECTABLELEN EQU $-OFFSET VECTABLE VER DW 0 ;DOS version UMB DB -1 MAXMCB DW 0 NBLKS DB 0 ;---------------------------------------------------------------------- ; These values identify the blocks. If the last bit is zero, the entry ; gets its own line in the list. ;---------------------------------------------------------------------- SYSTEM@ EQU 0 ;000 Unowned data PSP@ EQU 2 ;010 FREE@ EQU 4 ;100 ENV@ EQU 1 ;001 DATA@ EQU 3 ;011 Data owned by someone ;---------------------------------------------------------------------- ; These values are the offsets into the MCB header. ;---------------------------------------------------------------------- OWNER@ EQU 1 SIZE@ EQU 3 ;====================================================================== ; MAIN (Near) ; ; This procedure invokes the subroutines and defines program operation. ;---------------------------------------------------------------------- MAIN PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG CLD ;String moves forward ;---------------------------------------------------------------------- ; Display the program title. ;---------------------------------------------------------------------- MOV DX,OFFSET COPYRIGHT$ ;Display title MOV AH,9 ;Display string fn INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Get and save the DOS version. Used later. ;---------------------------------------------------------------------- MOV AH,30H ;Get DOS version in AX INT 21H ; thru DOS MOV [VER],AX ;Save the version ;---------------------------------------------------------------------- ; If ver 5+, save the current UMB state, then link them. ;---------------------------------------------------------------------- CMP AL,5 ;Dos 5 or later JB M_0 MOV AX,5802H ;Get current UMB link INT 21H ; thru DOS JC M_0 MOV [UMB],AL ;Save it MOV AX,5803H ;Set UMB to MOV BX,1 ; linked in chain INT 21H ; thru DOS M_0: ;---------------------------------------------------------------------- ; If there's not enough memory for at least 20 entries, don't continue. ;---------------------------------------------------------------------- CMP SP,(OFFSET MCBNORM + 15*ENTRYLEN + 512) JA M_1B M_1A: MOV AH,9 ;Display string MOV DX,OFFSET NOMEM$ ; saying not enuf mem INT 21H ; thru DOS JMP M_EXIT ;---------------------------------------------------------------------- ; Use all the memory in the segment. Determine the maximum entries. ; Limited to 255 (FF) by 1-byte index. ;---------------------------------------------------------------------- M_1B: MOV AX,SP ;End of segment MOV DI,OFFSET MCBNORM ;Start of new table SUB AX,512 ;Allow for stack SUB AX,DI ;= bytes free SUB DX,DX ; in DX:AX MOV CX,ENTRYLEN ;Length of an entry DIV CX ;AX = DX:AX/CX CMP AX,0FFH ;Max allowed JBE M_1C MOV AX,0FFH ;Limit it M_1C: MOV [MAXMCB],AX ;Save max entries ;---------------------------------------------------------------------- ; Initialize the new part of the table. ;---------------------------------------------------------------------- MUL CX ;DX:AX = CX*AX MOV CX,AX ;Put count in CX SUB AL,AL ;Store zeros REP STOSB ;---------------------------------------------------------------------- ; Point ES:BX to the list of DOS internal variables. ; Figure out the first system and DOS segment sizes. ;---------------------------------------------------------------------- MOV AH,52H ;Get IVARS INT 21H ; thru DOS ASSUME ES:NOTHING ;Changes ES MOV DI,OFFSET MCBTABLE + 3 * ENTRYLEN MOV AX,ES:[BX+2] ;Segment of DOS MOV [DI+ENTRYLEN],AX ;Save segment MOV CX,AX ;Save in CX SUB AX,[DI] ;Calc length and MOV [DI+4],AX ; store in table MOV BP,ES:[BX-2] ;Get 1st MCB header NEG CX ADD CX,BP ;CX=MCB-prev block MOV [DI+ENTRYLEN+4],CX ; gives length MOV DI,OFFSET MCBNORM ;Remainder of table ;---------------------------------------------------------------------- ; Fill in the normal entries for all normal blocks of memory. ;---------------------------------------------------------------------- SUB CX,CX ;Count entries M_2: CMP CX,[MAXMCB] ;Too many is error JAE M_1A INC CX ;Add this one MOV ES,BP ;Point ES to header ASSUME ES:NOTHING MOV [DI+0],BP ;Save MCB adr MOV AX,ES:[OWNER@] ;Save owner MOV [DI+2],AX MOV AX,ES:[SIZE@] ;Save length MOV [DI+4],AX ADD DI,ENTRYLEN ;Point to next entry INC BP ;Point to block ADD BP,AX ;Add len this block CMP BYTE PTR ES:[0],"Z" ;Z=last block JNE M_2 MOV [MAXMCB],CX ;Now=# normal entries ;---------------------------------------------------------------------- ; All memory blocks have been located and placed in our table. ; Scan only for PSP segments and mark them as such. ; 1. Fill in their parent index entries. ; 2. Locate and validate their environments. ; a. Mark env segments as such. ; b. Find PSP's name. ;---------------------------------------------------------------------- MOV DI,OFFSET MCBNORM ;Start of normal MCBs M_3A: MOV BP,[DI] ;Get header adr MOV ES,BP ; and address it ASSUME ES:NOTHING INC BP ;MCB adr CMP BP,[DI+2] ;Does it own itself? JE M_3B ;---------------------------------------------------------------------- ; Block is not a PSP. If block is free (owner=0), mark it as such. ;---------------------------------------------------------------------- CMP WORD PTR [DI+2],0 ;Owner=0? JNE M_3E MOV BYTE PTR [DI+6],FREE@ ;Mark as free JMP SHORT M_3E ;---------------------------------------------------------------------- ; This block is a PSP block (because it owns itself). ; ES addresses the PSP's header. From the PSP, get a pointer to the ; program's parent's PSP. ;---------------------------------------------------------------------- M_3B: MOV BYTE PTR [DI+6],PSP@ ;Mark as PSP MOV BX,ES:[16H+10H] ;Parent adr CALL LOCATE_SEG ;Return BL=index MOV [DI+7],BL ;FF=not found ;---------------------------------------------------------------------- ; From the PSP, get a pointer to the program's environment. ; COMMAND may have its env pointer=0. ;---------------------------------------------------------------------- MOV BX,ES:[2CH+10H] ;Environment adr OR BX,BX ;=0 JZ M_3D ;---------------------------------------------------------------------- ; To be a valid env, the segment adr must be in our table. ;---------------------------------------------------------------------- CALL LOCATE_SEG ;Return BL=index CMP BL,0FFH ;If FF, invalid JZ M_3D ;---------------------------------------------------------------------- ; The environment segment was valid. BX contains the index of the env ; segment in the MCB table. If that entry is owned by current PSP, mark ; the segment as type ENV. ;---------------------------------------------------------------------- MOV AX,ENTRYLEN ;Entry length MUL BX ; * number of entry MOV SI,AX ;Index into table CMP BP,[MCBTABLE][SI+2] ;Is PSP = env owner? JNE M_3D MOV BYTE PTR [MCBTABLE][SI+6],ENV@ ;---------------------------------------------------------------------- ; Get program name of this PSP. ; DOS 5.0+ needs only an MCB adr. ; DOS 3.x-4.x needs a valid PSP. ; DOS 2.x cannot supply the program name. ; ES = PSP header adr ; BX = index of env segment, FF if invalid ; DS:[MCBTABLE][SI] = env seg entry in table if BX valid ;---------------------------------------------------------------------- M_3D: CALL FIND_PSP_NAME ;---------------------------------------------------------------------- ; Loop for all table entries. ;---------------------------------------------------------------------- M_3E: ADD DI,ENTRYLEN ;Move to next entry LOOP M_3A ;---------------------------------------------------------------------- ; Now scan all the blocks again. Skip those that have already been ; identified. If the owner of an unidentified block is a PSP, mark it ; as DATA. If not, mark it as SYSTEM. ;---------------------------------------------------------------------- MOV CX,[MAXMCB] ;Number of blocks MOV DI,OFFSET MCBNORM ;Start of normal MCBs M_4A: CMP BYTE PTR [DI+6],0 ;If classified, skip it JNE M_4B ;---------------------------------------------------------------------- ; Determine if this block's owner is a valid PSP segment. ;---------------------------------------------------------------------- MOV BP,[DI] ;Get header adr MOV ES,BP ; and address it ASSUME ES:NOTHING MOV BX,ES:[OWNER@] ;Segment of owner CALL LOCATE_SEG ;Get table index CMP BL,0FFH ;FF means invalid JE M_4B ;---------------------------------------------------------------------- ; See if the owning segment is a PSP. ;---------------------------------------------------------------------- MOV AX,ENTRYLEN ;Entry length MUL BX ; * number of entry MOV SI,AX ;Index into table CMP BYTE PTR [SI+6],PSP@ ;Is owner PSP? JNE M_4B MOV BYTE PTR [DI+6],DATA@ ;Mark seg as data M_4B: ADD DI,ENTRYLEN ;Goto next entry LOOP M_4A ;---------------------------------------------------------------------- ; Output section. Print out the results by scanning the table again. ;---------------------------------------------------------------------- MOV AH,9 ;Display string MOV DX,OFFSET COLHEDS ;Column headings INT 21H ; thru DOS MOV CX,[MAXMCB] ;Real segments ADD CX,5 ; plus phony ones MOV DI,OFFSET MCBTABLE ;DS:DI->table ;---------------------------------------------------------------------- ; All entries that deserve a line in the display have their TYPE LSB=0. ;---------------------------------------------------------------------- M_5A: MOV BL,BYTE PTR [DI+6] ;BL = TYPE TEST BL,1 ;ZR=printout needed JZ M_5B JMP M_10 M_5B: ;---------------------------------------------------------------------- ; Print out the MCB header address for this entry. ;---------------------------------------------------------------------- MOV AX,[DI] ;Get MCB addr CALL HEX4 ; and display MOV AH,9 ;Display MOV DX,OFFSET SPC3$ ; some spaces INT 21H ; thru DOS ;---------------------------------------------------------------------- ; If the MCB is a PSP or there's an entry in the table for the name, ; print out the name. Otherwise, use a standard name. ;---------------------------------------------------------------------- MOV SI,OFFSET FREE_NAME ;Assume free CMP BL,FREE@ ;See if it is JE M_6A LEA SI,[DI+8] ;Point to table name CMP BL,PSP@ ;Is it PSP? JE M_6A CMP BYTE PTR [SI],0 ;=0 means no name here JNE M_6A MOV SI,OFFSET SYS_NAME ;Use default name M_6A: PUSH CX ;Save counter MOV CX,8 ;Chars to display M_6B: LODSB MOV DL,AL MOV AH,2 ;Display char INT 21H ; thru DOS LOOP M_6B POP CX ;Restore counter MOV AH,9 ;Display string MOV DX,OFFSET SPC3$ ;3 spaces INT 21H ; thru DOS ;---------------------------------------------------------------------- ; If MCB is a PSP, print out the MCB address of the parent. ; For system and free segments, skip this field. ;---------------------------------------------------------------------- CMP BL,PSP@ ;Is seg a PSP? JE M_7A MOV AH,9 ;Display string MOV DX,OFFSET SPC4$ ; blank field INT 21H JMP SHORT M_7B M_7A: MOV AL,[DI+7] ;Parent index MOV AH,ENTRYLEN ; *length MUL AH ; =offset ADD AX,OFFSET MCBTABLE ;Put effective addr MOV SI,AX ; into SI MOV AX,[SI] ;Get MCB CALL HEX4 ; and display it M_7B: MOV AH,9 ;Display string MOV DX,OFFSET SPC3$ ;3 spaces INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Count the segments owned by this block. ; Find which interrupt vectors point to owned segments. ;---------------------------------------------------------------------- CALL SUM_SEGS ;Ident segs, set vecs MOV [NBLKS],AL ;Save # segs CALL HEX2 ;Display # of segs MOV AH,9 ;Display string MOV DX,OFFSET SPC3$ ;3 spaces INT 21H ; thru DOS MOV AX,BX ;Get size in paras CALL HEX4 ;Display it MOV AH,9 ;Display string MOV DX,OFFSET SPC6$ ;6 spaces INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Print the hooked interrupt vectors as returned in VECTABLE by the ; previous call to SUM_SEGS. ;---------------------------------------------------------------------- PUSH CX ;Save counter MOV BL,1 ;Bit mask MOV BH,1 ;Row counter MOV SI,OFFSET VECTABLE ;Bit array location MOV CH,77H ;Search this many MOV CL,0 ;Prime current int TEST BL,[SI] ;NZ = bit set JNZ M_8B JMP SHORT M_8C ;---------------------------------------------------------------------- ; Loop for all interrupts and write to display. ;---------------------------------------------------------------------- M_8A: INC CL ;CL=current int CMP CL,CH ;CL>CH when done JA M_8D TEST BL,[SI] ;Is bit set? JZ M_8C ;---------------------------------------------------------------------- ; The bit is set for this vector. ; Determine if we need to move to a new line BEFORE we print. ;---------------------------------------------------------------------- ROL BH,1 ;CY=row full JNC M_8B MOV AH,9 ;Print string MOV DX,OFFSET TABS$ ; create new row INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Print the two-digit interrupt #. ;---------------------------------------------------------------------- M_8B: MOV AL,CL ;Display this vector CALL HEX2 ; as 2 digits MOV AH,2 ;Print MOV DL,20H ; a space INT 21H ; thru DOS ;---------------------------------------------------------------------- ; Advance interrupt counter and loop. ;---------------------------------------------------------------------- M_8C: ROL BL,1 ;NC = use same byte JNC M_8A INC SI ;Move to next byte JMP M_8A M_8D: POP CX ;Restore count ;---------------------------------------------------------------------- ; If SUM_SEGS indicated that this entry has more than one segment, ; print an expanded list. ;---------------------------------------------------------------------- CMP [NBLKS],1 ;Only one block? JE M_9E PUSH CX ;Save counter MOV CX,[MAXMCB] ;# normal segments MOV SI,OFFSET MCBNORM ;Table entries MOV BX,[DI+2] ;Owner field M_9A: CMP BX,[SI+2] ;Same owner? JNE M_9D ;---------------------------------------------------------------------- ; Print out a short summary of this segment. ;---------------------------------------------------------------------- MOV AH,9 MOV DX,OFFSET CRLF$ ;New line INT 21H MOV AH,9 MOV DX,OFFSET SPC4$ ;Some space INT 21H MOV AX,[SI] ;MCB header CALL HEX4 ;Display it MOV AH,9 MOV DX,OFFSET SPC4$ ;More space INT 21H MOV AL,[SI+6] ;Get type MOV DX,OFFSET ENV$ DEC AL JZ M_9B MOV DX,OFFSET PSP$ DEC AL JZ M_9B MOV DX,OFFSET DATA$ M_9B: MOV AH,9 ;Now display INT 21H M_9C: MOV AH,9 MOV DX,OFFSET SPC4$ ;More space INT 21H MOV AX,[SI+4] ;Length CALL HEX4 M_9D: ADD SI,ENTRYLEN LOOP M_9A POP CX ;Restore counter M_9E: ;---------------------------------------------------------------------- ; End of this line. Move to next entry. ;---------------------------------------------------------------------- MOV AH,9 ;Display string MOV DX,OFFSET CRLF$ ; new line INT 21H ; thru DOS M_10: ADD DI,ENTRYLEN ;Move to next entry DEC CX ;LOOP won't reach JZ M_EXIT JMP M_5A ;---------------------------------------------------------------------- ; Restore system state and terminate. ;---------------------------------------------------------------------- M_EXIT: MOV BL,[UMB] ;Original link state CMP BL,-1 JE M_11 SUB BH,BH MOV AX,5803 ;Set UBM link INT 21H ; thru DOS M_11: MOV AH,4CH ;Terminate program INT 21H ; thru DOS MAIN ENDP ;====================================================================== ; SUM_SEGS ;---------------------------------------------------------------------- ; Entry: ; DS:DI -> Table entry ; Exit: ; AL = number of segs (if PSP) ; 1, otherwise ; BX = Memory composed by these segments in paragraphs ;---------------------------------------------------------------------- ; Changes: AX BX ES ;---------------------------------------------------------------------- SUM_SEGS PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH CX ;Save registers PUSH SI PUSH BP ;---------------------------------------------------------------------- ; Clear out the interrupt vector bit flag table. ;---------------------------------------------------------------------- PUSH CS ;Point ES to this seg POP ES ASSUME ES:CSEG PUSH DI ;Save register MOV DI,OFFSET VECTABLE ;Point to flags MOV CX,VECTABLELEN ;This many bytes SUB AL,AL ;Store zeros REP STOSB POP DI ;Restore register ;---------------------------------------------------------------------- ; If a system segment, the owner field has no meaning so don't search ; the table. Just return the single block. ;---------------------------------------------------------------------- CMP BYTE PTR [DI+6],PSP@ ;Is this a PSP? JE SS_1 MOV AL,1 ;Say 1 block MOV BX,[DI+4] ;Say this size CALL SET_VECS ;Set int vects in table SS_EXIT: POP BP ;Restore registers POP SI POP CX RET ;---------------------------------------------------------------------- ; For a PSP, we have to scan only the normal entries. No one owns the ; first five. ;---------------------------------------------------------------------- SS_1: SUB AL,AL ;Init count SUB BX,BX ; and size MOV BP,[DI+2] ;Owner to match MOV SI,OFFSET MCBNORM ;Normal blks only MOV CX,[MAXMCB] SS_2A: CMP BP,[SI+2] ;Owned by us? JNE SS_2B INC AL ;Count the block ADD BX,[SI+4] ;Sum the size SS_2B: CALL SET_VECS ;Set the flags ADD SI,ENTRYLEN ;Move to next entry LOOP SS_2A JMP SS_EXIT SUM_SEGS ENDP ;====================================================================== ; SET_VECS ;---------------------------------------------------------------------- ; Entry: ; DS:DI -> Table entry to search ;---------------------------------------------------------------------- ; Changes: None ;---------------------------------------------------------------------- SET_VECS PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH AX ;Save registers PUSH BX PUSH CX PUSH DX PUSH SI PUSH BP PUSH DS ;---------------------------------------------------------------------- ; ;---------------------------------------------------------------------- MOV CX,77H ;Number vecs to test SUB SI,SI ;Pointer MOV DS,SI ; and segment ASSUME DS:NOTHING MOV BP,CS:[DI] ;MCB = lower seg limit MOV DX,BP ADD DX,CS:[DI+4] ;+LEN = Upper seg limit SV_1: PUSH CX ;Save counter LODSW ;Get vec offset MOV CL,4 SHR AX,CL ;/16 -> paras MOV CX,AX ;and save it LODSW ;Get vec seg ADD AX,CX ; add offset POP CX ;Restore counter CMP AX,BP ;Cmp VEC to lower limit JB SV_4 CMP AX,DX ;Cmp VEC to upper limit JA SV_4 ;---------------------------------------------------------------------- ; Vector points within this segment. Mark its entry in the table. ;---------------------------------------------------------------------- PUSH CX ;Save counter MOV BX,77H ;Total vectors - SUB BX,CX ; number done = vec # MOV CH,BL ;Save low 3 bits MOV CL,3 ;/8 to get SHR BX,CL ; byte offset in BX AND CH,7 ;Get low 3 bits MOV CL,CH ;Put into CL MOV CH,1 ;Bit mask SHL CH,CL ;Rotate to correct posn OR CS:[VECTABLE][BX],CH ;Set flag POP CX ;Restore counter ;---------------------------------------------------------------------- ; Repeat for all vectors. ;---------------------------------------------------------------------- SV_4: LOOP SV_1 ;---------------------------------------------------------------------- ; Restore registers and exit. ;---------------------------------------------------------------------- POP DS ASSUME DS:CSEG POP BP POP SI POP DX POP CX POP BX POP AX RET SET_VECS ENDP ;====================================================================== ; LOCATE_SEG ;---------------------------------------------------------------------- ; Entry: ; BX = segment (NOT MCB) to locate ; Exit: ; BX = -1, not found in table ; = n, index to table entry ;---------------------------------------------------------------------- ; Changes: BX ;---------------------------------------------------------------------- LOCATE_SEG PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH CX ;Save used registers PUSH SI DEC BX ;Change to header adr MOV CX,[MAXMCB] ;Entries to search MOV SI,OFFSET MCBNORM LS_1: CMP BX,[SI] ;Headers match? JNE LS_3 MOV BX,[MAXMCB] SUB BX,CX ADD BX,5 ;BX = index LS_EXIT: POP SI ;Restore registers POP CX RET LS_3: ADD SI,ENTRYLEN LOOP LS_1 MOV BX,-1 ;Indicate not found JMP LS_EXIT LOCATE_SEG ENDP ;====================================================================== ; FIND_PSP_NAME (Near) ; ; This routine attempts to locate a PSP's name. ;---------------------------------------------------------------------- ; Entry: ; ES = MCB segment ; BP = PSP segment = owner = MCB+1 ; BX = Index of env segment in MCBTABLE ; DS:[DI+8] = name destination ; Exit: ; DS:[DI+8] = name ;---------------------------------------------------------------------- ; Changes: AX DX SI ;---------------------------------------------------------------------- FIND_PSP_NAME PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH CX ;Save used registers PUSH DI PUSH ES ;---------------------------------------------------------------------- ; Fill the dest buffer with blanks. ;---------------------------------------------------------------------- MOV CX,8 ;Buffer length ADD DI,CX ;Add offset PUSH DI ;Save address PUSH CS ;Set ES to this seg POP ES ASSUME ES:CSEG MOV AL,20H ;Write blanks REP STOSB POP DI ;Get name dest ;---------------------------------------------------------------------- ; In DOS 5.0+, the filespec is listed in the MCB header. ;---------------------------------------------------------------------- CMP BYTE PTR [VER],5 ;Ver 5+ only JAE FN_1A ;---------------------------------------------------------------------- ; In DOS 3.0+, the filespec used to exec the program is stored ; is copied to a string and stored in the environment block. ;---------------------------------------------------------------------- CMP BYTE PTR [VER],3 ; if 2.x JA FN_2A ;---------------------------------------------------------------------- ; Under 2.x, no name can be found. Use the default response. ;---------------------------------------------------------------------- FN_0A: MOV SI,OFFSET NO_NAME ;Use this name FN_0B: MOV CX,4 ;Words to xfer FN_0C: LODSW ;Get a word DS:SI MOV DS:[DI],AX ; and save it INC DI INC DI LOOP FN_0C ;---------------------------------------------------------------------- ; Common exit. ;---------------------------------------------------------------------- FN_EXIT: POP ES ;Restore registers POP DI POP CX RET ;---------------------------------------------------------------------- ; Versions of DOS >= 5 put the program name in the MCB header as 8 ; chars max, zero terminated. Copy to dest buffer. ;---------------------------------------------------------------------- FN_1A: MOV AX,[DI-8] ;Get MCB seg MOV ES,AX ; in ES ASSUME ES:NOTHING MOV SI,8 ;DS:SI -> src MOV CX,SI ;Max chars FN_1B: MOV AL,ES:[SI] ;Get char INC SI ;Advance src OR AL,AL ;If AL=0, done JZ FN_EXIT MOV DS:[DI],AL ;Save char INC DI ;Advance dest LOOP FN_1B JMP FN_EXIT ;---------------------------------------------------------------------- ; Find the program name from the environment. ; Note that if BL=FF, then no environment is available. ;---------------------------------------------------------------------- ASSUME ES:CSEG FN_2A: CMP BL,0FFH ;If invalid, no-name JE FN_0A ;---------------------------------------------------------------------- ; Environment is valid. The PSP name can be found in the ; environment block. BX contains the index of the env block in the ; MCBTABLE. ;---------------------------------------------------------------------- MOV AX,[MCBTABLE][SI] ;Env header INC AX ;Env block MOV ES,AX ASSUME ES:NOTHING ;---------------------------------------------------------------------- ; Scan the environment for the double zero entry. ; Assumes the environment conforms to standard layout. ;---------------------------------------------------------------------- MOV SI,DI ;Save PSP pointer SUB DI,DI ;Starting offset FN_5A: MOV CX,ES:[DI] ;Get word in CX JCXZ FN_5B INC DI ;Advance by BYTE JMP FN_5A FN_5B: INC DI ;Found 00... INC DI ;...skip it ;---------------------------------------------------------------------- ; ES:DI points to word containing number of ASCIIZ strings that follow ; (always 1) EXCEPT if the owner is COMMAND. ;---------------------------------------------------------------------- MOV CX,ES:[DI] ;Get number of strings INC DI ;Skip it INC DI CMP CX,1 ;<>1 if COMMAND JE FN_6A MOV DI,SI ;Get pointer back MOV SI,OFFSET CMD_NAME JMP FN_0B ;---------------------------------------------------------------------- ; Find the end of the string. If no chars, use NO NAME. ;---------------------------------------------------------------------- FN_6A: SUB AL,AL ;Scan for final 0 MOV CX,0FFFFH ; this many bytes REPNE SCASB ;CMP AL,ES:[DI] NEG CX DEC CX DEC CX ;CX = chars in string JNZ FN_6B MOV DI,SI ;Restore pointer JMP FN_0A FN_6B: ;---------------------------------------------------------------------- ; DI points 1 char past the 0. ;---------------------------------------------------------------------- DEC DI ;Point DI to last... DEC DI ;...char of extension STD ;Scan backwards MOV AL,"." ;Scan for dot REPNE SCASB MOV DX,DI ;DX->last char of name MOV AL,"\" ;Scan for backslash REPNE SCASB CLD ;Restore direction INC DI ;Point DI to first... INC DI ;...char of name SUB DX,DI ;Subtract pointers to INC DX ; get length of string ;---------------------------------------------------------------------- ; ES:DI -> start of string ; DX = length of string ; Copy the name to buffer at DS:SI. ;---------------------------------------------------------------------- MOV CX,DX FN_7: MOV AL,ES:[DI] ;Get char INC DI MOV DS:[SI],AL ; and save it INC SI LOOP FN_7 JMP FN_EXIT FIND_PSP_NAME ENDP ;====================================================================== ; HEX4 - Write AX as 4 hex digits to std out ;---------------------------------------------------------------------- ; Entry: AX = value to display ; Exit : none ;---------------------------------------------------------------------- ; CHANGES: AX ;---------------------------------------------------------------------- HEX4 PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH AX ;Save register MOV AL,AH ;Show high digits first CALL HEX2 ;Display AL POP AX ;Restore low digits in AL ;---------------------------------------------------------------------- ; HEX2 - Write AL as 2 hex digits to std out ;---------------------------------------------------------------------- ; Entry: AL = value to display ; Exit : none ;---------------------------------------------------------------------- ; CHANGES: AX ;---------------------------------------------------------------------- HEX2 PROC NEAR ;Display AL ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH AX ;Save register PUSH CX ;Save CX during shift MOV CL,4 SHR AL,CL ;Get high 4 bits POP CX ;Restore CX CALL H2OUT ;Display upper AL digit POP AX ;Restore lower AND AL,0FH ;Mask and display ;---------------------------------------------------------------------- ; H2OUT - Write lower 4 bits of AL as 1 hex digit to std out ;---------------------------------------------------------------------- ; Entry: AL = lower 4 bits = digit to display ; Exit : none ;---------------------------------------------------------------------- ; CHANGES: AX ;---------------------------------------------------------------------- H2OUT PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH DX ADD AL,90H ;Convert AL to ASCII DAA ADC AL,40H DAA MOV DL,AL ;Char in DL MOV AH,2 ;Display INT 21H ; thru DOS POP DX RET H2OUT ENDP HEX2 ENDP HEX4 ENDP ;====================================================================== ; Additional data is allocated here when the program installs. ;---------------------------------------------------------------------- MCBTABLE DW 0H, 0, 40H DB SYSTEM@, 0, "Int Vect" ENTRYLEN EQU $-OFFSET MCBTABLE DW 40H, 0, 10H DB SYSTEM@, 0, "BiosData" DW 50H, 0, 20H DB SYSTEM@, 0, "Dos Data" DW 70H, 0, ? DB SYSTEM@, 0, "I/O " DW ?, 0, ? DB SYSTEM@, 0, "DOS " MCBNORM LABEL BYTE CSEG ENDS END ENTPT