I have come up with the following snippet by building upon the answers given to my StackOverflow question. Just trying to get some other eyeballs to review things so that they can point out any egregious mistakes that I probably made.
One area that was a bit tricky to me to get working is the bottom half of the readFileHeader
label; there seemed to be some conflicting information about the cmov
instruction and I still don't quite understand if I "should've" gone with an alternative that involves a jump or two. Sure, such a change is a micro-optimization but a huge part of this exercise to me is trying to understand the way different constructs affect the instruction pipeline and whatnot.
include WindowsApi.inc ; imports all the "magic" constants that are in the following code
include WString.inc ; imports the macro for creating constant unicode strings
var0 textequ <rcx>
var1 textequ <rdx>
var2 textequ <r8>
var3 textequ <r9>
var4 textequ <qword ptr [(rsp + 20h)]>
var5 textequ <qword ptr [(rsp + 28h)]>
var6 textequ <qword ptr [(rsp + 30h)]>
TRUE = 1h
.const
align 2
filePath: WString <C:/Temp/Validation.csv>
.code
Main proc
sub rsp, 1048h ; align with 16 while simultaneously making room on the stack for the "home space", some parameters, and a 4096 byte buffer
lea var0, filePath ; put address of file path into parameter slot 0
mov var1, FILE_ACCESS_READ ; put access mode into parameter slot 1
mov var2, FILE_SHARE_READ ; put share mode into parameter slot 2
xor var3, var3 ; put security attributes into parameter slot 3
mov var4, FILE_DISPOSITION_OPEN ; put disposition into parameter slot 4
mov var5, FILE_FLAG_NORMAL ; put flags into parameter slot 5
mov var6, WINDOWS_NULL ; put pointer to template handle into parameter slot 6
call CreateFile ; create file handle
cmp rax, WINDOWS_INVALID_HANDLE ; validate file handle
je exitMain ; skip to exit point if create validation failed
mov var5, rax ; save a reference to the file handle for later (taking advantage of the unused parameter slot 5)
jmp readFileHeader ; skip to read file header
readFileBody:
xor eax, eax ; TODO: something useful with the number of bytes read in ecx...
readFileHeader:
mov var0, var5 ; put file handle into parameter slot 0
lea var1, qword ptr [(rsp + 38h)] ; put pointer to file buffer into parameter slot 1
mov var2, 1000h ; put requested number of bytes to read into parameter slot 2
lea var3, var6 ; put pointer to actual number of bytes that were read into parameter slot 3 (taking advantage of the unused parameter slot 6)
mov var4, WINDOWS_NULL ; put overlapped pointer into parameter slot 4
call ReadFile ; read file handle
mov rcx, var6 ; put pointer to actual number of bytes that were read into rcx
mov edx, TRUE ; assume that body should be processed by storing TRUE in edx
test eax, eax ; validate file read operation (non-zero == no errors)
cmovz edx, eax ; store zero in edx if file read operation failed
test ecx, ecx ; check for end of file (non-zero == more data)
cmovz edx, ecx ; store zero in edx if end of file reached
test edx, edx ; test edx for zero
jne readFileBody ; skip to read file body if edx was not zero
readFileFooter:
; TODO: properly handle errors by inspecting the value of eax...
mov var0, var5 ; put the reference to the file handle into parameter slot 0
call CloseHandle ; close file handle
exitMain:
xor ecx, ecx ; set return value to zero
call ExitProcess ; return control to Windows
Main endp
end
Edit; here is the best alternative I have been able to imagine so far; reduces total number of instructions from 9 to 6:
call ReadFile ; read file handle
mov ecx, 0FFFFFFFFh ; put max 32-bit value into ecx
test eax, eax ; validate file read operation (non-zero == no errors)
cmovz ecx, eax ; if file read operation failed, put zero into ecx
and rcx, var6 ; if rcx is not zero, put the number of bytes read from var6 into rcx
jne readFileBody ; if rcx is not zero, skip to readFileBody
cmov
should and should not be used. I also suppose it is worth noting that I originally choose to go withcmov
because 1) it was easier for me to parse the code and 2) it seemed "bad" to jump "within a loop". \$\endgroup\$