Author Topic: Input integer Output integer  (Read 19438 times)

Offline XxWh1t3gh0stxX

  • Jr. Member
  • *
  • Posts: 13
Input integer Output integer
« on: November 13, 2012, 01:09:03 AM »
Hi, I am having trouble when I read in an integer from the keyboard and I try to display it to the screen back as the same integer. It pops out as an ASCII character. Am I doing this wrong or is there a way to read it as an integer and not a character, or do I need to convert it back? Thanks
Code: [Select]
; Compiling this code for 32-bit use:
;    nasm -f elf file.asm
;    gcc -m32 -o file file.o
;
;~.~. Definitions for readability: ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
        %define  SYS_EXIT   1
        %define  SYS_READ   3
        %define  SYS_WRITE  4
        %define  STDIN      0
        %define  STDOUT     1
        %define  STDERR     2
        %define  MAX_NUMBER   5000
SECTION .data
format: db "The number is %d." , 10
SECTION .bss
input: resd MAX_NUMBER
SECTION .text
extern printf
global main
main:
nop
GetInput:
        mov EAX, SYS_READ
        mov EBX, STDIN
        mov ECX, input
        int 80H
        mov DWORD[input + EAX - 1], 0
Calculate:
        mov EAX, DWORD[input]
Display:
        push EAX
        push format
        call printf
        add ESP, 8
        ret

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Input integer Output integer
« Reply #1 on: November 13, 2012, 01:51:50 AM »
Hopefully my comments will point you in the right direction. Just search for <~~
Code: [Select]
; Compiling this code for 32-bit use:
;    nasm -f elf file.asm
;    gcc -m32 -o file file.o
;
;~.~. Definitions for readability: ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
        %define  SYS_EXIT   1
        %define  SYS_READ   3
        %define  SYS_WRITE  4
        %define  STDIN      0
        %define  STDOUT     1
        %define  STDERR     2
        %define  MAX_NUMBER   5000
SECTION .data
format: db "The number is %d." , 10
SECTION .bss
input: resd MAX_NUMBER
SECTION .text
extern printf
extern atoi ; <~~ converts string to int32.
global main
main:
nop
GetInput:
        mov EAX, SYS_READ
        mov EBX, STDIN
        mov ECX, input
        mov EDX, MAX_NUMBER ; <~~ You must specify the length of the buffer.
        int 80H
        mov DWORD[input + EAX - 1], 0
Calculate:
;; <~~ atoi is a C function used in converting strings to integers.
push dword input
call atoi
add ESP, 4
Display:
        push EAX
        push format
        call printf
        add ESP, 8
        ret

About Bryant Keller
bkeller@about.me

Offline XxWh1t3gh0stxX

  • Jr. Member
  • *
  • Posts: 13
Re: Input integer Output integer
« Reply #2 on: November 13, 2012, 02:06:35 AM »
What if I choose to not use C calls would this end up being more difficult?

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Input integer Output integer
« Reply #3 on: November 13, 2012, 04:44:58 AM »
Sure. But keep in mind, you are already using C. printf and the setup code for main (__libc_start_main). Since you are already using these, there is absolutely no overhead for using atoi(). In fact, by using _more_ of the CRT, which is already loaded, you can make your code EASIER.

Code: [Select]
; Compiling this code for 32-bit use:
;    nasm -f elf file.asm
;    gcc -m32 -o file file.o
;
;~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.

SECTION .data
output_format: db "The number is "
input_format: db "%d" ; <~~ this is a bit of memory trickery, we reuse this part
end_of_format: db 0, 0 ; <~~ and change the first byte to a newline before output.

;~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.

SECTION .bss
input: resd 1 ; <~~ scanf handles conversion for us

;~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.

SECTION .text
extern printf
extern scanf
global main
main:
nop
GetInput:
;; Read input with scanf() function.
push dword input
push dword input_format
call scanf
add ESP, 8
Calculate:
;; No need to calculate anything, scanf() handles input conversion.
Display:
;; Display results using printf().
mov byte [end_of_format], 10 ; <~~ add a newline for printf()
        push dword [input] ; <~~ here we pass the contents of our input variable.
        push output_format
        call printf
        add ESP, 8
        ret
