NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: animefuns on April 01, 2012, 08:37:32 PM
-
I am trying to compare an input data with a integer value
here the basic code I am using
section .bss
input resb 2
section .text
global _start
_start:
mov eax, 3
mov ebx, 1
mov ecx, input
mov edx, 5
int 0x80
mov eax, [input]
cmp eax, 20 ; This is what I cannot get to work, it never compares it to 20 even if i enter 20 as input
je next
All I really want to know is how to do the If statement in Assembly to compare input with an integer.
I would really apprecaite any help with this, thank you.
-
Well, your basic problem is that "input" is characters. These characters are integers, of course, but they aren't the integers the characters represent. For example, if the user enters "20" (which I guess is what you're looking for), what's in the buffer is 0x32 ("2"), 0x30 ("0")... and a linefeed (0xA) 'cause sys_read doesn't return until it sees one.
section .bss
input resb 2
section .text
global _start
_start:
mov eax, 3
mov ebx, 1
mov ecx, input
mov edx, 5
int 0x80
Whoa! You've only reserved two bytes for your buffer and allowed the user to put (up to) five bytes into it. That's a "buffer overflow"! Very bad! Won't do any harm in this case, since there's nothing after your buffer, but don't do it! How many characters do you actually want to allow the user to enter? A full 32-bit integer will take (up to) 10 characters to represent. You probably want one extra to hold the linefeed. You may want to allow a minus sign, if you're going to handle signed integers. So you want to reserve (at least) that many bytes in your input buffer.
If the user keeps typing more than what you've allowed in edx (up until "enter" is hit, remember), the excess stays in the OS's input buffer (won't overwrite your buffer - edx bytes maximum), and will be read by the next sys_read - will end up on the command line, in this case. We really should flush the (OS's) input buffer at this point, but to "keep it simple" we can ignore that for now... (this is called "teaching sloppy solutions to beginners" - my bad!)
At this point, eax will contain the number of characters actually entered (including the linefeed). This can be useful to note/save, but we don't really need it.
mov eax, [input]
The input buffer will hold, for this example, say 0x32, 0x30, 0xA. Putting this in eax (as 0x000A3032) is not too useful. What you want to do is get one character at a time, subtract '0' (0x30, 48 decimal) to convert it from a character to the number it represents, multiply a "result so far" by ten, and add in the number... until done.
Since we want the result in eax, we might as well use that as a "result so far" - zero it first. Since ecx already points to our input buffer, we might as well use that. We want a full 32-bit register to add to our "result so far", but we only want a byte in it, with the upper bits zeroed. "movzx" will do that, but we can do it another way...
xor eax, eax ; (or mov eax, 0) zero "result so far"
xor ebx, ebx ; zero upper bits of ebx for our character
mov esi, 10 ; for a multiplier
top:
mov bl, [ecx] ; get a character
inc ecx ; get ready for the next one
cmp bl, '0' ; is it a valid decimal digit?
jb done
cmp bl, '9'
ja done
mul esi ; eax * esi -> edx:eax
add eax, ebx ; valid digit - add to "result so far"
jmp top ; do another
done:
; now you can compare integer to integer!
cmp eax, 20 ; This is what I cannot get to work, it never compares it to 20 even if i enter 20 as input
je next
That's untested and probably has errors - I'm kinda distracted just now - but that's the general idea...
Best,
Frank
-
Very distracted :)
AnimeFuns, as Frank has pointed out, the difficulty is that your program is attempting to perform three distinct tasks. You want your program to accept character input, and then test for a decimal value represented by those characters.
This requires these steps:
1. Read a string of characters.
2. Converting that string of characters into a number.
3. Compare that number with another number.
The third step is trivial. Getting there is not so simple.
Again, as Frank has indicated, the system call fills a memory buffer with byte values representing the characters entered, in the order they are entered. He has also given an idea of how that works.
He then put you on the correct track to tackle the second step, where you will convert that string into a number. That's when he must have gotten distracted.
There are a handful of way numbers may be represented in memory. The easiest to work with are Integers, and between signed and unsigned Integers, unsigned Integers are simplest.
Converting the characters to Integer involves converting each character code value to a digit, and then multiplying each digit by a power of ten according to its position. The conversion of the character code to a digit is pretty simply, because the ASCII codes for the digit characters are all in order. We simply need to subtract the value of "0" (which is 0x30 in hexadecimal, 48 is decimal).
Multiplying each digit is pretty simple, but the problem is that we need to know the position of the digit starting from the RIGHT side of the string. The first character in the memory buffer, however, is on the LEFT side of the string.
One approach is to begin by converting each character into a digit value and save that value on the stack, counting digits are we go. When the conversion of each character is complete, we know how many digits we have and the LAST digit is ready to be popped off the stack. Now, we can simply remove each digit from the stack and multiply it by a power of ten, starting with 1, and collect a running sum of the results. After each digit, we multiply our multiplier by ten to get the next power of ten.
There are a few complications. We have to take into account that if the string represents a number larger than 2^(32-1), our result will be incorrect due to overflow. The other issue is that we must be absolutely certain that we leave the stack in the same condition that we found it.
; This assumes that you have a string stored and have determined
; its maximum possible length.
MOV EDI, string
MOV ECX, [maxLength] ; Set loop counter
XOR EBX, EBX ; Use EBX to count characters
XOR EAX, EAX ; Clear all bits in EAX
.ReadDigits
MOV AL, byte [EDI] ; Get a character
INC EDI ; Point to the next character
CMP AL, '0' ; Test for numerical
JB .DigitsRead
CMP AL, '9'
JA .DigitsRead
PUSH EAX ; Place on stack
INC EBX
LOOP .ReadDigits ; Repeat
.DigitsRead:
MOV ECX, EBX ; Set loop counter
XOR EBX, EBX ; Clear sum
TEST ECX, ECX ; Test for no characters
JZ .Done
MOV ESI, 1 ; Set multiplier to 1
.Convert:
POP EAX ; Get digit from stack
MUL ESI ; Multiply
TEST EDX, EDX ; If EDX does not = 0, an overflow occurred
JNZ .overflow
ADD EBX, EAX ; Accumulate result
jc .Overflow ; If carry flag is set, an overflow occured
MOV EAX, 10 ; Use base 10 for decimal notation
MUL ESI ; Set multiplier to multiplier * base
MOV ESI, EAX
LOOP .Convert
MOV EAX, EBX ; Using EAX to hold result
JMP .Done
.Overflow:
POP EAX ; cleanup stack
LOOP .Overflow
; Here we'll just set the
; value to maximum. A more
; robust program would some
; how indicate that an error
; occurred.
MOV EAX, 0xFFFFFFFF
.Done:
;Integer value is now in EAX
-
Yeah, very distracted. Left out a vital step: subtracting '0'! Thanks CodeFoil!
Best,
Frank
-
Yeah, very distracted. Left out a vital step: subtracting '0'! Thanks CodeFoil!
Best,
Frank
I swear mine got left in the clip board!
Other wise, your approach is superior. My intention was to break the task down into simpler steps. Once each is understood, it's just one more step to interleave them to arrive at your code. Shuffling around, stack storage is eliminated and the conversion can be done from first to last.
; This assumes that you have a string stored and have determined
; its maximum possible length.
XOR EAX, EAX ; Set initial digit value to zero
MOV ECX, [maxLength] ; Set loop counter
TEXT ECX, ECX ; check for no characters
JZ .Done
MOV EDI, string
MOV ESI, 10 ; Set base
XOR EBX, EBX ; Use EBX to accumulate sum
.ReadandConvert:
MUL EBX, ESI ; Multiply current sum by base
MOV EBX, EAX ; and save
TEST EDX, EDX ; Check for overflow
JNZ .OverFlow
XOR EAX, EAX ; Clear all bits in EAX
MOV AL, byte [EDI] ; Get a character
INC EDI ; Point to the next character
CMP AL, '0' ; Test for numerical
JB .ReadAndConverted
CMP AL, '9'
JA .ReadAndConverted
SUB AL, '0' ; Adjust character code to digit value
ADD EBX, EAX ; and add to accumulated value
JC .Overflow ; Check for overflow
LOOP .ReadAndConvert ; Repeat
MOV EAX, EBX
JMP .Done
.ReadandConverted:
.Overflow:
; Here we'll just set the
; value to maximum. A more
; robust program would some
; how indicate that an error
; occurred.
MOV EAX, 0xFFFFFFFF
.Done: