Entering a non-integer into an unsigned integer using scanf in NASM causing problems in loops - nasm

Whenever I try entering a letter or symbol into my scanf for unsigned integers after previously entering an accepted number previously in the loop, it continually enters the previous number, causing the program to run indefinitely. How do I fix this? Here's an example code I wrote:
[bits 32]
global _main
extern _scanf
extern _printf
section .data
prompt_number db "Please select a number. Enter 0 to escape. ", 0
display_number db "You entered %u. ", 0
number_fmt db "%u", 0
number_input dd 0
section .bss
section .text
_main:
push ebp
mov ebp, esp
and esp, 0xfffffff0
_Label:
sub esp, 16
mov dword[esp], prompt_number
call _printf
add esp, 16
sub esp, 16
mov dword[esp], number_fmt
mov dword[esp+4], number_input
call _scanf
add esp, 16
sub esp, 16
mov dword[esp], display_number
mov eax, [number_input]
mov dword[esp+4], eax
call _printf
add esp, 16
cmp dword[number_input], 0
jne _Label
mov esp, ebp
mov eax, 1
pop ebp
ret

This is to do with how scanf operates in these circumstances, and a solution is to port one of the suggested solutions for Good way to flush scanf buffer when invalid entry entered from C.
My understanding is that if scanf can't map the input into the format string (in this case trying to convert an arbitrary character into an unsigned integer), then it gives up but doesn't remove anything from the input buffer. Thus, in this case, subsequent calls to scanf just use what is currently in the buffer.
Below is a quick attempt at porting the first solution (using getchar to consume the buffer). I can't vouch for its quality; this is basically my first run through programming assembly:
[bits 32]
global _main
extern _getchar
extern _scanf
extern _printf
section .data
prompt_number db "Please select a number. Enter 0 to escape. ", 0
display_number db "You entered %u. ", 0
scan_error db "Error scanning input. ", 0
number_fmt db "%u", 0
number_input dd 0
section .bss
section .text
_main:
push ebp
mov ebp, esp
and esp, 0xfffffff0
_Label:
sub esp, 16
mov dword[esp], prompt_number
call _printf
add esp, 16
sub esp, 16
mov dword[esp], number_fmt
mov dword[esp+4], number_input
call _scanf
add esp, 16
cmp eax, 0
jne _ScanSuccess
_ScanError:
sub esp, 16
mov dword[esp], scan_error
call _printf
add esp, 16
_ScanErrorConsumeInput:
sub esp, 16
call _getchar
add esp, 16
cmp eax, -1 ; most common value indicating end-of-file
je _Label
cmp eax, 10 ; newline in ASCII
je _Label
jmp _ScanErrorConsumeInput
_ScanSuccess:
sub esp, 16
mov dword[esp], display_number
mov eax, [number_input]
mov dword[esp+4], eax
call _printf
add esp, 16
cmp dword[number_input], 0
jne _Label
_Return:
mov esp, ebp
mov eax, 1
pop ebp
ret

Related

Assembly NASM x86 - Simple Stack Project