« Last Edit: November 13, 2012, 04:46:37 AM by Bryant Keller »

About Bryant Keller
bkeller@about.me

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Input integer Output integer
« Reply #4 on: November 13, 2012, 07:38:34 AM »
Sure you can do it without the bloated C calls! Whether it's more "difficult" or more "fun" is a matter of opinion... (no, there is no way to read an integer from the keyboard)

Code: [Select]
; Compiling this code for 32-bit use:
;    nasm -f elf file.asm
;    ld -melf_i386 -o file file.o
;

global _start

;~.~. Definitions for readability: ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
        %define  SYS_EXIT   1
        %define  SYS_READ   3
        %define  SYS_WRITE  4
        %define  STDIN      0
        %define  STDOUT     1
        %define  STDERR     2
%define LF 10
        %define  MAX_NUMBER   16
SECTION .data
    format: db "The number is: " ; not really used

SECTION .bss
    buffer resb MAX_NUMBER
    number resd 1

SECTION .text
_start:
    nop
GetInput:
    mov EAX, SYS_READ
    mov EBX, STDIN
    mov ECX, buffer
    mov edx, MAX_NUMBER
    int 80H
    cmp byte [ecx + eax - 1], LF
    jz Calculate

; pesky user has tried to overflow us!
; flush the buffer!
    sub esp, 4 ; temporary "buffer"
flush:
    mov eax, SYS_READ
    ; ebx still okay
    mov ecx, esp ; buffer is on the stack
    mov edx, 1
    int 80h
    cmp byte [ecx], LF
    jnz flush
    add esp, 4 ; "free" our "buffer"

Calculate:
    push buffer
    call atoi
    add esp, 4
    mov [number], eax ; not really used...

    add eax, 1 ; just to do something

    mov esi, buffer
    mov ecx, MAX_NUMBER
    call binasc
    mov byte [esi + ecx], LF
    inc ecx
   
Display:
    mov edx, ecx
    mov ecx, esi
    mov ebx, STDOUT
    mov eax, SYS_WRITE
    int 80h

exit:
    mov eax, SYS_EXIT
    xor ebx, ebx
    int 80h
;--------------------

;--------------------
atoi:
    push ebx
   
    mov edx, [esp + 8]  ; pointer to string
    xor ebx, ebx ; assume not negative
   
    cmp byte [edx], '-'
    jnz notneg
    inc ebx ; indicate negative
    inc edx ; move past the '-'
notneg:

    xor eax, eax        ; clear "result"
.top:
    movzx ecx, byte [edx]
    inc edx
    cmp ecx, byte '0'
    jb .done
    cmp ecx, byte '9'
    ja .done
   
    ; we have a valid character - multiply
    ; result-so-far by 10, subtract '0'
    ; from the character to convert it to
    ; a number, and add it to result.
   
    lea eax, [eax + eax * 4]
    lea eax, [eax * 2 + ecx - '0']

    jmp short .top
.done:
    test ebx, ebx
    jz notminus
    neg eax
notminus:
    pop ebx
    ret
;------------------------

;--------------------
; from Chuck Crayne - RIP, Chuck.
;convert binary to ascii
;call with eax = signed binary number
; esi = address of output string
; ecx = length of output string
;returns esi = 1st printed digit
; ecx = no of digits printed (includes sign if any)
; other registers preserved
binasc: push edx
push ebx
push edi
push eax
mov edi,esi ;save start of string
ba1: mov byte [esi],' ' ;fill string with blanks
inc esi
loop ba1
mov ebx,10 ;initialize divisor
or eax,eax ;value negative?
jns ba2 ;no problem
neg eax ;make it positive
ba2: xor edx,edx ;clear high part of dividend
div ebx ;divide by 10
add dl,'0' ;convert to ascii digit
dec esi ;step backwards through buffer
mov [esi],dl ;store digit
inc ecx
cmp esi,edi ;out of space
jz ba4 ;yes - quit
or eax,eax ;all digits printed?
jnz ba2 ;no - keep trucking
pop eax ;get original value
or eax,eax ;negative?
jns ba3 ;no - quit
dec esi ;place for sign
mov byte [esi],'-'
inc ecx ;add to char count
ba3: pop edi
pop ebx
pop edx
ret
ba4: pop eax
jmp ba3
;-------------------

