My task:
"Use the GetOpenFileName function to select a file. Check if the age of the file is less than 3 days, execute it. Otherwise, display a dialog box with a question about deleting the file. If the user agrees, wipe."
Problem:
I can't get the age of the file in any way. I'm trying to compare the creationTime returned by GetFileTime with the current time returned by GetSystemTimeAsFileTime. 3 days is (3 * 24 * 60 * 60 * 10000000) 100-ns intervals (FILETIME is measured in such units).
To simplify my task, I compare only the higher parts (dwHighDateTime) of FILETIME structures, the weight of the lower part is 429 seconds, less than 10 minutes.
Something like that:
invoke GetFileTime, hFile, addr ftCreate, NULL, NULL
invoke GetSystemTimeAsFileTime, addr ftNow
mov eax, ftNow.dwHighDateTime
sub eax, ftCreate.dwHighDateTime8
cmp eax, ((3 * 24 * 60 * 60 * 10000000) / 0x100000000)
jg l1
But I get this errors:
;for this: mov eax, ftNow.dwHighDateTime
Third.asm(63) : error A2006: undefined symbol : dwHighDateTime
;for this: cmp eax, ((3 * 24 * 60 * 60 * 10000000) / 0x100000000)
Third.asm(65) : error A2206: missing operator in expression
Help please with this problem.
P.S. If somebody heva another ideas to solve this problem(I mean about age of file), you can recomend it))
File.inc
include WINDOWS.inc
include user32.inc
include kernel32.inc
include comdlg32.inc
includelib user32.lib
includelib kernel32.lib
includelib comdlg32.lib
.data
Time_title db ' Lab_3',0
format db 'More than 3 days. Delete file?', 0
buf db 255 dup(0)
hFile dd 0
readed dd 0
hmem dd 0
File.asm
.386
.model flat,STDCALL
option casemap :none ;case sensitive
include Third.inc
include RADbg.inc
Mem_Alloc PROC Buf_Size:DWORD
add Buf_Size,4
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,Buf_Size
push eax
invoke GlobalLock,eax
pop [eax]
add eax,4
ret
Mem_Alloc endp
Mem_Free PROC DATA:DWORD
mov eax,DATA
sub eax,4
mov eax,[eax]
push eax
push eax
call GlobalUnlock
call GlobalFree
ret
Mem_Free endp
.code
Begin:
call main
invoke ExitProcess,NULL
main proc
LOCAL ftCreate, ftNow: FILETIME;
;LOCAL stUTC, stSysTime: SYSTEMTIME;
invoke Mem_Alloc, 1000h
mov hmem, eax
invoke Mem_Alloc, sizeof OPENFILENAME
mov edi, eax
assume edi: ptr OPENFILENAME
xor eax, eax
mov [edi].lStructSize, sizeof OPENFILENAME
mov [edi].lpstrFile, offset buf
mov [edi].nMaxFile, 255
invoke GetOpenFileName, edi
invoke CreateFile, [edi].lpstrFile, GENERIC_READ,\
FILE_SHARE_READ, NULL, OPEN_EXISTING,\
FILE_ATTRIBUTE_NORMAL, NULL
mov hFile,eax
;invoke GetFileTime, hFile, addr ftCreate, NULL, NULL
;invoke FileTimeToSystemTime, addr ftCreate, addr stSysTime
;invoke GetSystemTime, addr stUTC
invoke GetFileTime, hFile, addr ftCreate, NULL, NULL
invoke GetSystemTimeAsFileTime, addr ftNow
mov eax, ftNow.dwHighDateTime
sub eax, ftCreate.dwHighDateTime
cmp eax, ((3 * 24 * 60 * 60 * 10000000) / 0x100000000)
jg l1
invoke ReadFile, hFile, hmem, 1000h, addr readed, 0
invoke MessageBox, 0, hmem, addr Time_title, MB_OKCANCEL
jmp l2
l1:
invoke MessageBox, 0, addr format, addr Time_title, MB_OKCANCEL
cmp eax, IDOK
jne l2
invoke DeleteFile, addr [edi].lpstrFile
l2:
assume edi: dword
invoke CloseHandle, hFile
invoke Mem_Free, hmem
invoke Mem_Free, edi
ret
main endp
end Begin
Related
I have a simple fasm program, in this program I get some zeroed memory from windows through VirtualAlloc. I then have a procedure where I simply set up the parameters and make a call to StretchDIBits passing a pointer to the empty memory buffer. I therefore expect the screen should be drawn black. This however is not the case, and I can't for the life of me figure out why.
Below is the code.
format PE64 GUI
entry main
include 'C:\Users\bmowo\Desktop\Tools\fasm\INCLUDE\win64a.inc'
include '.\main_data.asm'
section '.text' code readable executable
main:
sub rsp,8 ;alignment
invoke GetModuleHandle,0
mov [WindowClass.hInstance], rax
invoke LoadIcon,0,IDI_APPLICATION
mov [WindowClass.hIcon],rax
mov [WindowClass.hIconSm],rax
invoke LoadCursor,0,IDC_ARROW
mov [WindowClass.hCursor],rax
mov rax, CS_OWNDC or CS_HREDRAW or CS_VREDRAW
mov [WindowClass.style], eax
invoke RegisterClassExA,WindowClass
test rax, rax
jz exit
invoke CreateWindowExA,0,WindowClassName,WindowTitle, WS_VISIBLE+WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0,0,[WindowClass.hInstance],0
mov [WindowHandle], rax
test rax, rax
jz exit
mov rax, 2*gigabyte
mov r10d, MEM_COMMIT or MEM_RESERVE
invoke VirtualAlloc,0,eax,r10d,PAGE_READWRITE
mov [Memory], rax
mov [GlobalRunning], 1
.main_loop:
cmp [GlobalRunning], 0
je exit
stdcall Win32MessagePump
jmp .main_loop
exit:
invoke ExitProcess,[Message.wParam]
proc BlitBuffer
locals
Width dd 0
Height dd 0
PixelsBase dq 0
DC dq 0
BitmapInfo BITMAPINFO 0
ScreenRect RECT
endl
;get window region
lea rax, [ScreenRect]
invoke GetClientRect, [WindowHandle], rax
;calculate width and height
mov ecx, [ScreenRect.bottom]
sub ecx, [ScreenRect.top]
mov [Height], ecx
mov ecx, [ScreenRect.left]
sub ecx, [ScreenRect.right]
mov [Width], ecx
BitmapInfoHeaderSize equ 40
BI_RGB equ 0
mov [BitmapInfo.biSize], BitmapInfoHeaderSize ;sizeof bitmapinfoheader is 40
mov eax, [Width]
mov [BitmapInfo.biWidth], eax
mov eax, [Height]
mov [BitmapInfo.biHeight], eax
mov [BitmapInfo.biPlanes], 1
mov [BitmapInfo.biBitCount], 32
mov [BitmapInfo.biCompression], BI_RGB
mov [BitmapInfo.biSizeImage], 0
mov [BitmapInfo.biXPelsPerMeter], 0
mov [BitmapInfo.biYPelsPerMeter], 0
mov [BitmapInfo.biClrUsed], 0
mov [BitmapInfo.biClrImportant], 0
mov [BitmapInfo.RGBQUADa], 0
mov [BitmapInfo.RGBQUADb], 0
mov [BitmapInfo.RGBQUADc], 0
mov [BitmapInfo.RGBQUADd], 0
mov rax, [Memory]
mov [PixelsBase], rax
invoke GetDC, [WindowHandle]
mov [DC], rax
lea rax, [BitmapInfo]
DIB_RGB_COLORS equ 0
invoke StretchDIBits, [DC],0,0,[Width],[Height],0,0,[Width],[Height],[PixelsBase], rax,DIB_RGB_COLORS,SRCCOPY
ret
endp
proc Win32ToggleWindowFullScreen
locals
WindowStyle dd 0
MonitorInfo MONITORINFO sizeof.MONITORINFO
endl
GWL_STYLE equ -16
SWP_NOOWNERZORDER equ 0x0200
SWP_FRAMECHANGED equ 0x0020
SWP_NOMOVE equ 0x0002
SWP_NOSIZE equ 0x0001
SWP_NOZORDER equ 0x0004
HWND_TOP equ 0
SWP_FRAMECHANGED equ 0x0020
WS_OVERLAPPEDWINDOW equ 0xcf0000
MONITOR_DEFAULTTOPRIMARY equ 1
invoke GetWindowLongA, [WindowHandle], GWL_STYLE
mov [WindowStyle], eax
mov rbx, WS_OVERLAPPEDWINDOW
and eax, ebx
jz .else
invoke GetWindowPlacement, [WindowHandle], GlobalWindowPlacement
mov r8, rax
cmp rax, 1
jb .end
invoke MonitorFromWindow,[WindowHandle], MONITOR_DEFAULTTOPRIMARY
lea rbx, [MonitorInfo]
invoke GetMonitorInfoA,rax,rbx
cmp rax, 1
jb .end
mov rbx, not WS_OVERLAPPEDWINDOW ;not rbx
mov eax, [WindowStyle]
and eax, ebx
invoke SetWindowLongA, [WindowHandle], GWL_STYLE, eax
mov eax, [MonitorInfo.rcMonitor.right]
sub eax, [MonitorInfo.rcMonitor.left]
mov r10d, [MonitorInfo.rcMonitor.bottom]
sub r10d, [MonitorInfo.rcMonitor.top]
mov r11, HWND_TOP or SWP_NOOWNERZORDER or SWP_FRAMECHANGED
invoke SetWindowPos, [WindowHandle],0,[MonitorInfo.rcMonitor.left],[MonitorInfo.rcMonitor.top],eax,r10d,r11d
jmp .end
.else:
mov eax, [WindowStyle]
or rax, WS_OVERLAPPEDWINDOW
invoke SetWindowLongA, [WindowHandle],GWL_STYLE,eax
invoke SetWindowPlacement,[WindowHandle],GlobalWindowPlacement
mov rax, SWP_NOOWNERZORDER or SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER
invoke SetWindowPos,[WindowHandle],0,0,0,0,0,eax
.end:
ret
endp
proc Win32MessagePump
.while_message:
invoke PeekMessageA,Message,[WindowHandle],0,0,PM_REMOVE
cmp eax, 0
je .end
cmp [Message.message], WM_KEYDOWN
je .keydown
cmp [Message.message], WM_PAINT
je .paint
.default:
invoke TranslateMessage,Message
invoke DispatchMessage,Message
jmp .while_message
.keydown:
stdcall Win32ToggleWindowFullScreen
.paint:
invoke BeginPaint,[WindowHandle],PaintStruct
stdcall BlitBuffer
invoke EndPaint,[WindowHandle],PaintStruct
jmp .while_message
.end:
ret
endp
proc Win32CallbackProc ;hwnd,wmsg,wparam,lparam
cmp edx, WM_DESTROY
je .close
cmp edx, WM_CLOSE
je .close
cmp edx, WM_QUIT
je .close
.default:
invoke DefWindowProcA,rcx,rdx,r8,r9
jmp .end
.close:
mov [GlobalRunning], 0
jmp .end
.end:
ret
endp
'''
GlobalWindowPlacement WINDOWPLACEMENT sizeof.WINDOWPLACEMENT
breakit equ int3
kilobyte equ 1024
megabyte equ 1024*1024
gigabyte equ 1024*1024*1024
struct BITMAPINFO
biSize dd ?
biWidth dd ?
biHeight dd ?
biPlanes dw ?
biBitCount dw ?
biSizeImage dd 0
biXPelsPerMeter dd 0
biYPelsPerMeter dd 0
biClrUsed dd 0
biClrImportant dd 0
RGBQUADa db 0
RGBQUADb db 0
RGBQUADc db 0
RGBQUADd db 0
ends
PaintStruct PAINTSTRUCT
struct MONITORINFO
cbSize dd ?
rcMonitor RECT ?
rcWork RECT ?
dwFlags dd ?
ends
section '.data' data readable writeable
GlobalRunning db 0
WindowClassName db "fasm app",0
WindowTitle db "Raytracer or Rasteriser or somethin",0
;WindowClass WNDCLASSEX sizeof.WNDCLASSEX,0,Win32CallbackProc,0,0,0,0,0,COLOR_WINDOW,0,WindowClassName,0
WindowClass WNDCLASSEX sizeof.WNDCLASSEX,0,Win32CallbackProc,0,0,0,0,0,0,0,WindowClassName,0
Message MSG
WindowHandle dq 0
WindowDC dq 0
Memory dq 0
section '.idata' import data readable writeable
library kernel,'kernel32.dll',\
user,'user32.dll',\
gdi, 'gdi32.dll'
import kernel,\
GetModuleHandle,'GetModuleHandleA',\
ExitProcess,'ExitProcess',\
VirtualAlloc, 'VirtualAlloc',\
VirtualFree, 'VirtualFree',\
GetLastError, 'GetLastError',\
SetLastError, 'SetLastError'\
import user,\
RegisterClassExA,'RegisterClassExA',\
CreateWindowExA,'CreateWindowExA',\
ShowWindow,'ShowWindow',\
UpdateWindow,'UpdateWindow',\
DefWindowProcA,'DefWindowProcA',\
GetMessage,'GetMessageA',\
TranslateMessage,'TranslateMessage',\
DispatchMessage,'DispatchMessageA',\
LoadCursor,'LoadCursorA',\
LoadIcon,'LoadIconA',\
GetClientRect,'GetClientRect',\
GetDC,'GetDC',\
ReleaseDC,'ReleaseDC',\
BeginPaint,'BeginPaint',\
EndPaint,'EndPaint',\
PostQuitMessage,'PostQuitMessage',\
MessageBoxA, 'MessageBoxA',\
PeekMessageA, 'PeekMessageA',\
GetWindowLongA, 'GetWindowLongA',\
GetWindowPlacement,'GetWindowPlacement',\
SetWindowPlacement, 'SetWindowPlacement',\
GetMonitorInfoA, 'GetMonitorInfoA',\
SetWindowLongA, 'SetWindowLongA',\
SetWindowPos, 'SetWindowPos',\
MonitorFromWindow, 'MonitorFromWindow'
import gdi,\
StretchDIBits, 'StretchDIBits',\
PatBlt, 'PatBlt'
conclusion. It's been pointed out to me that the invoke macro is clobbering the rax register which I was using for [BitmapInfo] so that was dumb. Fixing that issue results in the expected black screen.
Invoke macro is clobbering the parameter being passed through rax.
I'm sorry I don't know much about fasm, I tried to reproduce the problem through C++:
void* p = VirtualAlloc(NULL, 512 * 512, MEM_COMMIT, PAGE_READWRITE);
BITMAPINFO bi{};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 512;
bi.bmiHeader.biHeight = 512;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
StretchDIBits(hdc, 0, 0, 512, 512, 0, 0, 512, 512, p, &bi, DIB_RGB_COLORS, SRCCOPY);
I tried to use the above code and reproduced the problem. Under my test, I think the allocated memory space is not enough.
I modified it to:
void* p = VirtualAlloc(NULL, 512 * 512 * 4, MEM_COMMIT, PAGE_READWRITE);
Then it works for me.
I'm pretty new to fasm and I just recently started learning about procedures. My problem is that I have a proc and I want it to sort my list in a certain way. But when I run my code it just seems to sort some random numbers from memory. I don't quite know why it happens and I would appreciate any help.
Here's the code:
format PE gui 5.0
include 'D:\Flat Assembler\INCLUDE\win32a.inc'
entry start
section '.data' data readable writable
mas1 dw 2, -3, 1, -1, 3, -2, 5, -5, -4, 4
N = ($ - mas1) / 2
numStr db N dup('%d '), 0
strStr db '%s', 0
undefStr db 'undefined', 0
buff db 50 dup(?)
Caption db 'Result', 0
section '.code' code readable executable
start:
stdcall bubble, mas1
cinvoke wsprintf, buff, numStr
invoke MessageBox, 0, buff, Caption, MB_OK + MB_ICONINFORMATION
invoke ExitProcess, 0
proc bubble, mas:word
mov ecx, 0
mov ebx, 0
outerLoop:
cmp ecx, 10
je done
mov ebx, 2
innerLoop:
mov eax, 0
mov edx, 0
cmp [mas+ebx], 0 ;if(mas[j] > 0)
jge continue ;continue
mov ax, [mas+ebx-2]
cmp ax, [mas+ebx]
jle continue
mov dx, [mas+ebx]
mov [mas+ebx-2], dx
mov [mas+ebx], ax
continue:
cmp ebx, 18 ;10
je innerDone
add ebx, 2 ;inc ebx
jmp innerLoop
innerDone:
inc ecx
jmp outerLoop
done:
mov ecx, 0
mov ebx, 0
mov ebx, 18
mov ecx, N
print:
mov eax, 0
mov ax, [mas+ebx]
cwde
push eax
sub ebx, 2
loop print
ret
endp
section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL'
include 'D:\Flat Assembler\INCLUDE\API\kernel32.inc'
include 'D:\Flat Assembler\INCLUDE\API\user32.inc'
Error 1
stdcall bubble, mas1
...
proc bubble, mas:word
The parameter mas1 is an address and is pushed to the stack as a dword. Therefore you should not limit the argument mas to a word.
What your bubble procedure needs is the full address of the array. You get this via mov esi, [mas] that FASM will encode as if you would have written mov esi, [ebp+8]. EBP+8 is where the first argument (and in your program the only argument) resides, when the standard prologue push ebp mov ebp, esp is used.
Error 2
In your bubble procedure you push the resulting array to the stack hoping to have wsprintf use it from there, but once the bubble procedure executes its ret instruction, the epilogue code as well as the ret instruction itself will start eating your array and even return to the wrong address in memory!
If you're going to return an array via the stack, then store it above the return address and the argument(s). That's why I wrote in my program below:
sub esp, N*4 ; Space for N dwords on the stack
stdcall bubble, mas1
Error 3
cmp [mas+ebx], 0 ;if(mas[j] > 0)
jge continue ;continue
Your BubbleSort is wrong because you don't allow positive numbers to get compared!
Furthermore you make too many iterations that also continu for too long.
I tested below program on FASM 1.71.22 Don't forget to change the paths!
format PE gui 5.0
include 'C:\FASM\INCLUDE\win32a.inc'
entry start
section '.data' data readable writable
mas1 dw 2, -3, 1, -1, 3, -2, 5, -5, -4, 4
N = ($ - mas1) / 2
numStr db N-1 dup('%d, '), '%d', 0
;strStr db '%s', 0
;undefStr db 'undefined', 0
buff db 50 dup(?)
Caption db 'Result', 0
section '.code' code readable executable
start:
sub esp, N*4 ; Space for N dwords on the stack
stdcall bubble, mas1
cinvoke wsprintf, buff, numStr
invoke MessageBox, 0, buff, Caption, MB_OK + MB_ICONINFORMATION
invoke ExitProcess, 0
proc bubble uses ebx esi, mas
mov esi, [mas] ; Address of the array
mov ecx, (N-1)*2 ; Offset to the last item; Max (N-1) compares
outerLoop:
xor ebx, ebx
innerLoop:
mov ax, [esi+ebx]
mov dx, [esi+ebx+2]
cmp ax, dx
jle continue
mov [esi+ebx+2], ax
mov [esi+ebx], dx
continue:
add ebx, 2
cmp ebx, ecx
jb innerLoop
sub ecx, 2
jnz outerLoop
mov ebx, (N-1)*2
toStack:
movsx eax, word [esi+ebx]
mov [ebp+12+ebx*2], eax
sub ebx, 2
jnb toStack
ret
endp
section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL'
include 'C:\FASM\INCLUDE\API\kernel32.inc'
include 'C:\FASM\INCLUDE\API\user32.inc'
Error 2 revisited
IMO returning the resulting array through the stack would make better sense if your bubble procedure didn't modify the original array.
But in your present code you do, so...
Once you strike the toStack snippet from the bubble procedure, you can simply (after returning from the bubble procedure) push the word-sized elements of the array to the stack as dwords followed by using wsprintf.
...
start:
stdcall bubble, mas1
mov ebx, (N-1)*2
toStack:
movsx eax, word [mas1+ebx]
push eax
sub ebx, 2
jnb toStack
cinvoke wsprintf, buff, numStr
...
sub ecx, 2
jnz outerLoop
; See no more toStack here!
ret
endp
...
I am trying to make an array, and access it's values and print them out
After calling the WriteConsole subroutine, it is returning false, however, all the values are supplied. Here we can see that - https://imgur.com/a/vUfwOo6
Eax register is 0 after calling WriteConsole. Here you can see the register values, that are being pushed to the stack. https://imgur.com/a/gv6s4uG
Considering, that WriteConsole is WINAPI subroutine, that means it's stdcall. So, I am passing values right to left.
lpReserved -> 0
lpNumberOfCharsWritten -> offset to 00403028 (CharsWritten variable)
nNumberOfCharsToWrite -> Just 2, because in array only ints are present of length 2
*lpBuffer -> ebx register, which contains array lvalue
hConsoleOutput -> Output from GetStdHandle (In this case -> edx register -> A0)
My MASM code:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
include C:\masm32\include\masm32.inc
includelib C:\masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\msvcrt.lib ; Some default includes :P
.data
dArray dd 10 dup (?) ; Main array
CharsWritten dd ?
LoopCounter dd 0
StdHandle dd ?
.code
PrintArrayToScreen proc
mov eax, STD_OUTPUT_HANDLE
push eax
call GetStdHandle
mov StdHandle,eax
mov eax,[LoopCounter]
innerPrintLoop:
mov ecx,offset dArray
mov eax, [LoopCounter]
mov ebx,[ecx + eax * 4]
mov esi,offset CharsWritten
push 0
push esi
push 2
push ebx
mov edx,StdHandle
push edx
call WriteConsole
mov eax,[LoopCounter]
inc eax
mov LoopCounter,eax ; Storing the Loop Counter in the variable
cmp eax,11 ; +1 because of loop counter increment
jnz innerPrintLoop
ret
PrintArrayToScreen endp
arrayLoop proc ; Subroutine for the array filling
mov eax,offset dArray
mov bx,10
mov ecx,0
innerLoop:
mov [eax + ecx * 4],bx ; ecx * 4 => counter * 4 bytes
inc bx
add ecx,1
cmp ecx,10
jne innerLoop
mov eax,offset dArray
ret
arrayLoop endp
start:
call arrayLoop
call PrintArrayToScreen
mov eax,0
push eax
call ExitProcess
end start
From the documentation for WriteConsole:
lpBuffer [in]
A pointer to a buffer that contains characters to be written to the console screen buffer.
So you should be passing the address of the data to be written, but you're actually passing the data itself.
You could "fix" that by changing the line mov ebx,[ecx + eax * 4] to lea ebx,[ecx + eax * 4]. But note that WriteConsole doesn't do any integer-to-string conversion for you, so you still probably wouldn't get the result you expected. If you want that sort of functionality, use printf.
This question already has an answer here:
x86 MASM Assembly - Input Buffer holds old input despite FlushConsoleInputBuffer
(1 answer)
Closed 2 years ago.
I would like to get user input from the console using the ReadConsoleA prototype in /masm32/lib/kernel32.lib.
If the user inputs less than the maximum number of characters, the program runs fine, but if they give too many, the program uses the extra for the next input.
Language: MASM x86
Computer: Windows 10 x64
Driver.asm
.386
.model flat, C
.stack 100h
INCLUDELIB /masm32/lib/kernel32.lib
GetStdHandle PROTO Near32 STDCALL, nStdHandle: DWORD
WriteConsoleA PROTO Near32 STDCALL, handle:DWORD, lpBuffer:PTR BYTE, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:PTR DWORD, lpReserved:DWORD
ReadConsoleA PROTO Near32 STDCALL, handle:DWORD, lpBuffer:PTR BYTE, nNumberOfCharsToRead:DWORD, lpNumberOfCharsRead:PTR DWORD, lpVoid:DWORD
ExitProcess PROTO STDCALL, dwExitCode:DWORD
StrInput PROTO, addrStr:DWORD, dNumCharsToRead:DWORD
StrPrint PROTO, addrStr:DWORD
StrLen PROTO, addrStr:DWORD
.DATA
strPromptName db "Enter your name: ", 0
strInputName db 10 DUP(0)
strNewLine db 13, 10, 0
.CODE
Main PROC
MOV EAX, 0
; Ask for name, store it, print it, new line
INVOKE StrPrint, ADDR strPromptName
INVOKE StrInput, ADDR strInputName, LENGTHOF strInputName
INVOKE StrPrint, ADDR strInputName
INVOKE StrPrint, ADDR strNewLine
; Ask for name, store it, print it, new line
INVOKE StrPrint, ADDR strPromptName
INVOKE StrInput, ADDR strInputName, LENGTHOF strInputName
INVOKE StrPrint, ADDR strInputName
INVOKE StrPrint, ADDR strNewLine
INVOKE ExitProcess, 0
Main ENDP
StrInput PROC PUBLIC addrStr:DWORD, dNumCharsToRead:DWORD
.DATA
dNumCharsRead dd ? ; Holds the number of chars read from console
.CODE
PUSHAD ; Store all registers, don't trust others' functions
INVOKE GetStdHandle, -10 ; Standard input = -10, handle in EAX
INVOKE ReadConsoleA, EAX, addrStr, dNumCharsToRead, OFFSET dNumCharsRead, 0
MOV EDI, addrStr ; Goto front of string
ADD EDI, dNumCharsRead ; Goto just after last inputted char
SUB EDI, 2 ; Go back 2, to carriage return char
MOV BYTE PTR [EDI], 0 ; Change to 0 for null termination
POPAD ; Restore all registers
RET ; No return value, return to caller
StrInput ENDP
StrPrint PROC PUBLIC addrStr:DWORD
PUSHAD ; Store all registers, don't trust others' functions
INVOKE StrLen, addrStr ; Length in ECX
DEC ECX ; Don't print null terminator
INVOKE GetStdHandle, -11 ; Standard output = -11, handle in EAX
INVOKE WriteConsoleA, EAX, addrStr, ECX, 0, 0
POPAD ; Restore all registers
RET ; No return value, return to caller
StrPrint ENDP
StrLen PROC PUBLIC uses AX EDI addrStr:DWORD
MOV EDI, addrStr ; Store address for scanning
XOR AL, AL ; Store null term for scanning
MOV ECX, -1 ; ECX = len, will decrement over string (neg len)
CLD ; Clear direction flag, search forward
REPNE SCASB ; While char not 0, continue scanning
NEG ECX ; Make length positive
DEC ECX ; Off by one
RET ; Len in ECX, return to caller
StrLen ENDP
END Main
Good terminal output
Enter your name: abcdef << Allowed user input
abcdef
Enter your name: new << Allowed user input
new
Press any key to continue . . .
User input overflow
Enter your name: reallylongname << Allowed user input
reallylo
Enter your name: name << Didn't allow user input
Press any key to continue . . . << Also didn't print
Thank you Michael Petch! I changed StrInput and added FlushInputBuffer PROC as shown below.
Any tips or suggestions as to cleaner code is appreciated, but my question is suitably answered.
StrInput PROC PUBLIC addrStr:DWORD, dNumCharsToRead:DWORD
.DATA
dNumCharsRead dd ? ; Holds the number of chars read from console
.CODE
PUSHAD ; Store all registers, don't trust others' functions
INVOKE GetStdHandle, -10 ; Standard input = -10, handle in EAX
INVOKE ReadConsoleA, EAX, addrStr, dNumCharsToRead, OFFSET dNumCharsRead,
MOV EDI, addrStr ; Goto front of string
ADD EDI, dNumCharsRead ; Goto last inputted char
DEC EDI ; Back one, to last inputted char
CMP BYTE PTR [EDI], 0Ah ; Check if is line feed char
JE done ; If is, done
INVOKE FlushInputBuffer ; Else, clear all input
done:
DEC EDI ; Goto 0Dh char (2nd last char)
MOV BYTE PTR [EDI], 0 ; Set to zero for null termination
POPAD ; Restore all registers
RET ; No return value, return to caller
StrInput ENDP
FlushInputBuffer PROC
.DATA
strDummy db 255 DUP (?) ; Holds input overflow, dummy variable
dNumRead dd ? ; Holds number of chars read
.CODE
PUSHAD ; Store all registers, don't trust others' functions
flush_loop:
INVOKE GetStdHandle, -10 ; Standard input = -10, handle in EAX
INVOKE ReadConsoleA, EAX, ADDR strDummy, 255, ADDR dNumRead, 0
MOV EAX, dNumRead ; Store num read for comparison
CMP EAX, 255 ; If is less than 255, done reading
JE flush_loop ; Else keep reading
POPAD ; Restore all registers
RET ; Return to caller
FlushinputBuffer ENDP
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I've got the following code but I can't work out why if the number I enter is too high does it return the wrong number. It might be because of the data types and dividing and multiplying but I can't work out exactly why. if you know of why I would be grateful for the help.
.586
.model flat, stdcall
option casemap :none
.stack 4096
extrn ExitProcess#4: proc
GetStdHandle proto :dword
ReadConsoleA proto :dword, :dword, :dword, :dword, :dword
WriteConsoleA proto :dword, :dword, :dword, :dword, :dword
STD_INPUT_HANDLE equ -10
STD_OUTPUT_HANDLE equ -11
.data
bufSize = 80
inputHandle DWORD ?
buffer db bufSize dup(?)
bytes_read DWORD ?
sum_string db "The number was ",0
outputHandle DWORD ?
bytes_written dd ?
actualNumber dw 0
asciiBuf db 4 dup (0)
.code
main:
invoke GetStdHandle, STD_INPUT_HANDLE
mov inputHandle, eax
invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr bytes_read,0
sub bytes_read, 2 ; -2 to remove cr,lf
mov ebx,0
mov al, byte ptr buffer+[ebx]
sub al,30h
add [actualNumber],ax
getNext:
inc bx
cmp ebx,bytes_read
jz cont
mov ax,10
mul [actualNumber]
mov actualNumber,ax
mov al, byte ptr buffer+[ebx]
sub al,30h
add actualNumber,ax
jmp getNext
cont:
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov outputHandle, eax
mov eax,LENGTHOF sum_string ;length of sum_string
invoke WriteConsoleA, outputHandle, addr sum_string, eax, addr bytes_written, 0
mov ax,[actualNumber]
mov cl,10
mov bl,3
nextNum:
xor edx, edx
div cl
add ah,30h
mov byte ptr asciiBuf+[ebx],ah
dec ebx
mov ah,0
cmp al,0
ja nextNum
mov eax,4
invoke WriteConsoleA, outputHandle, addr asciiBuf, eax, addr bytes_written, 0
mov eax,0
mov eax,bytes_written
push 0
call ExitProcess#4
end main
Yes, it is plausible that your return value is capped by a maximum value. This maximum is either the BYTE boundary of 255 or the WORD boundary of 65536. Let me explain why, part by part:
mov inputHandle, eax
invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr bytes_read,0
sub bytes_read, 2 ; -2 to remove cr,lf
mov ebx,0
mov al, byte ptr buffer+[ebx]
sub al,30h
add [actualNumber],ax
In this part you are calling a Win32 API function, which always returns the return value in the register EAX. After it has returned, you assign the lower 8-bits of the 32-bit return value to byte ptr buffer+[ebx], subtract 30h from it. Then you MOV the 8-bit you just modified in AL and the 8-bit from the return-value preserved in AH as a block AX to a WORD variable by add [actualNumber],ax. So AH stems from the EAX return value and is quite of undefined. You may be lucky if it's 0, but that should not be assumed.
The next problem is the following sub-routine:
getNext:
inc bx
cmp ebx,bytes_read
jz cont
mov ax,10
mul [actualNumber]
mov actualNumber,ax
mov al, byte ptr buffer+[ebx]
sub al,30h
add actualNumber,ax
jmp getNext
You are moving the decimal base 10 to the WORD register AX and multiply it by the WORD variable [actualNumber]. So far, so good. But the result of a 16-bit*16-bit MUL is returned in the register pair AX:DX(lower:higher). So your mov actualNumber,ax solely MOVs the lower 16-bits to your variable (DX is ignored, limiting your result to result % 65536). So your maximum possible result is MAX_WORD = 65535. Everything else would just give you the modulo in AX.
After your mov al, byte ptr buffer+[ebx] your overwrite the lower 8-bits of this result with the BYTE pointed to by buffer[ebx] and then subtract 30h from it. Remember: the higher 8-bits of the result still remain in AH, the higher 8-bits of AX.
Then you (re)add this value to the variable actualNumber with add actualNumber,ax. Let me condense these last two paragraphs:
Operation | AX |
| AL AH |
mov actualNumber,ax | ................ |
mov al, byte ptr buffer+[ebx] | ........ AH |
sub al,30h | ....-30h AH |
add actualNumber,ax | ................ |
So, you are modifying the lower 8-bits of AX through AL and then add the higher 8-bits of actualNumber/AH to itself - effectively doubling AH and then adding this to actualNumber like this:
actualNumber = 256 * (2 * AH) + (byte ptr buffer[ebx]-30h) ; I doubt you want that ;-)
These problems may cause several deviations from the desired result.