I'm writing a subroutine to simply reprint decimal numbers as strings using the stack, but not getting the values I expected. When I run it through the debugger I see that I can't get the value from esi into al. I suspect that I'm not allowed to use esi in the manner that I am, but I'm not sure on another way I can do this. Also, I am not allowed to push the elements I'm storing in edx onto the stack.
Subroutine code:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
%define BUFLEN 256
SECTION .bss ; uninitialized data section
src_str: resb BUFLEN ; buffer for backwards number
dec_str: resb BUFLEN ; number will be converted and put in this buffer
rlen: resb 4 ; length
SECTION .text ; code begins here
global prt_dec
; Begin subroutine
prt_dec:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov eax, [esp + 28] ; store the decimal number 4 bytes each for each push, plus the eip
mov esi, src_str ; point esi to the backwards string buffer
mov edi, dec_str ; point edi to the new buffer
mov ebx, 10 ; stores the constant 10 in ebx
div_loop:
mov edx, 0 ; clear out edx
div ebx ; divide the number by 10
add edx, '0' ; convert from decimal to char
mov [esi], edx ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
cmp eax, 0 ; is there anything left to divide into?
jne div_loop ; if so, continue the loop
output_loop:
add esi, ecx ; move 1 element beyond the end of the buffer
mov al, [esi - 1] ; move the last element in the buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
cmp ecx, 0 ; if it doesn't equal 0, continue loop
jne output_loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
mov edx, 0
mov edx, rlen
int 080h
pop_end:
pop edi ; move the saved values back into their original registers
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret
; End subroutine
Driver:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
SECTION .data ; initialized data section
lf: db 10 ; just a linefeed
msg1: db " plus "
len1 equ $ - msg1
msg2: db " minus "
len2 equ $ - msg2
msg3: db " equals "
len3 equ $ - msg3
SECTION .text ; Code section.
global _start ; let loader see entry point
extern prt_dec
_start:
mov ebx, 17
mov edx, 214123
mov edi, 2223187809
mov ebp, 1555544444
push dword 24
call prt_dec
add esp, 4
call prt_lf
push dword 0xFFFFFFFF
call prt_dec
add esp, 4
call prt_lf
push 3413151
call prt_dec
add esp, 4
call prt_lf
push ebx
call prt_dec
add esp, 4
call prt_lf
push edx
call prt_dec
add esp, 4
call prt_lf
push edi
call prt_dec
add esp, 4
call prt_lf
push ebp
call prt_dec
add esp, 4
call prt_lf
push 2
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 080h
push 3
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 5
call prt_dec
add esp, 4
call prt_lf
push 7
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 080h
push 4
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 3
call prt_dec
add esp, 4
call prt_lf
; final exit
;
exit: mov EAX, SYSCALL_EXIT ; exit function
mov EBX, 0 ; exit code, 0=normal
int 080h ; ask kernel to take over
; A subroutine to print a LF, all registers are preserved
prt_lf:
push eax
push ebx
push ecx
push edx
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, lf
mov edx, 1 ; LF is a single character
int 080h
pop edx
pop ecx
pop ebx
pop eax
ret
Fixes I had on mind (asterisk denotes lines I did touch), hopefully it will be clear from comments what I did:
...
div_loop:
* xor edx, edx ; clear out edx
div ebx ; divide the number by 10
* add dl, '0' ; convert from decimal to char
* mov [esi], dl ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
* test eax,eax ; is there anything left to divide into?
* jnz div_loop ; if so, continue the loop
* ; (jnz is same instruction as jne, but in this context I like "zero" more)
* mov [rlen], ecx ; store number of characters into variable
output_loop:
* ; esi already points beyond last digit, as product of div_loop (removed add)
* dec esi ; point to last/previous digit
mov al, [esi] ; move the char from the div_loop buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
* jnz output_loop ; if it doesn't equal 0, continue loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
* mov edx, [rlen] ; read the number of digits from variable
int 080h
...

FInding length of String in NASM

I'm trying to check the length of a sting given in the program argument as part of a larger program. I need to place the value of the string length in a variable called n, which I've left uninitialized in BSS. I've tried a couple different methods of doing this, including the one Im trying right now which was given in another answer here. However when I try to print or use the result of this little algorithm, it always returns 7. I believe I'm getting the address of what I want and not the result but I cant figure what to change. Heres my code:
%include "asm_io.inc"
SECTION .data
fmt1: db "%d",10,0
argErrMsg: db "Needs 2 args",10,0
argOkMsg : db "Arguments ok",10,0
doneMsg: db "Program finished, now exiting",10,0
strErrMsg: db "String must be between 1 and 20 charaters",10,0
strOkMsg: db "String length ok",10,0
SECTION .bss
X: resd 20
i: resd 1
n: resd 1
k: resd 1
SECTION .text
global asm_main
extern printf
extern strlen
asm_main:
enter 0,0
pusha
CHECK_ARGS:
cmp dword [ebp+8],2
jne ERROR_ARGS
je OK_ARGS
ERROR_ARGS:
push dword argErrMsg
call printf
add esp,8
jmp EXIT
OK_ARGS:
push dword argOkMsg
call printf
add esp,8
jmp CHECK_STRING
CHECK_STRING:
mov eax, dword[ebp+16]
push eax ;This is the code I tried using from another answer
mov ecx,0
dec eax
count:
inc ecx
inc eax
cmp byte[eax],0
jnz count
dec ecx
ret
pop eax
mov [n],ecx ;Tried prining it here to see the result
push dword [n]
push dword fmt1
call printf
add esp,8
cmp byte [n],1
jl ERROR_STRING
cmp byte [n],20
jg ERROR_STRING
jmp OK_STRING ;The program always gets to here since [n] = 7?
ERROR_STRING:
push dword strErrMsg
call printf
add esp,8
jmp EXIT
OK_STRING:
push dword strOkMsg
call printf
add esp,8
jmp EXIT
EXIT:
push dword doneMsg
call printf
popa
leave
ret
To get the length of argv[1]:
mov edi,[ebp+12] ;edi = argv = &argv[0]
mov edi,[edi+4] ;edi = argv[1]
mov ecx,0ffffh ;scan for 0
xor eax,eax
repne scasb
sub ecx,0ffffh ;ecx = -(length + 1)
not ecx ;ecx = length
main should return a 0 in eax:
popa
xor eax,eax
leave
ret

