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
Related
I know this is outside the scope of the community but I had nowhere else to turn to. I need screenshots of Visual Studio after running the following programs for an assignment. My PC died and it is due in a few hours. There are two MASM programs and they are both interactive, any logical variables will do. You can send the link of the images to:mihijek116#vapaka.com (attachments will not work) if the question is closed. Thanks in advance
INCLUDE Irvine32.inc
.data
fname BYTE 16 Dup(?)
age dword ?
np BYTE 'Enter Name:',0dh, 0ah,0
ap BYTE 'Enter Age:',0dh, 0ah,0
yn BYTE 'Your name is ',0
yy BYTE ' born in ',0
cy dword 2022
dob dword ?
.code
main PROC
mov edx, offset np
call WriteString
mov edx, offset fn
mov ecx, Sizeof fn
call readstring
call Crlf
mov edx, offset ap
call WriteString
call readInt
mov age, eax
mov eax, cy
sub eax, age
mov yob, eax
mov edx, offset yn
call WriteString
mov edx, offset fn
call WriteString
mov edx, offset yy
call WriteString
mov eax, dob
call WriteDec
call Crlf
call WaitMsg
exit
main ENDP
END main
INCLUDE Irvine32.inc
.data
two dword 2 ; store two
three dword 3 ; store three
c dword 1 ; store 1
n dword ? ; n variable
answer dword ? ; answer variable
a1 dword ? ; store 2n^2
a2 dword ? ; store 3n
pr BYTE 'Enter n:',0dh, 0ah,0 ; n prompt
eq BYTE 'ans= ',0 ; ans
.code
n2n PROC ; 2*n*n
mov eax, two
mov ecx, n
mul ecx
mov ecx,n
mul ecx
mov a1,eax
ret
n2n ENDP
n3 PROC ; 3n
mov eax, three
mov ecx, n
mul ecx
mov a2,eax
ret
3n ENDP
main PROC
mov edx, offset pr
call WriteString ; Ask user for n
call readInt
mov n, eax ; Store user input in n
call n2n ; call procedure n2n
call 3n ; call procedure n3
mov ebx, a1
add ebx, a2
add ebx, c
mov answer, ebx ; store result in answer
mov edx, offset eq
call WriteString
mov eax, answer
call WriteDec
call WaitMsg
exit
main ENDP
END main
I've been working for days on an assembler subroutine to call Windows API function FormatMessageA, and I think I must have some systematic misunderstanding. My routine is shown below. I have traced it in debug mode, and I know that at the time the function is called: rcx has hex 1B00, which is the flag values I specified (dwFlags); rdx has hex 33, which is the bogus initial handle value that I plugged in to provoke the error (lpSource); r8 has 6, which is the error message number that was returned by GetLastError, which equates to ERROR_INVALID_HANDLE (dwMessageId); r9 has 0, for default language (dwLanguageId); rsp+32 has the address of msgptrp, which is my area to receive the address of the error message as allocated by the called function (lpBuffer); rsp+40 has hex 28, or decimal 40, which is the minimum number of characters I arbitrarily specified for the error message (nSize); and rsp+48 has the address of argmntsp, which according to the documentation should be ignored with the flags I specified (*Arguments).
If there's a problem, and obviously there is since rax is returned as zero, I suspect that it has to do with lpBuffer, which the documentation says has data type LPTSTR, whatever that is. Sometimes I want to shout, "Okay, that's what it is in terms of C++, but what is it really? I hope someone can easily spot where I ran off the rails, because I'm at my wits' end with this, and it's something I need to get working in order to do effective error checking for my future endeavors.
goterr PROC
;
.data
; flag values used:
; hex 00000100 FORMAT_MESSAGE_ALLOCATE_BUFFER
; hex 00000200 FORMAT_MESSAGE_IGNORE_INSERTS
; hex 00000800 FORMAT_MESSAGE_FROM_HMODULE
; hex 00001000 FORMAT_MESSAGE_FROM_SYSTEM
flagsp dd 00001B00h ; those flags combined
saveinitp dq ?
bmaskp dq 0fffffffffffffff0h
savshadp dq ?
msgptrp dq ?
handlep dd ?
argmntsp dd ?
;
.code
mov saveinitp, rsp ; save initial contents of stack pointer in this proc
sub rsp, 56 ; shadow space (max of 7 parameters * 8)
and rsp, bmaskp ; make sure it's 16 byte aligned
mov savshadp, rsp ; save address of aligned shadow area for this proc
;
mov handlep, ecx ; save passed I/O handle value locally
;
call GetLastError ; get the specific error
;
mov rsp, savshadp ; shadow area for this proc
mov ecx, flagsp ; flags for Windows error routine
mov edx, handlep ; handle passed from caller
mov r8d, eax ; msg id from GetLastError in low order dword
mov r9, 0 ; default language id
lea rax, msgptrp ; pointer to receive address of msg buffer
mov [rsp+32], rax ; put it on the stack
mov rax, 40 ; set lower doubleword to minimum size for msg buffer
mov [rsp+40], rax ; put it on the stack
lea rax, argmntsp ; variable arguments parameter (not used)
mov [rsp+48], rax ; put it on the stack
call FormatMessageA ; if rax eq 0 the called failed, otherwise rax is no chars.
mov rsp, saveinitp ; restore initial SP for this proc
ret
goterr ENDP
I am tasked with writing my first assembly code which takes a number between 1-12 entered by the user and outputs the factorial. I need to write 3 procedures one for the input, one to calculate factorial and one to print the result. I believe I have written it correctly (I didn't) but after getting the input from the user, the program terminates. I never see the result of my "Print" procedure. When trying to debug my code I get the error:
Program received signal SIGSEGV, Segmentation fault.
This error comes right after the first step of "invoke input"
Here is my complete code :
include c:\asmio\asm32.inc
includelib c:\asmio\asm32.lib
includelib c:\asmio\User32.lib ; SASM files for I/O
includelib c:\asmio\Kernel32.lib ; SASM files for I/O
input proto ; 0 parameters
Factorial proto nvx: dword ; 1 parameter
Print proto nax: dword, nf: dword ; 2 parameters
; -------------------------------------------------------
.const ; Section to declare and initialize constants
NULL = 0
; -------------------------------------------------------
.data ; Section to declare and initialize variables
nvx dword ?
nfx dword ?
nf dword ?
ask byte "Enter a number between 1-12: ", NULL
fin byte "! is ", NULL
; -------------------------------------------------------
.code ; The actual code begins here: Main program
main proc ; Just like C++ this is the main program
start:
invoke input
mov nvx, eax
invoke Factorial, nvx
mov nfx, eax
invoke Print, nvx, nfx
ret 0 ; need this line to return to caller
main endp ; End of the procedure main
; -------------------------------------------------------
input proc
mov edx, OFFSET ask
call WriteString
call ReadInt
ret
input endp
; -------------------------------------------------------
Factorial proc USES ECX EBX nv: dword
mov ecx, nv ;start loop counter at value given by user because we need to multiply this many times to find the factorial.
mov ebx, nv ; hold value of nv as divisor
inc nv ; This is to account for 0! We increment by one and after the loop divide by nv
mov eax, nv ; stores the largest number for multiplication
L1:
dec nv ; becomes next largest number
mul nv ; multiplication
mov eax, edx ; stores product into eax for next multiplication
loop L1
inc ebx
cdq
idiv ebx ; divide final product by original number + 1 for the correct factorial
ret
Factorial endp
Print proc nax: dword, na: dword
mov eax, nvx
call WriteString
mov eax, OFFSET fin
call WriteString
mov eax, na
call WriteString
ret
Print endp
end main ; End of the entire program
; -------------------------------------------------------
I want to call a function that will perform upper to lower case conversion to a user typed string, preserving the especial characters. This part works, but only for the first 4 characters, everything after that just gets truncated. I believe it is because I have defined the parameters as DWORD:
I have tried using PAGE, PARA and BYTE. The first two don't work and with byte says type missmatch.
upperToLower proc, source:dword, auxtarget:dword
mov eax, source ;Point to string
mov ebx, auxtarget ; point to destination
L1:
mov dl, [eax] ; Get a character from buffer
cmp byte ptr [eax], 0 ; End of string? (not counters)
je printString ; if true, jump to printString
cmp dl, 65 ; 65 == 'A'
jl notUpper ; if less, it's not uppercase
cmp dl, 90 ; 90 == 'Z'
jg notUpper ; if greater, it's not uppercase
xor dl, 100000b ; XOR to change upper to lower
mov [ebx], dl ; add char to target
inc eax ; Move counter up
inc ebx ; move counter up
jmp L1 ; loop
notUpper: ; not uppercase
mov [ebx], dl ; copy the letter
inc eax ;next letter
inc ebx
jmp L1
printString:
invoke WriteConsoleA, consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten, 0
ret
upperToLower endp
The PROTO:
upperToLower PROTO,
source: dword,
auxtarget: dword
Invoke:
invoke upperToLower, offset buffer, offset target
The buffer parameter is: buffer db 128 DUP(?)
How can I get printed the whole string, and not just the first 4 characters?
Why are only 4 characters being printed? You write the string to the console with:
invoke WriteConsoleA, consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten, 0
The sizeof auxtarget parameter is the size of auxtarget which is a DWORD (4 bytes) thus you are asking to only print 4 bytes. You need to pass the length of the string. You can easily do so by taking the ending address in EAX and subtracting the source pointer from it. The result would be the length of the string you traversed.
Modify the code to be:
printString:
sub eax, source
invoke WriteConsoleA, consoleOutHandle, auxtarget, eax, bytesWritten, 0
A version of your code that follows the C call convention, uses both a source and destination buffer, tests for the pointers to make sure they aren't NULL, does the conversion using a similar method described by Peter Cordes is as follows:
upperToLower proc uses edi esi, source:dword, dest:dword
; uses ESI EDI is used to tell assembler we are clobbering two of
; the cdecl calling convetions non-volatile registers. See:
; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
mov esi, source ; ESI = Pointer to string
test esi, esi ; Is source a NULL pointer?
jz done ; If it is then we are done
mov edi, dest ; EDI = Pointer to string
test edi, edi ; Is dest a NULL pointer?
jz done ; If it is then we are done
xor edx, edx ; EDX = 0 = current character index into the strings
jmp getnextchar ; Jump into loop at point of getting next character
charloop:
lea ecx, [eax - 'A'] ; cl = al-'A', and we do not care about the rest
; of the register
cmp cl, 25 ; if(c >= 'A' && c <= 'Z') c += 0x20;
lea ecx, [eax + 20h] ; without affecting flags
cmovna eax, ecx ; take the +0x20 version if it was in the
; uppercase range to start with
mov [edi + edx], al ; Update character in destination string
inc edx ; Go to next character
getnextchar:
movzx eax, byte ptr [esi + edx]
; mov al, [esi + edx] leaving high garbage in EAX is ok
; too, but this avoids a partial-register stall
; when doing the mov+sub
; in one instruction with LEA
test eax, eax ; Is the character NUL(0) terminator?
jnz charloop ; If not go back and process character
printString:
; EDI = source, EDX = length of string
invoke WriteConsoleA, consoleOutHandle, edi, edx, bytesWritten, 0
mov edx, sizeof buffer
done:
ret
upperToLower endp
A version that takes one parameter and changes the source string to upper case could be done this way:
upperToLower proc, source:dword
mov edx, source ; EDX = Pointer to string
test edx, edx ; Is it a NULL pointer?
jz done ; If it is then we are done
jmp getnextchar ; Jump into loop at point of getting next character
charloop:
lea ecx, [eax - 'A'] ; cl = al-'A', and we do not care about the rest
; of the register
cmp cl, 25 ; if(c >= 'A' && c <= 'Z') c += 0x20;
lea ecx, [eax + 20h] ; without affecting flags
cmovna eax, ecx ; take the +0x20 version if it was in the
; uppercase range to start with
mov [edx], al ; Update character in string
inc edx ; Go to next character
getnextchar:
movzx eax, byte ptr [edx] ; mov al, [edx] leaving high garbage in EAX is ok, too,
; but this avoids a partial-register stall
; when doing the mov+sub in one instruction with LEA
test eax, eax ; Is the character NUL(0) terminator?
jnz charloop ; If not go back and process character
printString:
sub edx, source ; EDX-source=length
invoke WriteConsoleA, consoleOutHandle, source, edx, bytesWritten, 0
done:
ret
upperToLower endp
Observations
A generic upperToLower function that does the string conversion would normally not do the printing itself. You'd normally call upperToLower to do the conversion only, then you'd output the string to the display in a separate call.
I'm new to writing assembly code and I'm having trouble printing out the values of my array using a loop. The code I have prints out the value of the counter and the not the values in the array, can someone please explain what I'm doing wrong, also how do I point to the top of the array? I have tried using different registers, but nothing seems to be working. My professor asks that I do it this way (if it seems inefficient):
.386
.model flat
ExitProcess PROTO NEAR32 stdcall, dwExitCode:dword
Include io.h
cr equ 0DH
Lf equ 0AH
.stack 4096
.data
newline byte CR, LF, 0
whitespace byte 32,32,0
arr dword 10 dup(?)
n dword 2
string byte 40 dup(?)
prompt byte "Please enter a value: ", 0
origArr byte "Original Array", 0
.code
_start:
mov ecx,n ; number of values in the array
lea ebx,arr ; address of the array
sub edi, edi
top: cmp ecx, 0
je done
output prompt
input string, 40
atod string
mov [arr+edi], ecx
add edi, 4
loop top
done: output origArr
mov ecx, n
call myproc
INVOKE ExitProcess, 0
PUBLIC _start
myproc proc near32
.data
val_str byte 11 dup(?), 0
.code
push eax
push edi
push ecx
sub edi,edi ; index register
top2: mov eax, [ebx+edi]
dtoa val_str, eax
output val_str
add edi,4 ; modify esi rather than ebx
loop top2
pop ecx
pop edi
pop eax
ret
myproc endp
END
Any suggestions are appreciated.
mov [arr+edi], ecx
You're storing the loop counter, rather than the return value of atod.