Author Topic: Building a list and averaging  (Read 10404 times)

Offline kazo0

  • Jr. Member
  • *
  • Posts: 3
Building a list and averaging
« on: March 11, 2011, 05:22:35 PM »
Hi all,

I need to read in 10 2-digit numbers from the user and then output the numbers along with the average.

I am able to read in the numbers one by one, the problem is I have no idea how to build a list to hold all of the 10 digits in one spot.

I am still very new and I do not understand a lot of things so everytime I read in from the user, the digit is placed into what I called 'buffer' and I defined this in the .bss section as:

buffer resb 1

after that I am trying to build a list with these numbers so I am trying to do:

mov [list+esi], buffer

where I defined 'list' in .bss as

list resb 10

and I keep getting the error: operation size not specified at the line "mov [list+esi], buffer"

I am really lost as to how to build this list and also the sizes that I am supposed to be specifying and indexing by. Any help would be appreciated.

Thanks!

Code: [Select]
segment .data
        msg db 'Enter a digit from 10 to 99: #'
        len equ $-msg

segment .bss
        buffer: resb 1
        list: resb 10
segment .text
        global _start
_start:
        mov esi, 0
        mov ecx, 10

read:   mov eax, 4
        mov ebx, 1
        mov edi, ecx
        mov ecx, msg
        mov edx, len
        int 0x80

        mov eax, 3
        mov ebx, 0
        mov ecx, buffer
        int 0x80
        mov [list+esi], buffer   <----- Error
        add edi, 1
        mov ecx, edi
        loop read

        mov eax, 4
        mov ecx, 1
        mov ecx, list
        mov edx, 20
        int 0x80

exit:   mov eax, 1
        int 0x80
« Last Edit: March 11, 2011, 05:27:53 PM by kazo0 »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Building a list and averaging
« Reply #1 on: March 12, 2011, 06:01:48 AM »
Well... you've got several issues here... Most important, perhaps, is that we can't get "numbers" from the keyboard, we get "characters"... specifically the ascii codes for the characters - the ascii code for '0' is 30h (48 decimal), the ascii code for '9' is 39h. Ascii codes outside of that range do not represent "decimal digits"  (what will you do if the pesky user enters one?). There is no character '10'! So you're going to have to read two characters, say '1' and '0', and figure out that they represent the number 10 (0Ah). That's what you want to put in your "list".

When you get to the end, you've got an average, say 15. There's no character '15' that we can print, we're going to have to print a '1' and then a '5'.

These "ascii-to-integer" and "integer-to-ascii" conversions are conveniently done as subroutines (no sense repeating the code every time you need to do it!). If you want to use the C library, "scanf" and "printf" will do nicely. So far, you've used only the int 80h interface - it's a shame to drag in the whole C library just to convert a few two-digit numbers!  (but it's sitting there in memory, just waiting to be used, and is fairly easy, if you want to do it that way).

I happen to think that it's a pretty good "exercise" for a beginner, figuring out how to put together a sequence of simple-minded CPU instructions to "do something useful". Other people think it's a "waste of time", making a beginner do that. Often there's a "read_int" and "write_int" routine included in your "course materials" that you can use. You can find examples to "go by" (or "borrow") fairly easily. We can discuss it further, if you want to go that route. You'll need to write, or learn to use, such a routine, in any case!

Code: [Select]
segment .data
        msg db 'Enter a digit from 10 to 99: #'
        len equ $-msg

You can't very well enter a number between 10 and 99 with just "a" digit. This is just a nit-pick, of course. You can say anything you like. It is traditional to be "user friendly" but you can start off with, "Listen up, Earthling!" if you want. I'd just refer to it as "a number"... if you say "decimal number" (even though that's what you want), they're liable to give you something with a decimal point in it (which you don't want!). Maybe even say "whole number"... gotta keep it simple for users - they get confused easily. :)

Code: [Select]
segment .bss
        buffer: resb 1

This isn't big enough! You'll want space for two bytes/characters, at least, plus perhaps another for the CR that will terminate input, or perhaps a terminating zero (if you're going to use C, you'll want a terminating zero... which the sys_read call does not provide - you'll have to add it yourself). I'd go with "resb 3", I think.

Code: [Select]
        list: resb 10
segment .text
        global _start
_start:
        mov esi, 0
        mov ecx, 10

read:   mov eax, 4
        mov ebx, 1
        mov edi, ecx

Okay, if you're going to do it that way. You might want to move this out of the middle of the sys_write setup, just for clarity...

Code: [Select]
        mov ecx, msg
        mov edx, len
        int 0x80

        mov eax, 3
        mov ebx, 0
        mov ecx, buffer

edx, the "max length to read" is still the length of your prompt, from above. You probably don't want to let the user enter that many characters - you only asked for two! (might want to allow three?)

Code: [Select]
        int 0x80
        mov [list+esi], buffer   <----- Error

Nasm demands a "size" here. Just "buffer", without "[]", refers to the address of the buffer, a dword. But your "list" is just bytes. Doesn't matter, basically you don't want to do it at all. At this point, you need that "ascii to number" routine. That will probably return the number in al (or eax - but we only need al). If you do:

Code: [Select]
mov [list + esi], al

... Nasm won't have to ask the size - the al register, being a byte, determines the size.

Code: [Select]
        add edi, 1

I think you want esi, here (to get to the next position in "list"). If you do this (edi), ecx will be incremented, too, and your loop will never end!

Code: [Select]
        mov ecx, edi
        loop read

The "loop" instruction uses ecx. The system calls want to use ecx, too. You can save and restore ecx as your loop counter by moving it into and out of edi (as you've done... almost), or by pushing and popping it. But you can do a loop without the "loop" instruction. S'pose you'd loaded edi with 10 (your loop count) up top where you used ecx. Then you could:

Code: [Select]
dec edi ; or sub edi, 1
jnz read

... does the same thing ("loop" is shorter, but slower), without depending on ecx. Either should work...

Code: [Select]
        mov eax, 4
        mov ecx, 1

ebx?

Code: [Select]
        mov ecx, list
        mov edx, 20

20? Okay, ten numbers at two bytes each... but "list" is only ten bytes! This isn't going to do what you want, anyway - you need to use that "number to ascii" routine, and print the resulting string...

Code: [Select]
        int 0x80

exit:   mov eax, 1
        int 0x80

It's "polite" to return some "meaningful" value (in ebx, but only bl "counts"). Zero usually indicates "no error". But you're not using it anyway. Good job remembering to exit cleanly back to the shell!

Think about what you want to do for those "ascii to integer" and "integer to ascii" routines. Easiest if you've been "given" some routines to use, but they aren't too hard to write - give it a shot. Or libc is there for us to use... We'll talk again...

Best,
Frank