Intel-base Assembly Language Segmentation Fault

Every time I try to run my code I get a Segmentation Fault. Can anyone point me in the right direction at what can be causing this?
Compiler is on Linux that I'm running the code from via PuTTY.
I think it has to do with mov dword [esp + #] but don't know about fixing it.
%include "asm_io.inc"
segment .data
display db "Area: %d | Points: %d | Probability: %d/%d",10,0
display2 db "Expected Outsome: %d", 0
radiusone db "Enter number ", 0
radiustwo db "Enter number ", 0
radiusthree db "Enter number ", 0
radiusfour db "Enter number ", 0
pointsone db "Enter number ", 0
pointstwo db "Enter number ", 0
pointsthree db "Enter number ", 0
pointsfour db "Enter number ", 0
segment .bss
r1 resd 1 ;Radius
r2 resd 1
r3 resd 1
r4 resd 1
p1 resd 1 ;Points
p2 resd 1
p3 resd 1
p4 resd 1
ca1 resd 1 ;Computed Area
ca2 resd 1
ca3 resd 1
ca4 resd 1
pi1 resd 1 ;radius*radius
pi2 resd 1
pi3 resd 1
pi4 resd 1
pb1 resd 1 ;Probability
pb2 resd 1
pb3 resd 1
pb4 resd 1
eo resd 1 ; Expected Outcome
segment .text
global asm_main
extern printf
asm_main:
enter 0,0
pusha
mov eax, radiusone
call print_string
call read_int
mov [r1], eax
mov eax, radiustwo
call print_string
call read_int
mov [r2], eax
mov eax, radiusthree
call print_string
call read_int
mov [r3], eax
mov eax, radiusfour
call print_string
call read_int
mov [r4], eax
;************************
mov eax, pointsone
call print_string
call read_int
mov [p1], eax
mov eax, pointstwo
call print_string
call read_int
mov [p2], eax
mov eax, pointsthree
call print_string
call read_int
mov [p3], eax
mov eax, pointsfour
call print_string
call read_int
mov [p4], eax
;************************
mov eax, [r1]
imul eax, [r1]
mov [pi1], eax
mov eax, [r2]
imul eax, [r2]
mov [pi2], eax
mov eax, [r3]
imul eax, [r3]
mov [pi3], eax
mov eax, [r4]
imul eax, [r4]
mov [pi4], eax
;**********************
mov eax, [r1]
mov [ca1], eax
mov eax, [ca2]
sub eax, [pi1]
mov [ca2], eax
mov eax, [ca3]
sub eax, [pi2]
mov [ca3], eax
mov eax, [ca4]
sub eax, [pi3]
mov [ca4], eax
;********************
mov eax, [r1]
imul eax, [p1]
mov [pb1], eax
mov eax, [r2]
imul eax, [p2]
mov [pb2], eax
mov eax, [r3]
imul eax, [p3]
mov [pb3], eax
mov eax, [r4]
imul eax, [p4]
mov [pb4], eax
;***********************
mov eax, [pb1]
add eax, [pb2]
add eax, [pb3]
add eax, [pb4]
mov [eo], eax
;************************
sub easp 10h
push dword [pi4]
push dword [ca1]
push dword [p1]
push dword [r1]
mov dword [esp], display
call printf
add esp, 10h
popa
mov eax, 0
leave
ret
Update: I added changes to the code with the use of pop function down where the call is and now it does get rid of the segmentation fault and I now get output, however, not the values I'd like.
output:
Area: 134520364 | Points: 134520380 | Probability: 134520396/134520424
Area: 134520260 | Points: 134520276 | Probability: 134520292/134520320
Area: 134520260 | Points: 134520276 | Probability: 134520292/134520320
when it should be
Area: 1 | Points: 17 | Probability: 1/64
I don't have a loop set so I'm unsure why 3 lines have been printed.
UPDATE2:
Made changes to the push by suggestion made I know get output looking better...
Area: 17 | Points: 1 | Probability: 64/134519817
Even though should be:
Area: 1 | Points: 17 | Probability: 1/64
how I have it in stack
1 , 17 , 1 , 64.... and my string for it is:
display db "Area: %d | Points: %d | Probability: %d/%d",10,0
So it looks like they are randomly placed
Do I need to add the mov dword [esp + 4], display ?
The seg fault comes from improperly exiting your program. ret is not the proper way to exit your program in Linux or Windows. Windows it is ExitProcess and Linux is is system call, or a call to exit from the C library. In your case, you are linking to the C Library to use printf and gcc will add startup code that gets run before your code, so you must call exit to properly terminate your program.
There are others issues, but this will fix the seg fault. Also, do as mbratch mentioned and pass parameters by pushing and adjusting esp after the call to printf
First off, the mov dword [esp + #], ... lines are overwriting what the push ... lines do, so you should get rid of those. Second, those numbers you're outputting look like addresses - indeed, when you do i.e. push pi4, you're pushing the address of the pi4 variable. Instead, you should push the contents of the variable, with push dword [pi4].

NASM: adding numbers of the main diagonal in a table

The program i am writing aims to take in a table from text file. The table is in the following format: The table is NxN, and the first line is the number, N. Each row of the table is then included on its own line. Therefore, the file has N + 1 lines.
The program should read in the table, and grab the numbers along the diagonal, going from top left to bottom right, and add them together, outputting the result to screen.
Currently, i am working on a procedure which takes as input the buffer which holds the row of numbers, along with which number the user wishes to retrieve. The intent is to return this in eax. However, it seems that this procedure currently causes a segfault. I have looked over my code and it seems to make sense to me. Below is both a sample table file and my source.
hw6_1.dat
5
2 45 16 22 4
17 21 67 29 65
45 67 97 35 87
68 34 90 72 7
77 15 105 3 66
hw6_1.asm
; this program demonstrates how to open files for reading
; It reads a text file line by line and displays it on the screen
extern fopen
extern fgets
extern fclose
extern printf
extern exit
global main
segment .data
readmode: db "r",0
filename: db "hw6_1.dat",0 ; filename to open
error1: db "Cannot open file",10,0
format_1: db "%d",10,0
format_2: db "%s",10,0
segment .bss
buflen: equ 256 ; buffer length
buffer: resd buflen ; input buffer
tempBuff: resd buflen
segment .text
main:
pusha
; OPENING FILE FOR READING
push readmode ; 1- push pointer to openmode
push filename ; 2- push pointer to filename
call fopen ; fopen retuns a filehandle in eax
add esp, 8 ; or 0 if it cannot open the file
cmp eax, 0
jnz .L1
push error1 ; report an error and exit
call printf
add esp, 4
jmp .L4
; READING FROM FILE
.L1:
mov ebx, eax ; save filepointer of opened file in ebx
; Get first line and pass to ecx
push ebx
push buflen
push buffer
call fgets
add esp, 12
cmp eax, 0
je .L3
;convert string -> numeric
push buffer
call parseInt
mov ecx, eax
.L2:
push ecx
push ebx ; 1- push filehandle for fgets
push dword buflen ; 2- push max number of read chars
push buffer ; 3- push pointer to text buffer
call fgets ; get a line of text
add esp, 12 ; clean up the stack
cmp eax, 0 ; eax=0 in case of error or EOF
je .L3
push buffer ; output the read string
call printf
add esp, 4
push dword 2
push buffer
call grabNum ;Get the 3rd number in the current line. Space delimited.
;do somehing with the number. For now, lets just output to screen.
push eax
push format_1
call printf
add esp, 8
pop ecx
dec ecx
cmp ecx, 0
jg .L2
;CLOSING FILE
.L3:
push ebx ; push filehandle
call fclose ; close file
add esp, 4 ; clean up stack
.L4:
popa
call exit
parseInt:
push ebp
mov ebp, esp
push ebx
push esi
mov esi, [ebp+8] ; esi points to the string
xor eax, eax ; clear the accumulator
.I1:
cmp byte [esi], 48 ; end of string?
jl .I2
mov ebx, 10
mul ebx ; eax *= 10
xor ebx, ebx
mov bl, [esi] ; bl = character
sub bl, 48 ; ASCII conversion
add eax, ebx
inc esi
jmp .I1
.I2:
pop esi
pop ebx
pop ebp
ret 4
grabNum:
;This method will grab a specified number in a sequence.
;Ex: passed in is buffer and the number 4. The 4th number will be
;returned. It is assumed to be a space delimited buffer.
mov esi, [esp + 4]
mov ecx, [esp + 8]
dec ecx
.skipNum:
;for each number in ecx, advance past a number in esi.
;this is done by decrementing ecx each time a "non-digit" is detected.
;Since the buffer is known to be space delimted, this is a valid strategy.
cmp ecx, 0
je .doneSkipping
cmp byte [esi], 48
jl .numSkipped
cmp byte [esi], 57
jg .numSkipped
inc esi
jmp .skipNum
.numSkipped:
inc esi
dec ecx
jmp .skipNum
.doneSkipping:
;now we grab the number from buffer in its ASCII form. We place it in tempBuff,
;and call parseInt. This should leave the number in integer form waiting in eax
;after the end of the grabNum call.
cmp byte [esi + 1 * ecx], 48
jl .retGrab
cmp byte [esi + 1 * ecx], 57
jg .retGrab
mov ebx, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], ebx
inc ecx
jmp .doneSkipping
.retGrab:
mov [tempBuff + 1 * ecx], byte 0
push tempBuff
call parseInt
ret 8
To be precise, the program prints out "45", the second number in the first row, as i intend at the moment, but seems to throw the segfault before the second line can be output to the screen.
Accidently used EBX register to hold some temporary data. This trashed the file handle and caused a segfault. EAX was used to hold this data instead, which fixes the issue!
To solve this problem I modified these 2 lines:
mov ebx, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], ebx
to be:
mov eax, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], eax

