Hi everybody,
I am a beginner that started MASM coding after I speed-read "The Art of Assembly Language" and "Introduction to 80x86 assembly language". Speed-read means I scooped interesting topics, read some 500 pages and so on. My problem is that I understand much better when I get to code then when I read page after page.
So, I am trying to build a basic calculator available to perform the 4 basic operands (+,-,*,/). So far, It's been going over exceptions, but not without some questions popping up, so here it goes:
; assume all the necessary includes are done and so on.
.data?
var1 dd, 0
var2 dd, 0
.code
start:
mov var1, sval(input("Enter the first number: "))
mov var2, sval(input("Enter the second number: "))
print chr$(13,10)
print str$(var1)
print chr$(' ')
print str$(var2)
print chr$(13,10,13,10)
xor eax, eax
add eax, var1
add eax, var2
print str$(eax)
print chr$(13,10,13,10)
mov eax, input("Press 'Enter' to exit. ")
exit
Questions:
1. Is there a way to _easy_ provide some kind of primitive-type check like those in high level languages to see if the user-input truly is an integer? If not, how do we do it the hard way then? We parse the input char by char and check if it is a number between 0-9?
2. I tried to add var1 and var2 to each other, like this: mov var1, var2. It did not compile and I guessed a register was the way to go, now I wonder, why is that so? Why is it not possible to add the two variables without adding them to a register?
3. Any other suggestions to my code?
As soon as I learn how to do type-control I move on to add the other operands and then a way to choose between the operands.
Thank you for your time!
yeah looping through each character in the string to make sure it's 0-9 is a good method.
Quote from: E^cube on July 21, 2010, 06:01:47 PM
yeah looping through each character in the string to make sure it's 0-9 is a good method.
Is there no other way?
If not, would it be a bad approach to learn how to create functions or classes in MASM, create a type-checking function/class in MASM and then re-use that function every time I need to type-check?
2. An assembler creates code to execute whatever the hardware has implemented, and the hardware has no memory to memory operations.
Dave.
Quote from: KeepingRealBusy on July 21, 2010, 06:08:22 PM
2. An assembler creates code to execute whatever the hardware has implemented, and the hardware has no memory to memory operations.
Dave.
Thank you! That answered it.
well - there are MOVS and CMPS string operations :P
as for the integer thing - i would probably parse until i see a decimal and make it be an integer
You are not allowed to say "Thank you." in this forum, nobody else does, why should you be allowed? :wink
Quote from: dedndave on July 21, 2010, 06:11:24 PM
well - there are MOVS and CMPS string operations :P
as for the integer thing - i would probably parse until i see a decimal and make it be an integer
But these are hardware macros that use the esi edi registers to access memory.
Dave.
i am not sure they are microcoded that way
they are native as far back as 8088/8086
at any rate, MOVS is ok - if you are moving a large amount of data
or a very small amount of data if it is "register convenient" and speed isn't critical
otherwise, you can probably write a faster loop :P
CMPS isn't fast at all on newer CPU's - perhaps that indicates that it is microcoded in RISC
Quote from: KeepingRealBusy on July 21, 2010, 06:14:14 PM
You are not allowed to say "Thank you." in this forum, nobody else does, why should you be allowed? :wink
Sorry, but I saw a point in clearing out that I understood the explanation so nobody else tried to explain. It was not just a "Thank you" but also a notification that the questions answer had be found. But sorry, I wont repeat it.
About the MOVS and CMPS, I will google and read the tutorials on them. Always good to know.
Please, please, please observe the "Smiles" before you take someone literally. That was meant to be a funny poke at those that say nothing when you have given them a factual response. I accep and appreciate your Thank you.
Dave.
Quote from: Ani_Skywalker on July 21, 2010, 06:25:23 PM
About the MOVS and CMPS, I will google and read the tutorials on them. Always good to know.
Try \masm32\help\opcodes.chm - better than Google in this case.
There are also some practical hints here (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm).
Welcome to the Forum :thumbu
Quote from: Ani_Skywalker
Is there no other way?
If not, would it be a bad approach to learn how to create functions or classes in MASM, create a type-checking function/class in MASM and then re-use that function every time I need to type-check?
At the assembler (machine code) level you are working with the "bare metal" of the processor, and the processor is pretty stupid. The implementation of the processor has gotten a lot more complicated over the years, but if you look at the original CPU designs from the 1970-80's you will get an idea of the simple state machine, serial processing model.
You have to write subroutines to do even the most trivial task, or use subroutines written by someone else.
Most of the CompSci students coming here have to write routines to convert ASCII digits into binary numbers, convert binary into decimal, add things, compute powers, create RPN calculators, etc.
The normal technique to filter characters is to perform the character input in an edit control and set up a subclass to filter characters. Processing the WM_CHAR message usually does the job fine.
If you are trying to pick it up from the console or command line you have no choice other than taking the string and checking it for non-allowed characters.
Hi guys,
Thank you for all your suggestions. And trust me, I've been reading them all. I read the masm32-editors help-files and looked up the links that jj2007 provided. Also, I've been looking up the movs, movsb and cmps commands.
Still, I am unfortunate to tell you that I'm stuck right where I were yesterday. I can't seem to pick out a byte at a time to compare it's ascii value and see if it is in the range of the ascii code for the 0-9 numbers. There is where I'm stuck at the moment. And if you could get some help from there, I would really appreciate it.
I tried code like the code below, found in the MASM32 Editors help chms. Also tried several other approaches, always with compile errors:
cld ; set direction flag forward
mov esi, source ; put address into the source index
mov edi, dest ; put address into the destination index
mov ecx, ln ; put the number of bytes to copy in ecx
; --------------------------------------------------
; repeat copying bytes from ESI to EDI until ecx = 0
; --------------------------------------------------
rep movsb
If you look to the code above, the first problem with is that I don't get what source and destination should be, if this is to be adapted to my current code.
Source and Destination are simple enough, When you COPY data from one register to another the SOURCE is the data to copy, the DESTINATION is where you copy the data to.
mov eax, 1234
Copy the immediate number 1234 into the EAX register. Source is the number 1234, destination is the EAX register.
Intel assembler notation has the destination first followed by the source (in 2 operand instructions)
add eax, ecx
Add the value in the ECX register to the value in the EAX register.
Quote from: Ani_Skywalker on July 22, 2010, 11:56:54 PM
Hi guys,
Thank you for all your suggestions. And trust me, I've been reading them all. I read the masm32-editors help-files and looked up the links that jj2007 provided. Also, I've been looking up the movs, movsb and cmps commands.
Still, I am unfortunate to tell you that I'm stuck right where I were yesterday. I can't seem to pick out a byte at a time to compare it's ascii value and see if it is in the range of the ascii code for the 0-9 numbers. There is where I'm stuck at the moment. And if you could get some help from there, I would really appreciate it.
I tried code like the code below, found in the MASM32 Editors help chms. Also tried several other approaches, always with compile errors:
cld ; set direction flag forward
mov esi, source ; put address into the source index
mov edi, dest ; put address into the destination index
mov ecx, ln ; put the number of bytes to copy in ecx
; --------------------------------------------------
; repeat copying bytes from ESI to EDI until ecx = 0
; --------------------------------------------------
rep movsb
If you look to the code above, the first problem with is that I don't get what source and destination should be, if this is to be adapted to my current code.
Try:
cld
mov esi, source ; put address into the source index
mov edi, dest ; put address into the destination index
mov ecx, ln ; put the number of bytes to copy in ecx
Again:
lodsb
stosb
or al,al
jz Exit
dec ecx
jnz Again
Exit:
Or to check for value ranges:
cld
mov esi, source ; put address into the source index
mov edi, dest ; put address into the destination index
mov ecx, ln ; put the number of bytes to copy in ecx
Again:
lodsb
stosb
cmp al,'0'
jz Exit
cmp al,'9'
ja Exit
dec ecx
jnz Again
Exit:
Dave.
You should avoid using LODS STOS without the REP prefix as they are very slow. Incremented pointers use less registers and are faster.
they are nice in areas when speed isn't critical, though
they are single byte instructions and they increment or decrement the index register for you :bg
Yes but its sloppy and slow 1980s technology, incremented pointters are simple enough to use, use less registers and are faster. the 8088 is dead ! :bg
we finally found something to disagree on :lol
i don't think it's sloppy
if i wanted to write a special command-line parser.....
how fast and how many times do you need to parse the command-line, anyways ?
LODSB / STOSB are perfect for that kind of loop
they keep the code small when speed is not an issue
.486
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data?
var1 dd, 0
var2 dd, 0
container dd, 0
.code
start:
mov var1, sval(input("Enter the first number: "))
cld
mov esi, var1 ; put address into the source index
mov edi, container ; put address into the destination index
mov ecx, 1 ; put the number of bytes to copy in ecx
Again:
lodsb
stosb
cmp al,'0'
jz Exit
cmp al,'9'
ja Exit
dec ecx
jnz Again
Exit:
mov var2, sval(input("Enter the second number: "))
print chr$(13,10)
print str$(var1)
print chr$(' ')
print str$(var2)
print chr$(13,10,13,10)
xor eax, eax
add eax, var1
add eax, var2
print str$(eax)
print chr$(13,10,13,10)
mov eax, input("Press 'Enter' to exit. ")
exit
end start
The above code does compile but I get an error during running, just after you entered the first number, meaning it starts to bug here:
cld
mov esi, var1 ; put address into the source index
mov edi, container ; put address into the destination index
mov ecx, 1 ; put the number of bytes to copy in ecx
Again:
lodsb
stosb
cmp al,'0'
jz Exit
cmp al,'9'
ja Exit
dec ecx
jnz Again
Exit:
The above code I got by you guys and it looks some those approaches I tried myself. Now, can you guys see why it goes wrong here?
Quote from: hutch-- on July 23, 2010, 12:47:13 AM
Source and Destination are simple enough, When you COPY data from one register to another the SOURCE is the data to copy, the DESTINATION is where you copy the data to.
Thank you Hutch, however, that part I knew. What I did not understand is how to adapt my code to that.
For example, I don't have a DESTINATION since I do not want to copy the data. I just want to parse the SOURCE byte by byte and condition each byte to see if it is in the range of ascii 0-9. I like to do this to be able to identify numbers from all other characters. So, since I don't need it, but every informative source I looked through use this approach, I am confused :(
mov ecx, 1 ; put the number of bytes to copy in ecx
...
dec ecx
Your loop will run exactly once.
Quote from: jj2007 on July 23, 2010, 12:39:47 PM
mov ecx, 1 ; put the number of bytes to copy in ecx
...
dec ecx
Your loop will run exactly once.
Yes indeed. But why does it crash after? And how do I get it to run as long as there is no bytes left to loop? :)
Ani,
You basically check each byte against two values, the highest and the lowest in the ascii/ansi range you are after to filter only number characters. In the masm32 help file there is an ascii/ansi charcters chart and there you will find the range, if the byte value is lower than 48 or greater than 57 then its not a number.
To do this you read each byte and compare it against the upper and lower limits, you allow characters that are within the limits, you disallow characters that are not.
Construct a loop that reads the characters up to the terminator which should be ascii 0 and with the characters you evaluate, only accept those that are within those limits.
Quote from: hutch-- on July 23, 2010, 01:10:29 PM
Construct a loop that reads the characters up to the terminator which should be ascii 0 and with the characters you evaluate, only accept those that are within those limits.
But it is here I fail :( The other part about checking for the byte to be within ascii 48-57 I've figured out. But at the moment, I'm stuck on how to separate a byte to read, do some comparison on it and then go back to read the next byte.
For example, look this again and I try to comment more on this, which part I understand and which I don't.
mov esi, var1 ; put address into the source index
mov edi, container ; put address into the destination index
mov ecx, 1 ; put the number of bytes to copy in ecx
Oki, here is where I'm stuck. How do I get this to loop more then once? When I used "ln" before, I got compile-error. I'm sure there should be some index-mover or something that points to the next byte.
Again:
lodsb
stosb
cmp al,'0'
jz Exit
cmp al,'9'
ja Exit
dec ecx
jnz Again
Exit:
Above is still to come, but I understand "Again" is a loop, it looks very much like "Labels" that I've come across in some other languages (Java, C#, C++). At the moment, let's ignore this part. How do I solve my first problem, the first one?
I've read the tutorials, and I can tell they DO bring this up, problem is, I don't get it since I'm stupid :(
sval gives you already an integer value. You want to examine the string returned by input. Try this:
.data?
var1 dd ?
var2 dd ?
container dd ?
TheBuffer db 80 dup(?)
.code
start:
mov esi, input("Enter the first number: ")
cld
mov container, offset TheBuffer
mov edi, container ; put address into the destination index
push edi ; stosb changes edi, so we save a copy
mov ecx, 80 ; put the number of bytes to copy in ecx
Again:
lodsb
stosb
cmp al, 0 ; 0, not '0'
jz Exit
cmp al, '9'
ja Exit
cmp al, '0'
jb Exit
dec ecx
jnz Again
Exit:
mov byte ptr [edi-1], 0 ; delimit your input string
pop edi
mov var1, sval(edi) ; convert string to integer
print "Result="
print str$(var1), 13, 10
exit
Here is a simple test piece that filters so you only have numbers in the destination. I was trying to avoid writing it for you as you should learn enough and write your own.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
chfilter PROTO :DWORD,:DWORD
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL buffer[64]:BYTE
LOCAL ptxt :DWORD
mov ptxt, ptr$(buffer)
fn chfilter,"1b2g3h4k4m5k7k8d9a0",ptxt
print ptxt,13,10
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
chfilter proc src:DWORD,dst:DWORD
push esi
push edi
mov esi, src
mov edi, dst
sub esi, 1
lbl0:
add esi, 1
mov al, [esi] ; read byte into register
test al, al ; check if its zero
jz quit
cmp al, 48
jl lbl0 ; is it less than 48 ?
cmp al, 57
jg lbl0 ; is it greater than 57 ?
mov [edi], al ; if within range, write it to destination buffer
add edi, 1
jmp lbl0
quit:
mov BYTE PTR [edi], 0
pop edi
pop esi
ret
chfilter endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start