NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: grvtiwari on June 03, 2012, 06:07:10 AM
-
Hi everyone.
I am just a beginner. I have huge interest in Assembly Programming. I started and now I am facing a lot of problems. I hope that my all questions would be answered here very soon. Thanks.
;Assembly Program to print a message
;30 May 2012
SECTION .data
msg db 'A' ;String to be printed
len equ $-msg ;Length of the String
msg1 db 65
len1 equ $-msg1
SECTION .bss
SECTION .code
global _start
_start:
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, len
int 80h
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, len1
int 80h
mov eax, 4
mov ebx, 1
mov ecx, 65
mov edx, 2
int 80h
mov eax, 1
mov ebx, 0
int 80h
Now, according to me, output of the code should be AAA but it is AA. The last write call is not working. With little messing up, I reached to a conclusion that ecx needs an address or a pointer to what we want to be printed. With that opinion, I tried to mov [ecx], 65 but an error popped up saying that operaion size not specified. I am facing again some problems in loop or let me be more specific, I know that when we take input, it is characters not integers. How to deal with the characters, is the main problem.
Until then.
Thanks in advance.
-
You're on the right track with:
mov [ecx], 65
Nasm complains about "operation size not specified" 'cause it doesn't know whether to move a byte (8 bits), a word (16 bits), or a dword (32 bits) into the address pointed to by ecx. You gotta tell it!
mov byte [ecx], 65
That may do what you want... if ecx still points to "msg1", you'll overwrite a byte 65 with another byte 65... not too useful, but it should work. In your first two writes, you use "len" and "len1" in edx - both 1 - but on the third attempted write, you use 2 in edx. What did you have in mind there?
You might want to use something like...
...
section .bss
unknown resb 1
section .text ; ".text" is the standard name, not ".code"
...
mov eax, 4
mov ebx, 1
mov ecx, unknown
mov byte [ecx], 65 ; now it's known!
mov edx, 1
int 80h
...
If you don't want to specify another variable for the "unknown" character, you could do:
...
push 65
mov ecx, esp ; buffer's on our stack
mov edx, 1
int 80h
add esp, 4 ; clean up the stack
...
Or one I like...
...
mov al, 65
call putc
...
;exit!
...
;-----------------------
; prints the character in al to stdout.
; returns - all caller's regs preserved.
putc:
push edx
push ecx
push ebx
push eax ; must be pushed last - doubles as our buffer
mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 80h
pop eax
pop ebx
pop ecx
pop edx
ret
;---------
That saves more of the caller's regs than is usual, and doesn't return any result - or possible error! - not very efficient, but it's convenient. :)
You'll probably want to print a linefeed (10 decimal, 0Ah) last, just for a neater display...
Best,
Frank
-
First. Thanks for the prompt reply.
I got every bit of it. And seriouly,. I didnt knew that mov [eax], 65
would overwrite the very first byte pointed by eax register. Thanks for it. I too have thoughts to back up all the registers during the system call. Its just that I had not any need for it till now. Anyway, I was actually trying to enter the count value from the user. After reading your lot of replies over other topics, now I know that we enter characters not numbers. For example, when I enter 1 plus linefeed, it gets stored as two byte, one for 49 and other for line feed. Suppose, I kept the input buffer to 1 byte only, I entered 1 which would be character (and at the same time, this makes character A(ASCII 65 another option), so I think that if I stored this 1 or A into count variable and later moved it in ECX, then my loop should work for 49 times for 1 and 65 time for A, but I am always tormented by infinite loop. Why is that happening ?
-
You don't show any loop... but I can imagine. A loop needs to have some "exit condition", or it will be an "infinite loop". The actual "loop" instruction loops ecx times, decrementing ecx each time until ecx becomes zero, at which time it falls through into whatever code follows the "loop" instruction - that's its "exit condition". If you're not using the "loop" instruction (common - "loop" is short but slow) you need to arrange some "exit condition"... or loop forever.
For example, if you were doing something with keyboard input, it would usually end with a linefeed, so you might want to do something like...
...
mov esi, buffer
looptop:
mov al, esi
inc esi ; get ready for next one
cmp al, 10
jz loopend
; do something with the character in al
jmp looptop
loopend:
...
Printing out our own "ASCII chart" might make an interesting exercise... although "man ascii" will do it for us. Note that ascii codes less than 32 decimal (20h) are "control codes", not real "characters" so trying to print them will just mess up the display. "True ASCII" is 7 bit code so you probably don't want to print anything over 127 decimal (7Fh)...
If you post the infinite loop that's tormenting you, we can probably help, but you can probably figure it out on your own if you think about what the "exit condition" needs to be to do what you want...
Best,
Frank
-
Here is the code which is just pesking me up.
SECTION .data
msg db 'Enter the number',10
len equ $-msg
msg1 db 'Message',10
len1 equ $-msg1
SECTION .bss
count resb 1
SECTION .text
global _start ;Entry point of the Program
_start:
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, len
int 80h
mov eax, 3
mov ebx, 0
mov ecx, count
mov edx, 1
int 80h
mov ecx, count
call loopmsg
mov eax, 1
mov ebx, 0
int 80h
loopmsg:
push ecx
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, len1
int 80h
pop ecx
loop loopmsg
ret
Please, I am just a beginner. And I am currently stuck on the loop. After this, with your wish, I will create program for ASCII table.
Thanks Again.
-
And sir, I intentionally took edx as 1 for reading input. I do not have any idea on how to deal with two bytes in input buffer. So, I tought it would be easy for me to get 1 input only. And sir, I know if user prints 1ls -l then line feed, what will happen. Credit to you.
Thanks.
-
I think your main problem is right here:
mov ecx, count
call loopmsg
You're loading ecx with the address of the "count" variable. This isn't actually "infinite", but is way more than you want! You probably want "[contents]" of the "count" variable...
mov ecx, [count]
call loopmsg
But that isn't quite correct, either. For one thing, using ecx will get four bytes from "[count]", and we only wanted one. For another thing, sys_read will put a "character" (its ASCII code) in "[count]", which is not the same as the number the character represents. We can fix these issues (there are potentially more) without introducing any new instructions...
mov ecx, 0 ; xor ecx, ecx better? - just to zero upper bits
mov cl, [count]
sub cl, '0' ; or 48 or 30h - convert to number
call loopmsg
The "movzx" instruction would come in handy here...
movzx ecx, byte [count]
would "zero extend" the byte into all of ecx, making sure that the upper bits are zero which is what we want. (The "movsx" varient "sign extends" a byte - or word - into a 32 bit register. If you don't yet "get" how we represent negative numbers... don't worry about it) Note that Nasm knows how big ecx or cl are, so we don't need to specify a size, but in this case we do.
This ASSumes a "well behaved" user who will enter just one character which is a decimal digit, but within those limits should do what you want. You'll find that dealing with erroneous or unexpected input takes much more code than just doing the "work" we're interested in... but we "should" do it! We can get to that...
(my name's "Frank" - "Sir" was my grandfather. :) )
Best,
Frank
-
Hi again. I know my questions are more than easy for you. But believe, I nearly want to bang my head against wall but I want to learn thats why I am still here. I will be here.
Anyway, your last reply provided me a bit of confusion.
mov esi, buffer
looptop:
mov al, esi
inc esi ; get ready for next one
cmp al, 10
jz loopend
; do something with the character in al
jmp looptop
loopend:
Why we aren't comparing for [al] to be 10. I mean that al should contain an address to a location that holds 10. Isn't ?
Suppose user entered 8000 plus linefeed. I pointed esi to the location and then changed this '8000' (which is 4 byte, isn't ?), to 8000 (I think would be able to be stuffed inside a byte, so 1 byte), obviously this 8000 would now be in a register. But I cant use it to print. I need an address. So, the currect operaion must be
mov byte[ecx], eax ; If eax contains 8000
or
mov byte[ecx], 8000
for displaying it on the stdout.
And one more thing, to display 12 or 22 anything with more than 1 digit, do I need more than 1 byte ? I mean if I store 65 in a variable and then mov ecx, var it would print A. So, I think it would be needed to changed to more than 1 byte. 1 for '6' and other for '5' ?
-
With all I learned till now, I tried to create to input like 12 plus linefeed. Convert it to number 12, since I do not know a way to print 12, so I tried to print a msg 12 times, but it is not working. What is it I am doing wrong ?
;Assembly Program to accept input and turn it into real integer..
;
;4 June 2012
SECTION .data
msg db 'Enter the number: ',10 ;Input String
len equ $-msg ;Length of the String
SECTION .bss
input resb 10 ;Input Reserved Buffer
SECTION .text
global _start
_start:
mov ecx, msg ;Moving the string address to be printed
mov edx, len ;Setting up the output length
call print ;Calling the read procedure
mov ecx, input ;Moving the input address
mov edx, 10 ;Setting up the Input Buffer Space
call read ;Calling the read procedure
mov esi, input ;Source Index to the Input
mov eax, 10
mov ecx, 0
call jump
jump:
mov ebx, [esi] ;Stroing the content in here
inc esi ;Pointing to the next
cmp ebx, 10 ;Compare for new line
jz jumpend ;if zero flag is set, jump to jumpend procedire
sub ebx, '0' ;Changed it to integer
mul ecx ;multiply by 10
add ecx, ebx ;Adding ecx and ebx
call jump ;Calling the jump procedure
jumpend:
push ecx ;Since ECX contains final conversion, save it
mov ecx, msg ;Setting up the Msg to be printed
mov edx, len ;Setting up the len
call print ;calling print
pop ecx ;Popping the value of ECX
loop jumpend ;Runnning a loop to check that we converted sucessfully
call exit ;Calling the Exit Procedure
print:
mov eax, 4 ;Setting up the write system call
mov ebx, 1 ;Setting up the stdout
int 80h ;Kernel ! You Do it.
ret ;Return to callee
read:
mov eax, 3 ;Setting up the Read system call
mov ebx, 0 ;Setting up the stdin
int 80h ;Kernel ! You Do it.
ret ;Return to callee
exit:
mov eax, 1 ;Settiing up the Exit system call
mov ebx, 0 ;Setting up the return status
int 80h ;Kernel ! You Do it.
-
Well, it doesn't help that I blew the example! I meant [esi]! (although your questions are "easy", I'm still capable of making a mistake!)
; address of buffer into esi
mov esi, buffer
looptop:
; get one character/byte into al
mov al, [esi] ; <- error in previous code!
inc esi ; get ready for next one
; see if it's the expected end of input
cmp al, 10
; if so, we're all done
jz loopend
; do something with the character in al
jmp looptop
loopend:
So al isn't an address - an address takes 32 bits (or 16 bits in old dos code, or 64 bits in 64-bit code), but a single byte/character from our buffer...
Suppose user entered 8000 plus linefeed. I pointed esi to the location and then changed this '8000' (which is 4 byte, isn't ?),
Right... but with the linefeed, 5. But you probably want a bigger buffer than that - a 32-bit number could need 10 digits... plus 1 for the linefeed... maybe a leading '+' or '-'? We're not dealing with zero-terminated strings here, but don't forget to leave a byte for it if you need it...
count resb 16
... ought to be plenty. Won't hurt to make the buffer too big...
to 8000 (I think would be able to be stuffed inside a byte, so 1 byte), obviously this 8000 would now be in a register.
Yes, but the number 8000 won't fit in a byte (only up to 255 in a byte). It would fit in a word, in this case, but might as well use a full 32-bit register - there's no advantage to using 16-bit registers in 32-bit code.
But I cant use it to print. I need an address.
Right, and worse... to print it you need to convert the number 8000 back into '8', '0', '0', '0'... But I thought we were going to use this number the user entered as a count to repeat "message"? We could print it too, I suppose...
It seems to me that you might be "stuck" on the "convert the 4 characters '8000' to the number 8000" part. Or maybe you've got that? I fear I confused you by typoing the example. Where are we?
Best,
Frank
Ah, new post! Lemme see...
;Assembly Program to accept input and turn it into real integer..
;
;4 June 2012
SECTION .data
msg db 'Enter the number: ',10 ;Input String
len equ $-msg ;Length of the String
SECTION .bss
input resb 10 ;Input Reserved Buffer
SECTION .text
global _start
_start:
mov ecx, msg ;Moving the string address to be printed
mov edx, len ;Setting up the output length
call print ;Calling the read procedure
mov ecx, input ;Moving the input address
mov edx, 10 ;Setting up the Input Buffer Space
call read ;Calling the read procedure
mov esi, input ;Source Index to the Input
mov eax, 10
mov ecx, 0
call jump
jump:
mov ebx, [esi] ;Stroing the content in here
At this point, you've put 4 bytes into ebx, but we really only want one at a time... but we eventually want it in a 32-bit register. How about an "and" to zero out the high bits and leave just a byte untouched?
and ebx, 0FFh
inc esi ;Pointing to the next
cmp ebx, 10 ;Compare for new line
jz jumpend ;if zero flag is set, jump to jumpend procedire
sub ebx, '0' ;Changed it to integer
mul ecx ;multiply by 10
add ecx, ebx ;Adding ecx and ebx
call jump ;Calling the jump procedure
Do you really want to "call" jump here? I would think "jmp jump"...
jumpend:
push ecx ;Since ECX contains final conversion, save it
mov ecx, msg ;Setting up the Msg to be printed
mov edx, len ;Setting up the len
call print ;calling print
pop ecx ;Popping the value of ECX
loop jumpend ;Runnning a loop to check that we converted sucessfully
call exit ;Calling the Exit Procedure
print:
mov eax, 4 ;Setting up the write system call
mov ebx, 1 ;Setting up the stdout
int 80h ;Kernel ! You Do it.
ret ;Return to callee
read:
mov eax, 3 ;Setting up the Read system call
mov ebx, 0 ;Setting up the stdin
int 80h ;Kernel ! You Do it.
ret ;Return to callee
exit:
mov eax, 1 ;Settiing up the Exit system call
mov ebx, 0 ;Setting up the return status
int 80h ;Kernel ! You Do it.
Well... I think I need a nap. I'll be back! :)
Best,
Frank
-
Yes. I did it. I misread the instruction for mul command. nxn gives 2n bits. If we use 32 bit reg multiplication, so, upper 32 bit get in edx and lower in eax, so in short after multiplying, I was losing eax.
Here is the code.
;Assembly Program to accept input and turn it into real integer..
;Arc-PC
;4 June 2012
SECTION .data
msg db 'Enter the number: ',10 ;Input String
len equ $-msg ;Length of the String
msg1 db 'Goodluck for ASM :) .', 10 ;String2
len1 equ $-msg1 ;Length of my msg
SECTION .bss
input resb 10 ;Input Reserved Buffer
SECTION .text
global _start
_start:
mov ecx, msg ;Moving the string address to be printed
mov edx, len ;Setting up the output length
call print ;Calling the read procedure
mov ecx, input ;Moving the input address
mov edx, 10 ;Setting up the Input Buffer Space
call read ;Calling the read procedure
mov esi, input ;Source Index to the Input
mov eax, 10
mov ecx, 0
call jump
jump:
mov ebx, [esi] ;Stroing the content in here
and ebx, 00FFh
inc esi ;Pointing to the next
cmp ebx, 10 ;Compare for new line
jz jumpend ;if zero flag is set, jump to jumpend procedire
sub ebx, '0' ;Changed it to integer
mul ecx ;Its 32x32, so lower 32 in eax and upper 32 in edx
mov ecx, eax ;Moving the lower 32 bit in ecx
mov eax, 10 ;Setting eax to 10 again
add ecx, ebx ;Adding ecx and ebx
jmp jump ;Calling the jump procedure
jumpend:
push ecx ;Since ECX contains final conversion, save it
mov ecx, msg1 ;Setting up the Msg to be printed
mov edx, len1 ;Setting up the len
call print ;calling print
pop ecx ;Popping the value of ECX
loop jumpend ;Runnning a loop to check that we converted sucessfully
call exit ;Calling the Exit Procedure
print:
mov eax, 4 ;Setting up the write system call
mov ebx, 1 ;Setting up the stdout
int 80h ;Kernel ! You Do it.
ret ;Return to callee
read:
mov eax, 3 ;Setting up the Read system call
mov ebx, 0 ;Setting up the stdin
int 80h ;Kernel ! You Do it.
ret ;Return to callee
exit:
mov eax, 1 ;Settiing up the Exit system call
mov ebx, 0 ;Setting up the return status
int 80h ;Kernel ! You Do it.
I was trying to put 10 as '1' and '0'. I have an idea. Suppose we have count in ecx. Check for ecx >10, if yes, ecx /10 and store the mod somewhere, then change that mod to char. But I do not know what to do after it. I mean how store it for printing. Another issue is numbers would be in reverse order.
A hint please...
Thanks..
-
That seems to work great. I still think you've got a "call" in there where you don't need it - "call jump" could just fall through, and I moved "exit" up to where it could fall through. Since they aren't really subroutines - we don't "ret" from them - we shouldn't really "call" them. Worked okay the way you had it...
I added a "number_to_string" routine. As you observe, we get the digits in the wrong order, so we push 'em on the stack, counting each one. Then, when eax gets down to zero after the "div", we're done, so pop 'em back off the stack and store 'em in a buffer to print. There are other ways to do this, but this is "easy"(?) to understand...
;Assembly Program to accept input and turn it into real integer..
;Arc-PC
;4 June 2012
SECTION .data
msg db 'Enter the number: ',10 ;Input String
len equ $-msg ;Length of the String
msg1 db 'Goodluck for ASM :) .', 10 ;String2
len1 equ $-msg1 ;Length of my msg
SECTION .bss
input resb 10 ;Input Reserved Buffer
ascbuf resb 12 ; buffer for string
count resd 1
SECTION .text
global _start
_start:
mov ecx, msg ;Moving the string address to be printed
mov edx, len ;Setting up the output length
call print ;Calling the read procedure
mov ecx, input ;Moving the input address
mov edx, 10 ;Setting up the Input Buffer Space
call read ;Calling the read procedure
mov esi, input ;Source Index to the Input
mov eax, 10
mov ecx, 0
; call jump
jump:
mov ebx, [esi] ;Stroing the content in here
and ebx, 00FFh
inc esi ;Pointing to the next
cmp ebx, 10 ;Compare for new line
jz jumpend1 ;if zero flag is set, jump to jumpend procedire
sub ebx, '0' ;Changed it to integer
mul ecx ;Its 32x32, so lower 32 in eax and upper 32 in edx
mov ecx, eax ;Moving the lower 32 bit in ecx
mov eax, 10 ;Setting eax to 10 again
add ecx, ebx ;Adding ecx and ebx
jmp jump ;Calling the jump procedure
jumpend1:
mov [count], ecx
jumpend:
push ecx ;Since ECX contains final conversion, save it
mov ecx, msg1 ;Setting up the Msg to be printed
mov edx, len1 ;Setting up the len
call print ;calling print
pop ecx ;Popping the value of ECX
loop jumpend ;Runnning a loop to check that we converted sucessfully
mov eax, [count]
mov edi, ascbuf
call number_to_string
mov ecx, ascbuf ; buffer
mov edx, eax ; length returned from number_to_string
call print
; call exit ;Calling the Exit Procedure
exit:
mov eax, 1 ;Settiing up the Exit system call
mov ebx, 0 ;Setting up the return status
int 80h ;Kernel ! You Do it.
; subroutines
print:
mov eax, 4 ;Setting up the write system call
mov ebx, 1 ;Setting up the stdout
int 80h ;Kernel ! You Do it.
ret ;Return to caller
read:
mov eax, 3 ;Setting up the Read system call
mov ebx, 0 ;Setting up the stdin
int 80h ;Kernel ! You Do it.
ret ;Return to callee
;------------------
; call with eax = number
; edi = buffer to put string
; returns eax = length of string
number_to_string:
xor ecx, ecx
mov ebx, 10
.top:
xor edx, edx
div ebx
add edx, '0'
push edx
inc ecx
test eax, eax
jnz .top
mov edx, ecx
.poploop:
pop eax
stosb
loop .poploop
mov eax, edx
ret
;--------------------
I had to rearrange a couple of things to make it work, but I think you'll still recognize it. Would look better with another linefeed at the end... maybe should have saved some of the registers the "number_to_string" routine trashes... could be better, but it prints the number...
Best,
Frank
-
I am trying to make my own program for converting number to string. Lets share my progress.
;Program to convert number to string
;Arc-PC
;7 June 2012
SECTION .data
msg db 'hey! I am there always', 10
len equ $-msg
msg2 db 10
len2 equ $-msg2
msg3 db 0
SECTION .bss
SECTION .data
global _start
_start:
;for 32 bit divison, Processor assumes dividend to be of 64 bits, upper 32 in EDX, lower 32 in EAX
;Divisor is supposed to be provided explitly, like div ebx -> divide EDX EAX by EBX
;Quotient is stored in EAX and Remainder in EDX
mov eax, 1250 ;A number to be changed
mov ebx, 10 ;To be divided by 10
mov ecx, 0
loopend:
xor edx, edx
div ebx ;Divide it
add edx, '0'
push edx
inc ecx
cmp eax, ebx ;CHeck whether quotient gets zero
jle exit
loop loopend ;looping the loopend
exit:
mov eax, 1 ;Setting up the EXIT Ccall
mov ebx, 0 ;Setting up the return status to OS
int 80h ;Yo ! Kernel do it.
Well, I seem to stuck at how to pop the stack because I do not know how to manage ecx for count and to pop simultaneously without pushing ecx on stack. I think the portion I have coded is working fine.
And sir. I lost my phone yesterday. I used to use internet through that phone only. Somehow, I managed to get net connection and now I am posting the progress.
Thanks
-
Good! Looks like you're trying a worthwhile optimization over what I showed you...
;Program to convert number to string
;Arc-PC
;7 June 2012
SECTION .data
msg db 'hey! I am there always', 10
len equ $-msg
msg2 db 10
len2 equ $-msg2
msg3 db 0
SECTION .bss
SECTION .data
You probably want "section .text" here. There are some "flags" (just a bit in a dword) set in the linkable object file header which is passed on to the executable file header. One of them indicates "writeable" (otherwise it's "read only") and another indicates "executable". A ".text" section is supposed to be "read only" (the "writable" bit is clear) and "executable" (bit set to one). It won't hurt to make the code section writable (although there may be some "security" implications). My CPU, and apparently yours, will actually execute code even if the "executable" bit is clear. However, I understand that some newer CPUs have an "NX" bit which will prevent execution in a section that doesn't have the "executable" bit set in the section header (I can't confirm this from my own experience, but that's what I'm told). So I think it's "safer" to let Nasm set these bits the way they're "supposed" to be. Nasm "knows" several section names (see the Manual) - note that "SECTION" can be either upper or lower case, but the name must be lowercase!
global _start
_start:
;for 32 bit divison, Processor assumes dividend to be of 64 bits, upper 32 in EDX, lower 32 in EAX
;Divisor is supposed to be provided explitly, like div ebx -> divide EDX EAX by EBX
;Quotient is stored in EAX and Remainder in EDX
mov eax, 1250 ;A number to be changed
mov ebx, 10 ;To be divided by 10
mov ecx, 0
loopend:
xor edx, edx
div ebx ;Divide it
add edx, '0'
push edx
inc ecx
cmp eax, ebx ;CHeck whether quotient gets zero
jle exit
Good! If eax is less than 10, the final digit is in eax and we can save a "div" (a very slow instruction) by stopping here. I don't think we want to stop if eax = 10! Also... "jl" is for signed integers, "jb" is for unsigned integers. Neither Nasm nor the CPU knows whether we intend an integer to be treated as signed or unsigned, we need to use different instructions. If the high bit of eax (bit 31) were set, "jl" would take the jump, since eax would be negative if treated as signed. "jb" would take the jump all the way up to eax = 0xFFFFFFFF - eax would be "below", but not "less" (if that makes any sense). This is why the C language is picky about "types"! If you try to compare an "int" with a "uint", the compiler will squawk. It needs to generate different code for a signed comparison or an unsigned comparison. You can fix this with a "cast", but this could potentially hide subtle errors - better to make the variables the same "type" in the first place (I did not understand this when I was trying to teach myself C - I thought the compiler was being "nit-picky" with me - but now I understand "why" - an advantage to learning asm!). In this case, I don't see how eax could ever have the high bit set (potentially "negative" if we treat it as signed), so it shouldn't make any difference... but "jb" would be "more correct". "jl" should work, but I don't think you want the "e" - if eax is 10 exactly we need one more "div". (I don't think we actually want to jump to "exit" at this point, but that'll work itself out)
loop loopend ;looping the loopend
I don't think you want to "loop" at this point. You increment ecx to count each digit, "loop" decrements ecx each time. So "loop" will continue forever since we increment ecx, but ecx won't count digits either, since "loop" decrements it! If I understand where you're going with this, you want an unconditional "jmp" here. It won't become an infinite loop since you've got an "exit condition" elsewhere.
exit:
mov eax, 1 ;Setting up the EXIT Ccall
mov ebx, 0 ;Setting up the return status to OS
int 80h ;Yo ! Kernel do it.
As I'm sure you know, we aren't quite ready to exit yet. When the "div" has gotten eax "below" (or "less than"... but not equal) 10, eax is our final digit. We still need to save (and count) this digit. We can "add '0'" either before or after pushing (or saving by another method) to "convert" number to character, as long as we do it someplace. You'll get to that...
It is entirely possible to use some other register besides ecx as a counter. "loop" uses ecx, but we can do the same thing with...
dec esi
; or sub esi, 1
; or lea esi, [esi -1]
jnz looptop
You actually use "loopend:" for this label - a terrible name for a label at the top of a loop! It doesn't matter to the "code" - the name is replaced by a number (the address of the label) - but for the sake of the poor "coder", "meaningful" names for labels or variables help a lot. (that's a nit-pick!)
As you observe, "push" is not a convenient way to save a value, since we're using the stack for our digits. What I did in the code I posted, was to save the "digit count" in another register. I used edx - since we're done with the "div" at this point, it's free for our use. Since I wanted to return the digit count, eax would have been convenient, but "stosb" uses al (a part of eax) and edi (es:edi actually, but in Windows/Linux/BSD segment registers all point to the same memory - this might not be the case in "your OS"... it isn't in dos!) "stosb" does:
mov [edi], al
inc edi
You can do the same thing, using other registers. I used "stosb" because it's short - one byte - but not particulary fast, and it depends on specific registers - there may not be much "advantage" to it.
As I mentioned, there are other ways of doing this. For example, we can get the digits in the "right order" by starting at the "end" of the buffer and working "backwards" as we get the digits, instead of pushing and popping them. This has the disadvantage that we don't know where the actual text representing the number starts. Since we're not using C, we can return more than one value. I've written a "number to text" routine that returns the "start of buffer" in ecx and the "length" in edx - all ready for the call to "print". Fairly "efficient" but potentially confusing, since it's not "standard" and definitely not "portable"!
Another approach is to avoid the slow "div" instruction entirely. It is possible to write a faster routine by using repeated "sub"s... or by using the reciprocal of 10 and using "mul" (this involves "fixed point" code). I don't have examples of either of these methods, but could probably(?) figure it out if I had to... Using "div" is slow, but "easy"...
You'll learn a lot by trying it yourself - learning what doesn't work is almost as valuable as learning what does work! So... "carry on!"
I can't help you with the phone problem. :)
Best,
Frank
-
Well, I think I removed the mistakes. And yes, I did it. Only thing that now needs to be included in here is to input a number then changing it to actual number and then back to original. And sir, you showed me the routine number_to_string. I had to code my own version because I seriouslly didn't knew about 'stosb'. It will take a bit time to reach there. And sir, please do not do my home work *(as you did for number_to_string). I am here to learn. I will do it. just give me a hint, a path, i will cover it.
;Program to convert number to string
;Arc-PC
;7 June 2012
SECTION .data
msg db 'hey! I am there always', 10
len equ $-msg
msg2 db 10
len2 equ $-msg2
msg3 db 0
SECTION .bss
output resb 20
SECTION .text
global _start
_start:
;for 32 bit divison, Processor assumes dividend to be of 64 bits, upper 32 in EDX, lower 32 in EAX
;Divisor is supposed to be provided explitly, like div ebx -> divide EDX EAX by EBX
;Quotient is stored in EAX and Remainder in EDX
mov eax, 6250 ;A number to be changed
mov ebx, 10 ;To be divided by 10
mov ecx, 0
loopend:
xor edx, edx ;Xoring EDX as it would contain the remainder, if not flushed, would screw the divison
div ebx ;Divide it
add edx, '0' ;Making it a Char
push edx ;Pushing Remainder on Stack
inc ecx ;Incrementing ECX
cmp eax, ebx ;CHeck whether quotient gets zero
jl end
jmp loopend ;looping the loopend
end:
add eax, '0' ;Final quotient is still waiting to be pushed, make it a char
push eax ;Push it on the stack
inc ecx ;Incrementing the ECX
mov esi, output ;Store the output adress
combine:
pop eax ;Store the content
mov [esi], eax ;Move it to array
inc esi ;Increment it
loop combine ;loop till ECX hold it
exit:
mov byte[esi], 10 ;Setting up the Line Feed
mov eax, 4 ;Setting up the Write Call
mov ebx, 1 ;Setting up stdout
mov ecx, output ;Taking the addrss of output
mov edx, 10 ;Wont be used I think. Linefeed would be used.
int 80h ;Print it.
mov eax, 1 ;Setting up the EXIT Ccall
mov ebx, 0 ;Setting up the return status to OS
int 80h ;Yo ! Kernel do it.
Thanks Again....
-
I am referring a book on Assembly Language on LInux. It recommends us to use a "io.mac" and "io.o" file for complex input output procedure(in fact, every book I came across, pushed a io.o file to use it to reduce the complexicity. This upsets me. When I am already ready to bang my head to learn the most complex low level language, why there is a need to hide any more details. Show me how exactly their read and write function works. ). I have a query regrading their procedure in io.o file. For example, there is a call
PutStr prompt_msgl ; request first number
Getint CX ; CX = first number
PutStr prompt_msgl; request second number
Getint DX ; DX = second number
I understand how PutStr works. But I do not understand how this GetInt Works. Wait a minute I think I guessed how this GetInt works. There is no call instruction before GetInt which means it must be a Macro. Let me look up there. Then, What this means
%macro PutInt 1
or this
%macro GetInt 1
%ifnidni %1,AX
push AX
call proc_GetInt
mov %1,AX
pop AX
%else
call proc_GetInt
%endif
%endmacro
-
Sivarama Dandamudi? Sounds like his work. I haven't read his book, but I downloaded the example code including "io.mac" and "io.o". I haven't got source code for "io.o", and I understand it isn't in the book either... but we can disassemble it... It looks to me like Dr. Dandamudi originally wrote this for dos and converted it for Linux. It looks to me like he died before he was completely finished. In the macro you show, for example, "push ax"... That's a "legal" instruction, and works, but we'd get better performance if the stack were kept aligned at 4 bytes (for 32-bit code). There are a few 16-bit registers used in io.o, too. Not nice to speak ill of the dead, but this "isn't the way I would have done it."...
I agree with you about "hiding the details"! There's a reason this is (often!) done - there's a limited amount of time available in a formal "course", and if you show/teach/learn all the nitty-gritty details, you won't get very far before the end of the course! If the goal is to "obtain the program" (or just to "pass the course"), it makes sense to do this. If the goal is to "learn assembly language", it seems a shame to hide away the "good stuff". Inevitably, there's a "black box" at the end which we have to "just use" without understanding how it works (int 80h, for example), but I like to "see" as much as possible. Just my opinion...
Is there a "question" in this? Do you want to see the disassembly of "proc_GetInt"? Same idea as what you do, but he handles invalid input (and overflow) better.
Sorry I "did your homework" too much. I learn "by example" better than by "RTFM", so that's how I try to explain things. I don't know exactly what "hint" (or hints) you need, or I would limit it to that. "Just call printf" (or "just use io.mac/io.o") doesn't seem like enough. I'll try...
Best,
Frank
-
Yes. It is the same book. I do not want to disassemble the code. Actually, I want to understand how he passed the parameters in GetInt AX. I mean it is not call by value(through registers) or through referance(through stack). And what %1 means here ?
That's all I want to know. I have a very bad habit. I want to code everything without any io.o . I mean when I say that I can code in assembly, then I should really be able to do that. After 10 years, I do not wish to seek help of a io.o to do simplest thing. It hurts the programmer in me.
Thanks. I will mention exactly what I require.
And I was expecting some comments on my previous number to string program.
Thanks.
-
I am referring a book on Assembly Language on LInux. It recommends us to use a "io.mac" and "io.o" file for complex input output procedure(in fact, every book I came across, pushed a io.o file to use it to reduce the complexicity. This upsets me. When I am already ready to bang my head to learn the most complex low level language, why there is a need to hide any more details.
If you're goal is to learn assembly, then you should be focusing on learning the instructions, coding procedures and building various algorithms to help refine your understanding of the low level coding. The reason most people get detoured from assembly early on is because you can't just learn assembly. From the second that you want to display the results of data movements or calculations, you immediately have to become familiar with intricate components of whatever operating system you're using. And what's worse, each operating system does things different, so that part of the code is useless when you move between various operating systems (and sometimes between different versions of the operating system).
What this guy has done is to wrap the operating system specific stuff in macros so the learner can focus on assembly without having to deal with system specific things.
I don't exactly agree with the way he's done it. To be honest, the easiest method would be to teach the students assembly, and early on in the course provide them with the details of using calls to external procedures provided by a portable language (like C).
I understand how PutStr works. But I do not understand how this GetInt Works. Wait a minute I think I guessed how this GetInt works. There is no call instruction before GetInt which means it must be a Macro.
Yep, sure is.
Let me look up there. Then, What this means
%macro PutInt 1
It literally means "declare a macro of the name 'PutInt' that takes 1 argument".
or this
%macro GetInt 1
%ifnidni %1,AX
push AX
call proc_GetInt
mov %1,AX
pop AX
%else
call proc_GetInt
%endif
%endmacro
What we have here is a wrapper to a procedure that this guy wrote. I'll try to brake it down for ya.
%macro GetInt 1
Just like in the previous, we are declaring a macro named "GetInt" that takes 1 argument.
%ifnidni %1,AX
This looks more complex than it really is... NASM uses a % followed by a number to identify which macro argument it's referencing. The %ifnidni directive compares two identifiers against each other (ignoring case) and executes the following block of code if the two identifiers are not the same.
push AX
call proc_GetInt
mov %1,AX
pop AX
If the %ifnidni line finds that the argument passed to 'GetInt' was NOT the AX register, we must preserve the value of AX, call the procedure 'proc_GetInt' which returns with the integer in AX, store that integer in the memory location or register that was passed as the parameter to 'GetInt', and finally restore the AX register in case the code that called the macro had something valuable in AX.
%else
call proc_GetInt
%endif
This part is an alternate compilation. If the argument to 'GetInt' actually was the AX register, then we don't have to do anything except call the procedure 'proc_GetInt', and since it returns the integer in AX, you're register is already updated.
%endmacro
This line specifies that we are done declaring our 'GetInt' macro.
That's all I want to know. I have a very bad habit. I want to code everything without any io.o . I mean when I say that I can code in assembly, then I should really be able to do that. After 10 years, I do not wish to seek help of a io.o to do simplest thing. It hurts the programmer in me.
You should get out of that habit because it's a very unrealistic goal. There are very few chances in a programmers life where he can do just "pure assembly". Usually that's when you're writing framework code that'll be used by others who will deal with the system specific issues. If you code on a computer, you're going to use other peoples code, you should get used too it. Think about it, say you stop using io.o, you'll still be using the operating systems API. If you get away from the operating systems API, then you're still using the BIOS API. It's an almost never-ending cycle which only prevents you from getting any real work done.
In fact, about the only way you can write code that isn't backed by someone else's API is if you break out Verilog/VHDL and design yourself a custom CPU then get a company to print you an ASIC and code exclusively for that system. That'd be a waste of time if you ask me..
-
That's all I want to know. I have a very bad habit. I want to code everything without any io.o . I mean when I say that I can code in assembly, then I should really be able to do that. After 10 years, I do not wish to seek help of a io.o to do simplest thing. It hurts the programmer in me.
Yeah... so what's the bad habit? :)
"%1", in this context, means the first parameter passed to the macro (%2 would be the second parameter, etc.). So he IS passing parameters (to the macro, not to "proc_getInt") in registers. I guess a variable would work, too, but you'd have to provide the '[]'s. "GetInt [my_number]" would(?) work, "GetInt my_number" would not. There's a pretty good(?) section on macros in the Nasm Manual. I like Nasm, but the macro syntax is uglier than a bulldog's hind end (IMO).
I didn't make any comment on your number to string program because I thought it was pretty good. One thing that could perhaps be improved, if you want to use it as a subroutine, is to pass the buffer - a "place to put it" - as a parameter rather than hard-coding it as "output". Maybe allow the number to be someplace besides eax - although you'll want it in eax for the "div" eventually. I'll look at it again to see if I can find any nits to pick, but basically, "Good job!"
Best,
Frank
P.S. I see Bryant disagrees with me on the "bad habit". No surprise, a lot of people disagree with me on that issue. If Guarav had told me in a personal message that he hopes to learn to write an OS eventually (which he did), would that change your opinion on "always have to use someone else's API"?
-
If Guarav had told me in a personal message that he hopes to learn to write an OS eventually (which he did), would that change your opinion on "always have to use someone else's API"?
Your rehashing of what Bryant said is taken out of a context that was about focusing on learning assembly language. In shorter words: straw man (http://en.wikipedia.org/wiki/Straw_man) :P
However, if you want to throw operating system development into the mix, then that means you'll have to accept a broadening of API to include any software accessible interface; whether that is a source-level API, ABI of the PC BIOS, or anything else which essentially exposes functionality in a "black box" (inputs and outputs) manner.
With respect to the above, Bryant already touched upon things that reinforces his premise, e.g. BIOS. Want to guess what word the last letter of the acronym ACPI (http://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface) represents? The list goes on, so I would agree that "always" (even as you put it) is still quite a sound assertion.
To step back a bit and refocus on the learning assembly language part, I would suggest using whatever you can to minimize worrying about I/O (the not-so-simplest things) so that you can focus on assembly language principles. Sometimes that means reading up on API documentation for a particular I/O system/library so you can observe your code in action.
-
P.S. I see Bryant disagrees with me on the "bad habit". No surprise, a lot of people disagree with me on that issue. If Guarav had told me in a personal message that he hopes to learn to write an OS eventually (which he did), would that change your opinion on "always have to use someone else's API"?
Not totally. Like I mentioned above:
I don't exactly agree with the way he's done it. To be honest, the easiest method would be to teach the students assembly, and early on in the course provide them with the details of using calls to external procedures provided by a portable language (like C).
This method would separate the user from system dependent issues as much as possible while still allowing them a method to view the results of their low level code. All without the worry of a possible system change crippling the courseware.
-
Well, I agree to some of the points of everyone. For Bryant Keller sir, What if I understand myself a better, outstanding programmer just because I want to stand out of crowd. Today, with this system independent education technique, don't you see we all preparing a generation which won't know about read and write system call (the true core of assembly is being low level). I think with this, we will all lost low level knowledge. I am not a great student or a great philosopher either, I am just a student who is curious, very curious. I am in favour of low level system dependent thing. Everything should be simple but not simplest. Just because it is difficult, so it should be avoided, should not be done. I think low level knowledge is what makes a developer different and yes, a world-class programmer.
Keith Kanios Sir, I am again just a beginner, I want to grasp all the knowledge till the level of BIOS. It is only thing that would be available to me while trying to develop my OS. I was trying to learn Assembly from last 3 years. I belong to India, not so resource rich country. I had to download e-books. Everything book I tried, presented my a different syntax, a different macro file, a different assembler, a different OS. I finally decided to get over with it. I was quite acquainted with instruction because they do not change over any book. I decided to learn the few basic things from internet and through practice so as to not to get lost in any of sea of diff assemblers. I am making progress and believe me, I want to share all I know and I will.
Finally, Frank Kotler Sir, I do not why but I do share a kind of special bonding with you. It feels good to be interacting with a professional assembler developer, a developer who developed the software I use for assembling. You can count me as the one who would never leave your side. :) I am like that only. Just to let you know, I want to be like you, filled with all sort of knowledge and fledged with the capability to answer any question. I want to be coder. In my views, all ASM coders are true coder. I mean look at the IDEs today, many of us do not even know how a thing actually works. Before starting to learn ASM, I used to think I knew C language, now I am over with this misconception. Learning never stops, and I wish, it never does for me.
Sorry, for my bad English. Its not my native language. It has been on my mind since I made my first post.
Sorry, If I was rude anywhere, any time.
Thanks.
-
Sir, I am again just a beginner
All levels of experience are welcome here.
...trying to develop my OS.
That is a very big topic for a beginner to tackle. If you are going to persue that then, as a suggestion, get the source code for FreeDOS ( or Linux if you think you can handle that level of complexity ) and try to understand how the code causes the hardware to behave. Good luck to you.
-
See? I told ya a lot of people disagree with me! :)
To clarify a couple of things... I am by no means a "professional assembler developer" - strictly a "dabbler". While it is true that I've got a (very!) little code in Nasm, I'm not a good C coder and prefer to leave it to others. I think of myself as more of an "advocate" than a "developer". Where it says "hero", "experienced beginner" would be more accurate! If you want to "be like me", try to be "like me, only not so lazy" - you'll get farther!
To get back more nearly on topic... "io.o" and GetInt/PutInt generally, "hides away" two things. One is the interface to the OS. We can't do much about that. The other is the number<->text conversion(s). That should be pretty "portable" (as long as we're using ascii) and I don't see why we shouldn't drag it out into the daylight and poke at it. How does it work? How else could it be done? Is there a "best" way?
Keith and Bryant both mention the importance of the "interface". Learning to use one as a beginner, and then to provide one. So what's a good "interface" to your "number to string"? Passing parameters in registers is easy in asm. Passing parameters on the stack would allow you to call it from C as well as asm. I would have said that a "regular C calling convention" would be best... but I see that for 64-bit code they've changed it back to passing parameters in registers... 32-bit code is fast becoming obsolete, so I dunno...
Just to refresh my memory, here's the last code you posted... with some comments marked with -->...
;Program to convert number to string
;Arc-PC
;7 June 2012
SECTION .data
msg db 'hey! I am there always', 10
len equ $-msg
msg2 db 10
len2 equ $-msg2
msg3 db 0
SECTION .bss
output resb 20
;--> output_size equ $ - output ?
SECTION .text
global _start
_start:
;for 32 bit divison, Processor assumes dividend to be of 64 bits, upper 32 in EDX, lower 32 in EAX
;Divisor is supposed to be provided explitly, like div ebx -> divide EDX EAX by EBX
;Quotient is stored in EAX and Remainder in EDX
mov eax, 6250 ;A number to be changed
mov ebx, 10 ;To be divided by 10
mov ecx, 0
;--> xor ecx, ecx is shorter, and I don't think it's any slower
loopend:
xor edx, edx ;Xoring EDX as it would contain the remainder, if not flushed, would screw the divison
div ebx ;Divide it
add edx, '0' ;Making it a Char
push edx ;Pushing Remainder on Stack
inc ecx ;Incrementing ECX
cmp eax, ebx ;CHeck whether quotient gets zero
jl end
jmp loopend ;looping the loopend
;--> perhaps "jae loopend" would replace "jl" and "jmp"
end:
add eax, '0' ;Final quotient is still waiting to be pushed, make it a char
push eax ;Push it on the stack
inc ecx ;Incrementing the ECX
mov esi, output ;Store the output adress
combine:
pop eax ;Store the content
mov [esi], eax ;Move it to array
;--> "mov [esi], al"? you don't really want all four bytes of eax
;--> doesn't do any harm, 'cause you overwrite the unwanted bytes, but...
inc esi ;Increment it
loop combine ;loop till ECX hold it
exit:
mov byte[esi], 10 ;Setting up the Line Feed
mov eax, 4 ;Setting up the Write Call
mov ebx, 1 ;Setting up stdout
mov ecx, output ;Taking the addrss of output
mov edx, 10 ;Wont be used I think. Linefeed would be used.
;--> no, it'll print all ten bytes! esi - ecx should give you the actual length
int 80h ;Print it.
mov eax, 1 ;Setting up the EXIT Ccall
mov ebx, 0 ;Setting up the return status to OS
int 80h ;Yo ! Kernel do it.
TightCoderEx has some thoughts here:
http://forum.nasm.us/index.php?topic=1410.0
Best,
Frank
-
Well, I agree to some of the points of everyone. For Bryant Keller sir, What if I understand myself a better, outstanding programmer just because I want to stand out of crowd. Today, with this system independent education technique, don't you see we all preparing a generation which won't know about read and write system call (the true core of assembly is being low level). I think with this, we will all lost low level knowledge. I am not a great student or a great philosopher either, I am just a student who is curious, very curious. I am in favour of low level system dependent thing. Everything should be simple but not simplest. Just because it is difficult, so it should be avoided, should not be done. I think low level knowledge is what makes a developer different and yes, a world-class programmer.
The reason for the platform dependency is for the classroom. When you have a room full of (200-300) students that you're trying to teach concepts of introductory level assembly, and some of the students own Windows, others have Linux, and others have Mac OSX. How are all these students going to validate their homework if their home computer won't run the examples they are building.
I'm all in favor of low level programming, truth is I don't really see a difference in the abstraction of those macros and the abstraction of those system calls. You talk about OS development with Keith, but those system calls aren't going to be there for you when you do OS development. That being the case, learning system calls is as useless learning these macros.