Bryant's version, stripped, 2712 bytes. My version, stripped, 600 bytes. Tell me again about "overhead"?

Best,
Frank


Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Input integer Output integer
« Reply #5 on: November 13, 2012, 04:54:37 PM »
Bryant's version, stripped, 2712 bytes. My version, stripped, 600 bytes. Tell me again about "overhead"?

I have to throw a red flag here.  This is not an apples-to-apples comparison.  For simple ascii integer input/output no doubt Frank's modified version is smaller.  However, if/when the requirements expand to include floating point values then the use of scanf/printf is preferred.  That is the main difference in overhead - full real number support vs. integer only.  In addition, I believe that the C library init code could also be eliminated in this case.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Input integer Output integer
« Reply #6 on: November 13, 2012, 06:00:39 PM »
True, it's more of a cherry vs watermelon comparison. True, scanf/printf includes much functionallity that we don't need or use. True, my homemade atof/ftoa (which isn't very good!) is larger (but not as large as hooking in the C calls) - 1500 bytes in a simple demo.  True, we can do it without the "startup code" (init, CRT, whatever you call it)... to my continued astonishment, we don't need that to use printf/scanf/etc. I can try a version like that, if anyone cares... True, the C library is in memory anyway - might as well use it. But "absolutely no overhead" is a stretch!

XxWh1t3gh0stxX asked if we can do it without the C calls. Yeah, we can.

Best,
Frank


Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Input integer Output integer
« Reply #7 on: November 14, 2012, 01:09:49 AM »
Frank,

Of course doing the code in pure assembly is more optimal. In your example, you didn't just switch to system calls, you took away the C library which he was already using completely. My mention of "no overhead" was that he was already using __libc_start_main() and printf() which require the CRT, which means using other features of the CRT is not going to create any overhead for the application in question.

For examples sake, lets take and change your _start declaration back to main and build how he compiled it earlier:

Code: [Select]
; Compiling this code for 32-bit use:
;    nasm -f elf file.asm
;    gcc -m32 -o file file.o
;    strip ./file
;


;~.~. Definitions for readability: ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
        %define  SYS_EXIT   1
        %define  SYS_READ   3
        %define  SYS_WRITE  4
        %define  STDIN      0
        %define  STDOUT     1
        %define  STDERR     2
%define LF 10
        %define  MAX_NUMBER   16
SECTION .data
    format: db "The number is: " ; not really used

SECTION .bss
    buffer resb MAX_NUMBER
    number resd 1

SECTION .text
global main
main:
    nop
GetInput:
    mov EAX, SYS_READ
    mov EBX, STDIN
    mov ECX, buffer
    mov edx, MAX_NUMBER
    int 80H
    cmp byte [ecx + eax - 1], LF
    jz Calculate

; pesky user has tried to overflow us!
; flush the buffer!
    sub esp, 4 ; temporary "buffer"
flush:
    mov eax, SYS_READ
    ; ebx still okay
    mov ecx, esp ; buffer is on the stack
    mov edx, 1
    int 80h
    cmp byte [ecx], LF
    jnz flush
    add esp, 4 ; "free" our "buffer"

Calculate:
    push buffer
    call atoi
    add esp, 4
    mov [number], eax ; not really used...

    add eax, 1 ; just to do something

    mov esi, buffer
    mov ecx, MAX_NUMBER
    call binasc
    mov byte [esi + ecx], LF
    inc ecx
   
Display:
    mov edx, ecx
    mov ecx, esi
    mov ebx, STDOUT
    mov eax, SYS_WRITE
    int 80h

exit:
    mov eax, SYS_EXIT
    xor ebx, ebx
    int 80h
;--------------------

;--------------------
atoi:
    push ebx
   
    mov edx, [esp + 8]  ; pointer to string
    xor ebx, ebx ; assume not negative
   
    cmp byte [edx], '-'
    jnz notneg
    inc ebx ; indicate negative
    inc edx ; move past the '-'
notneg:

    xor eax, eax        ; clear "result"
.top:
    movzx ecx, byte [edx]
    inc edx
    cmp ecx, byte '0'
    jb .done
    cmp ecx, byte '9'
    ja .done
   
    ; we have a valid character - multiply
    ; result-so-far by 10, subtract '0'
    ; from the character to convert it to
    ; a number, and add it to result.
   
    lea eax, [eax + eax * 4]
    lea eax, [eax * 2 + ecx - '0']

    jmp short .top
.done:
    test ebx, ebx
    jz notminus
    neg eax
notminus:
    pop ebx
    ret
;------------------------

;--------------------
; from Chuck Crayne - RIP, Chuck.
;convert binary to ascii
;call with eax = signed binary number
; esi = address of output string
; ecx = length of output string
;returns esi = 1st printed digit
; ecx = no of digits printed (includes sign if any)
; other registers preserved
binasc: push edx
push ebx
push edi
push eax
mov edi,esi ;save start of string
ba1: mov byte [esi],' ' ;fill string with blanks
inc esi
loop ba1
mov ebx,10 ;initialize divisor
or eax,eax ;value negative?
jns ba2 ;no problem
neg eax ;make it positive
ba2: xor edx,edx ;clear high part of dividend
div ebx ;divide by 10
add dl,'0' ;convert to ascii digit
dec esi ;step backwards through buffer
mov [esi],dl ;store digit
inc ecx
cmp esi,edi ;out of space
jz ba4 ;yes - quit
or eax,eax ;all digits printed?
jnz ba2 ;no - keep trucking
pop eax ;get original value
or eax,eax ;negative?
jns ba3 ;no - quit
dec esi ;place for sign
mov byte [esi],'-'
inc ecx ;add to char count
ba3: pop edi
pop ebx
pop edx
ret
ba4: pop eax
jmp ba3
;-------------------

Code: [Select]
-rwxr-xr-x 1 bkeller bkeller 3012 Nov 13 20:08 file
My earlier version when built using the same commands (including the strip) is:

Code: [Select]
-rwxr-xr-x 1 bkeller bkeller 2928 Nov 13 20:12 file
There is no overhead because he has already incurred the primary penalty for using the start-up routine.
« Last Edit: November 14, 2012, 01:17:56 AM by Bryant Keller »

About Bryant Keller
bkeller@about.me

Offline XxWh1t3gh0stxX

  • Jr. Member
  • *
  • Posts: 13
Re: Input integer Output integer
« Reply #8 on: November 14, 2012, 01:17:52 AM »
Thanks guys for all the help I solved it using scanf().

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Input integer Output integer
« Reply #9 on: November 14, 2012, 01:19:11 AM »
No problem XxWh1t3gh0stxX. And Frank is right, if you decide to forgo the CRT itself completely (using _start and making use of the system calls) then the code will be dramatically lighter.

About Bryant Keller
bkeller@about.me

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Input integer Output integer
« Reply #10 on: November 14, 2012, 02:04:59 AM »
What if I choose to not use C calls would this end up being more difficult?

So, to answer your question, doing it yourself is initially "more difficult".  There are many design criteria and implementation details you need to factor in.  Making use of code already written by professionals is A Good ThingTM.

However, as an assembly language programmer how else do you plan on learning without doing?  Practice makes perfect, and writing your own atoi() or any other function for that matter is a great way to experiment, fail, learn from your failures, then succeed.