;====================================================================== ; TRACE 1.00 * Copyright (c) 1992, Robert L. Hummel ; PC Magazine Assembly Language Lab Notes ; ; TRACE reads a disk's FAT, tracing either a file or a subdirectory. ; It displays a list of cluster numbers used by the file and shows the ; equivalent DOS sector numbers. ;---------------------------------------------------------------------- CSEG SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG ORG 100H ;COM file format ENTPT: JMP MAIN ;====================================================================== ; Program data area. ;---------------------------------------------------------------------- CR EQU 13 LF EQU 10 BLANK EQU 32 ;---------------------------------------------------------------------- ; Messages. ;---------------------------------------------------------------------- COPYRIGHT$ DB CR,LF,"TRACE 1.00 ",254," Copyright (c) 1992," DB " Robert L. Hummel",CR,LF DB "PC Magazine Assembly Language Lab Notes",LF CRLF$ DB CR,LF,"$" USAGE$ DB "Usage: TRACE pathname",CR,LF DB " where pathname = [d:][path][filename.ext]" DB CR,LF,"$" TITLE$ DB "F CLUSTER DOS SECTORS",CR,LF,"$" MEMERR$ DB "Not Enough Memory To Execute$" INVDRIVE$ DB "Drive Letter Is Invalid$" DRIVEERR$ DB "Error Accessing Target Drive$" NOTFOUND$ DB "Pathname Doesn't Exist$" NOROOT$ DB "Can't TRACE Root Or JOINed Drive$" BADCLUS$ DB "Cluster In Allocation Chain Was Marked Bad$" INVCLUS$ DB "Invalid Cluster Number In Allocation Chain$" FATERR$ DB "Error Accessing Target FAT$" CHAINENDS$ DB " ---- Chain Ends",CR,LF,LF DB "Total Clusters : $" NUMFRAGS$ DB "h",CR,LF,"Number Fragments: $" SPC5$ DB 5 DUP(BLANK),"$" ;---------------------------------------------------------------------- ; Program variables. ;---------------------------------------------------------------------- FILESPEC DW 0 ;Pointer to filespec OLDDRIVE DB 0 ;Current disk drive OLDDIR DB "\",64 DUP(0) ;Holds current dir NEWDRIVE DB 0 ;Trace drive FES_12 EQU 00FH FES_16 EQU 0FFH FES DB FES_16 ;Assume 16-bit FAT HUGEDISK DW 0 ;Non-zero if huge disk NCLUS DD 0 NFRAGS DD 0 ;---------------------------------------------------------------------- ; Drive data structure. Holds info about the disk retrieved from the ; Drive Parameter Block. ;---------------------------------------------------------------------- DRIVE_DATA LABEL BYTE BPS DW 0 ;Bytes per sector SPC DW 0 ;Sectors per cluster FFS DW 0 ;First FAT sector FDS DW 0 ;First Data sector MCN DW 0 ;Maximum cluster number ;---------------------------------------------------------------------- ; This file control block is used to open the directory entry. ;---------------------------------------------------------------------- XFCB LABEL BYTE ;Extended FCB structure DB 0FFH ;Extended FCB signature DB 5 DUP (0) ;(Reserved) DB 16H ;Search attribute DB 0 ;Default drive DB ".",7 DUP(BLANK) ;Current dir's name DB 3 DUP(BLANK) ; and extension DW 0 ;Current block # DW 0 ;Record size DD 0 ;Size of file (bytes) DW 0 ;Date DW 0 ;Time DB 8 DUP(0) ;(Reserved) DB 0 ;Current record # DD 0 ;Random record # ;---------------------------------------------------------------------- ; The extended directory entry structure written to the DTA by Find ; First. ;---------------------------------------------------------------------- XDIR LABEL BYTE ;Extended directory DB 0FFH ;Extended FCB signature DB 5 DUP (0) ;(Reserved) DB 0 ;Search attribute used DB 0 ;Drive DB 8 DUP (0) ;Name DB 3 DUP (0) ;Extension DB 0 ;File's attribute DB 10 DUP (0) ;Reserved DW 0 ;Time DW 0 ;Date FIRSTCLUSTER DW 0 ;Starting cluster DD 0 ;File's size ;---------------------------------------------------------------------- ; MAIN procedure. ;---------------------------------------------------------------------- MAIN PROC NEAR ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG ;---------------------------------------------------------------------- ; Initialize and display program title. ;---------------------------------------------------------------------- CLD ;String moves forward MOV CX,AX ;Save drive status MOV AH,9 ;Display string fn MOV DX,OFFSET COPYRIGHT$ ; located here INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; If the first command line argument contained a invalid drive spec, AL ; will = FFh. If so, report an error to the user. ;---------------------------------------------------------------------- CMP CL,0FFH ;Check for invalid drv JNE M_1 MOV AH,9 ;Display string MOV DX,OFFSET INVDRIVE$ ; in DX INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Skip a line and terminate the program. ;---------------------------------------------------------------------- M_EXIT: MOV AH,9 ;Display string MOV DX,OFFSET CRLF$ ; located here INT 21H ; Thru DOS MOV AX,4C00H ;Terminate program INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; We need the current drive either to use during disk reads or if we ; have to change the default drive and later restore it. ;---------------------------------------------------------------------- M_1: MOV AH,19H ;Get current drive # INT 21H ; Thru DOS MOV [OLDDRIVE],AL ;Save current drive MOV [NEWDRIVE],AL ;Initialize cur drive ;---------------------------------------------------------------------- ; Find the first non-blank character on the command line. If no ; arguments were specified, display the usage message. ;---------------------------------------------------------------------- M_2A: MOV SI,81H ;Cmdline adr in PSP SUB CX,CX ;Clear to 0 ADD CL,[SI-1] ;Chars on command line JNZ M_2D M_2B: MOV AH,9 ;Display string MOV DX,OFFSET USAGE$ ; located here INT 21H ; Thru DOS JMP M_EXIT ;Don't change path/drv M_2D: LODSB ;Get a character CMP AL,BLANK ;Skip leading blanks JNE M_2E LOOP M_2D ;Continue scan JCXZ M_2B ;Jump if all blanks M_2E: ;---------------------------------------------------------------------- ; Scan to find the end of the command line. ;---------------------------------------------------------------------- DEC SI ;Point to first char MOV DI,SI ; and save it M_2F: LODSB ;Get char CMP AL,BLANK ;Scan non-blanks JE M_2G LOOP M_2F INC SI ;Compensate for DEC M_2G: DEC SI ;Back up to blank MOV BYTE PTR [SI],0 ;Make ASCIIZ XCHG SI,DI ;SI=start DI=end ;---------------------------------------------------------------------- ; If the spec contained a drive, make it the current drive. ; DOS will have placed the drive number in the first FCB. ; If A: was specified, the FCB will contain 1, not 0. Adjust the value. ; If the FCB contains 0, it means no drive was specified. ; If present, remove the d: drive spec from the command line argument. ;---------------------------------------------------------------------- MOV DL,DS:[5CH] ;Get drive from FCB DEC DL ;If 0,turns sign bit on JS M_3 MOV [NEWDRIVE],DL ;Save working drive MOV AH,0EH ;Select current drive INT 21H ; Thru DOS INC SI ;Skip "d:" INC SI ; in spec CMP BYTE PTR [SI],0 ;Spec empty now? JE M_2B M_3: MOV [FILESPEC],SI ;Save pointer to spec ;---------------------------------------------------------------------- ; Save the current directory on this drive so we can restore it later. ;---------------------------------------------------------------------- MOV AH,47H ;Get current directory SUB DL,DL ; on current drive MOV SI,OFFSET OLDDIR+1 ;Store here INT 21H ; Thru DOS JNC M_4 MOV AH,9 MOV DX,OFFSET DRIVEERR$ ;Did we have a problem? INT 21H JMP SHORT M_7B ;Restore drive only M_4: ;---------------------------------------------------------------------- ; The Find First function writes a directory structure for the found ; file to the area of memory pointed to by the DTA. ;---------------------------------------------------------------------- MOV AH,1AH ;Set DTA MOV DX,OFFSET XDIR ; to this area INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Assume that the command line argument is a directory spec. ; Attempt to change to the new directory. ; If the call works, the filespec is for a directory. ;---------------------------------------------------------------------- MOV AH,3BH ;Set directory MOV DX,[FILESPEC] ; to this string INT 21H ; Thru DOS JC M_5A ;---------------------------------------------------------------------- ; If the [.] entry is found, the Find First function fills in the DTA ; with returns directory structure in XDIR. ;---------------------------------------------------------------------- MOV AH,11H ;Find First Match MOV DX,OFFSET XFCB ; for this FCB INT 21H ; Thru DOS OR AL,AL ;AL=0 if successful JZ M_9 ;---------------------------------------------------------------------- ; The spec was a root directory (since we CD'd successfully to it), but ; not a subdir (since it didn't contain a "." file entry). Since we ; can't trace the root directory, command line must be in error. ;---------------------------------------------------------------------- MOV AH,9 ;Display string MOV DX,OFFSET NOROOT$ ; located here INT 21H ; Thru DOS JMP SHORT M_7A ;---------------------------------------------------------------------- ; The spec wasn't a directory since the CD call failed. Assume that ; it's a file and parse the spec backwards to the first backslash, then ; try to set the resultant directory. ;---------------------------------------------------------------------- M_5A: MOV CX,DI ;From end of cmd line SUB CX,[FILESPEC] ; subtract start => len M_5B: DEC DI ;Back up a char CMP BYTE PTR [DI],"\" ; is it a backslash? LOOPNE M_5B ;---------------------------------------------------------------------- ; If we exited the loop because we ran out of characters (CX=0) then ; we'll assume that only a filename was specified. ; Otherwise, a backslash was found. ;---------------------------------------------------------------------- JE M_6A DEC DI ;Compensate for INC JMP SHORT M_8A ;---------------------------------------------------------------------- ; DI points to the last backslash in the pathname. ; Overwrite the backslash with a 0, and make the path the current dir. ; Separate the path from the filespec. If the path string is a single ; backslash, handle it special. Otherwise, if we still can't, then the ; argument was invalid. ;---------------------------------------------------------------------- M_6A: MOV BYTE PTR [DI],0 ;Change path to ASCIIZ MOV DX,[FILESPEC] ;Is start of filespec CMP DI,DX ; this backslash? JNE M_6B DEC DX ;Back up MOV BYTE PTR [DI-1],"\" ;Create a spec M_6B: MOV AH,3BH ;Set directory INT 21H ; Thru DOS JNC M_8A M_6C: MOV DX,OFFSET NOTFOUND$ ;Assume an error MOV AH,9 ;Display string fn INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Restore the directory on the target drive. ;---------------------------------------------------------------------- M_7A: MOV AH,3BH ;Set directory MOV DX,OFFSET OLDDIR ; to what we found INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Restore the original drive. ;---------------------------------------------------------------------- M_7B: MOV AH,0EH ;Set current drive MOV DL,[OLDDRIVE] ; to original INT 21H ; Thru DOS JMP M_EXIT ;---------------------------------------------------------------------- ; Parse the filename into the FCB. ;---------------------------------------------------------------------- M_8A: MOV CX,9 ;8 chars + dot MOV SI,OFFSET XFCB+8 ;Name field=dest INC DI ;Skip \ M_8B: MOV AL,BYTE PTR [DI] ;Get char INC DI ; and point to next CMP AL,"." ;Was it the separator? JNE M_8C MOV CX,3 ;3 extension chars max MOV SI,OFFSET XFCB+16 ;Extension dest JMP M_8B M_8C: OR AL,AL ;Was it end of data? JZ M_8D MOV BYTE PTR [SI],AL ;Put char in XFCB INC SI ; advance dest pointer LOOP M_8B ;Continue parsing CMP BYTE PTR [DI],0 ;Shouldn't be any left JNE M_6C M_8D: ;---------------------------------------------------------------------- ; Now use Find First to locate the filespec that remains. ;---------------------------------------------------------------------- MOV AH,11H ;Find First Match MOV DX,OFFSET XFCB ; for this FCB INT 21H ; Thru DOS OR AL,AL ;AL=0 if successful JNZ M_6C ;---------------------------------------------------------------------- ; Now we know the starting cluster number of the file/directory. ; We need to determine some information in order to trace the FAT. ; ; BPS - Bytes per DOS sector. So we can read the FAT using Int 25h. ; ; SPC - Cluster mask = sectors per cluster - 1. Used to determine ; actual disk space used by the file/directory. ; ; FFS - First FAT Sector. The logical sector number that contains the ; first sector of the FAT. ; ; FDS - First Data Sector. The logical sector number of the first data ; sector on the disk. ; ; MCN - Maximum cluster number. So we can determine if a FAT entry is ; invalid. ; ; FES - FAT entry size. This is 12 or 16 bits. Needed to calculate the ; next entry in an allocation chain. Calculated indirectly. ; ;---------------------------------------------------------------------- ; Use the undocumented function Get Default DPB to examine the disk. ; Copy the needed information to local storage. ;---------------------------------------------------------------------- M_9: MOV AH,1FH ;Get Default DPB INT 21H ; Undocumented DOS fn ASSUME DS:NOTHING ;Function changes DS MOV DI,OFFSET DRIVE_DATA ;Dest=Drive data area MOV AX,DS:[BX][2] ;Bytes per sector STOSW ;Write to ES:DI MOV AL,DS:[BX][4] ;Sector mask INC AL ; +1=SPC SUB AH,AH ;Zero extend it STOSW MOV AX,DS:[BX][6] ;# reserved sectors STOSW ; = first FAT sector MOV AX,DS:[BX][0BH] ;First Data Sector STOSW MOV AX,DS:[BX][0DH] ;Max cluster number STOSW PUSH CS ;Put CS segment POP DS ; into DS ASSUME DS:CSEG ;---------------------------------------------------------------------- ; From the information in the DPB, determine if this disk is using a ; 12-bit or 16-bit FAT. (Initial value is 16-bit.) ; IF (# clusters < 4087) THEN FAT=12-bit ELSE FAT=16-bit ;---------------------------------------------------------------------- CMP AX,4086 ;Check # of clusters JA M_10 MOV BYTE PTR [FES],FES_12 ;Set FAT type flag M_10: ;---------------------------------------------------------------------- ; If this drive is formatted as a huge partition, it will have more ; than 65536 total sectors. To determine this, multiply the number of ; clusters (in AX) by the number of sectors per cluster. After the ; multiplication, DX will hold the high-order word of the result. If ; the product is greater than 65535, DX will be non-zero. ;---------------------------------------------------------------------- MOV DX,[SPC] ;Sectors per cluster MUL DX ;*# clusters MOV [HUGEDISK],DX ;Use DX as flag ;---------------------------------------------------------------------- ; Make sure we've got enough memory to hold one FAT sector which we ; read during the tracing procedure. ;---------------------------------------------------------------------- MOV AX,SP ;Get the end of segment SUB AX,OFFSET FATBUF+512 ; subtract prog/stack SHR AX,1 ;Divide by 2 to see if CMP AX,[BPS] ; enough for 2 sectors? JAE M_11C MOV DX,OFFSET MEMERR$ ;Assume memory error M_11B: MOV AH,9 INT 21H JMP M_7A ;Restore dir/drive M_11C: ;---------------------------------------------------------------------- ; Now trace the spec's allocation chain through the FAT, printing out a ; status report as we go. ;---------------------------------------------------------------------- MOV AH,9 ;Display string MOV DX,OFFSET TITLE$ ; located here INT 21H ; Thru DOS SUB DI,DI ;Previous cluster # MOV AX,[FIRSTCLUSTER] ;Start here M_12A: CALL SHOW_CLUSTER ;Display cluster info JNC M_12B OR DX,DX ;If CY, DX!=0 is error JNZ M_11B JMP SHORT M_13 M_12B: ADD WORD PTR [NCLUS],1 ;Count the cluster ADC WORD PTR [NCLUS+2],0 ; in the total CALL NEXT_CLUSTER ;Get next JC M_11B ;CY = error JMP M_12A ; else continue ;---------------------------------------------------------------------- ; Summarize the spec's cluster info. ;---------------------------------------------------------------------- M_13: MOV AH,9 ;Display string MOV DX,OFFSET CHAINENDS$ ; located here INT 21H ; Thru DOS MOV AX,WORD PTR [NCLUS+2] ;High word CALL HEXWORD MOV AX,WORD PTR [NCLUS] ;Low word CALL HEXWORD MOV AH,9 ;Display string MOV DX,OFFSET NUMFRAGS$ ; located here INT 21H ; Thru DOS MOV AX,WORD PTR [NFRAGS+2] ;High word CALL HEXWORD MOV AX,WORD PTR [NFRAGS] ;Low word CALL HEXWORD MOV AH,2 ;Display char MOV DL,"h" ; in DL INT 21H ; Thru DOS JMP M_7A MAIN ENDP ;====================================================================== ; SHOW_CLUSTER (Near) ; ; If the cluster number is valid, display it and translate it to the ; equivalent DOS sectors numbers. ;---------------------------------------------------------------------- ; Entry: ; AX = current cluster number ; DI = previous cluster number ; Exit: ; If NC, success ; AX = current cluster number ; DI = current cluster number ; If CY, ; DX = 0, chain ended normally ; != 0, offset of error message ;---------------------------------------------------------------------- ; Changes: BX DX DI BP ;---------------------------------------------------------------------- SHOW_CLUSTER PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG PUSH AX ;Save register ;---------------------------------------------------------------------- ; Cluster numbers 0 and 1 are reserved. ;---------------------------------------------------------------------- MOV DX,OFFSET INVCLUS$ ;Error message CMP AX,1 ;0 or 1? JBE SC_ERR ;---------------------------------------------------------------------- ; A value of (F)FF8h or greater indicates that this is the last cluster ; in the allocation chain. ;---------------------------------------------------------------------- SUB DX,DX ;Assume end of chain MOV BX,0FFF7H ;16-bit value AND BH,[FES] ;Mask if needed CMP AX,BX ;Check value JB SC_2 JA SC_ERR ;---------------------------------------------------------------------- ; Drop thru here if AX=(F)FF7h, indicating a bad cluster. This is bad ; news and indicates some file corruption. ;---------------------------------------------------------------------- MOV DX,OFFSET BADCLUS$ ;Error message SC_ERR: STC ;Carry=error SC_EXIT: POP AX ;Restore register RET ;---------------------------------------------------------------------- ; Test for a valid cluster number. ;---------------------------------------------------------------------- SC_2: MOV DX,OFFSET INVCLUS$ ;Error message CMP AX,[MCN] ;Max cluster number JAE SC_ERR ;---------------------------------------------------------------------- ; See if this cluster number is sequential with the previous one. If ; not, it begins a new fragment. ;---------------------------------------------------------------------- MOV DL,BLANK ;Assume consecutive INC DI ;Point 1 past prev clus CMP AX,DI ;Is it current cluster? JE SC_3 ADD WORD PTR [NFRAGS],1 ;Increase 32-bit frag ADC WORD PTR [NFRAGS+2],0 ; counter by 1 MOV DI,AX ;Update cluster tracker MOV DL,175 ;Fragment character SC_3: MOV BP,AX ;Save cluster in BP MOV AH,2 ;Display char in DL INT 21H ; Thru DOS MOV AH,2 ;Display char MOV DL,BLANK ; in DL INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Display the cluster number and translate to equivalent DOS sector ; numbers. ;---------------------------------------------------------------------- MOV AX,BP ;Retrieve cluster # CALL HEXWORD ;Display AX MOV AH,9 ;Display string MOV DX,OFFSET SPC5$ ; 5 spaces INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Convert the cluster number in AX into an equivalent series of DOS ; sector number. ;---------------------------------------------------------------------- MOV AX,BP ;Get back cluster # SUB AX,2 ;Disregard ID bytes MOV BX,[SPC] ;Sectors per cluster MUL BX ;DX:AX=sectors ADD AX,[FDS] ;Offset from FDS ADC DX,0 ;In case 32-bit # PUSH DX ;Save high-order word PUSH AX ; and low-order word XCHG AX,DX ;Get high-order word CALL HEXWORD ; display it MOV AX,DX ;Then low-order word CALL HEXWORD MOV AH,2 ;Display char MOV DL,"-" ; in DL INT 21H ; Thru DOS POP AX ;Retrieve originals POP DX DEC BX ADD AX,BX ;Add cluster length ADC DX,0 ;In case 32-bit # XCHG AX,DX ;Get high-order word CALL HEXWORD ; display it MOV AX,DX ;Then low-order word CALL HEXWORD ;---------------------------------------------------------------------- ; Start a new line and return to caller. ;---------------------------------------------------------------------- MOV AH,9 ;Display string MOV DX,OFFSET CRLF$ ; at DX INT 21H ; Thru DOS CLC ;Say success JMP SC_EXIT SHOW_CLUSTER ENDP ;====================================================================== ; NEXT_CLUSTER (Near) ; ; Given a cluster number, reads the appropriate FAT sector into memory, ; looks up the entry in the FAT, and retrieves the number of the next ; cluster in the chain. Assumes the passed cluster number is valid. ;---------------------------------------------------------------------- ; Entry: ; AX = valid cluster number ; Exit : ; If NC, ; AX = next cluster number ; If CY, ; DX = error message ;---------------------------------------------------------------------- ; Changes: AX BX CX DX BP ;---------------------------------------------------------------------- FATSECT DW -1 ;# of FAT sect in mem NEXT_CLUSTER PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG ;---------------------------------------------------------------------- ; From the cluster number, determine in which sector of the FAT this ; cluster is located. 12-bit and 16-bit FATs are decoded differently. ;---------------------------------------------------------------------- SUB DX,DX ;Prepare for long DIV CMP [FES],FES_12 ;Check FAT type JNE NC_1A ;---------------------------------------------------------------------- ; Decode a 12-bit FAT entry. ; 1. Translate the 12-bit cluster number into byte offset into FAT. ; Since the maximum cluster number for a 12-bit FAT is 4086, and ; since each FAT entry is 1.5 bytes, the maximum offset into the ; FAT is 6129 [17F1h] bytes. This will always fit into a word. ;---------------------------------------------------------------------- SUB BP,BP ;Create a zero MOV BX,AX ;Multiply ADD AX,AX ; AX ADD AX,BX ; by 3 SHR AX,1 ;Divide by 2 RCL BP,1 ;Save carry flag state JMP SHORT NC_1B ;---------------------------------------------------------------------- ; Decode a 16-bit FAT entry. ; 1. Translate cluster number into byte offset into FAT. ; Note that while the cluster number can never exceed 16 bits, the ; byte offset into the FAT can. ;---------------------------------------------------------------------- NC_1A: SHL AX,1 ;Multiply AX by 2 RCL DX,1 ;Move CY into DX NC_1B: ;---------------------------------------------------------------------- ; 2. Divide the offset by the number of bytes in a sector to determine ; into which sector of the FAT this offset points. ;---------------------------------------------------------------------- MOV CX,[BPS] ;Bytes per sector DIV CX ;AX=sects, DX=bytes ;---------------------------------------------------------------------- ; 3. If the required FAT sector (in AX) isn't the first one in memory, ; load it and the one following it. (This accommodates a 12-bit FAT ; entry that spans a sector boundary.) ;---------------------------------------------------------------------- CMP AX,[FATSECT] ;Is this sect in mem? JE NC_2A ;---------------------------------------------------------------------- ; Read in the FAT sectors starting at AX. ;---------------------------------------------------------------------- MOV [FATSECT],AX ;Say this one's here CALL READ_FAT JNC NC_2A MOV DX,OFFSET FATERR$ ;Report problem, CY set NC_EXIT: RET ;---------------------------------------------------------------------- ; 4. Read a word at the specified offset (in DX). If 16-bit FAT, we're ; done. For a 12-bit FAT, mask off the appropriate bits. ;---------------------------------------------------------------------- NC_2A: MOV BX,DX ;Offset is in DX MOV AX,WORD PTR [FATBUF][BX] ;Get the word CMP [FES],FES_12 ;Check FAT type JNE NC_2C ;---------------------------------------------------------------------- ; For 12-bit FATs, if the original cluster was even, use the lower 12 ; bits of the word. If the original cluster was odd, use the upper 12 ; bits of the word. ;---------------------------------------------------------------------- OR BP,BP ;Was mult whole word? JZ NC_2B MOV CL,4 ;Shift count SHR AX,CL ;Use upper 12 bits JMP SHORT NC_2C NC_2B: AND AH,0FH ;Use lower 12 bits NC_2C: CLC ;Say success JMP NC_EXIT NEXT_CLUSTER ENDP ;====================================================================== ; READ_FAT (Near) ; ; This routine reads a sector from the disk into the FATBUF. It ; automatically adjusts for huge (type 6) disks. ;---------------------------------------------------------------------- ; Entry: ; AX = Number of the FAT sector to read. ; Exit: None ;---------------------------------------------------------------------- ; Changes: AX BX CX ;---------------------------------------------------------------------- DISK_PACKET LABEL BYTE FIRST_SECTOR DW 0,0 ;32-bit sector number DW 1 ;Number of sectors DW OFFSET FATBUF ;Buffer offset BUF_SEG DW 0 ; and segment READ_FAT PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG PUSH DX ;Save register ;---------------------------------------------------------------------- ; Convert the relative FAT sector to an absolute logical sector number. ;---------------------------------------------------------------------- ADD AX,[FFS] ;Convert to disk sector ;---------------------------------------------------------------------- ; If this is not a huge disk, use the normal function protocol. ;---------------------------------------------------------------------- CMP [HUGEDISK],0 JNE RFS_1A MOV DX,AX ;Starting sector MOV AL,[NEWDRIVE] ;Read from this drive MOV BX,OFFSET FATBUF ;Put data here MOV CX,2 ;Read two sectors JMP SHORT RFS_1B ;---------------------------------------------------------------------- ; Use the type 6 disk protocol. Because it's a 16-bit FAT, we only have ; to read one sector. ;---------------------------------------------------------------------- RFS_1A: MOV [FIRST_SECTOR],AX MOV [BUF_SEG],DS MOV AL,[NEWDRIVE] ;Read from this drive MOV BX,OFFSET DISK_PACKET MOV CX,-1 ;Signal type 6 RFS_1B: INT 25H ;Direct disk read POP DX ;Discard old flags POP DX ;Restore register RET READ_FAT ENDP ;====================================================================== ; HEXWORD - Write AX as 4 hex digits to std out ;---------------------------------------------------------------------- ; Entry: ; AX = value to display ; Exit : None ;---------------------------------------------------------------------- ; CHANGES: None ;---------------------------------------------------------------------- HEXWORD PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG PUSH CX PUSH DX MOV CX,4 H_1: PUSH CX MOV CL,4 ROL AX,CL POP CX PUSH AX AND AL,0FH ADD AL,90H ;Convert AL to ASCII DAA ADC AL,40H DAA MOV AH,2 ;Display char MOV DL,AL ; in DL INT 21H ; Thru DOS POP AX LOOP H_1 POP DX POP CX RET HEXWORD ENDP ;====================================================================== FATBUF EQU $ CSEG ENDS END ENTPT