; ============================================================================= ; ; Litos8 files ; ; ============================================================================= ; ----------------------------------------------------------------------------- ; Exported user functions (46): ; GetCurPath - get current path ; SetCurPath - set current path ; AbsPath - absolutize path ; OpenFile - open file ; CreateFileDir - create and open file or directory ; CreateFile - create and open file ; CreateDir - create directory ; DeleteFileDir - delete file or directory ; DeleteFile - delete file ; DeleteDir - delete directory ; RenameFileDir - rename file or directory ; MoveFile - move file ; ReadFile - read from file ; WriteFile - write to file ; FlushFile - flush file writes ; CloseFile - close file ; OpenDir - open directory ; FindFiles - search files or directories ; FileExist - check file or directory if exists ; FileAttr - get file attributes from FILE structure ; GetFileAttr - get file attributes ; SetFileAttr - set file attributes ; FileSize - get file size from FILE structure ; GetFileSize - get file size ; SetFileSize - set file size ; FileDateTime - get file date and time from FILE structure ; GetFileDateTime - get file date and time ; SetFileDateTime - set file date and time ; FilePos - get file seek position ; FileSeek - set file seek position ; FileSetPos - set file seek position from begin ; FileReset - reset file position to begin ; AddFileName - add filename from FILE structure ; OpenRoot - open root directory ; ClearFileDesc - clear FILE descriptor ; GetClustSect - get first absolute sector of cluster ; PrepFileMask - prepare filename mask ; FileSDD - get SDD descriptor from FILE structure ; GetNextClust - get next FAT entry ; SetNextClust - set next FAT entry ; FindFreeClust - find free cluster ; SetLastFree - set last free cluster ; ReadFAT - read FAT sector into cache ; WriteFAT - write FAT sector from cache ; FlushFAT - write FAT sector, if it was modified ; DiskFree - get free space on disk ; ----------------------------------------------------------------------------- CODEINIT_SECTION ; ----------------------------------------------------------------------------- ; INIT: Initialize current path ; ----------------------------------------------------------------------------- ; ------------- initialize last free cluster CurPathInit: mov byte [LastFreeClust],2 ; set last free cluster ; ------------- clear current path mov di,CurPathFILE ; DI <- FILE descriptor call ClearFileDesc ; clear FILE descriptor ; ------------- find boot disk mov cx,[SDDNum] ; CX <- number of system disks jcxz CurPathInit9 ; no system disk mov bx,SDDTab ; BX <- system disk tables mov al,[BootDisk] ; AL <- boot disk CurPathInit2: mov si,[bx+SDD_BDD] ; SI <- BDD descriptor cmp al,[si+BDD_Disk]; check BIOS disk number je short CurPathInit4 ; disk has been found add bx,SDD_size ; BX <- next BDD descriptor loop CurPathInit2 ; check next BDD descriptor ; ------------- not found, use first fixed disk mov cx,[SDDNum] ; CX <- number of system disks mov bx,SDDTab ; BX <- system disk tables CurPathInit3: test byte [bx+SDD_FlagsDisk],SDDF_Valid ; valid format? jnz short CurPathInit4 ; use this fixed disk add bx,SDD_size ; BX <- next BDD descriptor loop CurPathInit3 ; check next BDD descriptor mov bx,SDDTab ; BX <- use first disk ; ------------- fill up FILE structure CurPathInit4: mov byte [di+FILE_Name],PATHCHAR ; mark root directory mov al,[bx+SDD_FlagsDisk] ; AL <- flags and disk and al,SDDF_DiskMask ; AL <- system disk push ax ; push AX (AL=system disk) shl al,1 shl al,1 shl al,1 shl al,1 ; AL <- shift disk to bit 4 position mov byte [di+FILE_DirInx],al ; set device type and index mov byte [di+FILE_Attr],DIR_DIR ; set directory attribute mov ah,[bx+SDD_RootSect] ; AH <- number of root sectors mov al,0 shl ax,1 ; AX <- root size mov [di+FILE_Size],ax ; root directory size ; ------------- set current path text pop ax ; pop AX (AL=system disk) add al,"A" ; AL <- disk letter mov bx,CurPath ; BX <- current path mov byte [bx+TEXT_Length],3 ; size of current path text mov [bx+TEXT_Text],al ; set disk letter mov word [bx+TEXT_Text+1],":" + PATHCHAR*256 ; ":/" chars CurPathInit9: ret CODE_SECTION ; ----------------------------------------------------------------------------- ; Read FAT sector into cache ; ----------------------------------------------------------------------------- ; INPUT: AX = relative FAT sector ; BX = pointer to SDD descriptor ; OUTPUT: SI = sector buffer SectBuf ; CY = read error ; NOTES: FAT buffer will be flushed if other FAT sector is needed. ; ----------------------------------------------------------------------------- ; ------------- push registers ReadFAT: push ax ; push AX push cx ; push CX push dx ; push DX ; ------------- prepare number of FAT tables -> CX mov ch,0 ; CH <- 0 mov cl,[bx+SDD_FatNum] ; CX <- number of FAT tables ; ------------- recalculate to absolute sector -> DX:AX add ax,[bx+SDD_ResSect] ; add reserved sectors (boot) cwd ; DX:AX <- sectors add ax,[bx+SDD_StartSect] ; add absolute sector LOW adc dx,[bx+SDD_StartSect+2] ; add absolute sector HIGH ; ------------- flush old FAT sector, if needed to be changed cmp ax,[LastFATSect] ; check FAT sector LOW jne short ReadFAT2 ; need to be changed cmp dx,[LastFATSect+2] ; check FAT sector HIGH je short ReadFAT6 ; FAT sector need not to be changed ReadFAT2: call FlushFAT ; flush old FAT sector ; ------------- save last FAT sector mov [LastFATSect],ax ; last FAT sector LOW mov [LastFATSect+2],dx ; last FAT sector HIGH ; ------------- read FAT sector DX:AX to cache ReadFAT6: call SDiskReadSect ; read cached sector jnc short ReadFAT8 ; OK ; ------------- next FAT table add ax,[bx+SDD_FatSect] ; shift to next FAT table adc dx,byte 0 ; carry loop ReadFAT6 ; try next FAT table stc ; set error flag ; ------------- pop registers ReadFAT8: pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Write last FAT sector from cache, if it was modified ; ----------------------------------------------------------------------------- ; INPUT: BX = pointer to SDD descriptor ; OUTPUT: CY = write error (all FAT copies are invalid) ; ----------------------------------------------------------------------------- FlushFAT: cmp byte [ModiFAT],0 ; was FAT sector modified? je short WriteFAT9 ; FAT sector was not modified ; === WriteFAT function must follow ; ----------------------------------------------------------------------------- ; Write last FAT sector from cache ; ----------------------------------------------------------------------------- ; INPUT: BX = pointer to SDD descriptor ; OUTPUT: CY = write error (all FAT copies are invalid) ; NOTES: FlushFAT should be called after all writes. ; ----------------------------------------------------------------------------- ; ------------- push registers WriteFAT: push ax ; push AX push cx ; push CX push dx ; push DX push bp ; push BP ; ------------- prepare number of FAT tables -> CX mov byte [ModiFAT],0 ; FAT sector was not modified mov ch,0 ; CH <- 0 mov cl,[bx+SDD_FatNum] ; CX <- number of FAT tables mov bp,cx ; BP <- error counter ; ------------- get last FAT sector mov ax,[bx+SDD_BDD] ; AX <- BDD descriptor mov [OldSectBDD],ax ; set BDD descriptor mov ax,[LastFATSect] ; AX <- last FAT sector LOW mov dx,[LastFATSect+2] ; DX <- last FAT sector HIGH ; ------------- write FAT sector DX:AX from cache WriteFAT4: mov [OldSect],ax ; set sector LOW mov [OldSect+2],dx ; set sector HIGH call BDiskWriteSect ; write cached sector sbb bp,byte 0 ; error counter ; ------------- next FAT table add ax,[bx+SDD_FatSect] ; shift to next FAT table adc dx,byte 0 ; carry loop WriteFAT4 ; try next FAT table cmp bp,byte 1 ; check number of errors ; ------------- pop registers pop bp ; pop BP pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX WriteFAT9: ret ; ----------------------------------------------------------------------------- ; Get next FAT entry ; ----------------------------------------------------------------------------- ; INPUT: AX = current cluster ; BX = pointer to SDD descriptor ; OUTPUT: AX = new cluster (-1 = end of file) ; CY = error (AX undefined) ; ----------------------------------------------------------------------------- ; ------------- push registers GetNextClust: push cx ; push CX push dx ; push DX push si ; push SI push di ; push DI ; ------------- check if use FAT16 test byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12? jnz short GetNextClust2 ; yes, FAT12 ; ------------- FAT16: prepare sector number -> AX and offset in sector -> DI mov di,ax ; DI <- current cluster shl di,1 ; DI <- cluster * 2 and di,SECTSIZE-1 ; mask offset in sector mov al,ah ; AL <- cluster/256 mov ah,0 ; AX <- sector with this entry ; ------------- read sector call ReadFAT ; read FAT sector jc short GetNextClust9 ; error ; ------------- get FAT16 entry -> AX add si,di ; SI <- entry offset lodsw ; load entry cmp ax,FAT16_ENDMIN ; end of file? jae short GetNextClust7 ; end of file jmp short GetNextClust8 ; ------------- prepare sector number -> AX and offset in sector -> DI GetNextClust2: mov cx,ax ; CX <- cluster mov dx,3 ; DX <- 3 nibbles per entry mul dx ; DX:AX <- nibble index shr dx,1 rcr ax,1 ; DX:AX <- nibble index / 2 mov di,ax ; DI <- offset and di,SECTSIZE-1 ; DI <- offset in sector mov al,ah mov ah,dl mov dh,0 ; DX:AX <- offset/256 shr dx,1 rcr ax,1 ; AX <- sector number ; ------------- read sector for 1st byte call ReadFAT ; read FAT sector jc short GetNextClust9 ; error ; ------------- get first byte of entry -> DL add si,di ; SI <- byte address mov dl,[si] ; DL <- first byte of FAT12 entry sub si,di ; SI <- sector buffer ; ------------- sector number -> AX and offset -> SI of 2nd byte inc di ; increase offset in sector cmp di,SECTSIZE ; is offset valid? jb short GetNextClust4 ; offset is valid ; ------------- read sector for 2nd byte xor di,di ; DI <- new offset inc ax ; increase sector call ReadFAT ; read FAT sector jc short GetNextClust9 ; error ; ------------- get second byte of entry -> DH GetNextClust4: add si,di ; SI <- byte address mov dh,[si] ; DH <- second byte of FAT12 entry ; ------------- rotate and mask entry xchg ax,dx ; AX <- 2 bytes of entry shr cx,1 ; odd entry? jnc short GetNextClust6 ; even entry mov cl,4 ; CL <- number of rotations shr ax,cl ; AX <- rotate entry GetNextClust6: and ax,0fffh ; AX <- mask entry cmp ax,FAT12_ENDMIN ; end of file? jb short GetNextClust8 ; valid cluster ; ------------- end of file GetNextClust7: xor ax,ax ; AX <- 0 dec ax ; AX <- -1 GetNextClust8: clc ; operation OK ; ------------- pop registers GetNextClust9: pop di ; pop SI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Set FAT entry ; ----------------------------------------------------------------------------- ; INPUT: AX = next cluster (use FAT16 constants) ; DX = current cluster ; BX = pointer to SDD descriptor ; OUTPUT: CY = error ; NOTES: FlushFAT need to be called after FAT operations. ; ----------------------------------------------------------------------------- ; ------------- push registers SetNextClust: push ax ; push AX push cx ; push CX push dx ; push DX push si ; push SI push di ; push DI push bp ; push BP xchg ax,bp ; BP <- next cluster ; ------------- check if use FAT16 test byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12? jnz short SetNextClust2 ; yes, FAT12 ; ------------- FAT16: prepare sector number -> AX and offset in sector -> DI mov di,dx ; DI <- current cluster shl di,1 ; DI <- cluster * 2 and di,SECTSIZE-1 ; mask offset in sector mov al,dh ; AL <- cluster/256 mov ah,0 ; AX <- sector with this entry ; ------------- read sector call ReadFAT ; read FAT sector jc short SetNextClust9 ; error ; ------------- set FAT16 entry add di,si ; DI <- entry offset xchg ax,bp ; AX <- next cluster stosw ; set entry jmp short SetNextClust8 ; here is NC ; ------------- prepare new cluster -> BP SetNextClust2: mov ch,dl ; CH <- cluster LOW mov cl,4 ; CL <- number of rotations and bp,0fffh ; BP <- mask new cluster test ch,1 ; odd cluster? jz short SetNextClust3 ; even cluster shl bp,cl ; rotate cluster to 2nd nibble ; ------------- prepare sector number -> AX and offset in sector -> DI SetNextClust3: mov ax,3 ; AX <- 3 nibbles per entry mul dx ; DX:AX <- nibble index shr dx,1 rcr ax,1 ; DX:AX <- nibble index / 2 mov di,ax ; DI <- offset and di,SECTSIZE-1 ; DI <- offset in sector mov al,ah mov ah,dl mov dl,dh mov dh,0 ; DX:AX <- offset/256 shr dx,1 rcr ax,1 ; AX <- sector number ; ------------- read sector for 1st byte call ReadFAT ; read FAT sector jc short SetNextClust8 ; error ; ------------- set first byte of FAT12 entry mov dx,bp ; DX <- next cluster add si,di ; SI <- byte address and byte [si],0fh ; mask old value or [si],dl ; set first byte of FAT12 entry test ch,1 ; odd cluster? jnz short SetNextClust4 ; odd cluster mov [si],dl ; set first byte of FAT12 entry SetNextClust4: sub si,di ; SI <- sector buffer mov byte [ModiFAT],1 ; set modified flag ; ------------- sector number -> AX and offset -> SI of 2nd byte inc di ; increase offset in sector cmp di,SECTSIZE ; is offset valid? jb short SetNextClust5 ; offset is valid ; ------------- read sector for 2nd byte xor di,di ; DI <- new offset inc ax ; increase sector call ReadFAT ; read FAT sector jc short SetNextClust9 ; error ; ------------- get second byte of entry -> AL SetNextClust5: add si,di ; SI <- byte address xchg ax,bp ; AX <- next cluster and byte [si],0f0h ; mask old value or [si],ah ; set second byte of FAT12 entry test ch,1 ; odd cluster? jz short SetNextClust8 ; even cluster mov [si],ah ; set second byte of FAT12 entry SetNextClust8: mov byte [ModiFAT],1 ; set modified flag ; ------------- pop registers SetNextClust9: pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Find free cluster ; ----------------------------------------------------------------------------- ; INPUT: BX = pointer to SDD descriptor ; OUTPUT: AX = free cluster ; CY = error (AX undefined) ; NOTES: Searching from LastFreeClust position. ; ----------------------------------------------------------------------------- ; ------------- push registers FindFreeClust: push cx ; push CX push dx ; push DX push si ; push SI ; ------------- prepare registers mov cx,[bx+SDD_ClustNum] ; CX <- number of clusters mov si,cx ; SI <- number of clusters mov dx,[LastFreeClust] ; DX <- last free cluster inc si ; SI <- last cluster index ; ------------- increase cluster index FindFreeClust2: inc dx ; DX <- next cluster cmp dx,si ; check end of disk jbe short FindFreeClust4 ; cluster number is OK mov dx,2 ; DX <- wrap to start of disk ; ------------- check, if this cluster is free FindFreeClust4: mov ax,dx ; AX <- current cluster call GetNextClust ; get next FAT entry -> AX jc short FindFreeClust9 ; error or ax,ax ; free cluster has been found? loopnz FindFreeClust2 ; check next cluster stc ; set error flag jnz short FindFreeClust9 ; free cluster not found ; ------------- free cluster has been found xchg ax,dx ; AX <- free cluster mov [LastFreeClust],ax ; save new last free cluster clc ; clear error flag ; ------------- pop registers FindFreeClust9: pop si ; pop SI pop dx ; pop DX pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Set last free cluster ; ----------------------------------------------------------------------------- ; INPUT: AX = last free cluster (2..) ; BX = pointer to SDD descriptor ; NOTES: Used by FindFreeClust function. ; ----------------------------------------------------------------------------- SetLastFree: mov [LastFreeClust],ax ; set last free cluster ret ; ----------------------------------------------------------------------------- ; Get free space on disk ; ----------------------------------------------------------------------------- ; INPUT: AL = disk number (0..) ; OUTPUT: DX:AX = free space on disk (in bytes, 0 on error) ; BX:CX = used space on disk (in bytes, 0 on error) ; CY = error (DX:AX, BX:CX = 0 on error) ; ----------------------------------------------------------------------------- ; ------------- push registers DiskFree: push si ; push SI ; ------------- get system disk descriptor -> BX call SDiskGetDesc ; get system disk descriptor -> BX jc short DiskFree8 ; error ; ------------- get free clusters -> SI mov dx,FAT16_MIN ; DX <- start cluster mov cx,[bx+SDD_ClustNum] ; CX <- number of clusters xor si,si ; SI <- counter of free clusters DiskFree2: mov ax,dx ; AX <- cluster call GetNextClust ; get next cluster -> AX jc short DiskFree8 ; error cmp ax,byte 1 ; free cluster? adc si,byte 0 ; increase counter of free clusters inc dx ; DX <- next cluster loop DiskFree2 ; next cluster ; ------------- calculate used space -> BX:CX and free space -> DX:AX mov cx,[bx+SDD_ClustSize] ; CX <- cluster size mov ax,[bx+SDD_ClustNum] ; AX <- number of clusters sub ax,si ; AX <- used clusters mul cx ; DX:AX <- used space xchg ax,cx ; CX<-used space LOW, AX<-cluster size mov bx,dx ; BX <- used space HIGH mul si ; DX:AX <- free space clc ; clear error flag jmp short DiskFree9 ; ------------- error DiskFree8: xor ax,ax ; AX <- 0 xor bx,bx ; BX <- 0 xor cx,cx ; CX <- 0 xor dx,dx ; DX <- 0 stc ; set error flag ; ------------- pop registers DiskFree9: pop si ; pop SI ret ; ----------------------------------------------------------------------------- ; Get system disk descriptor from FILE structure ; ----------------------------------------------------------------------------- ; INPUT: DI = FILE structure ; OUTPUT: BX = pointer to SDD descriptor (or NULL on CY error) ; CY = error, invalid disk number (returns BX = NULL) ; ----------------------------------------------------------------------------- FileSDD: push ax ; push AX test byte [di+FILE_Attr],DIR_Device ; device? jz short FileSDD2 ; not device test byte [di+FILE_DirInx],FILEDEV_MASKTYPE ; system disk? stc ; set error flag jnz short FileSDD6 ; error, this is not system disk FileSDD2: mov al,[di+FILE_DirInx] ; AL <- disk shr al,1 shr al,1 shr al,1 shr al,1 ; AL <- system disk call SDiskGetDesc ; get system disk descriptor -> BX FileSDD6: pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Prepare filename mask ; ----------------------------------------------------------------------------- ; INPUT: CX = filename length ; SI = filename ; DI = file mask buffer (length 11: 8 name + 3 extension) ; OUTPUT: CY = mask contains "?" characters ; ----------------------------------------------------------------------------- ; ------------- push registers PrepFileMask: push ax ; push AX push cx ; push CX push dx ; push DX push si ; push SI push bp ; push BP mov bp,di ; BP <- push DI ; ------------- clear filename mask push cx ; push CX mov al," " ; AL <- space mov cx,11 ; CX <- filename length rep stosb ; clear filename mask pop cx ; pop CX ; ------------- special case "." mov di,bp ; DI <- destination buffer cmp cx,byte 1 ; 1 character? jne short PrepFileMask1 cmp byte [si],"." ; "." directory? jne short PrepFileMask1 mov byte [di],"." ; set mask to "." jmp short PrepFileMask8 ; ------------- special case ".." PrepFileMask1: cmp cx,byte 2 ; 2 characters? jne short PrepFileMask2 cmp word [si],".." ; ".." directory? jne short PrepFileMask2 mov word [di],".." ; set mask to ".." jmp short PrepFileMask8 ; ------------- filename mask - name part PrepFileMask2: lea dx,[bp+8] ; DX <- end of name PrepFileMask22: jcxz PrepFileMask8 ; no character left lodsb ; AL <- load one character call UpperCase ; AL <- convert to upper case letter dec cx ; decrement character counter cmp al,"." ; extension? je short PrepFileMask5 ; extension cmp al,"*" ; asterisk? jne short PrepFileMask3 ; no mov al,"?" ; substitute with "?" dec si ; return character inc cx ; return character counter PrepFileMask3: stosb ; store one character cmp di,dx ; end of name? jne short PrepFileMask22 ; next character ; ------------- skip to extension PrepFileMask4: jcxz PrepFileMask8 ; no character left lodsb ; AL <- load one character dec cx ; decrement character counter cmp al,"." ; extension? jne short PrepFileMask4 ; ------------- prepare search filename mask - extension part PrepFileMask5: mov di,dx ; DI <- buffer with mask, extension lea dx,[bp+11] ; DX <- end of extension PrepFileMask6: jcxz PrepFileMask8 ; no character left lodsb ; load one character call UpperCase ; AL <- convert to upper case letter dec cx ; decrement character counter cmp al,"." ; extension? je short PrepFileMask8 ; invalid character cmp al,"*" ; asterisk? jne short PrepFileMask7 ; no mov al,"?" ; substitute with "?" dec si ; return character inc cx ; return character counter PrepFileMask7: stosb ; store one character cmp di,dx ; end of extension? jne short PrepFileMask6 ; next character ; ------------- check "?" characters PrepFileMask8: mov di,bp ; DI <- mask buffer mov cx,11 ; CX <- mask length mov al,"?" ; AL <- character to search repne scasb ; find "?" character clc ; clear error flag jne short PrepFileMask9 ; "?" character not found stc ; set error flag ; ------------- pop registers PrepFileMask9: mov di,bp ; DI <- pop DI pop bp ; pop BP pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Get first absolute sector of cluster ; ----------------------------------------------------------------------------- ; INPUT: AX = cluster (0=root) ; BX = pointer to SDD descriptor ; OUTPUT: DX:AX = absolute sector ; ----------------------------------------------------------------------------- ; ------------- push registers GetClustSect: push cx ; push CX ; ------------- root directory or ax,ax ; root ? jnz short GetClustSect2 ; no mov ax,[bx+SDD_RootStart] ; AX <- root start LOW mov dx,[bx+SDD_RootStart+2] ; DX <- root start HIGH jmp short GetClustSect4 ; ------------- calculate sector GetClustSect2: dec ax dec ax ; AX <- cluster - 2 mov ch,0 ; CH <- 0 mov cl,[bx+SDD_SectClust] ; CX <- sectors per cluster mul cx ; DX:AX <- offset of cluster add ax,[bx+SDD_ClustStart] ; AX <- add data start LOW adc dx,[bx+SDD_ClustStart+2] ; DX <- add data start HIGH ; ------------- pop registers GetClustSect4: pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Load directory entry ; ----------------------------------------------------------------------------- ; INPUT: DX:AX = absolute sector ; BX = pointer to SDD descriptor ; SI = pointer to DIR entry in SectBuf ; DI = pointer to FILE structure ; OUTPUT: DI = pointer to next FILE structure ; DESTROYS: SI ; ----------------------------------------------------------------------------- ; ------------- push registers LoadDirEntry: push ax ; push AX push cx ; push CX push dx ; push DX ; ------------- copy entry mov cx,DIR_size/2 ; CX <- directory entry size/2 rep movsw ; copy directory entry ; ------------- clear current offset and cluster mov [di-32+FILE_ClustFile],cx;clear current cluster in file mov [di-32+FILE_ClustOff],cx ; clear offset in cluster ; ------------- relative sector with directory entry sub ax,[bx+SDD_StartSect] ; AX <- relative sector LOW sbb dx,[bx+SDD_StartSect+2] ; DX <- relative sector HIGH mov [di-32+FILE_DirL],ax ; dir entry LOW mov [di-32+FILE_DirH],dl ; dir entry HIGH ; ------------- dir entry in sector and disk sub si,SectBuf+DIR_size ; SI <- entry offset mov cl,5 ; CL <- number of rotations shr si,cl ; DI <- entry index mov ch,[bx+SDD_FlagsDisk] ; CH <- system disk and ch,SDDF_DiskMask ; CH <- mask system disk dec cx ; CL <- 4 shl ch,cl ; CH <- disk to position 4 mov cl,ch ; CL <- disk or cx,si ; CL <- add entry index mov [di-32+FILE_DirInx],cl ; dir entry in sector ; ------------- start absolute cluster mov ax,[di-32+FILE_Cluster] ; AX <- start cluster mov [di-32+FILE_ClustDisk],ax ; set current abs. cluster ; ------------- pop registers pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Find entry in directory ; ----------------------------------------------------------------------------- ; INPUT: AX = first cluster of directory (0=root) ; BX = pointer to SDD descriptor ; CX = filename length (0=find first unused DIR entry) ; DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR) ; DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR) ; SI = filename (may contain "*" and "?") ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0=no limit) ; OUTPUT: BP = number of files ; CY = error, file not found or some error ; NOTES: Deleted entry is found only if mask starts with FATDELCHAR. ; ----------------------------------------------------------------------------- ; Local variables: %define FDE_ATTRMASK [bp+13] ; mask of attributes %define FDE_ATTRREQ [bp+12] ; required attributes %define FDE_FILEREM [bp+6] ; counter of remaining files %define FDE_FILEN [bp+4] ; counter of files %define FDE_CLUSTER [bp+2] ; current cluster %define FDE_FILE [bp+0] ; pointer to FILE structure (NULL=none) ; ------------- push registers FindDirEntry: push ax ; [bp+16] push AX push cx ; [bp+14] push CX push dx ; [bp+12] push DX FDE_ATTR push si ; [bp+10] push SI push di ; [bp+8] push DI or bp,bp ; no limit? jnz short FindDirEntry1 ; no dec bp ; BP <- -1, no limit FindDirEntry1: push bp ; [bp+6] push BP FDE_FILEREM xor dx,dx ; DX <- 0 push dx ; [bp+4] FDE_FILEN push ax ; [bp+2] push AX FDE_CLUSTER push di ; [bp+0] push DI FDE_FILE mov bp,sp ; BP <- local variables ; ------------- prepare file mask SFileMask mov di,SFileMask ; DI <- file mask buffer call PrepFileMask ; prepare file mask or cx,cx ; empty file name? jnz short FindDirEntry2 ; no mov byte [SFileMask],0 ; mark empty file name ; ------------- prepare to search one cluster FindDirEntry2: mov ax,FDE_CLUSTER ; AX <- cluster call GetClustSect ; DX:AX <- absolute sector mov ch,0 ; CH <- 0 mov cl,[bx+SDD_SectClust] ; CX <- sectors per cluster ; ------------- load one sector FindDirEntry3: call SDiskReadSect ; read cached sector jnc short FindDirEntry32 ; ok jmp FindDirEntry9 ; error FindDirEntry32: mov di,si ; DI <- sector ; ------------- find file in one sector FindDirEntry4: mov si,SFileMask ; SI <- filename mask cmp byte [di],0 ; end of directory? jne short FindDirEntry40 ; not end of directory cmp byte [si],0 ; search unused entry? je short FindDirEntry47 ; found unused entry jmp FindDirEntry94 ; no FindDirEntry40: cmp byte [di],FATDELCHAR ; deleted entry? jne short FindDirEntry41 ; no cmp byte [si],FATDELCHAR ; required deleted entry? jne short FindDirEntry5 ; skip this entry FindDirEntry41: push ax ; push AX (sector LOW) push cx ; push CX (sector counter) push di ; push DI (sector pointer) mov al,[di+DIR_Attr] ; AL <- attributes and al,DIR_Mask ; mask attributes cmp word [di],". " ; this directory? jne short FindDirEntry42 ; no or al,DIR_THISDIR ; set "this directory" flag FindDirEntry42: cmp word [di],".." ; upper directory? jne short FindDirEntry43 ; no or al,DIR_UPDIR ; set "up directory" flag FindDirEntry43: and al,FDE_ATTRMASK ; mask attributes cmp al,FDE_ATTRREQ ; check attributes jne FindDirEntry46 ; attributes do not suit mov cl,11 ; CX <- filename length FindDirEntry44: lodsb ; AL <- character from mask inc di ; increase pointer cmp al,"?" ; check this character? je short FindDirEntry45 ; don't check dec di ; decrease pointer scasb ; check character FindDirEntry45: loope FindDirEntry44 ; check next character FindDirEntry46: pop di ; pop DI (sector pointer) pop cx ; pop CX (sector counter) pop ax ; pop AX (sector LOW) jne short FindDirEntry5 ; entry not found ; ------------- get this entry FindDirEntry47: inc word FDE_FILEN ; increase counter of files mov si,FDE_FILE ; SI <- FILE pointer or si,si ; check FILE pointer jz short FindDirEntry48 ; only get number of files push di ; push DI (sector pointer) xchg si,di ; SI <- DIR entry, DI <- FILE entry call LoadDirEntry ; load directory entry mov FDE_FILE,di ; next FILE entry pop di ; pop DI FindDirEntry48: dec word FDE_FILEREM ; file counter jz short FindDirEntry94 ; end of search ; ------------- next entry in sector FindDirEntry5: lea di,[di+DIR_size] ; DI <- next entry cmp di,SectBuf+SECTSIZE ; end of sector? jb short FindDirEntry4 ; try next entry ; ------------- next sector in cluster inc ax ; increase sector LOW jnz short FindDirEntry6 ; no carry inc dx ; carry FindDirEntry6: loop FindDirEntry3 ; try next sector ; ------------- move to next directory cluster FindDirEntry7: mov ax,FDE_CLUSTER ; AX <- cluster call GetNextClust ; get next cluster jc short FindDirEntry9 ; error mov FDE_CLUSTER,ax ; BP <- cluster inc ax ; end of directory? jz short FindDirEntry94 ; end of directory jmp FindDirEntry2 ; next cluster FindDirEntry94: cmp word FDE_FILEN,byte 1 ; check number of files ; ------------- pop registers FindDirEntry9: pop bp ; destroy FDE_FILE pop bp ; destroy FDE_CLUSTER pop bp ; pop BP (number of entries FDE_FILEN) pop di ; destroy FDE_FILEREM pop di ; pop DI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret %undef FDE_ATTRMASK %undef FDE_ATTRREQ %undef FDE_FILEREM %undef FDE_FILEN %undef FDE_CLUSTER %undef FDE_FILE ; ----------------------------------------------------------------------------- ; Clear FILE descriptor ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; ----------------------------------------------------------------------------- ClearFileDesc: push ax ; push AX push cx ; push CX push di ; push DI mov ax," " ; AX <- space character mov cx,5 ; CX <- name length / 2 rep stosw ; clear file name stosb ; clear last character xor ax,ax ; AX <- 0 stosb ; clear first byte mov cx,(FILE_size-12)/2 ; CX <- size of FILE structure / 2 rep stosw ; clear FILE structure pop di ; pop DI pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Open root directory ; ----------------------------------------------------------------------------- ; INPUT: AL = system disk (0..) ; DI = pointer to destination FILE structure ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers OpenRoot: push ax ; push AX push bx ; push BX ; ------------- get SDD descriptor -> BX call SDiskGetDesc ; get SDD descriptor jc short OpenRoot9 ; invalid system disk ; ------------- reload system disk info call SDiskReload ; reload system disk jc short OpenRoot9 ; error ; ------------- clear FILE descriptor call ClearFileDesc ; clear FILE descriptor ; ------------- build FILE descriptor mov byte [di+FILE_Name],PATHCHAR ; mark root directory shl al,1 shl al,1 shl al,1 shl al,1 ; AL <- shift disk to bit 4 position mov byte [di+FILE_DirInx],al ; set device type and index mov byte [di+FILE_Attr],DIR_DIR ; set directory attribute mov ah,[bx+SDD_RootSect] ; AH <- number of root sectors mov al,0 shl ax,1 ; AX <- root size mov [di+FILE_Size],ax ; root directory size ; ------------- pop registers OpenRoot9: pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Search files internal - prepare destination FILE structure ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; SI = new offset in TEXT ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0 = no limit) ; OUTPUT: DI = new FILE structure ; BP = 1 on last entry, or unchanged ; ----------------------------------------------------------------------------- FindFilesDI: inc si ; SI <- skip separator cmp si,[bx+TEXT_Length] ; check end of text jb short FindFilesDI6 ; it is not last entry mov bp,1 ; BP <- found 1 entry or di,di ; is destination buffer valid? jnz short FindFilesDI8 ; buffer is valid FindFilesDI6: mov di,FindFileFILE ; DI <- temporary FILE structure FindFilesDI8: dec si ; return SI value ret ; ----------------------------------------------------------------------------- ; Search files internal - open device ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; CX = old offset in TEXT ; SI = new offset in TEXT ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0 = no limit) ; OUTPUT: CY = error, file not found or some error ; BP = 1 on last entry, or unchanged ; ----------------------------------------------------------------------------- ; ------------- push registers FindFilesODev: push cx ; push CX push si ; push SI push di ; push DI ; ------------- prepare destination FILE structure -> DI call FindFilesDI ; prepare FILE pointer -> DI ; ------------- open device xchg si,cx ; SI <- old offset, CX <- new offset sub cx,si ; CX <- length lea si,[bx+TEXT_Text+si] ; SI <- device name call OpenDevice ; open device ; ------------- pop registers pop di ; pop DI pop si ; pop SI pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Search files internal - open root directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; SI = new offset in TEXT ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0 = no limit) ; OUTPUT: CY = error, file not found or some error ; BP = 1 on last entry, or unchanged ; DESTROYS: AX ; ----------------------------------------------------------------------------- ; ------------- push registers FindFilesORoot: push di ; push DI ; ------------- prepare destination FILE structure -> DI call FindFilesDI ; prepare FILE pointer -> DI ; ------------- get disk -> AL mov al,[FindFileFILE+FILE_DirInx] ; AL <- disk shr al,1 shr al,1 shr al,1 shr al,1 ; AL <- system disk ; ------------- open root directory call OpenRoot ; open root directory ; ------------- pop registers pop di ; pop DI ret ; ----------------------------------------------------------------------------- ; Search files internal - open file ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; CX = old offset in TEXT ; DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR) ; DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR) ; SI = new offset in TEXT ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0 = no limit) ; OUTPUT: CY = error, file not found or some error ; BP = number of files on last entry, or 0 on error, or unchanged ; DESTROYS: AX ; ----------------------------------------------------------------------------- ; ------------- push registers FindFilesOFile: push bx ; push BX push cx ; push CX push si ; push SI ; ------------- prepare to check end of text -> AX mov ax,si ; AX <- new part inc ax ; skip separator sub ax,[bx+TEXT_Length] ; check end of text ; ------------- prepare text -> SI and length -> CX xchg si,cx ; SI <- old offset, CX <- new offset sub cx,si ; CX <- length lea si,[bx+TEXT_Text+si] ; SI <- file name ; ------------- get disk descriptor -> BX push di ; push DI mov di,FindFileFILE ; DI <- temporary FILE structure call FileSDD ; get disk descriptor -> SDD pop di ; pop DI jc short FindFilesOFile4 ; invalid disk ; ------------- check end of text or ax,ax ; end of text? mov ax,[FindFileFILE+FILE_Cluster]; AX <- directory cluster jns short FindFilesOFile6 ; end of text, last entry ; ------------- find path entry push dx ; push DX push bp ; push BP push di ; push DI mov di,FindFileFILE ; DI <- FILE temporary mov bp,1 ; BP <- load 1 entry mov dx,DIR_DIR*256+DIR_DIR ; required attributes call FindDirEntry ; find directory entry pop di ; pop DI pop bp ; pop BP pop dx ; pop DX jnc short FindFilesOFile8 ; ------------- error FindFilesOFile4:xor bp,bp ; BP <- 0, no files found on error stc ; set error flag jmp short FindFilesOFile8 ; ------------- find last entry (files) FindFilesOFile6:call FindDirEntry ; find directory entry ; ------------- pop registers FindFilesOFile8:pop si ; pop SI pop cx ; pop CX pop bx ; pop BX ret ; ----------------------------------------------------------------------------- ; Search files or directories ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR) ; DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR) ; DI = array of FILE structures (NULL = only get number of files) ; BP = max. number of files (0 = no limit) ; OUTPUT: CY = error, file not found or some error ; BP = number of files ; NOTES: Deleted entry is found only if mask starts with FATDELCHAR. ; ----------------------------------------------------------------------------- ; ------------- push registers FindFiles: push ax ; push AX push cx ; push CX push si ; push SI ; ------------- copy current path FILE structure push di ; push DI mov si,CurPathFILE ; SI <- current path FILE mov cx,FILE_size/2 ; size of FILE structure mov di,FindFileFILE ; DI <- temporary FILE structure rep movsw ; copy FILE structrure pop di ; pop DI ; ------------- end of text xor si,si ; SI <- offset in text cmp si,[bx+TEXT_Length] ; end of text? je short FindFiles94 ; end of text ; ------------- find next path separator ":", "/" or "\" FindFiles1: push dx ; push DX mov ax,"\/" ; separator 1 and 2 mov dl,":" ; separator 3 mov cx,si ; CX <- push old offset in text call TextFind3ListNext ; find next separator pop dx ; pop DX jnc short FindFiles2 ; separator has been found mov si,[bx+TEXT_Length] ; SI <- limit to text end jmp short FindFiles3 ; ------------- device FindFiles2: cmp byte [bx+TEXT_Text+si],":" ; device? jne short FindFiles3 ; not device call FindFilesODev ; open device jnc short FindFiles8 ; next part of filename FindFiles94: xor bp,bp ; BP <- 0, no files found on error stc ; set error flag jmp short FindFiles9 ; error ; ------------- path is not allowed on device other than system disk FindFiles3: test byte [FindFileFILE+FILE_Attr],DIR_Device ; device? jz short FindFiles4 ; not device test byte [FindFileFILE+FILE_DirInx],FILEDEV_MASKTYPE ; SYS? jnz short FindFiles94 ; not system disk ; ------------- auto open root directory on new system disk cmp cx,si ; empty name? je short FindFiles42 ; empty name, this is root push bp ; push BP push si ; push SI mov si,cx ; don't act as last entry call FindFilesORoot ; open root directory pop si ; pop SI pop bp ; pop BP jc short FindFiles94 ; error ; ------------- empty name - root directory FindFiles4: cmp cx,si ; empty text? jne short FindFiles5 ; no FindFiles42: call FindFilesORoot ; open root directory jc short FindFiles94 ; error jmp short FindFiles8 ; next part of filename ; ------------- find directory entry FindFiles5: test byte [FindFileFILE+FILE_Attr],DIR_DIR ; directory? jz short FindFiles94 ; error, not directory call FindFilesOFile ; find file or dir entry jc short FindFiles9 ; error, not found ; ------------- next part of filename FindFiles8: mov cx,si ; CX <- next part inc cx ; CX <- skip separator mov si,cx ; SI <- next part cmp cx,[bx+TEXT_Length] ; check end of text jb short FindFiles1 ; analyte next part of path ; ------------- pop registers FindFiles9: pop si ; pop SI pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Open file ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file name (with path) ; DI = pointer to destination FILE structure ; OUTPUT: CY = error, file not found ; ----------------------------------------------------------------------------- OpenFile: push dx ; push DX mov dx,DIR_DIR*256 ; attributes (no directory) jmp short OpenDir2 ; ----------------------------------------------------------------------------- ; Open directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT directory name (with path) ; DI = pointer to destination FILE structure ; OUTPUT: CY = error, directory not found ; ----------------------------------------------------------------------------- OpenDir: push dx ; push DX mov dx,(DIR_DIR+DIR_THISDIR+DIR_UPDIR)*256+DIR_DIR ; attr. OpenDir2: push bp ; push BP mov bp,1 ; 1 file entry call FindFiles ; open file or directory pop bp ; pop BP pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Check if file or directory exists ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT directory name (with path) ; OUTPUT: CY = error, file not found ; AX = number of files ; ----------------------------------------------------------------------------- FileExist: push dx ; push DX push di ; push DI xchg ax,bp ; AX <- push BP mov dx,(DIR_THISDIR+DIR_UPDIR)*256 ; attributes xor di,di ; DI <- 0, no FILE structure xor bp,bp ; BP <- 0, no limit call FindFiles ; open file or directory xchg ax,bp ; pop BP, AX <- number of files pop di ; pop DI pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Get file attributes from FILE structure ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; OUTPUT: AL = file attributes (including DIR_THISDIR and DIR_UPDIR) ; ----------------------------------------------------------------------------- FileAttr: mov al,[di+FILE_Attr] ; AL <- attributes and al,DIR_Mask ; mask attributes cmp word [di+FILE_Name],". " ; this directory? jne short FileAttr2 ; no or al,DIR_THISDIR ; set "this directory" flag FileAttr2: cmp word [di+FILE_Name],".." ; up directory? jne short FileAttr4 ; no or al,DIR_UPDIR ; set "up directory" flag FileAttr4: ret ; ----------------------------------------------------------------------------- ; Get file attributes ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file or directory name (with path) ; OUTPUT: CY = error, file not found (AL = 0) ; AL = file attributes (incl.DIR_THISDIR and DIR_UPDIR,0 on err.) ; ----------------------------------------------------------------------------- ; ------------- push registers GetFileAttr: push dx ; push DX push di ; push DI push bp ; push BP ; ------------- find file xor dx,dx ; DX <- attributes, any file mov di,FindFileFILE ; DI <- temporary FILE structure mov bp,1 ; 1 file entry call FindFiles ; open file or directory ; ------------- error mov al,0 ; AL <- 0 on error jc short GetFileAttr4 ; error ; ------------- get attributes call FileAttr ; get file attributes clc ; clear error flag ; ------------- pop registers GetFileAttr4: pop bp ; pop BP pop di ; pop DI pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Set file attributes ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file or directory name (with path) ; AL = new file attributes (cannot change DIR attribute) ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers SetFileAttr: push ax ; push AX push dx ; push DX push di ; push DI push bp ; push BP ; ------------- find file xor dx,dx ; DX <- attributes, any file mov di,FindFileFILE ; DI <- temporary FILE structure mov bp,1 ; 1 file entry call FindFiles ; open file or directory jc short SetFileAttr4 ; error ; ------------- check attributes mov ah,[di+FILE_Attr] ; AH <- old file attributes test ah,DIR_Device stc ; set error flag jnz short SetFileAttr4 ; invalid attributes xor ah,al test ah,DIR_DIR ; check DIR attribute stc ; set error flag jnz short SetFileAttr4 ; cannot change DIR flag ; ------------- set new attributes and al,DIR_Mask ; mask valid attributes or al,DIR_Dirty ; set dirty flag mov [di+FILE_Attr],al ; set new attributes call FlushFile ; write changes ; ------------- pop registers SetFileAttr4: pop bp ; pop BP pop di ; pop DI pop dx ; pop DX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Get file size from FILE structure ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; OUTPUT: DX:AX = file size ; NOTES: Does not modify flags. ; ----------------------------------------------------------------------------- FileSize: mov ax,[di+FILE_Size] ; AX <- file size LOW mov dx,[di+FILE_Size+2] ; DX <- file size HIGH ret ; ----------------------------------------------------------------------------- ; Get file size ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file or directory name (with path) ; OUTPUT: CY = error, file not found (DX:AX = 0) ; DX:AX = file size (0 on error) ; ----------------------------------------------------------------------------- ; ------------- push registers GetFileSize: push di ; push DI push bp ; push BP ; ------------- find file xor ax,ax ; AX <- 0 xor dx,dx ; DX <- attributes, any file mov di,FindFileFILE ; DI <- temporary FILE structure mov bp,1 ; 1 file entry call FindFiles ; open file or directory jc short GetFileSize4 ; error ; ------------- get size call FileSize ; get file size -> DX:AX ; ------------- pop registers GetFileSize4: pop bp ; pop BP pop di ; pop DI ret ; ----------------------------------------------------------------------------- ; Modify file (set current date and time) ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; ----------------------------------------------------------------------------- ; ------------- push registers ModifyFile: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI ; ------------- directory does not change date and time test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short ModifyFile8 ; directory ; ------------- current date call GetDate ; get current date call PackDOSDate ; pack date to DOS file format mov [di+FILE_Date],ax ; set current date ; ------------- current time call GetTime ; get current time call PackDOSTime ; pack time to DOS file format mov [di+FILE_Time],ax ; set current time ; ------------- set dirty flag ModifyFile8: or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag ; ------------- pop registers pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Set file (or directory) size ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; DX:AX = new file size ; OUTPUT: CY = error ; NOTES: Does not initialize content of new clusters. ; Directory size only growths, not decreases. ; ----------------------------------------------------------------------------- ; ------------- push registers SetFileSize: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI ; ------------- modify file call ModifyFile ; modify file ; ------------- update FILE entry test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short SetFileSize2 ; directory does not use size cmp dx,[di+FILE_Size+2] ; check file size HIGH jne short SetFileSize1 ; need update cmp ax,[di+FILE_Size] ; check file size LOW jne short SetFileSize1 ; need update SetFileSize92: jmp SetFileSize9 ; file size is OK SetFileSize1: mov [di+FILE_Size],ax ; set new size LOW mov [di+FILE_Size+2],dx ; set new size HIGH ; ------------- get system disk descriptor -> BX SetFileSize2: call FileSDD ; get disk descriptor -> BX jc short SetFileSize92 ; error ; ------------- convert file size to sectors -> DX:AX add ax,SECTSIZE-1 ; round size up adc dx,byte 0 ; carry rcr dx,1 rcr ax,1 ; DX:AX <- size / 2 mov al,ah mov ah,dl mov dl,dh mov dh,0 ; DX:AX <- number of sectors ; ------------- get new number of clusters -> CX mov cl,[bx+SDD_SectClust] ; CL <- sectors per cluster call GetClustNum ; get number of clusters jc short SetFileSize92 ; error xchg ax,cx ; CX <- number of clusters ; ------------- prepare end of chain flag -> SI mov si,FAT12_RESMIN ; SI <- FAT12 invalid cluster test byte [bx+SDD_FlagsDisk],SDDF_FAT16 ; FAT16 ? jz short SetFileSize3 ; no mov si,FAT16_RESMIN ; SI <- FAT16 invalid cluster ; ------------- find last valid cluster -> DX,AX SetFileSize3: xor dx,dx ; DX <- 0, index of current cluster mov ax,[di+FILE_Cluster] ; AX <- first cluster jcxz SetFileSize34 ; new end of chain reached SetFileSize32: cmp ax,si ; old end of chain? jae short SetFileSize5 ; old end of chain reached mov dx,ax ; DX <- new current cluster call GetNextClust ; get next cluster -> AX jc short SetFileSize92 ; error loop SetFileSize32 ; next cluster ; ------------- mark end of file cmp ax,si ; end of chain reached? jae short SetFileSize9 ; end of chain has been found test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short SetFileSize9 ; directory does not decrease xchg ax,cx ; CX <- next cluster mov ax,FAT16_END ; AX <- end mark jmp short SetFileSize44 SetFileSize34: test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short SetFileSize9 ; directory does not decrease mov [di+FILE_Cluster],si ; chain end add word [di+FILE_Cluster],byte FAT16_END-FAT16_RESMIN or word [di+FILE_ClustDisk],byte NOCLUST ; invalidate ; ------------- shorten chain SetFileSize4: cmp ax,si ; end of chain reached? jae short SetFileSize8 ; end of chain has been found cmp ax,FAT16_MIN ; check cluster jb short SetFileSize8 ; disk error mov dx,ax ; DX <- new current cluster call GetNextClust ; get next cluster -> AX jc short SetFileSize8 ; error xchg ax,cx ; CX <- next cluster xor ax,ax ; AX <- free mark SetFileSize44: call SetNextClust ; set next cluster (mark it free) jc short SetFileSize8 ; error xchg ax,cx ; AX <- next cluster jmp short SetFileSize4 ; next cluster ; ------------- extend chain SetFileSize5: or dx,dx ; valid cluster? jz short SetFileSize54 ; no valid cluster SetFileSize52: mov [LastFreeClust],dx ; last free cluster SetFileSize54: call FindFreeClust ; find next free cluster -> AX jc short SetFileSize8 ; error or dx,dx ; first cluster? jnz short SetFileSize56 ; not first cluster mov [di+FILE_Cluster],ax ; mark first cluster or word [di+FILE_ClustDisk],byte NOCLUST ; invalidate jmp short SetFileSize58 SetFileSize56: call SetNextClust ; set next cluster jc short SetFileSize8 ; error SetFileSize58: xchg ax,dx ; DX <- new current cluster mov ax,FAT16_END ; AX <- end mark call SetNextClust ; mark end cluster jc short SetFileSize8 loop SetFileSize5 ; next cluster ; ------------- flush FAT sectors (CY = error) SetFileSize8: lahf ; AH <- flags call FlushFAT ; flush FAT sectors mov al,0 ; AL <- 0 adc al,al ; AL <- error flag or ah,al ; set carry flag sahf ; flags <- AH ; ------------- pop registers SetFileSize9: pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Get file date and time from FILE structure ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; OUTPUT: AL = second (0..58, can be 0..62, only even second) ; CL = minute (0..59, can be 0..63) ; CH = hour (0..23, can be 0..31) ; DL = day (1..31, can be 0..31) ; DH = month (1..12, can be 0..15) ; SI = year (1980..2117) ; ----------------------------------------------------------------------------- ; ------------- push registers FileDateTime: push bx ; push BX push ax ; push AX ; ------------- unpack date -> DL=day, DH=month, SI=year mov ax,[di+FILE_Date] ; AX <- DOS date call UnpackDOSDate ; unpack date ; ------------- unpack time -> BH=second, CL=minute, CH=hour mov ax,[di+FILE_Time] ; AX <- DOS time call UnpackDOSTime ; unpack time ; ------------- pop registers pop ax ; pop AX mov al,bh ; AL <- second pop bx ; pop BX ret ; ----------------------------------------------------------------------------- ; Get file date and time ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file or directory name (with path) ; OUTPUT: CY = error, file not found (all entries = 0) ; AL = second (0..58, can be 0..62, only even second) ; CL = minute (0..59, can be 0..63) ; CH = hour (0..23, can be 0..31) ; DL = day (1..31, can be 0..31) ; DH = month (1..12, can be 0..15) ; SI = year (1980..2117) ; ----------------------------------------------------------------------------- ; ------------- push registers GetFileDateTime:push di ; push DI push bp ; push BP ; ------------- find file mov al,0 ; AL <- 0 xor cx,cx ; CX <- 0 xor dx,dx ; DX <- attributes, any file xor si,si ; SI <- 0 mov di,FindFileFILE ; DI <- temporary FILE structure mov bp,1 ; 1 file entry call FindFiles ; open file or directory jc short GetFileDateTim4 ; error ; ------------- get date and time call FileDateTime ; get file date and time clc ; clear error flag ; ------------- pop registers GetFileDateTim4:pop bp ; pop BP pop di ; pop DI ret ; ----------------------------------------------------------------------------- ; Set file date and time ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file or directory name (with path) ; AL = second (0..58, can be 0..62, only even second) ; CL = minute (0..59, can be 0..63) ; CH = hour (0..23, can be 0..31) ; DL = day (1..31, can be 0..31) ; DH = month (1..12, can be 0..15) ; SI = year (1980..2117) ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers SetFileDateTime:push ax ; push AX push cx ; push CX push dx ; push DX push di ; push DI push bp ; push BP ; ------------- prepare DOS date -> AX and time -> CX push bx ; push BX mov bh,al ; BH <- second call PackDOSTime ; pack time pop bx ; pop BX xchg ax,cx ; CX <- time call PackDOSDate ; pack date ; ------------- find file xor dx,dx ; DX <- attributes, any file mov di,FindFileFILE ; DI <- temporary FILE structure mov bp,1 ; 1 file entry call FindFiles ; open file or directory jc short SetFileDateTim4 ; error ; ------------- set new date and time mov [di+FILE_Date],ax ; set date mov [di+FILE_Time],cx ; set time or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag call FlushFile ; write changes ; ------------- pop registers SetFileDateTim4:pop bp ; pop BP pop di ; pop DI pop dx ; pop DX pop cx ; pop CX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Add filename from FILE structure ; ----------------------------------------------------------------------------- ; INPUT: SI = pointer to FILE structure ; BX = destination TEXT (filename will be added after old text) ; OUTPUT: BX = old or new pointer to TEXT structure ; CY = memory error ; ----------------------------------------------------------------------------- ; ------------- push registers AddFileName: push cx ; push CX push dx ; push DX push si ; push SI push di ; push DI push bp ; push BP ; ------------- get length of extension -> DX push si ; push SI mov dx,3 ; DX <- max. length of extension add si,11-1 ; SI <- end of extension AddFileName2: cmp byte [si]," " ; blank character? ja short AddFileName3 ; found valid character dec si ; SI <- shift text pointer dec dx ; decrease length of extension jnz short AddFileName2 ; check next character AddFileName3: pop si ; pop SI ; ------------- get length of name -> BP push si ; push SI mov bp,8 ; DX <- max. length of name add si,8-1 ; SI <- end of name AddFileName4: cmp byte [si]," " ; blank character? ja short AddFileName5 ; found valid character dec si ; SI <- shift text pointer dec bp ; decrease length of name jnz short AddFileName4 ; check next character AddFileName5: pop si ; pop SI ; ------------- resize data buffer mov cx,[bx+TEXT_Length] ; CX <- current length of text mov di,cx ; DI <- old length of text add cx,bp ; add length of name cmp dl,1 ; check extension cmc ; CY = some extension adc cx,dx ; add length of extension (and ".") call TextResize ; resize text string jc short AddFileName8 ; memory error ; ------------- copy name (here is NC) lea di,[bx+TEXT_Text+di] ; DI <- destination address mov cx,bp ; CX <- length of name push si ; push SI rep movsb ; copy name pop si ; pop SI ; ------------- copy extension (here is NC) mov cx,dx ; CX <- length of extension jcxz AddFileName8 ; no extension mov byte [di],"." ; store extension separator inc di ; DI <- increase destination pointer lea si,[si+8] ; SI <- start of extension rep movsb ; copy extension ; ------------- pop registers AddFileName8: pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Get file seek position ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; OUTPUT: DX:AX = offset in file (0 on error) ; CY = invalid disk (DX:AX = 0) ; ----------------------------------------------------------------------------- ; ------------- push registers FilePos: push bx ; push BX ; ------------- offset in device or root mov dx,[di+FILE_ClustOff+2] ; DX <- offset HIGH mov ax,[di+FILE_ClustOff] ; AX <- offset LOW test byte [di+FILE_Attr],DIR_Device ; device? jnz short FilePos4 ; device cmp byte [di+FILE_Name],PATHCHAR ; root? je short FilePos4 ; root ; ------------- get system disk descriptor -> BX xor ax,ax ; AX <- 0 xor dx,dx ; DX <- 0 call FileSDD ; get disk descriptor -> BX jc short FilePos4 ; error ; ------------- get current position -> DX:AX mov ax,[di+FILE_ClustFile] ; AX <- cluster in file mul word [bx+SDD_ClustSize] ; DX:AX <- offset add ax,[di+FILE_ClustOff] ; add offset in cluster adc dx,byte 0 ; carry clc ; clear error flag ; ------------- pop registers FilePos4: pop bx ; pop BX ret ; ----------------------------------------------------------------------------- ; Set file seek position ; ----------------------------------------------------------------------------- ; INPUT: DX:AX = offset in file or in device (0 on error) ; DI = pointer to FILE structure ; CL = seek mode (SEEK_BEG, SEEK_CUR, SEEK_END) ; OUTPUT: CY = invalid disk ; NOTES: Can seek behind end of file. ; ----------------------------------------------------------------------------- ; ------------- push registers FileSeek: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI ; ------------- seek from begin cmp cl,SEEK_BEG ; seek from begin? je short FileSeek3 ; seek from begin ; ------------- seek from end cmp cl,SEEK_END ; seek from end? jne short FileSeek2 ; no add ax,[di+FILE_Size] ; AX <- offset from end LOW adc dx,[di+FILE_Size+2] ; DX <- offset from end HIGH jmp short FileSeek3 ; ------------- seek from current position FileSeek2: xchg ax,cx ; CX <- offset LOW xchg dx,bx ; BX <- offset HIGH call FilePos ; get current position -> DX:AX jc short FileSeek9 ; error add ax,cx ; AX <- offset LOW adc dx,bx ; DX <- offset HIGH ; ------------- seek device or root FileSeek3: test byte [di+FILE_Attr],DIR_Device ; device? jnz short FileSeek4 ; device cmp byte [di+FILE_Name],PATHCHAR ; root? jne short FileSeek5 ; not root FileSeek4: mov [di+FILE_Off],ax ; set new offset LOW mov [di+FILE_Off+2],dx ; set new offset HIGH jmp short FileSeek9 ; ------------- get system disk descriptor -> BX FileSeek5: call FileSDD ; get disk descriptor -> BX jc short FileSeek9 ; error ; ------------- recalculate offset to cluster mov si,[bx+SDD_ClustSize] ; SI <- cluster size xchg ax,cx ; CX <- offset LOW xchg ax,dx ; AX <- offset HIGH xor dx,dx ; DX <- 0 div si ; AX <- cluster HIGH, DX <- offset xchg ax,cx ; CX <- cluster HIGH, AX <- offset LOW div si ; CX:AX <- cluster, DX <- offset jcxz FileSeek6 ; no overflow or ax,byte -1 ; AX <- limit cluster mov dx,si ; DX <- cluster size dec dx ; DX <- limit offset FileSeek6: mov [di+FILE_ClustOff],dx ; set new offset in cluster ; ------------- invalidate disk cluster mov dx,[di+FILE_ClustFile] ; DX <- current cluster cmp ax,dx ; cluster changed? je short FileSeek9 ; cluster not changed mov [di+FILE_ClustFile],ax ; set new cluster inc dx ; DX <- next cluster cmp ax,dx ; cluster increased? jne short FileSeek8 ; no mov ax,[di+FILE_ClustDisk] ; AX <- current cluster on disk inc ax ; invalid cluster? jz short FileSeek9 ; invalid cluster dec ax ; AX <- current cluster call GetNextClust ; get next cluster jc short FileSeek8 ; error mov [di+FILE_ClustDisk],ax ; new cluster jmp short FileSeek9 FileSeek8: or word [di+FILE_ClustDisk],byte NOCLUST ; invalidate ; ------------- pop registers FileSeek9: pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Set file seek position from begin ; ----------------------------------------------------------------------------- ; INPUT: DX:AX = offset in file or in device from begin (0 on error) ; DI = pointer to FILE structure ; OUTPUT: CY = invalid disk ; NOTES: Can seek behind end of file. ; ----------------------------------------------------------------------------- FileSetPos: push cx ; push CX mov cl,SEEK_BEG ; seek mode call FileSeek ; set file pointer pop cx ; pop CX ret ; ----------------------------------------------------------------------------- ; Reset file position to begin ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure ; OUTPUT: CY = invalid disk ; ----------------------------------------------------------------------------- ; ------------- push registers FileReset: push ax ; push AX push dx ; push DX ; ------------- reset file pointer xor ax,ax ; AX <- 0 xor dx,dx ; DX <- 0 call FileSetPos ; set file pointer ; ------------- pop registers pop dx ; pop DX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Get current path ; ----------------------------------------------------------------------------- ; OUTPUT: BX = pointer to CTEXT with current path ; ----------------------------------------------------------------------------- GetCurPath: mov bx,CurPath ; BX <- current path ret ; ----------------------------------------------------------------------------- ; Set current path ; ----------------------------------------------------------------------------- ; INPUT: BX = pointer to TEXT with new current path ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers SetCurPath: push ax ; push AX push bx ; push BX push cx ; push CX push si ; push SI push di ; push DI ; ------------- absolutize path call AbsPath ; absolutize path jc short SetCurPath8 ; error xchg ax,bx ; BX <- new TEXT ; ------------- check path cmp word [bx+TEXT_Length],byte 3 ; minimal length jb short SetCurPath7 ; invalid path mov ax,[bx+TEXT_Text] ; AX <- first 2 characters cmp ah,":" ; 1-leter disk? jne short SetCurPath7 ; invalid device sub al,"A" ; AL <- system disk push bx ; push BX call SDiskGetDesc ; get SDD descriptor pop bx ; pop BX jc short SetCurPath7 ; invalid disk mov ax,"?*" ; invalid characters call TextFind2ListFirst ; check characters jnc short SetCurPath7 ; chars not allowed ; ------------- open current directory mov di,CurPathFILE ; DI <- current directory FILE call OpenDir ; open directory jnc short SetCurPath2 ; operation OK call TextFree ; destroys temporary text stc ; set error flag jmp short SetCurPath8 ; ------------- limit path length SetCurPath2: mov cx,[bx+TEXT_Length] ; CX <- length of current path cmp cx,PATHMAX ; check path length jbe SetCurPath4 ; path length is OK mov cx,PATHMAX ; limit path length ; ------------- copy new current path SetCurPath4: mov di,CurPath+2 ; DI <- current path mov [di-TEXT_Text+TEXT_Length],cx ; set length of path lea si,[bx+TEXT_Text] ; SI <- new path rep movsb ; copy new path ; ------------- destroy temporary PATH SetCurPath7: call TextFree ; destroy temporary text clc ; clear error flag ; ------------- pop registers SetCurPath8: pop di ; pop DI pop si ; pop SI pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Absolutize path ; ----------------------------------------------------------------------------- ; INPUT: BX = pointer to TEXT with relative path ; OUTPUT: AX = new TEXT with absolute path (without ending "\", uppercase) ; CY = error (AX = 0 on error) ; ----------------------------------------------------------------------------- ; ------------- push registers AbsPath: push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI push di ; push DI push bp ; push BP mov bp,bx ; BP <- input TEXT ; ------------- duplicate current path -> DI mov bx,CurPath ; BX <- current path call TextDup ; duplicate current path jnc short AbsPath0 ; OK jmp AbsPath9 ; memory error AbsPath0: xchg ax,di ; DI <- output TEXT ; ------------- add path separator xor si,si ; SI <- offset in input TEXT AbsPath1: mov bx,di ; BX <- output TEXT call TextAddPathSep ; add path separator mov di,bx ; DI <- new output TEXT jc short AbsPath82 ; memory error ; ------------- find next path separator -> SI mov bx,bp ; BX <- input TEXT mov ax,"\/" ; separator 1 and 2 mov dl,":" ; separator 3 mov cx,si ; CX <- push old offset in text call TextFind3ListNext ; find next separator jnc short AbsPath2 ; separator has been found mov si,[bx+TEXT_Length] ; SI <- limit to text end jmp short AbsPath3 ; ------------- device AbsPath2: cmp byte [bx+TEXT_Text+si],":" ; device? jne short AbsPath3 ; not device push si ; push SI (new offset) and word [di+TEXT_Length],byte 0 ; destroy path xchg cx,si ; SI <- old offset, CX <- new offset sub cx,si ; CX <- length of device name inc cx ; CX <- including ":" lea si,[bp+TEXT_Text+si] ; SI <- input text mov bx,di ; BX <- output TEXT call TextAddBuf ; add device mov di,bx ; DI <- new output TEXT pop si ; pop SI (new offset) jc short AbsPath82 ; memory error jmp short AbsPath32 ; add path separator ; ------------- root AbsPath3: cmp si,cx ; empty segment? jne short AbsPath4 ; not root mov bx,di ; BX <- output TEXT mov al,":" ; AL <- separator xchg si,cx ; CX <- new offset call TextFindChFirst ; find ":" separator -> SI xchg si,cx ; SI <- new offset, CX <- ":" offset jc short AbsPath8 ; internal error? inc cx ; CX <- including ":" mov [bx+TEXT_Length],cx ; left only device AbsPath32: call TextAddPathSep ; add path separator mov di,bx ; DI <- new output TEXT AbsPath82: jc short AbsPath8 ; memory error jmp short AbsPath7 ; next segment ; ------------- skip "." segment AbsPath4: mov ax,si ; AX <- new offset sub ax,cx ; AX <- segment length cmp ax,byte 1 ; 1 char? jne short AbsPath5 ; not 1 char xchg si,cx ; SI <- old offset, CX <- new offset cmp byte [bx+TEXT_Text+si],"." ; "." segment? xchg si,cx ; SI <- new offset, CX <- old offset je short AbsPath7 ; skip this segment ; ------------- upper directory ".." AbsPath5: cmp ax,byte 2 ; 2 chars? jne short AbsPath6 ; not 2 chars xchg si,cx ; SI <- old offset, CX <- new offset cmp word [bx+TEXT_Text+si],".." ; ".." segment? xchg si,cx ; SI <- new offset, CX <- old offset jne short AbsPath6 ; not ".." segment mov bx,di ; BX <- output TEXT call TextDelPathSep ; delete end path separator mov al,PATHCHAR ; AL <- path separator xchg si,cx ; CX <- new offset call TextFindChLast ; find last path separator xchg si,cx ; SI <- new offset, CX <- path sep. jc short AbsPath7 ; already root inc cx ; CX <- including path separator mov [bx+TEXT_Length],cx ; set new text length jmp short AbsPath7 ; next segment ; ------------- add segment AbsPath6: push si ; push SI (new offset) mov si,cx ; SI <- old offset lea si,[bx+TEXT_Text+si] ; SI <- input text xchg ax,cx ; CX <- segment length mov bx,di ; BX <- output TEXT call TextAddBuf ; add device mov di,bx ; DI <- new output TEXT pop si ; pop SI (new offset) jc short AbsPath8 ; memory error ; ------------- shift to next segment AbsPath7: inc si ; SI <- skip path separator cmp si,[ds:bp+TEXT_Length] ; end of text? jae short AbsPath72 ; end of text jmp AbsPath1 ; next segment ; ------------- convert to uppercase AbsPath72: mov bx,di ; BX <- output TEXT call TextUpper ; convert to uppercase letters ; ------------- delete last path separator call TextDelPathSep ; delete end path separator xchg ax,bx ; AX <- output TEXT clc ; clear error flag jmp short AbsPath9 ; ------------- memory error, destroy output TEXT AbsPath8: mov bx,di ; BX <- output TEXT call TextFree ; destroy text xor ax,ax ; AX <- invalidate output TEXT stc ; set error flag ; ------------- pop registers AbsPath9: pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX ret ; ----------------------------------------------------------------------------- ; Read from file ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure (must be open) ; SI = destination buffer ; CX = number of bytes ; OUTPUT: CY = error ; CX = successfully readed bytes ; ----------------------------------------------------------------------------- ; Local variables: %define RF_FILE [bp+2] ; pointer to FILE structure %define RF_READED [bp+0] ; successfully readed bytes %define RF_SIZEREM [bp-2] ; remaining number of bytes %define RF_BUFF [bp-4] ; destination buffer %define RF_CHSIZE [bp-6] ; size of continuous chain %define RF_CHOFFH [bp-8] ; start offset of continuous chain HIGH %define RF_CHOFFL [bp-10] ; start offset of continuous chain LOW %define RF_CHCLUST [bp-12] ; last cluster of chain ; ------------- push registers ReadFile: push ax ; push AX push bx ; push BX push dx ; push DX push si ; push SI push bp ; push BP push di ; [bp+2] RF_FILE (-> DI) xor ax,ax ; AX <- 0 push ax ; [bp+0] RF_READED (-> CX) mov bp,sp ; BP <- local variables push cx ; [bp-2] RF_SIZEREM push si ; [bp-4] RF_BUFF push ax ; [bp-6] RF_CHSIZE push ax ; [bp-8] RF_CHOFFH push ax ; [bp-10] RF_CHOFFL push ax ; [bp-12] RF_CHCLUST ; ------------- read from device (here is AX = 0) xor dx,dx ; DX <- 0 mov bl,[di+FILE_DirInx] ; BL <- device type and index test byte [di+FILE_Attr],DIR_Device ; device? jnz short ReadFile2 ; read data from device ; ------------- read from root directory call FileSDD ; get SDD descriptor -> BX jc short ReadFile92 ; error, invalid disk number cmp byte [di+FILE_Name],PATHCHAR ; root? jne short ReadFile3 ; not root mov al,0 ; AL <- 0 mov ah,[bx+SDD_RootStart] ; AX <- start sector LOW * 256 mov dx,[bx+SDD_RootStart+1] ; DX <- start sector HIGH sub ah,[bx+SDD_StartSect] ; AX <- start sector relative LOW sbb dx,[bx+SDD_StartSect+1] ; DX <- start sector HIGH shl ax,1 ; AX <- start offset LOW rcl dx,1 ; DX <- start offset HIGH mov bl,[di+FILE_DirInx] ; BL <- disk and index of entry and bl,FILEDEV_MASKINX ; mask index -> system disk type ReadFile2: add ax,[di+FILE_Off] ; AX <- current offset LOW add dx,[di+FILE_Off+2] ; DX <- current offset HIGH add [di+FILE_Off],cx ; shift offset LOW adc word [di+FILE_Off+2],byte 0 ; shift offset HIGH mov di,si ; DI <- buffer mov si,cx ; SI <- required size call DevRead ; read from device pushf ; push flags sub si,cx ; SI <- readed bytes mov RF_READED,si ; set readed bytes popf ; pop flags ReadFile92: jmp ReadFile9 ; ------------- limit data size (to end of file) ReadFile3: test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short ReadFile4 ; directory has unknown size call FilePos ; get file seek position -> DX:AX mov cx,[di+FILE_Size] ; CX <- file size LOW mov si,[di+FILE_Size+2] ; SI <- file size HIGH sub cx,ax ; CX <- remaining bytes LOW sbb si,dx ; SI <- remaining bytes HIGH jnz short ReadFile4 ; data size is OK cmp cx,RF_SIZEREM ; check number of bytes jae short ReadFile4 ; number of bytes is OK mov RF_SIZEREM,cx ; limit number of bytes ; ------------- update start disk cluster ReadFile4: mov ax,[di+FILE_ClustDisk] ; AX <- current disk cluster cmp ax,byte NOCLUST ; valid cluster? jne short ReadFile5 ; current cluster is valid mov ax,[di+FILE_Cluster] ; AX <- start cluster mov dx,[di+FILE_ClustFile] ; DX <- relative cluster or dx,dx ; first cluster? jz short ReadFile48 ; use first cluster ReadFile42: cmp ax,byte NOCLUST ; end of file? je short ReadFile48 ; end of file call GetNextClust ; get next cluster jc short ReadFile92 ; error dec dx ; cluster counter jnz ReadFile42 ; get next cluster ReadFile48: mov [di+FILE_ClustDisk],ax ; set new disk cluster ; ------------- pointer is behind end of file (here is AX = disk cluster) ReadFile5: cmp ax,byte NOCLUST ; valid cluster? je short ReadFile82 ; not valid cluster, end of data ; ------------- check continuous chain and flush chain mov dx,RF_CHCLUST ; DX <- last cluster inc dx ; DX <- next cluster cmp ax,dx ; is chain continuous? je short ReadFile6 ; chain is continuous call ReadFileChFlush ; load data of one chain jc short ReadFile9 ; error ; ------------- mark start of chain ReadFile6: cmp word RF_CHSIZE,byte 0 ; any chain? jne short ReadFile64 ; chain was already started sub ax,byte 2 ; AX <- relative cluster jc short ReadFile9 ; invalid cluster mov RF_CHCLUST,ax ; chain cluster xchg ax,dx ; DX <- cluster mov al,[bx+SDD_SectClust] ; AL <- sectors per cluster cbw ; AX <- sectors per cluster mul dx ; DX:AX <- relative sector add ax,[bx+SDD_ClustStart] ; AX <- absolute sector LOW adc dx,[bx+SDD_ClustStart+2] ; DX <- absolute sector HIGH sub ax,[bx+SDD_StartSect] ; AX <- sector on system disk LOW sbb dx,[bx+SDD_StartSect+2] ; DX <- sector on sys.disk HIGH mov dh,dl mov dl,ah mov ah,al mov al,0 ; DX:AX <- sector * 256 shl ax,1 rcl dx,1 ; DX:AX <- offset on system disk add ax,[di+FILE_ClustOff] ; add offset in cluster adc dx,byte 0 ; carry mov RF_CHOFFL,ax ; chain start LOW mov RF_CHOFFH,dx ; chain start HIGH ; ------------- prepare size in this cluster ReadFile64: mov ax,[bx+SDD_ClustSize] ; AX <- cluster size mov dx,ax ; DX <- cluster size sub ax,[di+FILE_ClustOff] ; AX <- remainder to end of cluster cmp ax,RF_SIZEREM ; check remaining bytes jbe short ReadFile65 ; data size is OK mov ax,RF_SIZEREM ; AX <- limit data size ReadFile65: or ax,ax ; any data left? ReadFile82: jz short ReadFile8 ; end of data add RF_CHSIZE,ax ; increase chain size sub RF_SIZEREM,ax ; decrease remaining data ; ------------- shift to next cluster add [di+FILE_ClustOff],ax ; shift cluster offset cmp dx,[di+FILE_ClustOff] ; is cluster full? ja short ReadFile8 ; end of data and word [di+FILE_ClustOff],byte 0 ; clear cluster offset inc word [di+FILE_ClustFile] ; increase cluster in file mov ax,[di+FILE_ClustDisk] ; AX <- cluster on disk call GetNextClust ; get next cluster jc short ReadFile9 ; error mov [di+FILE_ClustDisk],ax ; set new disk cluster jmp ReadFile5 ; ------------- flush last chain ReadFile8: call ReadFileChFlush ; load data of one chain ; ------------- pop registers (CY=error) ReadFile9: mov sp,bp ; SP <- destroy local variables pop cx ; pop CX <- RF_READED pop di ; pop DI pop bp ; pop BP pop si ; pop SI pop dx ; pop DX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Read from file internal - flush chain ; ----------------------------------------------------------------------------- ; INPUT: BP = local variables ; OUTPUT: CY = error ; DESTROYS: CX, DX ; ----------------------------------------------------------------------------- ReadFileChFlush:push ax ; push AX push bx ; push BX push di ; push DI mov cx,RF_CHSIZE ; CX <- size of chain clc ; clear error flag jcxz ReadFileChFlsh6 ; chain is empty mov ax,RF_CHOFFL ; AX <- start of chain LOW mov dx,RF_CHOFFH ; DX <- start of chain HIGH mov di,RF_FILE ; DI <- FILE structure mov bl,[di+FILE_DirInx] ; BL <- disk and index of entry and bl,FILEDEV_MASKINX ; mask index -> system disk type mov di,RF_BUFF ; DI <- buffer call DevRead ; read data pushf ; push flags mov ax,RF_CHSIZE ; AX <- size of chain sub ax,cx ; AX <- readed bytes add RF_READED,ax ; increase readed counter add RF_BUFF,ax ; shift buffer address and word RF_CHSIZE,byte 0 ; clear size of chain popf ; pop flags ReadFileChFlsh6:pop di ; pop DI pop bx ; pop BX pop ax ; pop AX ret %undef RF_FILE %undef RF_READED %undef RF_SIZEREM %undef RF_BUFF %undef RF_CHSIZE %undef RF_CHOFFH %undef RF_CHOFFL %undef RF_CHCLUST ; ----------------------------------------------------------------------------- ; Write to file ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure (must be open) ; SI = source buffer ; CX = number of bytes ; OUTPUT: CY = error ; CX = successfully written bytes ; ----------------------------------------------------------------------------- ; Local variables: %define WF_FILE [bp+2] ; pointer to FILE structure %define WF_WRITTEN [bp+0] ; successfully written bytes %define WF_SIZEREM [bp-2] ; remaining number of bytes %define WF_BUFF [bp-4] ; source buffer %define WF_CHSIZE [bp-6] ; size of continuous chain %define WF_CHOFFH [bp-8] ; start offset of continuous chain HIGH %define WF_CHOFFL [bp-10] ; start offset of continuous chain LOW %define WF_CHCLUST [bp-12] ; last cluster of chain ; ------------- push registers WriteFile: push ax ; push AX push bx ; push BX push dx ; push DX push si ; push SI push bp ; push BP push di ; [bp+2] WF_FILE (-> DI) xor ax,ax ; AX <- 0 push ax ; [bp+0] WF_WRITTEN (-> CX) mov bp,sp ; BP <- local variables push cx ; [bp-2] WF_SIZEREM push si ; [bp-4] WF_BUFF push ax ; [bp-6] WF_CHSIZE push ax ; [bp-8] WF_CHOFFH push ax ; [bp-10] WF_CHOFFL push ax ; [bp-12] WF_CHCLUST ; ------------- check R/O attribute test byte [di+FILE_Attr],DIR_RO ; check R/O attribute stc ; set error flag jnz short WriteFile92 ; cannot save to file ; ------------- modify file call ModifyFile ; modify file ; ------------- write to device (here is AX = 0) xor dx,dx ; DX <- 0 mov bl,[di+FILE_DirInx] ; BL <- device type and index test byte [di+FILE_Attr],DIR_Device ; device? jnz short WriteFile2 ; write data to device ; ------------- write to root directory call FileSDD ; get SDD descriptor -> BX jc short WriteFile92 ; error, invalid disk number cmp byte [di+FILE_Name],PATHCHAR ; root? jne short WriteFile3 ; not root mov al,0 ; AL <- 0 mov ah,[bx+SDD_RootStart] ; AX <- start sector LOW * 256 mov dx,[bx+SDD_RootStart+1] ; DX <- start sector HIGH sub ah,[bx+SDD_StartSect] ; AX <- start sector relative LOW sbb dx,[bx+SDD_StartSect+1] ; DX <- start sector HIGH shl ax,1 ; AX <- start offset LOW rcl dx,1 ; DX <- start offset HIGH mov bl,[di+FILE_DirInx] ; BL <- disk and index of entry and bl,FILEDEV_MASKINX ; mask index -> system disk type WriteFile2: add ax,[di+FILE_Off] ; AX <- current offset LOW add dx,[di+FILE_Off+2] ; DX <- current offset HIGH add [di+FILE_Off],cx ; shift offset LOW adc word [di+FILE_Off+2],byte 0 ; shift offset HIGH mov di,si ; DI <- buffer mov si,cx ; SI <- required size call DevWrite ; write to device pushf ; push flags sub si,cx ; SI <- readed bytes mov WF_WRITTEN,si ; set written bytes popf ; pop flags WriteFile92: jmp WriteFile9 ; ------------- increase file (or directory) size WriteFile3: call FilePos ; get file seek position -> DX:AX add ax,WF_SIZEREM ; add required size adc dx,byte 0 ; carry test byte [di+FILE_Attr],DIR_DIR ; directory? jnz short WriteFile36 ; directory has unknown size cmp dx,[di+FILE_Size+2] ; check file size HIGH jne short WriteFile34 cmp ax,[di+FILE_Size] ; check file size LOW WriteFile34: jbe short WriteFile4 ; file size is OK WriteFile36: call SetFileSize ; set file (directory) size jc short WriteFile92 ; error ; ------------- update start disk cluster WriteFile4: mov ax,[di+FILE_ClustDisk] ; AX <- current disk cluster cmp ax,byte NOCLUST ; valid cluster? jne short WriteFile5 ; current cluster is valid mov ax,[di+FILE_Cluster] ; AX <- start cluster mov dx,[di+FILE_ClustFile] ; DX <- relative cluster or dx,dx ; first cluster? jz short WriteFile48 ; use first cluster WriteFile42: cmp ax,byte NOCLUST ; end of file? je short WriteFile48 ; end of file call GetNextClust ; get next cluster jc short WriteFile92 ; error dec dx ; cluster counter jnz WriteFile42 ; get next cluster WriteFile48: mov [di+FILE_ClustDisk],ax ; set new disk cluster ; ------------- pointer is behind end of file (here is AX = disk cluster) WriteFile5: cmp ax,byte NOCLUST ; valid cluster? je short WriteFile82 ; not valid cluster, end of data ; ------------- check continuous chain and flush chain mov dx,WF_CHCLUST ; DX <- last cluster inc dx ; DX <- next cluster cmp ax,dx ; is chain continuous? je short WriteFile6 ; chain is continuous call WriteFileChFlsh ; save data of one chain jc short WriteFile9 ; error ; ------------- mark start of chain WriteFile6: cmp word WF_CHSIZE,byte 0 ; any chain? jne short WriteFile64 ; chain was already started sub ax,byte 2 ; AX <- relative cluster jc short WriteFile9 ; invalid cluster mov WF_CHCLUST,ax ; chain cluster xchg ax,dx ; DX <- cluster mov al,[bx+SDD_SectClust] ; AL <- sectors per cluster cbw ; AX <- sectors per cluster mul dx ; DX:AX <- relative sector add ax,[bx+SDD_ClustStart] ; AX <- absolute sector LOW adc dx,[bx+SDD_ClustStart+2] ; DX <- absolute sector HIGH sub ax,[bx+SDD_StartSect] ; AX <- sector on system disk LOW sbb dx,[bx+SDD_StartSect+2] ; DX <- sector on sys.disk HIGH mov dh,dl mov dl,ah mov ah,al mov al,0 ; DX:AX <- sector * 256 shl ax,1 rcl dx,1 ; DX:AX <- offset on system disk add ax,[di+FILE_ClustOff] ; add offset in cluster adc dx,byte 0 ; carry mov WF_CHOFFL,ax ; chain start LOW mov WF_CHOFFH,dx ; chain start HIGH ; ------------- prepare size in this cluster WriteFile64: mov ax,[bx+SDD_ClustSize] ; AX <- cluster size mov dx,ax ; DX <- cluster size sub ax,[di+FILE_ClustOff] ; AX <- remainder to end of cluster cmp ax,WF_SIZEREM ; check remaining bytes jbe short WriteFile65 ; data size is OK mov ax,WF_SIZEREM ; AX <- limit data size WriteFile65: or ax,ax ; any data left? WriteFile82: jz short WriteFile8 ; end of data add WF_CHSIZE,ax ; increase chain size sub WF_SIZEREM,ax ; decrease remaining data ; ------------- shift to next cluster add [di+FILE_ClustOff],ax ; shift cluster offset cmp dx,[di+FILE_ClustOff] ; is cluster full? ja short WriteFile8 ; end of data and word [di+FILE_ClustOff],byte 0 ; clear cluster offset inc word [di+FILE_ClustFile] ; increase cluster in file mov ax,[di+FILE_ClustDisk] ; AX <- cluster on disk call GetNextClust ; get next cluster jc short WriteFile9 ; error mov [di+FILE_ClustDisk],ax ; set new disk cluster jmp WriteFile5 ; ------------- flush last chain WriteFile8: call WriteFileChFlsh ; load data of one chain ; ------------- pop registers (CY=error) WriteFile9: mov sp,bp ; SP <- destroy local variables pop cx ; pop CX <- WF_WRITTEN pop di ; pop DI pop bp ; pop BP pop si ; pop SI pop dx ; pop DX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Write to file internal - flush chain ; ----------------------------------------------------------------------------- ; INPUT: BP = local variables ; OUTPUT: CY = error ; DESTROYS: CX, DX ; ----------------------------------------------------------------------------- WriteFileChFlsh:push ax ; push AX push bx ; push BX push di ; push DI mov cx,WF_CHSIZE ; CX <- size of chain clc ; clear error flag jcxz WriteFileChFls6 ; chain is empty mov ax,WF_CHOFFL ; AX <- start of chain LOW mov dx,WF_CHOFFH ; DX <- start of chain HIGH mov di,WF_FILE ; DI <- FILE structure mov bl,[di+FILE_DirInx] ; BL <- disk and index of entry and bl,FILEDEV_MASKINX ; mask index -> system disk type mov di,WF_BUFF ; DI <- buffer call DevWrite ; write data pushf ; push flags mov ax,WF_CHSIZE ; AX <- size of chain sub ax,cx ; AX <- readed bytes add WF_WRITTEN,ax ; increase readed counter add WF_BUFF,ax ; shift buffer address and word WF_CHSIZE,byte 0 ; clear size of chain popf ; pop flags WriteFileChFls6:pop di ; pop DI pop bx ; pop BX pop ax ; pop AX ret %undef WF_FILE %undef WF_WRITTEN %undef WF_SIZEREM %undef WF_BUFF %undef WF_CHSIZE %undef WF_CHOFFH %undef WF_CHOFFL %undef WF_CHCLUST ; ----------------------------------------------------------------------------- ; Flush file (write directory entry if needed) ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure (must be open) ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers FlushFile: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI ; ------------- check if file is modified test byte [di+FILE_Attr],DIR_Dirty ; is file dirty? jz short FlushFile9 ; file is not dirty and byte [di+FILE_Attr],~DIR_Dirty ; clear dirty flag ; ------------- device and root directory does not have dir entry test byte [di+FILE_Attr],DIR_Device ; device? jnz short FlushFile9 ; device cmp byte [di+FILE_Name],PATHCHAR ; root? je short FlushFile9 ; root ; ------------- get system disk descriptor -> BX call FileSDD ; get disk descriptor -> BX jc short FlushFile9 ; error ;-------------- load sector with directory entry mov ax,[di+FILE_DirL] ; AX <- sector LOW mov dh,0 ; DH <- 0 mov dl,[di+FILE_DirH] ; DL <- sector HIGH add ax,[bx+SDD_StartSect] ; AX <- absolute sector LOW adc dx,[bx+SDD_StartSect+2] ; DX <- absolute sector HIGH call SDiskReadSect ; read sector into cache jc short FlushFile9 ; error ; ------------- copy directory entry push di ; push DI mov al,[di+FILE_DirInx] ; AL <- dir entry and al,0fh ; AX <- index of entry in sector mov ah,DIR_size ; AH <- size of directory entry mul ah ; AX <- offset of directory entry add si,ax ; SI <- address of directory entry xchg si,di ; SI <- FILE entry, DI <- DIR entry mov cx,DIR_size/2 ; CX <- size of directory entry rep movsw ; copy directory entry pop di ; pop DI ; ------------- write sector call BDiskWriteSect ; write sector ; ------------- pop registers FlushFile9: pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Close file ; ----------------------------------------------------------------------------- ; INPUT: DI = pointer to FILE structure (must be open) ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- flush file CloseFile: call FlushFile ; flush file ; ------------- destroy file descriptor pushf ; push flags call ClearFileDesc ; clear file descriptor popf ; pop flags ret ; ----------------------------------------------------------------------------- ; Create and open file or directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; DL = attributes (incl. DIR_DIR) ; DI = pointer to destination FILE structure ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers CreateFileDir: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI push bp ; push BP ; ------------- find file name -> SI push dx ; push DX mov ax,"\/" ; separator 1 and 2 mov dl,":" ; separator 3 call TextFind3ListLast ; find last separator pop dx ; pop DX inc si ; SI <- start of name ; ------------- current directory start cluster -> AX, and SDD descriptor -> BP mov ax,[CurPathFILE+FILE_Cluster];AX<-current path cluster push bx ; push BX push di ; push DI mov di,CurPathFILE ; DI <- current path FILE call FileSDD ; get SDD descriptor from file -> BX mov bp,bx ; BP <- SDD descriptor pop di ; pop DI pop bx ; pop BX ; ------------- get directory start cluster -> AX, and SDD descriptor -> BP or si,si ; is any path? jz short CreateFileDir4 ; no path push bx ; push BX mov cx,si ; CX <- path length call TextLeft ; get path -> AX jc short CreateFileDir3 ; error xchg ax,bx ; BX <- path call OpenDir ; open directory mov ax,[di+FILE_Cluster] ; AX <- directory cluster pushf ; push flags call TextFree ; delete temporary text call FileSDD ; get SDD descriptor from file -> BX mov bp,bx ; BP <- SDD descriptor popf ; pop flags CreateFileDir3: pop bx ; pop BX jnc short CreateFileDir4 CreateFileDir92:jmp CreateFileDir9 ; error ; ------------- check if such dir entry already exists CreateFileDir4: mov cx,[bx+TEXT_Length] ; CX <- text length sub cx,si ; CX <- file name length lea si,[bx+TEXT_Text+si] ; SI <- file name text push dx ; push DX xor dx,dx ; DX <- 0, attributes do not matter mov bx,bp ; BX <- SDD descriptor mov bp,1 ; 1 file call FindDirEntry ; find directory entry pop dx ; pop DX cmc ; CY = file found OK jc short CreateFileDir92 ; error, file already exists ; ------------- find first deleted entry push cx ; push CX push dx ; push DX push si ; push SI xor dx,dx ; DX <- 0, attributes do not matter mov si,DeletedFileMask ; SI <- deleted file mask mov cx,4 ; CX <- length of deleted file mask mov bp,1 ; 1 file call FindDirEntry ; find first deleted file pop si ; pop SI pop dx ; pop DX pop cx ; pop CX jnc short CreateFileDir5 ; found OK ; ------------- find first unused entry push cx ; push CX push dx ; push DX xor dx,dx ; DX <- 0, attributes do not matter xor cx,cx ; CX <- 0, length of unused file mask mov bp,1 ; 1 file call FindDirEntry ; find first deleted file pop dx ; pop DX pop cx ; pop CX jnc short CreateFileDir5 ; found OK ; ------------- increase directory size or ax,ax ; root ? stc ; set zero flag jz short CreateFileDir92 ; root call CreateFileDirS ; increase directory size jc short CreateFileDir92 ; error ; ------------- find first unused entry push cx ; push CX push dx ; push DX xor dx,dx ; DX <- 0, attributes do not matter xor cx,cx ; CX <- 0, length of unused file mask mov bp,1 ; 1 file call FindDirEntry ; find first deleted file pop dx ; pop DX pop cx ; pop CX jc short CreateFileDir9 ; error ; ------------- initilize entry ; here is BX = SDD descriptor, DL = attributes, AX = directory cluster CreateFileDir5: xchg ax,bp ; BP <- directory start cluster mov dh,[di+FILE_DirInx] ; DH <- entry index push dx ; push DX (DL=attributes, DH=index) mov ax,[di+FILE_DirDW] ; AX <- sector LOW mov dx,[di+FILE_DirDW+2] ; DX <- sector HIGH call ClearFileDesc ; clear FILE entry mov [di+FILE_DirDW],ax ; return sector LOW mov [di+FILE_DirDW+2],dx ; return sector HIGH mov dh,dl mov dl,ah mov ah,al mov al,0 ; DX:AX <- sector * 256 shl ax,1 rcl dx,1 ; DX:AX <- sector offset in bytes call ModifyFile ; set current date and time call PrepFileMask ; prepare filename pop cx ; CL <- attributes, CH <- entry index jc short CreateFileDir9 ; cannot contain "?" characters cmp byte [di],"." ; invalid name je short CreateFileDir54 ; invalid file name cmp byte [di+8]," " ; check extension jne short CreateFileDir56 ; valid name cmp byte [di]," " ; invalid name CreateFileDir54:stc ; set error flag je short CreateFileDir9 ; invalid file name ; ------------- set attributes CreateFileDir56:and cl,DIR_Mask ; mask valid attributes mov byte [di+FILE_Attr],cl ; set attributes ; ------------- invalidate start cluster mov word [di+FILE_Cluster],FAT16_END ; FAT16 end test byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12 ? jz short CreateFileDir6 ; no mov byte [di+FILE_Cluster+1],FAT12_END/256 ; FAT12 end ; ------------- create first cluster of directory CreateFileDir6: call CreateFileDirD ; initialize directory jc short CreateFileDir9 ; error ; ------------- add entry offset -> DX:AX CreateFileDir7: mov bl,ch ; BL <- disk mov cl,4 shl ch,cl ; CH <- index * 16 mov cl,ch ; CL <- index * 16 mov ch,0 shl cx,1 ; CX <- offset in sector add ax,cx adc dx,byte 0 ; DX:AX <- offset of entry ; ------------- write DIR entry and bl,FILEDEV_MASKINX ; mask index -> system disk type mov cx,DIR_size ; CX <- entry size call DevWrite ; write data ; ------------- pop registers CreateFileDir9: pop bp ; pop BP pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX CreateFileDirD9:ret ; ============= initialize new directory ; CL = attributes, DI = FILE structure, BX = SDD descriptor, BP = dir. cluster ; ------------- check if it is directory CreateFileDirD: test cl,DIR_DIR ; directory? jz short CreateFileDirD9 ; not directory ; ------------- push registers push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push di ; push DI ; ------------- find free cluster call FindFreeClust ; find free cluster jnc short CreateFileDirD2 CreateFileDirD1:jmp CreateFileDirD8 ; not found CreateFileDirD2:mov [di+FILE_Cluster],ax ; set cluster ; ------------- mark this cluster as used xchg ax,dx ; DX <- cluster mov ax,FAT16_END ; AX <- end mark call SetNextClust ; mark cluster as last cluster jc short CreateFileDirD1 ; error call FlushFAT ; flush FAT buffer jc short CreateFileDirD1 ; error ; ------------- prepare absolute sector of cluster xchg ax,dx ; AX <- cluster call GetClustSect ; get absolute sector mov [OldSect],ax ; sector number LOW mov [OldSect+2],dx ; sector number HIGH mov cl,[bx+SDD_SectClust] ; CL <- sectors per cluster mov ch,0 mov bx,[bx+SDD_BDD] ; BX <- BDD descriptor mov [OldSectBDD],bx ; set old BDD descriptor ; ------------- clear sector buffer mov dx,[di+FILE_Cluster] ; DX <- this cluster mov di,SectBuf ; DI <- sector buffer push di ; push DI push cx ; push CX xor ax,ax ; AX <- 0 mov cx,SECTSIZE/2 ; CX <- sector size rep stosw ; clear sector buffer pop cx ; pop CX pop di ; pop DI ; ------------- initialize first 2 entries call ClearFileDesc ; clear FILE entry call ModifyFile ; set current date and time mov byte [di],"." ; mark as "THIS" directory mov byte [di+DIR_Attr],DIR_DIR ; set attributes mov [di+DIR_Cluster],dx ; set current cluster add di,byte 32 ; DI <- second DIR entry call ClearFileDesc ; clear FILE entry call ModifyFile ; set current date and time mov word [di],".." ; mark as "UP" directory mov byte [di+DIR_Attr],DIR_DIR ; set attributes mov [di+DIR_Cluster],bp ; set parent cluster ; ------------- write one sector CreateFileDirD4:call BDiskWriteSect ; write sector from buffer jc short CreateFileDirD8 ; error ; ------------- clear sector push cx ; push CX mov di,SectBuf ; DI <- sector buffer xor ax,ax ; AX <- 0 mov cx,SECTSIZE/2 ; CX <- sector size rep stosw ; clear sector buffer pop cx ; pop CX ; ------------- next sector add word [OldSect],byte 1 ; increase sector number adc word [OldSect+2],byte 0 ; carry loop CreateFileDirD4 ; next sector and word [OldSectBDD],byte 0 ; invalidate BDD descriptor ; ------------- pop registers CreateFileDirD8:pop di ; pop DI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ============= increase directory size ; BX = SDD descriptor, AX = directory cluster ; ------------- push registers CreateFileDirS: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push di ; push DI ; ------------- find last cluster -> DX CreateFileDirS2:mov dx,ax ; DX <- current cluster call GetNextClust ; get next cluster -> AX jc short CreateFileDirS8 ; error cmp ax,byte -1 ; end of chain? jne short CreateFileDirS2 ; get next cluster ; ------------- get next cluster mov [LastFreeClust],dx ; last free cluster call FindFreeClust ; find next free cluster -> AX jc short CreateFileDirS8 ; error ; ------------- link to previous cluster call SetNextClust ; set next cluster jc short CreateFileDirS8 ; error call FlushFAT ; flush FAT buffer jc short CreateFileDirS8 ; error ; ------------- mark end of chain xchg ax,dx ; DX <- new current cluster mov ax,FAT16_END ; AX <- end mark call SetNextClust ; mark end cluster jc short CreateFileDirS8 ; error call FlushFAT ; flush FAT buffer jc short CreateFileDirS8 ; error ; ------------- prepare absolute sector of cluster xchg ax,dx ; AX <- cluster call GetClustSect ; get absolute sector mov [OldSect],ax ; sector number LOW mov [OldSect+2],dx ; sector number HIGH ; ------------- clear sector mov di,SectBuf ; DI <- sector buffer xor ax,ax ; AX <- 0 mov cx,SECTSIZE/2 ; CX <- sector size rep stosw ; clear sector buffer ; ------------- get number of sectors -> CX mov cl,[bx+SDD_SectClust] ; CL <- sectors per cluster mov ch,0 mov bx,[bx+SDD_BDD] ; BX <- BDD descriptor mov [OldSectBDD],bx ; set old BDD descriptor ; ------------- write sectors CreateFileDirS4:call BDiskWriteSect ; write sector from buffer jc short CreateFileDirS8 ; error add word [OldSect],byte 1 ; increase sector number adc word [OldSect+2],byte 0 ; carry loop CreateFileDirS4 ; next sector and word [OldSectBDD],byte 0 ; invalidate BDD descriptor ; ------------- pop registers CreateFileDirS8:pop di ; pop DI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Create and open file ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; DI = pointer to destination FILE structures ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- CreateFile: push dx ; push DX mov dl,0 ; DL <- attributes call CreateFileDir ; create file pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Create directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT dir name (with path) ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- CreateDir: push dx ; push DX push di ; push DI push bp ; push BP mov bp,sp ; BP <- push SP sub sp,FILE_size ; local buffer mov di,sp ; DI <- local buffer mov dl,DIR_DIR ; DL <- attributes call CreateFileDir ; create file mov sp,bp ; pop SP pop bp ; pop BP pop di ; pop DI pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Delete file or directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT filename (with path) ; DL = required attributes ; DH = mask of attributes ; OUTPUT: CY = error ; ----------------------------------------------------------------------------- ; ------------- push registers DeleteFileDir: push ax ; push AX push bx ; push BX push cx ; push CX push dx ; push DX push si ; push SI push di ; push DI push bp ; push BP ; ------------- temporary FILE descriptor sub sp,FILE_size ; local variables mov di,sp ; DI <- FILE descriptor ; ------------- find and open file or dh,DIR_THISDIR+DIR_UPDIR ; including ".." and "." and dl,~(DIR_THISDIR+DIR_UPDIR) ; not ".." or "." mov bp,1 ; BP <- 1 entry to find call FindFiles ; find and open file jc short DeleteFileDir9 ; file not found ; ------------- do some checks mov al,[di+FILE_Name] ; AL <- first character of name cmp al,0 ; free entry je short DeleteFileDir8 ; free entry is not valid cmp al,FATDELCHAR ; deleted entry je short DeleteFileDir8 ; deleted entry is not valid cmp al,PATHCHAR ; root? je short DeleteFileDir8 ; root is not valid test byte [di+FILE_Attr],DIR_Device ; device? jnz short DeleteFileDir8 ; device is not valid ; ------------- get SDD descriptor -> BX call FileSDD ; get SDD descriptor -> BX jc short DeleteFileDir9 ; invalid disk ; ------------- check directory, if it is empty mov ax,[di+FILE_Cluster] ; AX <- first cluster test byte [di+FILE_Attr],DIR_DIR ; directory? jz short DeleteFileDir2 ; not directory mov si,DeletedFileAll ; SI <-"*.*" mov cx,3 ; CX <- mask length xor dx,dx ; DX <- 0, any files xor di,di ; DI <- 0, only test mov bp,3 ; BP <- 3, number of files call FindDirEntry ; find directory entry mov di,sp ; DI <- FILE descriptor jc short DeleteFileDir9 ; error cmp bp,3 ; any files except "." and ".." ? jae short DeleteFileDir8 ; error, any files ; ------------- prepare end of chain flag -> SI DeleteFileDir2: mov si,FAT12_RESMIN ; SI <- FAT12 invalid cluster test byte [bx+SDD_FlagsDisk],SDDF_FAT16 ; FAT16 ? jz short DeleteFileDir4 ; no mov si,FAT16_RESMIN ; SI <- FAT16 invalid cluster ; ------------- delete clusters DeleteFileDir4: cmp ax,si ; end of chain reached? jae short DeleteFileDir6 ; end of chain has been found cmp ax,FAT16_MIN ; check cluster jb short DeleteFileDir9 ; disk error mov dx,ax ; DX <- new current cluster call GetNextClust ; get next cluster -> AX jc short DeleteFileDir9 ; error xchg ax,cx ; CX <- next cluster mov ax,FAT16_FREE ; AX <- free mark call SetNextClust ; set next cluster (mark it free) jc short DeleteFileDir9 ; error xchg ax,cx ; AX <- next cluster jmp short DeleteFileDir4 ; next cluster DeleteFileDir6: call FlushFAT ; flush FAT sectors jc short DeleteFileDir9 ; error ; ------------- mark this entry as deleted mov byte [di+FILE_Name],FATDELCHAR ; delete mark or byte [di+FILE_Attr],DIR_Dirty ; dirty flag call FlushFile ; write directory entry jmp short DeleteFileDir9 ; ------------- pop registers DeleteFileDir8: stc ; set error flag DeleteFileDir9: lea sp,[di+FILE_size] ; pop SP pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop dx ; pop DX pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Delete file ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file name (with path) ; OUTPUT: CY = error or file is R/O. ; ----------------------------------------------------------------------------- DeleteFile: push dx ; push DX mov dx,(DIR_DIR+DIR_RO)*256 ; DX <- attributes call DeleteFileDir ; delete file pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Delete directory ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT directory name (with path) ; OUTPUT: CY = error, directory is R/O or it is not empty. ; ----------------------------------------------------------------------------- DeleteDir: push dx ; push DX mov dx,(DIR_DIR+DIR_RO)*256+DIR_DIR ; DX <- attributes call DeleteFileDir ; delete file pop dx ; pop DX ret ; ----------------------------------------------------------------------------- ; Rename file or directory (in place) ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file name (with path) ; AX = TEXT file new name (without path, can contain ? and *) ; OUTPUT: CY = error (new name cannot be with path) ; ----------------------------------------------------------------------------- ; ------------- push registers RenameFileDir: push ax ; push AX push bx ; push BX push cx ; push CX push si ; push SI push di ; push DI push bp ; push BP sub sp,FILE_size ; local buffer mov di,sp ; DI <- local FILE descriptor ; ------------- open source file mov dx,(DIR_THISDIR+DIR_UPDIR)*256 ; attributes mov bp,1 ; BP <- max. number of files call FindFiles ; open file jc short RenameFileDir9 ; cannot open file ; ------------- check if new name has path xchg ax,bx ; BX <- new name mov ax,"\/" ; separator 1 and 2 mov dl,":" ; separator 3 call TextFind3ListLast ; find last separator cmc ; CY = separator found jc short RenameFileDir9 ; error, cannot be with path ; ------------- prepare destination mask push di ; push DI lea si,[bx+TEXT_Text] ; SI <- filename text mov cx,[bx+TEXT_Length] ; CX <- filename length mov di,SFileMask ; DI <- file mask buffer call PrepFileMask ; prepare file mask mov si,di ; SI <- file mask pop di ; pop DI ; ------------- rename file push di ; push DI mov cx,11 ; CX <- filename length RenameFileDir4: lodsb ; AL <- load character inc di ; increase destination pointer cmp al,"?" ; let old character? je short RenameFileDir5 ; skip this character dec di ; decrease destination pointer stosb ; store new character RenameFileDir5: loop RenameFileDir4 ; next character pop di ; pop DI ; ------------- save new directory entry or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag call FlushFile ; save directory entry ; ------------- pop registers RenameFileDir9: lea sp,[di+FILE_size] ; pop SP pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Move file ; ----------------------------------------------------------------------------- ; INPUT: BX = TEXT file name (with path) ; AX = TEXT file new name (with path, cannot contain ? or *) ; OUTPUT: CY = error (cannot move to another disk) ; ----------------------------------------------------------------------------- ; ------------- push registers MoveFile: push ax ; push AX push bx ; push BX push cx ; push CX push si ; push SI push di ; push DI push bp ; push BP sub sp,2*FILE_size ; local buffer ; ------------- open source file mov di,sp ; DI <- source FILE descriptor mov dx,(DIR_THISDIR+DIR_UPDIR+DIR_DIR)*256 ; attributes mov bp,1 ; BP <- max. number of files call FindFiles ; open file jc short MoveFile9 ; cannot open file ; ------------- create destination file mov si,di ; SI <- source FILE descriptor add di,byte FILE_size ; DI <- destination FILE descriptor xchg ax,bx ; BX <- new name call CreateFile ; create file jc short MoveFile9 ; cannot create new file ; ------------- check disk mov al,[si+FILE_DirInx] ; AL <- source disk mov ah,[di+FILE_DirInx] ; AH <- destination disk and ax,0f0f0h ; mask disk cmp al,ah ; check disk je short MoveFile4 ; disk is OK mov byte [di+FILE_Name],FATDELCHAR ; delete character or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag call FlushFile ; save directory entry stc ; set error flag jmp short MoveFile9 ; ------------- copy file parameters MoveFile4: push di ; push DI add si,FILE_Time ; SI <- time entry add di,FILE_Time ; DI <- time entry mov cl,10/2 ; CX <- time, date, cluster, size rep movsw ; copy other entries pop di ; pop DI ; ------------- save new directory entry or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag call FlushFile ; save directory entry jc short MoveFile9 ; error ; ------------- delete source entry mov di,sp ; DI <- source entry mov byte [di+FILE_Name],FATDELCHAR ; delete character or byte [di+FILE_Attr],DIR_Dirty ; set dirty flag call FlushFile ; save directory entry ; ------------- pop registers MoveFile9: mov di,sp ; DI <- local buffers lea sp,[di+2*FILE_size] ; pop SP pop bp ; pop BP pop di ; pop DI pop si ; pop SI pop cx ; pop CX pop bx ; pop BX pop ax ; pop AX ret ; ----------------------------------------------------------------------------- ; Constant data ; ----------------------------------------------------------------------------- CONST_SECTION DeletedFileMask:db FATDELCHAR ; deleted file mask (length: 4) DeletedFileAll: db "*.*" ; ----------------------------------------------------------------------------- ; Uninitialised data ; ----------------------------------------------------------------------------- DATA_SECTION LastFATSect: resd 1 ; last FAT absolute sector ModiFAT: resb 1 ; 1=FAT sector is modified, 0=no LastFreeClust: resw 1 ; last allocated free cluster ; ------------- current path align 4, resb 1 CurPathFILE: resb FILE_size ; current directory CurPath: resb PATHMAX+2 ; CTEXT current path ; ------------- search file align 4, resb 1 FindFileFILE: resb FILE_size ; FindFile temporaty FILE structure SFileMask: resb 11 ; search filename mask