This library contains wrapper macros which encapsulate LinABI calls for most typical file operations in 64bit Linux . See also equivalent homonymous macroinstructions in linf32.htm, winf32.htm, winf64.htm.
File Access method | Open | Data transfer | Close |
---|---|---|---|
read file at once | - | FileLoad | FileClose |
write file at once | - | FileStore | - |
standard read | FileOpen | FileRead | FileClose |
standard write | FileCreate | FileWrite, FileWriteLn, FileWrite$ | FileClose |
standard write at the end of file | FileAppend | FileWrite, FileWriteLn, FileWrite$ | FileClose |
memory mapped read | FileMapOpen | - | FileClose |
memory mapped write | FileMapCreate | - | FileClose |
buffered read | FileStreamOpen | FileStreamReadByte, FileStreamRead, FileStreamReadLn | FileClose |
buffered write | FileStreamCreate | FileStreamWriteByte, FileStreamWriteWord, FileStreamWriteDword, FileStreamWrite, FileStreamWriteLn, FileStreamWrite$ | FileClose |
buffered write at end of file | FileStreamAppend | FileStreamWriteByte, FileStreamWriteWord, FileStreamWriteDword, FileStreamWrite, FileStreamWriteLn, FileStreamWrite$ | FileClose |
append to file | - | FileEnclose, FileEncloseLn, FileEnclose$ | - |
special functions | FileReset | FileAssign, FileExists?, FileNameParse, FileMove, FileMkDir, FileEach | FileDelete |
Macros return the results in registers, usually RAX. Other registers do not change. CF is set when error occurs, ZF is set at the end of file, DF must be always reset. Other flags are undefined.
Association of the FILE64 object with the name of file is provided separately, using the macro FileAssign. File must be assigned before opening.
Macro names suffixed with ~Ln write LineFeed character after the data. Macro names suffixed with ~$ expect NULL-terminated input data.
Acces methods at once, stream, memory-map allocate dynamic virtual memory. It will be freed in FileClose.
Access method | Virtual memory | |
---|---|---|
reserved | comitted | |
At once load | filesize | filesize |
At once store | none | none |
Standard | none | none |
MemoryMapped | filesize | pagesize |
Streamed | buffersize | buffersize |
File access method at once loads entire file content into comitted memory, so it is suitable for small files only. The loaded/stored file doesn't need explicit opening nor closing.
This access method is equivalent to PHP functions file_get_contents, file_put_contents.
Access method enclose opens the file at its end for writing, appends the data and closes file in one operation.
Standard read/write methods only encapsulate ABI functions read/write and they can process files of any size. Example:
Memory mapped file access reserves memory for the entire file contents virtually loaded to memory.
Streamed (buffered) file access method allocates its own memory buffer to accelerate reading/writing of small pieces of data. This method is also suitable for processing text files line by line. Example:
Common limitations: This library cannot be used if special requirements are required, such as overlapped file access, other than normal file attributes, nonstandard share disposition, files bigger than 2 GB, filename longer than 260 characters.
Runtime procedures defined in this library use macros LinABI, Dispatch, status32.htm.
linf64 HEAD INCLUDEHEAD1 lins.htm, linsfile.htm, linabi.htm, cpuext.htm, status32.htm
FILE64 STRUC .Ptr DQ Q ; Pointer to the current data content in memory-mapped|buffered data. .Top DQ Q ; Pointer to the end of the memory-mapped|buffered data. .BufPtr DQ Q ; Address of the start of memory-map|allocated buffer. .BufSize DQ Q ; Size of memory map|buffer. .Pos DQ Q ; Zero based offset of the current file position: lseek(.Handle,.Pos,SEEK_SET). .Size DQ Q ; File size in bytes. .Handle DQ Q ; File descriptor. .MapHandle DQ Q ; Pointer to the start of mapped memory, identical with .BufPtr. .Status DD D ; File status flags, see FileStatusEnc. .NameOffs DD D ; Offset of the filename without path inside the .Name. Zero if no path in .Name. .ExtOffs DD D ; Offset of the file extension inside the .Name. .Name D NAME_MAX * B ; Zero terminated file name. ENDSTRUC FILE64
FILE64.Status
.fi64StFound EQU 0x00000010 ; File was once succesfully opened. fi64StAppend EQU 0x00100000 ; Created file should move its pointer to the end. fi64StStdIO EQU 0x00200000 ; FILE64.Handle is standard input|output. fi64StAllocated EQU 0x00400000 ; FILE64.BufPtr is pointer to the allocated memory. fi64StUnicode EQU 0x00800000 ; FILE64.Name is in WIDE characters (not used in Linux). fi64StMapCreated EQU 0x01000000 ; FILE64.MapHandle is valid for writing. fi64StMapOpened EQU 0x02000000 ; FILE64.MapHandle is valid for reading. fi64StMapped EQU 0x04000000 ; FILE64.Ptr is pointer to the mapped memory. fi64StCreated EQU 0x10000000 ; FILE64.Handle is valid for writing. fi64StOpened EQU 0x20000000 ; FILE64.Handle is valid for reading.
FileNameParse %MACRO FileNamePtr,Size=-1, Unicode=%^UNICODE PUSHQ %Size,%FileNamePtr CALL FileNameParse@RT:: FileNameParse@RT:: PROC1 PUSH RBX,RSI SUB EBX,EBX ; Prepare returned value of RAX. SUB ECX,ECX ; Prepare returned value of RCX. MOV RSI,[RSP+3*8] ; %FileNamePtr. MOV RDX,[RSP+4*8] ; %Size. TEST RDX JS .NextChar: ADD RDX,RSI ; End of input string or -1. .NextChar: CMP RSI,RDX JNB .End: LODSB Dispatch AL,47,46,0,58,92 ; slash, dot, null, colon, backslash. ; Ordinary character. LEA RAX,[RSI-1] TEST RBX JNZ .NextChar: MOV RBX,RAX JMP .NextChar: .46: ; dot . LEA RCX,[RSI-1] JMP .NextChar: .47: ; colon, slash, backslash .58: .92: MOV RBX,RSI JMP .NextChar: .0: DEC RSI ; Byte NULL. .End:MOV RDX,RSI TEST RCX JNZ .E1: MOV RCX,RSI .E1: TEST RBX JNZ .E2: MOV RBX,RSI .E2: MOV RAX,RBX POP RSI,RBX RET 2*8 ENDPROC1 FileNameParse@RT:: %ENDMACRO FileNameParse
theFile.Name
member.
.Name, .NameOffs, .ExtOffs,
of theFile
are set.SIZE# FILE64.Name
, i.e. NAME_MAX=260 bytes.FileAssign %MACRO theFile,Name$1,Name$2,,, Size=-1, Unicode=%^UNICODE %IF %#<2 ; > %ERROR ID=5941, 'File name missing in macro "FileAssign".' %EXITMACRO FileAssign %ENDIF PUSHQ 0 ; End-of-arguments marker. ArgNr %FOR %#..2,STEP=-1 PUSHQ %*{%ArgNr} ; Pointers to strings, start with the last one. %ENDFOR ArgNr PUSHQ %Size, %theFile, RSP ADDQ [RSP],(2+%#)*8 CALL FileAssign@RT:: POP RSP ; Restore stack to equilibrum. FileAssign@RT:: PROC1 PUSH RAX,RBX,RCX,RDX,RBP,RSI,RDI MOV RBX,[RSP+9*8] ; %theFile LEA RBP,[RSP+11*8] ; Pointer to the 1st namepointer. LEA RDI,[RBX+FILE64.Name] LEA RDX,[RDI+SIZE#FILE64.Name] ; End of reserved room. .10:XCHG RBP,RSI LODSQ ; Load pointer to namestring. MOV RBP,RAX XCHG RSI,RBP TEST RAX ; Check the end of pointers. JZ .50: MOV RCX,[RSP+10*8] ; Parameter %Size=. .20:LODSB CMP AL,0 JE .10: CMP RDI,RDX JNB .40: STOSB DEC RCX JNZ .20: JMP .10: .40:SUB EAX,EAX STC ; Overflow. .50:MOV RCX,RDI STOSB LEA RDX,[RBX+FILE64.Name] PUSHF SUB RCX,RDX FileNameParse RDX,Size=RCX LEA RDX,[RBX+FILE64.Name] SUB RAX,RDX SUB RCX,RDX MOV [RBX+FILE64.NameOffs],EAX MOV [RBX+FILE64.ExtOffs],ECX POPF POP RDI,RSI,RBP,RDX,RCX,RBX,RAX RET ENDPROC1 FileAssign@RT:: %ENDMACRO FileAssign
* ?
are allowed in the file name.
FileExists? %MACRO theFile PUSHQ %theFile CALL FileExists?@RT:: FileExists?@RT:: PROC1 PUSH RAX,RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+8*8] ; theFile. LEA RDI,[RBX+FILE64.Name] MOV ESI,O_RDONLY+O_DIRECTORY XOR EDX,EDX LinABI open,RDI,RSI,RDX, Fastmode=Yes ; Try to open a directory. MOV RDI,RAX ; Returned fd or error. SAL RAX ; Copy SF to CF. JNC .80: ; Return with CF=0,ZF=0 if directory exists. MOV ESI,O_RDONLY LEA RDI,[RBX+FILE64.Name] LinABI open,RDI,RSI,RDX, Fastmode=Yes ; Try to open a file. MOV RDI,RAX ; Returned fd or error. SAL RAX ; Copy SF to CF. JC .90: SetSt [RBX+FILE64.Status],fi64StFound XOR EDX,EDX ; Set CF=0, ZF=1 if file exists. .80: PUSHFQ LinABI close,RDI,Fastmode=Yes POPFQ .90:POP R11,RDI,RSI,RDX,RCX,RBX,RAX RET 1*8 ENDPROC1 FileExists?@RT:: %ENDMACRO FileExists?
FileMkDir %MACRO theFile PUSHQ %theFile CALL FileMkDir@RT:: FileMkDir@RT:: PROC1 PUSH RBX,RCX,RBP,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. LEA RBX,[RBX+FILE64.Name] SUB ECX,ECX MOV RDI,RBX DEC RCX XOR EAX,EAX REPNE SCASB MOV RBP,RDI ; ASCIIZ FileName is now RBX..RBP. MOV RDI,RBX .20: MOV RCX,RBP MOV AL,'/' CMP [RDI],AL JNE .30: INC RDI .30: SUB RCX,RDI REPNE SCASB ; Search for the next slash. MOV AL,0 CLC JNE .90: ; Done when no more slashes left in FileName. MOV [RDI-1],AL ; Temporarily replace slash with NULL. MOV ESI,00777q XCHG RBX,RDI LinABI mkdir,RDI,RSI,Fastmode=Yes XCHG RDI,RBX MOVB [RDI-1],'/' ; Restore original slash. TEST EAX JZ .20: CMP EAX,-EEXIST ; EEXIST=17 is the only tolerated error. JE .20: .80: STC .90:POP R11,RDI,RSI,RBP,RCX,RBX RET 1*8 ENDPROC1 FileMkDir@RT:: %ENDMACRO FileMkDir
FileGetSize %MACRO FileHandle PUSHQ %FileHandle CALL FileGetSize@RT:: FileGetSize@RT:: PROC1 PUSH RCX,RBP,RSI,RDI,R11 MOV RBP,RSP MOV RDI,[RBP+6*8] ; FileHandle. MOV ECX,SIZE# STAT ADD ECX,7 AND ECX,~7 SUB RSP,RCX MOV RSI,RSP ; STAT LinABI fstat,RDI,RSI,Fastmode=Yes MOV RCX,[RSI+STAT.st_size] MOV RSP,RBP CMP RAX,-ERR_MAX CMC JC .90: MOV RAX,RCX .90:POP R11,RDI,RSI,RBP,RCX RET 1*8 ENDPROC1 FileGetSize@RT:: %ENDMACRO FileGetSize
FileOpen %MACRO theFile PUSHQ %theFile CALL FileOpen@RT:: FileOpen@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. LEA RDI,[RBX+FILE64.Name] XOR EDX,EDX MOV [RBX+FILE64.Size],RDX MOV [RBX+FILE64.Pos],RDX MOV [RBX+FILE64.Ptr],RDX MOV [RBX+FILE64.Top],RDX LinABI open,RDI,O_RDONLY,RDX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: SetSt [RBX+FILE64.Status],fi64StOpened+fi64StFound MOV [RBX+FILE64.Handle],RAX FileGetSize RAX JC .90: MOV [RBX+FILE64.Size],RAX MOV [RBX+FILE64.Top],RAX .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileOpen@RT:: %ENDMACRO FileOpen
FileCreate %MACRO theFile PUSHQ %theFile CALL FileCreate@RT:: FileCreate@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; ^theFile. XOR EAX,EAX MOV [RBX+FILE64.Ptr],RAX MOV [RBX+FILE64.Top],RAX LEA RDI,[RBX+FILE64.Name] MOV ESI,O_CREAT|O_WRONLY|O_TRUNC JNSt [RBX+FILE64.Status],fi64StAppend,.10: MOV ESI,O_CREAT|O_RDWR|O_APPEND ; The macro is used by FileAppend. .10: LinABI open,RDI,RSI,777q,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: SetSt [RBX+FILE64.Status],fi64StCreated MOV [RBX+FILE64.Handle],RAX FileGetSize RAX JC .90: MOV [RBX+FILE64.Size],RAX MOV [RBX+FILE64.Pos],RAX .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileCreate@RT:: %ENDMACRO FileCreate
FileAppend %MACRO theFile PUSHQ %theFile CALL FileAppend@RT:: FileAppend@RT:: PROC1 PUSH RBX MOV RBX,[RSP+2*8] ; ^theFile SetSt [RBX+FILE64.Status],fi64StAppend FileCreate RBX POP RBX RET 1*8 ENDPROC1 FileAppend@RT:: %ENDMACRO FileAppend
FileFlush %MACRO theFile PUSHQ %theFile CALL FileFlush@RT:: FileFlush@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. MOV RAX,-EBADFD ; Error: file is not open for write. JNSt [RBX+FILE64.Status],fi64StAllocated,.80: JNSt [RBX+FILE64.Status],fi64StCreated, .80: MOV RSI,[RBX+FILE64.BufPtr] ; Start of buffer. MOV RDX,[RBX+FILE64.Ptr] ; Next free position in buffer. MOV [RBX+FILE64.Ptr],RSI SUB RDX,RSI MOV RAX,RDX JZ .90: ; If nothing to flush. MOV RDI,[RBX+FILE64.Handle] LinABI write,RDI,RSI,RDX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JNC .90: .80: STC .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileFlush@RT:: %ENDMACRO FileFlush
FileClose %MACRO theFile ArgNr %FOR 1..%# PUSHQ %*{%ArgNr} CALL FileClose@RT:: %ENDFOR ArgNr FileClose@RT:: PROC1 PUSH RAX,RBX,RCX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile JSt [RBX+FILE64.Status],fi64StMapped,.10: FileFlush RBX JMPS .20: .10: LinABI munmap,[RBX+FILE64.MapHandle],[RBX+FILE64.Size],Fastmode=Yes .20: JNSt [RBX+FILE64.Status],fi64StAllocated,.30: LinABI munmap,[RBX+FILE64.BufPtr],[RBX+FILE64.BufSize],Fastmode=Yes .30: JNSt [RBX+FILE64.Status],fi64StOpened|fi64StCreated, .40: LinABI close,[RBX+FILE64.Handle],Fastmode=Yes .40: LEA RCX,[RBX+FILE64.NameOffs] SUB RCX,RBX SHR ECX,2 XOR EAX,EAX MOV RDI,RBX REP STOSD ; Clear the FILE object except for Name and Offs. POP R11,RDI,RSI,RCX,RBX,RAX RET 1*8 ENDPROC1 FileClose@RT:: %ENDMACRO FileClose
FileMove %MACRO SourceFile, DestinationFile PUSHQ %DestinationFile, %SourceFile CALL FileMove@RT:: FileMove@RT:: PROC1 PUSH RCX,RSI,RDI,R11 MOV RSI,[RSP+5*8] ; SourceFile. MOV RDI,[RSP+6*8] ; DestinationFile. FileClose RSI,RDI FileMkDir RDI JC .90: LEA RSI,[RSI+FILE64.Name] LEA RDI,[RDI+FILE64.Name] XCHG RSI,RDI LinABI rename,RDI,RSI,Fastmode=Yes MOV ECX,EAX SAL ECX,1 ; Copy SF to CF. .90: POP R11,RDI,RSI,RCX RET 2*8 ENDPROC1 FileMove@RT:: %ENDMACRO FileMove
FileDelete %MACRO theFile ArgNr %FOR 1..%# PUSHQ %*{%ArgNr} CALL FileDelete@RT:: %ENDFOR ArgNr FileDelete@RT:: PROC1 PUSH RCX,RDI,R11 MOV RDI,[RSP+4*8] ; theFile. FileClose RDI LEA RDI,[RDI+FILE64.Name] LinABI unlink,RDI,Fastmode=Yes MOV ECX,EAX SAL ECX,1 ; Copy SF to CF. POP R11,RDI,RCX RET 1*8 ENDPROC1 FileDelete@RT:: %ENDMACRO FileDelete
FILE64.Name
is replaced
with the resolved file name found in directory. The file is not open.
FileEach %MACRO theFile, CallbackProc PUSHQ %CallbackProc, %theFile CALL FileEach@RT:: FileEach@RT:: PROC1 %FileEachMaskSize %SETA (SIZE# FILE64.Name + 7) & ~7 ; Local buffer for the filename mask. %FileEachBufSize %SETA (SIZE# DIRENT64 + 7) & ~7 ; Local buffer for DIRENT64 records. PUSH RAX,RBX,RCX,RDX,RSI,RDI,R9,R11 MOV RBX,[RSP+9*8] ; RBX=^theFile. MOV ECX,%FileEachMaskSize SUB EAX,EAX MOV [RSP+7*8],RAX ; Initialize returned RAX with NULL. SUB RSP,RCX ADD EAX,[RBX+FILE64.NameOffs] MOV RDI,RSP LEA RSI,[RAX+RBX+FILE64.Name] REP MOVSB ; Copy the ASCIIZ mask without path to LocalVar %FileEachMask$. JNZ .10: ; Test if theFile.NameOffs=0. ; The mask was specified without path, e.g.doc*.txt. ; Default path ./ will be injected to FILE64.Name, i.e../doc*.txt. MOV ECX,SIZE#FILE64.Name-2 LEA RSI,[RCX+RBX+FILE64.Name-1] LEA RDI,[RSI+2] STD REP MOVSB CLD DEC RDI MOVW [RDI],'./' MOVB [RDI+SIZE#FILE64.Name-1],0 FileNameParse RDI SUB RAX,RDI SUB RCX,RDI MOV [RBX+FILE64.NameOffs],EAX MOV [RBX+FILE64.ExtOffs],ECX .10: LEA RDI,[RBX+FILE64.Name] ; RAX=FILE64.NameOffs. MOVB [RDI+RAX-1],0 ; Temporary replace / with NULL. MOV R9,RDI LinABI open,RDI,O_RDONLY+O_DIRECTORY+O_NOATIME,0,Fastmode=Yes MOV EDX,[RBX+FILE64.NameOffs] MOVB [R9+RDX-1],'/' ; Replace temporary NULL back with slash in the mask name. MOV EDX,%FileEachBufSize SUB RSP,RDX ; RDX=Size of temporary buffer. MOV RDI,RAX ; Directory descriptor (or error code from sys_open). .20: MOV RSI,RSP ; Pointer to temporary buffer for DIRENT64 records. LinABI getdents64,RDI,RSI,RDX,Fastmode=Yes TEST EAX JZ .End: ; When there are no more dir entries. CMP RAX,-ERR_MAX JNC .End: ; When error occured. LEA RCX,[RSI+RAX] ; End of DIRENT64 records in buffer. .30: CMP RSI,RCX JNB .20: ; Go to read the next portion of dir entries. CALL .DirEnt: ; Process the DIRENT64 record at RSI. Keep all GPR. JC .End: MOVZX EAX,[RSI+DIRENT64.d_reclen] ADD RSI,RAX JMP .30: .DirEnt:PROC ; If the resolved name [RSI+DIRENT64.d_name] complies with %FileEachMask$, ; update FILE64.Name at [%Param1] with the resolved name and then call [%Param2]. ; Input: RSI=^DIRENT64 ; RBX=^FILE64 ; Output:CF and RAX as returned from callback. Other registers are unchanged. PUSH RBX,RCX,RDX,RBP,RSI,RDI CMP [RSI+DIRENT64.d_type],DT_REG JNE .NoMatch: ; Skip sockets, pipes, devices and other nonregular files. LEA RDI,[RSI+DIRENT64.d_name] ; ^name from the directory. MOV RDX,RDI LEA RSI,[RSP+7*8+%FileEachBufSize] ; ^file mask without path. .D1: LODSB ; Byte from file mask. MOV AH,[RDI] ; Byte from file name. INC RDI Dispatch AL,0x00,0x2A,0x3F; Test on end-of-mask and wildcard. CMP AL,AH ; Ordinary character match? JE .D1: JMP .NoMatch: .0x00: CMP AH,0 JE .Match: JMP .NoMatch: .0x3F: CMP AH,0 ; Question mark '?' in mask complies with one not-nul UTF8 character. JE .NoMatch: NOT EAX ; Invert bits in UTF8 AH. SUB ECX,ECX BSR CX,AX ; Scan AH, start with MSbit. SUB RCX,15 JZ .D1: INC RCX SUB RDI,RCX ; Advance RDI by the size of UTF8 character - 1. JMP .D1: .0x2A: ; Asterix '*' complies with zero or more UTF8 characters. LODSB ; The mask character/byte which follows '*'. Dispatch AL,0x2A,0x3F,0n00 .D3: CMP AH,0 JE .NoMatch: CMP AL,AH JE .D1: MOV AH,[RDI] INC RDI JMP .D3: .NoMatch: CLC JMP .D9: .Match: ; File name at RDX complies with the file mask. Perform callback. .0n00: MOV RSI,RDX MOV ECX,SIZE# FILE64.Name MOV EAX,[RBX+FILE64.NameOffs] SUB ECX,EAX JNA .NoMatch: LEA RDI,[RBX+FILE64.Name+RAX] MOV RDX,RDI REP MOVSB MOV RSI,RDX ; Pointer to resolved file name without path. MOV RAX,[RSP+17*8+%FileEachBufSize+%FileEachMaskSize] ; ^CallbackProc. CALL RAX ; Perform the callback. MOV [RSP+14*8+%FileEachMaskSize+%FileEachBufSize],RAX ; Pass the returned RAX. .D9: POP RDI,RSI,RBP,RDX,RCX,RBX RET ENDP .DirEnt: .End: LinABI close,RDI, Fastmode=On ADD RSP,%FileEachMaskSize+%FileEachBufSize POP R11,R9,RDI,RSI,RDX,RCX,RBX,RAX RET 2*8 ENDPROC1 FileEach@RT: %ENDMACRO FileEach
FileLoad %MACRO theFile PUSHQ %theFile CALL FileLoad@RT:: FileLoad@RT:: PROC1 PUSH RBX,RCX,RDX,RDI,R8,R9,R10,R11 MOV RBX,[RSP+9*8] ; ^theFile. FileOpen RBX ; Returns CF=0,RAX=file size. JC .80: MOV [RBX+FILE64.BufSize],RAX TEST RAX ; Zero-sized files are not supported. JZ .80: MOV RSI,RAX LinABI mmap,0,RSI,PROT_READ+PROT_WRITE,MAP_PRIVATE+MAP_ANONYMOUS,-1,0,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .80: MOV [RBX+FILE64.BufPtr],RAX MOV [RBX+FILE64.Ptr],RAX ADD [RBX+FILE64.Top],RAX SetSt [RBX+FILE64.Status],fi64StAllocated MOV RSI,RAX LinABI read,[RBX+FILE64.Handle],RSI,[RBX+FILE64.BufSize],Fastmode=Yes CMP RAX,-ERR_MAX CMC .80: PUSHFQ ; Save CF. PUSH RAX LinABI close,[RBX+FILE64.Handle] RstSt [RBX+FILE64.Status],fi64StCreated+fi64StOpened POP RAX POPFQ ; Restore CF. POP R11,R10,R9,R8,RDI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileLoad@RT:: %ENDMACRO FileLoad
FileStore %MACRO theFile,DataPtr,DataSize PUSHQ %DataSize,%DataPtr,%theFile CALL FileStore@RT:: FileStore@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; ^theFile. FileCreate RBX JC .90: MOV RSI,[RSP+8*8] ; DataPtr. MOV RDX,[RSP+9*8] ; DataSize. LinABI write,[RBX+FILE64.Handle],RSI,RDX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: MOV [RBX+FILE64.Size],RAX ADD [RBX+FILE64.Pos],RAX FileClose RBX .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 3*8 ENDPROC1 FileStore@RT:: %ENDMACRO FileStore
FileReset %MACRO theFile, Position %IF "%Position" === "" PUSHQ 0 %ELSE PUSHQ %Position %ENDIF PUSHQ %theFile CALL FileReset@RT:: FileReset@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. MOV RSI,[RSP+8*8] ; Position. MOV RAX,-EBADF ; Error code "file is not opened". JNSt [RBX+FILE64.Status],fi64StOpened,.90: LinABI lseek,[RBX+FILE64.Handle],RSI,SEEK_SET,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: MOV [RBX+FILE64.Pos],RAX MOV RSI,[RBX+FILE64.BufPtr] ADD RSI,RAX MOV [RBX+FILE64.Ptr],RSI JSt [RBX+FILE64.Status],fi64StMapOpened,.90: MOV [RBX+FILE64.Top],RSI .90: POP R11,RDI,RSI,RDX,RCX,RBX RET 2*8 ENDPROC1 FileReset@RT:: %ENDMACRO FileReset
FileRead %MACRO theFile,DataPtr,DataSize PUSHQ %DataSize,%DataPtr,%theFile CALL FileRead@RT:: FileRead@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. MOV RAX, -EBADFD ; Error code: file is not open. TEST [RBX+FILE64.Status],fi64StOpened STC JZ .90: MOV RSI,[RSP+8*8] ; DataPtr. MOV RDX,[RSP+9*8] ; DataSize. LinABI read,[EBX+FILE64.Handle],RSI,RDX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: ADD [RBX+FILE64.Pos],RAX TEST RAX ; Check ZF=EOF. .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 3*8 ENDPROC1 FileRead@RT:: %ENDMACRO FileRead
FileWriteRT %MACRO ; Common runtime procedure declaration for macros FileWrite* %IF %# & 1 = 0 %ERROR ID=5946, 'Macro FileWrite expects odd number of arguments.' %EXITMACRO FileWriteRT %ENDIF PUSHQ 0 ; Mark the end of arguments. ArgNr %FOR %#..2,STEP=-2 PUSHQ %*{%ArgNr}, %*{%ArgNr-1} %ENDFOR ArgNr PUSHQ %theFile,RSP ADDQ [RSP],(%#+1)*8 CALL FileWrite@RT:: POP RSP FileWrite@RT::PROC1 PUSH RBX,RCX,RDX,RBP,RSI,RDI MOV RBX,[RSP+8*8] ; theFile. MOV RAX,-EBADFD ; File was not opened. TESTD [RBX+FILE64.Status],fi64StCreated STC JZ .90: PUSHQ 0 ; Returned RAX will accumulate written size. LEA RBP,[RSP+10*8] ; Pointer to the first DataPtr. .20: XCHG RBP,RSI LODSQ ; DataPtr. MOV RBP,RAX TEST RAX ; Test if it's the end of pointers. JZ .80: LODSQ ; DataSize. XCHG RSI,RBP MOV RDX,RAX INC RAX JNZ .30: MOV RCX,RDX ; DataSize=-1, let's find the actual size. MOV RDI,RSI REPNE SCASB SUB RDI,RSI LEA RDX,[RDI-1] .30: LinABI write,[RBX+FILE64.Handle],RSI,RDX,Fastmode=Yes CMP RAX,-ERR_MAX JC .40: STC LEA RSP,[RSP+1*8] ; Keep CF and RAX=error code. JMP .90: .40: ADD [RBX+FILE64.Size],RAX ADD [RBX+FILE64.Pos],RAX ADD [RSP+0*8],RAX ; Accumulate total written size. JMP .20: .80: POP RAX .90:POP RDI,RSI,RBP,RDX,RCX,RBX RET ENDP1 FileWrite@RT %ENDMACRO FileWriteRT
FileWrite %MACRO theFile,DataPtr,DataSize,,,, FileWriteRT %* %ENDMACRO FileWrite
FileWriteLn %MACRO theFile,DataPtr,DataSize,,, FileWrite %*, =B 0x0A, 1 %ENDMACRO FileWriteLn
FileWrite$ %MACRO theFile,DataPtr1,DataPtr2,DataPtr3,,, %ArgList %SET %theFile ArgNr %FOR 2..%# %ArgList %SET %ArgList,%*{%ArgNr},-1 %ENDFOR ArgNr FileWriteRT %ArgList %ENDMACRO FileWrite$
FileEnclose %MACRO theFile,DataPtr,DataSize,,,, FileAppend %theFile JC FileEnclose%.Abort: FileWrite %* JC FileEnclose%.Abort: FileClose %theFile FileEnclose%.Abort: %ENDMACRO FileEnclose
FileEncloseLn %MACRO theFile,DataPtr,DataSize,DataPtr2,DataSize2,,, FileAppend %theFile JC FileEncloseLn%.Abort: FileWriteLn %* JC FileEncloseLn%.Abort: FileClose %theFile FileEncloseLn%.Abort: %ENDMACRO FileEncloseLn
FileEnclose$ %MACRO theFile,DataPtr1,DataPtr2,DataPtr3,,, FileAppend %theFile JC FileEnclose$%.Abort: FileWrite$ %* JC FileEnclose$%.Abort: FileClose %theFile FileEnclose$%.Abort: %ENDMACRO FileEnclose$
FileMapOpen %MACRO theFile PUSHQ %theFile CALL FileMapOpen@RT:: FileMapOpen@RT:: PROC1 PUSH RBX,RCX,RDX,RDI,R8,R9,R10,R11 MOV RBX,[RSP+9*8] ; theFile. XOR ESI,ESI FileOpen RBX JC .End: TEST EAX JZ .End: ; If the file has size=0. LinABI mmap,0,RAX,PROT_READ+PROT_EXEC,MAP_SHARED,[RBX+FILE64.Handle],0,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .End: MOV RSI,RAX MOV [RBX+FILE64.Ptr],RAX MOV [RBX+FILE64.Top],RAX MOV [RBX+FILE64.BufPtr],RAX MOV [RBX+FILE64.MapHandle],RAX MOV RAX,[RBX+FILE64.Size] MOV [RBX+FILE64.BufSize],RAX ADD [RBX+FILE64.Top],RAX SetSt [RBX+FILE64.Status],fi64StMapped+fi64StMapOpened .End:POP R11,R10,R9,R8,RDI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileMapOpen@RT:: %ENDMACRO FileMapOpen
FileMapCreate %MACRO theFile,FileSize %IF %# > 1 PUSHQ %FileSize %ELSE PUSHQ 0 %ENDIF PUSHQ %theFile CALL FileMapCreate@RT:: FileMapCreate@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,R8,R9,R10,R11 MOV RBX,[RSP+9*8] ; theFile. FileAppend RBX JC .90: MOV RSI,RAX ; Current file size. MOV RCX,[RSP+10*8] ; Requested FileSize. JRCXZ .10: MOV RSI,RCX .10: LinABI ftruncate,[RBX+FILE64.Handle],RSI,Fastmode=Yes TEST EAX STC JNZ .90: LinABI mmap,RAX,RSI,PROT_READ+PROT_WRITE+PROT_EXEC,MAP_SHARED,[RBX+FILE64.Handle],RAX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: SetSt [RBX+FILE64.Status],fi64StMapCreated+fi64StMapped+fi64StAllocated+fi64StCreated MOV RDI,RAX MOV [RBX+FILE64.Size],RSI MOV [RBX+FILE64.MapHandle],RAX MOV [RBX+FILE64.BufPtr],RAX ADD RAX,RSI MOV [RBX+FILE64.Ptr],RAX MOV [RBX+FILE64.Top],RAX XCHG RAX,RSI .90: POP R11,R10,R9,R8,RSI,RDX,RCX,RBX RET 2*8 ENDPROC1 FileMapCreate@RT:: %ENDMACRO FileMapCreate
FileStreamOpen %MACRO theFile,BufSize=16K PUSHQ %BufSize,%theFile CALL FileStreamOpen@RT:: FileStreamOpen@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R8,R9,R10,R11 MOV RBX,[RSP+10*8] ; theFile. FileOpen RBX JC .90: MOV RSI,[RSP+11*8] ; BufSize. MOV [RBX+FILE64.BufSize],RSI XOR EDI,EDI LinABI mmap,RDI,RSI,PROT_READ+PROT_WRITE,MAP_PRIVATE+MAP_ANONYMOUS,-1,RDI,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: SetSt [RBX+FILE64.Status],fi64StAllocated MOV [RBX+FILE64.MapHandle],RAX MOV [RBX+FILE64.BufPtr],RAX MOV [RBX+FILE64.Ptr],RAX MOV [RBX+FILE64.Top],RAX MOV RAX,[RBX+FILE64.Size] .90: POP R11,R10,R9,R8,RDI,RSI,RDX,RCX,RBX RET 2*8 ENDPROC1 FileStreamOpen@RT:: %ENDMACRO FileStreamOpen
FileStreamRead %MACRO theFile,DataPtr,DataSize PUSHQ %DataSize,%DataPtr,%theFile CALL FileStreamRead@RT:: FileStreamRead@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 SUB EDX,EDX MOV RBX,[RSP+7*8] ; theFile. MOV RAX,-EBADFD ; Error: file is not open. TESTD [RBX+FILE64.Status],fi64StOpened STC JZ .90: TESTD [RBX+FILE64.Status],fi64StAllocated STC JZ .90: MOV RDI,[RSP+8*8] ; DataPtr. .10: MOV RSI,[RBX+FILE64.Ptr] .20: MOV RCX,[RBX+FILE64.Top] SUB RCX,RSI ; How many bytes is left in buffer. JNA .50: CMP RCX,[RSP+9*8] ; Is it enough for the requested DataSize? JA .60: SUB [RSP+9*8],RCX ADD RDX,RCX REP MOVSB MOV [RBX+FILE64.Ptr],RSI JMP .20: .50: MOV RSI,[RBX+FILE64.BufPtr] MOV [RBX+FILE64.Ptr],RSI MOV [RBX+FILE64.Top],RSI LinABI read,[RBX+FILE64.Handle],RSI,[RBX+FILE64.BufSize],Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: ADD [RBX+FILE64.Top],RAX JMP .20: .60: MOV RCX,[RSP+9*8] ; DataSize. ADD RDX,RCX REP MOVSB MOV [RBX+FILE64.Ptr],RSI .70: MOV RAX,RDX ADD [RBX+FILE64.Pos],RDX TEST RAX .90: POP R11,RDI,RSI,RDX,RCX,RBX RET 3*8 ENDPROC1 FileStreamRead@RT:: %ENDMACRO FileStreamRead
FileStreamReadLn %MACRO theFile PUSHQ %theFile CALL FileStreamReadLn@RT:: FileStreamReadLn@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R11 MOV RBX,[RSP+7*8] ; theFile. MOV RAX,-EBADFD ; Error: file is not open. TESTD [RBX+FILE64.Status],fi64StOpened STC JZ .90: TESTD [RBX+FILE64.Status],fi64StAllocated STC JZ .90: .10: MOV RDI,[RBX+FILE64.Ptr] MOV RCX,[RBX+FILE64.Top] MOV [RSP+2*8],RDI ; Returned ESI. MOV RDX,RDI SUB RCX,RDI JNA .30: MOV AL,10 ; Search for LineFeed. REPNE SCASB JE .50: CMP RDX,[RBX+FILE64.BufPtr] JE .50: ; When EOL is out of buffer and start of line is not ; at buffer's beginning, the buffer will be reloaded with the current line. MOV RCX,RDX SUB RCX,RDI LinABI lseek,[RBX+FILE64.Handle],RCX,SEEK_CUR,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: .30: MOV RSI,[RBX+FILE64.BufPtr] MOV [RBX+FILE64.Ptr],RSI MOV [RBX+FILE64.Top],RSI LinABI read,[RBX+FILE64.Handle],RSI,[RBX+FILE64.BufSize],Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: TEST RAX JZ .90: ; End of file. ADD RAX,RSI MOV [RBX+FILE64.Top],RAX JMP .10: .50: MOV [RBX+FILE64.Ptr],RDI MOV RAX,RDI SUB RAX,RDX ADD [RBX+FILE64.Pos],RAX .90:POP R11,RDI,RSI,RDX,RCX,RBX RET 1*8 ENDPROC1 FileStreamReadLn@RT:: %ENDMACRO FileStreamReadLn
FileStreamReadByte %MACRO theFile PUSHQ %theFile CALL FileStreamReadByte@RT:: FileStreamReadByte@RT:: PROC1 PUSH RBX,RCX,RDX,RBP,RSI,RDI,R11 MOV RBX,[RSP+8*8] ; theFile. .10: MOV RSI,[RBX+FILE64.Ptr] CMP RSI,[RBX+FILE64.Top] JB .50: MOV RSI,[RBX+FILE64.BufPtr] MOV [RBX+FILE64.Ptr],RSI MOV [RBX+FILE64.Top],RSI MOV RBP,RAX LinABI read,[RBX+FILE64.Handle],RSI,[RBX+FILE64.BufSize],Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: ADD [RBX+FILE64.Top],RAX TEST RAX MOV RAX,RBP JZ .90: JMP .10: ; This time it will not fail. .50: LODSB MOV [RBX+FILE64.Ptr],RSI INCQ [RBX+FILE64.Pos] CLC .90: POP R11,RDI,RSI,RBP,RDX,RCX,RBX RET 1*8 ENDPROC1 FileStreamReadByte@RT:: %ENDMACRO FileStreamReadByte
FileStreamCreate %MACRO theFile,BufSize=16K PUSHQ %BufSize,%theFile CALL FileStreamCreate@RT:: FileStreamCreate@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R8,R9,R10,R11 MOV RBX,[RSP+10*8] ; theFile. RstSt [RBX+FILE64.Status],fi64StAppend FileCreate RBX JC .90: MOV RSI,[RSP+11*8] ; BufSize. XOR EAX,EAX LinABI mmap,RAX,RSI,PROT_READ+PROT_WRITE+PROT_EXEC,MAP_PRIVATE+MAP_ANONYMOUS,-1,RAX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: MOV [RBX+FILE64.BufSize],RSI MOV [RBX+FILE64.BufPtr],RAX MOV [RBX+FILE64.Ptr],RAX ADD RAX,RSI MOV [RBX+FILE64.Top],RAX SetSt [RBX+FILE64.Status],fi64StAllocated MOV RAX,[RBX+FILE64.Size] .90:POP R11,R10,R9,R8,RDI,RSI,RDX,RCX,RBX RET 2*8 ENDPROC1 FileStreamCreate@RT:: %ENDMACRO FileStreamCreate
FileStreamAppend %MACRO theFile,BufSize=16K PUSHQ %BufSize,%theFile CALL FileStreamAppend@RT:: FileStreamAppend@RT:: PROC1 PUSH RBX,RCX,RDX,RSI,RDI,R8,R9,R10,R11 MOV RBX,[RSP+10*8] ; theFile. SetSt [RBX+FILE64.Status],fi64StAppend FileCreate RBX JC .90: MOV RSI,[RSP+11*8] ; BufSize. XOR EAX,EAX LinABI mmap,RAX,RSI,PROT_READ+PROT_WRITE+PROT_EXEC,MAP_PRIVATE+MAP_ANONYMOUS,-1,RAX,Fastmode=Yes CMP RAX,-ERR_MAX CMC JC .90: MOV [RBX+FILE64.BufSize],RSI MOV [RBX+FILE64.BufPtr],RAX MOV [RBX+FILE64.Ptr],RAX ADD RAX,RSI MOV [RBX+FILE64.Top],RAX SetSt [RBX+FILE64.Status],fi64StAllocated MOV RAX,[RBX+FILE64.Size] .90:POP R11,R10,R9,R8,RDI,RSI,RDX,RCX,RBX RET 2*8 ENDPROC1 FileStreamAppend@RT:: %ENDMACRO FileStreamAppend
FileStreamWriteByte %MACRO theFile PUSHQ %theFile CALL FileStreamWriteByte@RT:: FileStreamWriteByte@RT:: PROC1 PUSH RAX,RBX,RDI MOV RBX,[RSP+4*8] ; theFile. .10: MOV RDI,[RBX+FILE64.Ptr] CMP RDI,[RBX+FILE64.Top] JB .50 FileFlush RBX ; Write the buffer if it is full. JNC .10: MOV [RSP+2*8],RAX ; Returned RAX on error. JMP .90: .50: STOSB MOV RAX,[RBX+FILE64.Pos] MOV [RBX+FILE64.Ptr],RDI INC RAX MOV [RBX+FILE64.Pos],RAX MOV [RBX+FILE64.Size],RAX CLC .90: POP RDI,RBX,RAX RET 1*8 ENDPROC1 FileStreamWriteByte@RT:: %ENDMACRO FileStreamWriteByte
FileStreamWriteWord %MACRO theFile PUSHQ %theFile CALL FileStreamWriteWord@RT:: FileStreamWriteWord@RT:: PROC1 PUSH RAX,RBX,RCX,RDI MOV RBX,[RSP+5*8] ; theFile. .10: MOV RDI,[RBX+FILE64.Ptr] LEA RCX,[RDI+1] CMP RCX,[RBX+FILE64.Top] JB .50: FileFlush RBX ; Write the buffer if it is full. JNC .10: MOV [RSP+3*8],RAX ; Returned RAX on error. JMP .90: .50: STOSW MOV RAX,[RBX+FILE64.Pos] MOV [RBX+FILE64.Ptr],RDI ADD EAX,2 MOV [RBX+FILE64.Pos],RAX MOV [RBX+FILE64.Size],RAX CLC .90: POP RDI,RCX,RBX,RAX RET 1*8 ENDPROC1 FileStreamWriteWord@RT:: %ENDMACRO FileStreamWriteWord
FileStreamWriteDword %MACRO theFile PUSHQ %theFile CALL FileStreamWriteDword@RT:: FileStreamWriteDword@RT:: PROC1 PUSH RAX,RBX,RCX,RDI MOV RBX,[RSP+5*8] ; the File. .10: MOV RDI,[RBX+FILE64.Ptr] LEA RCX,[RDI+3] CMP RCX,[RBX+FILE64.Top] JB .50 FileFlush RBX ; Write the buffer if it is full. JNC .10: MOV [RSP+3*8],RAX ; Returned RAX on error. JMP .90: .50: STOSD MOV RAX,[RBX+FILE64.Pos] MOV [RBX+FILE64.Ptr],RDI ADD EAX,4 MOV [RBX+FILE64.Pos],RAX MOV [RBX+FILE64.Size],RAX CLC .90: POP RDI,RCX,RBX,RAX RET 1*8 ENDPROC1 FileStreamWriteDword@RT:: %ENDMACRO FileStreamWriteDword
FileStreamWriteRT %MACRO ; Common runtime procedure for macros FileStreamWrite* %IF %# & 1 = 0 %ERROR ID=5946, 'Macro FileStreamWrite expects odd number of arguments.' %EXITMACRO FileStreamWriteRT %ENDIF PUSHQ 0 ; Mark the end of arguments. ArgNr %FOR %#..2,STEP=-2 PUSHQ %*{%ArgNr}, %*{%ArgNr-1} %ENDFOR ArgNr PUSHQ %theFile,RSP ADDQ [RSP],(%#+1)*8 CALL FileStreamWrite@RT:: POP RSP FileStreamWrite@RT::PROC1 PUSH RBX,RCX,RDX,RBP,RSI,RDI MOV RBX,[RSP+8*8] ; theFile. MOV RAX,-EBADFD ; File was not opened. TESTD [RBX+FILE64.Status],fi64StCreated STC JZ .90: TESTD [RBX+FILE64.Status],fi64StAllocated STC JZ .90: PUSHQ 0 ; Returned RAX will accumulate written size. LEA RBP,[RSP+10*8] ; Pointer to the first DataPtr. .20: XCHG RBP,RSI LODSQ ; DataPtr. MOV RBP,RAX TEST RAX ; Test if it's the end of pointers. JZ .80: LODSQ ; DataSize. XCHG RSI,RBP MOV RDX,RAX INC RAX JNZ .30: MOV RCX,RDX ; DataSize=-1, let's find the actual size. MOV RDI,RSI REPNE SCASB SUB RDI,RSI LEA RDX,[RDI-1] .30: MOV RDI,[RBX+FILE64.Ptr] ; RDX is DataSize. MOV RCX,[RBX+FILE64.Top] SUB RCX,RDI JA .40: FileFlush RBX JNC .30: LEA RSP,[RSP+1*8] ; Keep CF and RAX=error code. JMP .90: .40: SUB RDX,RCX JAE .50: ADD RCX,RDX .50: ADD [RBX+FILE64.Pos],RCX ADD [RBX+FILE64.Size],RCX ADD [RSP+0*8],RCX ; Accumulate total written size. REP MOVSB MOV [RBX+FILE64.Ptr],RDI CMP RDX,RCX JG .30 JMP .20: .80: POP RAX .90:POP RDI,RSI,RBP,RDX,RCX,RBX RET ENDP1 FileStreamWrite@RT:: %ENDMACRO FileStreamWriteRT
FileStreamWrite %MACRO theFile,DataPtr1,DataSize1,DataPtr2,DataSize2,,, FileStreamWriteRT %* %ENDMACRO FileStreamWrite
FileStreamWriteLn %MACRO theFile,DataPtr1,DataSize1,DataPtr2,DataSize2,,, FileStreamWriteRT %*, =B 0x0A, 1 %ENDMACRO FileStreamWriteLn
FileStreamWrite$ %MACRO theFile,DataPtr$1,DataPtr$2,DataPtr$3,,, %ArgList %SET %theFile ArgNr %FOR 2..%# %ArgList %SET %ArgList,%*{%ArgNr},-1 %ENDFOR ArgNr FileStreamWriteRT %ArgList %ENDMACRO FileStreamWrite$
ENDHEAD linf64