;======================================================================
; 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