Calling sys_read two times [Nasm, Linux]

Twice i try to read character from input, the first time "call getUserAction" works fine, but the main problem is, that the system didn't call the second read func, it just exits. Here is a little part of my code
SECTION .bss
buffer resb 1
SECTION .text
global _start
_start:
...some unuseful and commented code
call getUserAction ;reading 1 byte from console, works fine
...
jmp count
call exit
count:
...
call getUserAction; don't work, just exits from program without err
...commented code to debug
call exit
getUserAction: ;proc to read from console 1 byte
mov eax, 3
mov ebx, 0
mov ecx, buffer
mov edx, 1
int 80h
ret
Also i tried to put code from getUserAction in "count" proc, but it changes nothing.
UPD: Answer on first comment:
After the second "getUserAction":
mov eax, [buffer]
cmp eax, 'u'
je setUsersValues
cmp eax, 'e'
je callFunc
call exit
UPD2: I'm so sorry, here is all code
%define newline 10,0
%define A1 -7
%define A2 3
%define A3 2
%define A4 4
%define A5 2
SECTION .data
labInfo db "============Lab_3============",newline
labInfoLen equ $ - labInfo
mainMenu db "Choose the ex:",newline,\
" r - call count",newline,\
" t - call beep", newline,\
" y - call exit func",newline
mainMenuLen equ $ - mainMenu
funcStr db "Here func func func", newline
funcStrLen equ $ - funcStr
countPromt db "Please,choose the variant of variables value",newline,\
" u - user defined values", newline,\
" e - execerciese defined values", newline,\
"Your choise:"
promtLen equ $ - countPromt
SECTION .bss
buffer resb 1
resultValue resb 1
%macro calculateFunc 5
push eax
push edx
push ecx
mov eax, %1
mov ecx, %2
add eax, ecx
mov ecx, %3
imul ecx
mov ecx, %4
xor edx, edx
idiv ecx
mov ecx, %5
add eax, ecx
mov [resultValue], eax
pop ecx
pop edx
pop eax
%endmacro
SECTION .text
global _start
_start:
;call showPromt
push labInfo
push labInfoLen
call printStr
add esp, 8
;call showVarsList
push mainMenu
push mainMenuLen
call printStr
add esp, 8
call getUserAction
;get get get action
mov eax, [buffer]
cmp eax, 'r'
je count
cmp eax, 't'
je beep
cmp eax, 'y'
je exit
jmp _start
count:
;showFuncInfo
push funcStr
push funcStrLen
call printStr
add esp, 8
;showProposal
push countPromt
push promtLen
call printStr
add esp, 8
call getUserAction
mov eax, [buffer]
cmp eax, 'u'
je setUsersValues
cmp eax, 'e'
je callFunc
call exit
ret
setUsersValues:
nop; add some code later
ret
callFunc:
calculateFunc A1,A2,A3,A4,A5
add byte [resultValue], '0'
push resultValue
push 1
call printStr ; print Result
add esp, 8
ret
printStr:
push ebp
mov ebp, esp
mov eax, 4
mov ebx, 1
mov ecx, [ebp+12]
mov edx, [ebp+8]
int 80h
pop ebp
ret
getUserAction:
mov eax, 3
mov ebx, 0
mov ecx, buffer
mov edx, 1
int 80h
ret
beep:
nop;add some code later
ret
exit:
mov eax, 1
xor ebx, ebx
int 80h
without seeing all the code, your buffer should be at least 2 bytes. the size for both sys_read and sys_write should be 2 not 1.

Resources