Author Topic: Help Needed  (Read 35498 times)

Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Help Needed
« 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.

Code: [Select]
;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.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #1 on: June 03, 2012, 11:26:46 AM »
You're on the right track with:
Code: [Select]
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!
Code: [Select]
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...
Code: [Select]
...
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:
Code: [Select]
...
  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...
Code: [Select]
...
  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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #2 on: June 03, 2012, 12:55:47 PM »
First. Thanks for the prompt reply.

I got every bit of it. And seriouly,. I didnt knew that
Code: [Select]
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 ?

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #3 on: June 03, 2012, 10:39:23 PM »
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...
Code: [Select]
...
  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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #4 on: June 04, 2012, 12:34:45 AM »
Here is the code which is just pesking me up.

Code: [Select]


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.

Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #5 on: June 04, 2012, 12:39:13 AM »
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.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #6 on: June 04, 2012, 02:52:41 AM »
I think your main problem is right here:
Code: [Select]
        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...
Code: [Select]
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...
Code: [Select]
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...
Code: [Select]
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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #7 on: June 04, 2012, 07:52:06 AM »
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.
Code: [Select]
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
Code: [Select]
mov byte[ecx], eax     ; If eax contains 8000
or

Code: [Select]
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' ?


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #8 on: June 04, 2012, 12:27:10 PM »
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 ?
Code: [Select]
;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.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #9 on: June 04, 2012, 02:02:43 PM »
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!)
Code: [Select]
; 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...

Quote
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...
Code: [Select]
count resb 16
... ought to be plenty. Won't hurt to make the buffer too big...

Quote
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.

Quote
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...
Code: [Select]
;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?
Code: [Select]
  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"...

Code: [Select]
    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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #10 on: June 04, 2012, 08:22:12 PM »
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.

Code: [Select]
;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..

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #11 on: June 05, 2012, 05:07:43 AM »
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...

Code: [Select]
;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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #12 on: June 07, 2012, 06:58:04 PM »
I am trying to make my own program for converting number to string. Lets share my progress.
Code: [Select]
;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

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help Needed
« Reply #13 on: June 08, 2012, 01:50:40 AM »
Good! Looks like you're trying a worthwhile optimization over what I showed you...

Code: [Select]
;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!

Code: [Select]
    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)

Code: [Select]
        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.

Code: [Select]
    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...
Code: [Select]
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:
Code: [Select]
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


Offline grvtiwari

  • Jr. Member
  • *
  • Posts: 12
Re: Help Needed
« Reply #14 on: June 09, 2012, 09:33:53 PM »
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.
Code: [Select]
;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....