Author Topic: help needed: input/output using int 0x80  (Read 21209 times)

Offline madad58

  • Jr. Member
  • *
  • Posts: 2
help needed: input/output using int 0x80
« on: March 10, 2012, 03:45:26 AM »

Hello, I'm taking a system hardware class this semester and a few weeks back we started learning NASM. I had an assignment due a few weeks ago that needed me to write a NASM program. Here are the instructions:



The objective of this programming problem is to learn how input/output can be
performed in assembly language using the operating system (kernel) function int 0x80
and observe a few things related to input/output in general.
Write a NASM assembly language program to do the following.
? Read a signed integer number (up to 4 digits) from the standard input device
(keyboard) using the int 0x80 kernel function.
? Print the input data (exactly as read) on the standard output device (screen) using
the int 0x80 kernel function with a description like (if the input number is –1234):
The entered number is –1234
? Copy (move) the input data (exactly as read, without any change or conversion) into
the eax and ebx registers.
? Divide the input data by two by shifting the contents of the eax register to the right
by one bit and print the resultant data (exactly as in the eax register) with a
description like
Half of the entered number is
? Multiply the input data by two by shifting the contents of the ebx register to the left
by one bit and print the resultant data (exactly as in the ebx register) with a
description like
Double of the entered number is
Compare the output to the expected results and explain any discrepancy. Discuss what
needs to be done to print the results correctly.


I couldn't for the life of me write the program out properly... Now I have a new assignment due that requires a more complex program so I decided to go back to the initial one and attempt it before tackling the harder one.
This is as far as I can get... I really suck as NASM and seem to be missing minor details that are preventing me from writing proper programs.
I'm able to print the first message, read the number that the user inputs and then reprint it. For some reason my program ends there. I've been at this for hours and can't figure out why this is happening. If anybody can give me some help, it'd be greatly appreciated. I'd like to get an intuitive understanding of why my program isn't working and some guidance on how to continue.

Thanks in advance for your time and support.


Code: [Select]
segment      .data
   msg1   db   'Please input a signed integer number (up to 4 digits):',
   msg1len equ   $ - msg1   
   msg2   db   'The entered number is: ',
   msg2len   equ $ - msg2
   msg3   db 'Half if the entered number is',
   msgglen equ $ - msg3
   msg4   db 'Double of the entered number is'
   msg4len equ $ - msg4
   
segment      .bss
   num1   resb   2
   num2   resb   2

segment      .text
   global _start

_start:

PRINTQUERRY:
         mov eax,4         ;system call number for output(sys_write)
         mov ebx,1         ;default output device
         mov ecx, msg1      ;message to write
         mov edx, msg1len   ;message length   
         int 0x80         ;call kernel
         
GETNUMBER:         
         mov eax, 3        ;system call for input
         mov ebx, 2         ;default input device
         mov ecx, num1      ;stores the input into num1   
         mov edx, 4
         int 0x80
PRINTMESSAGE:
         mov eax, 4
         mov ebx, 1
         mov ecx, msg2      ;second message to write
         mov edx, msg2len   ;second message length   
         int 0x80

PRINTNUMBER:
         mov eax, 4
         mov ebx, 1
         mov ecx, num1      ;prints number
         mov edx, 4
         int 0x80
SHIFT:
         mov al, [num1]
         shr al, 1
         mov [num2],al
         
PRINTHALF:   
         mov eax, 4
         mov ebx, 1
         mov ecx, [num2]
         mov edx, 4
         int 0x80

         mov eax,1            ; The system call for exit (sys_exit)
         mov ebx,0            ; Exit with return code of 0 (no error)
         int 0x80    ;call kernel

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: help needed: input/output using int 0x80
« Reply #1 on: March 10, 2012, 07:21:28 AM »
I think we've had this assignment before. Maybe not...


Hello, I'm taking a system hardware class this semester and a few weeks back we started learning NASM. I had an assignment due a few weeks ago that needed me to write a NASM program. Here are the instructions:



The objective of this programming problem is to learn how input/output can be
performed in assembly language using the operating system (kernel) function int 0x80
and observe a few things related to input/output in general.
Write a NASM assembly language program to do the following.
? Read a signed integer number (up to 4 digits) from the standard input device
(keyboard) using the int 0x80 kernel function.

This is deceptive! We don't read "a signed integer number" from the keyboard. We read characters.

And ambiguous! Four digits plus the minus sign? Later instructions indicate so. That's five characters... and the linefeed makes six. sys_read doesn't return until it gets a linefeed, y'know... This can be dangerous! Try typing "1234ls(enter)". Hmmm... We really should flush the buffer after getting input! Them pesky users will type any old thing that comes into their head, y'know... For now, ASSume a well-behaved user...

Quote
? Print the input data (exactly as read) on the standard output device (screen) using
the int 0x80 kernel function with a description like (if the input number is –1234):
The entered number is –1234

Okay, this we can do... except that "-1234" will take five characters, not four...

Quote
? Copy (move) the input data (exactly as read, without any change or conversion) into
the eax and ebx registers.

Okay...

Quote
? Divide the input data by two by shifting the contents of the eax register to the right
by one bit and print the resultant data (exactly as in the eax register) with a
description like
Half of the entered number is

Mmmm... yeah, okay...

Quote
? Multiply the input data by two by shifting the contents of the ebx register to the left
by one bit and print the resultant data (exactly as in the ebx register) with a
description like
Double of the entered number is

Yeah... we just trashed ebx printing the other stuff, but it can be fixed...

Quote
Compare the output to the expected results and explain any discrepancy. Discuss what
needs to be done to print the results correctly.

Okay, I get it. It isn't supposed to work correctly. You're trying to "halve" and "double" characters, not numbers.

Quote
I couldn't for the life of me write the program out properly... Now I have a new assignment due that requires a more complex program so I decided to go back to the initial one and attempt it before tackling the harder one.

A good plan!

Quote
This is as far as I can get... I really suck as NASM and seem to be missing minor details that are preventing me from writing proper programs.

It gets easier as you gain experience. Honest.

Quote
I'm able to print the first message, read the number that the user inputs and then reprint it. For some reason my program ends there.

Well, you don't do too much more. When "printing half the number", you make an error "[num2]" instead of "num2" - that's why it isn't printing anything. Doesn't print anything sensible anyway.

Here's your code back with some "corrections". (I've left some of it for you to fill in) It reads and print's only four characters, so it won't do "-1234", as I guess it's supposed to - you might want to fix that... If the pesky user enters more, garbage comes out on the command line...

Code: [Select]
segment      .data
   msg1   db   'Please input a signed integer number (up to 4 digits):',
   msg1len equ   $ - msg1   
   msg2   db   'The entered number is: '
   msg2len   equ $ - msg2
   msg3   db 'Half of the entered number is'
   msgglen equ $ - msg3
   msg4   db 'Double of the entered number is'
   msg4len equ $ - msg4
   
segment      .bss
; too small!!!
;   num1   resb   2
;   num2   resb   2
   num1   resb   6
   num2   resb   6
    num3 resb 6
segment      .text
   global _start

_start:

PRINTQUERRY:
         mov eax,4         ;system call number for output(sys_write)
         mov ebx,1         ;default output device
         mov ecx, msg1      ;message to write
         mov edx, msg1len   ;message length   
         int 0x80         ;call kernel
         
GETNUMBER:         
         mov eax, 3        ;system call for input

; no, thats stderr!
;         mov ebx, 2         ;default input device
         mov ebx, 0         ;default input device
         mov ecx, num1      ;stores the input into num1   
         mov edx, 4
         int 0x80
PRINTMESSAGE:
         mov eax, 4
         mov ebx, 1
         mov ecx, msg2      ;second message to write
         mov edx, msg2len   ;second message length   
         int 0x80

PRINTNUMBER:
         mov eax, 4
         mov ebx, 1
         mov ecx, num1      ;prints number
         mov edx, 4
         int 0x80
SHIFT:
; at this point, you're not following instructions!
; "into the eax and ebx registers"

         mov eax, [num1]
mov ebx, [num1]
         shr eax, 1
         mov [num2],eax
; at this point, the instructions say to print the first result
; however, this'll trash ebx, so we better save it!
; maybe double it first...
shl ebx, 1
mov [num3], ebx
       
; print out the message about it first!
PRINTHALF:   
         mov eax, 4
         mov ebx, 1
; no, no no, you've got "[contents]" here
; you want the address of the stuff to write!
;         mov ecx, [num2]
         mov ecx, num2
         mov edx, 4
         int 0x80

; and the message about the "doubled" number

; and the "doubled" number, "num3"
mov eax, 4
mov ebx, 1
mov ecx, num3
mov edx, 4
int 0x80


         mov eax,1            ; The system call for exit (sys_exit)
         mov ebx,0            ; Exit with return code of 0 (no error)
         int 0x80    ;call kernel

I suppose the "more complex" program is handling numbers "right", so it probably isn't worth spending too much time perfecting this one. Look in nearby posts to see what needs to be done...

Best,
Frank



Offline madad58

  • Jr. Member
  • *
  • Posts: 2
Re: help needed: input/output using int 0x80
« Reply #2 on: March 10, 2012, 06:28:40 PM »
I want to start off by saying: Thank you for your help, Frank. It's really appreciated.

I fixed my code up and have made some headway but it still doesn't seem to be working properly.

Code: [Select]
segment .data
msg1 db 'Please input a signed integer number (up to 4 digits):',
msg1len equ $ - msg1
msg2 db 'The entered number is: ',
msg2len equ $ - msg2
msg3 db 'Half of the number is: ',
msg3len equ $ - msg3
msg4 db 'Double of the number is: '
msg4len equ $ - msg4

segment .bss
num1 resb 6
num2 resb 6
num3 resb 6

segment .text
global _start

_start:

PRINTQUERRY:
mov eax,4 ;system call number for output(sys_write)
mov ebx,1 ;default output device
mov ecx, msg1 ;message to write
mov edx, msg1len ;message length
int 0x80 ;call kernel

GETNUMBER:
mov eax, 3  ;system call for input
mov ebx, 0 ;default input device
mov ecx, num1 ;stores the input into num1
mov edx, 4 ; SHOULD THIS BE MORE? Do I have to take into account the line feed?
int 0x80

PRINTMESSAGE:
mov eax, 4
mov ebx, 1
mov ecx, msg2 ;second message to write
mov edx, msg2len ;second message length
int 0x80

PRINTNUMBER:
mov eax, 4
mov ebx, 1
mov ecx, num1 ;prints number
mov edx, 4
int 0x80

STORENUMBERS:
mov eax, [num1] ;storing the number into eax
mov ebx, [num1] ;storing the number into ebx

SHIFT:
shr eax, 1 ;dividing
mov [num2], eax ;storing half of the numb into num2
shl ebx, 1
mov [num3], ebx ;storing double of the numb into num3

PRINTHALFMESSAGE:
mov eax, 4
mov ebx, 1
mov ecx, msg3
mov edx, msg3len
int 0x80

PRINTHALF:
mov eax, 4
mov ebx, 1
mov ecx, num2
mov edx, 4
int 0x80

PRINTDOUBLEMESSAGE:
mov eax, 4
mov ebx, 1
mov ecx, msg4
mov edx, msg4len
int 0x80

PRINTDOUBLE:
mov eax, 4
mov ebx, 1
mov ecx, num3
mov edx, 4
int 0x80


mov eax,1            ; The system call for exit (sys_exit)
mov ebx,0            ; Exit with return code of 0 (no error)
int 0x80 ;call kernel

This is the output of the code:

Please input a signed integer number (up to 4 digits):1234
The entered number is: 1234Half of the number is: Double of the number is: bdfh

I'm thinking that a possible cause of the problem is how I use the edx. You mentioned something about the linefeed character so I'm thinking that in GETNUMBER, since I'm asking for 4 digits, I should make edx 5 to include the linefeed character? And when printing out the adjusted numbers should I do the same?

I also have a few other questions:

I'm not too familiar with the details of input/output in nasm... when the program reads in 1234, I now know that it's not the actual number but I'm not sure exactly what it is. Does it read the characters as ASCII? As in 1234 == 31 32 33 34 (hex). And how does edx affect the input/output size?

I also seem to have difficulty differentiating between [num1] and num1. The way I see it is that num1 is the address where the number is stored and [num1] is like dereferencing the address and getting the value stored there. So when I want to manipulate the actual number (like when shifting), I should use the [num1]. Is that about right?


Also, when I looked at sample codes online, I usually see int 80h, not int 0x80... what's the difference between these two and why would my teacher want me to use int 0x80?

Thanks in advance for you time and help.

Regards,
Adam
« Last Edit: March 10, 2012, 06:30:41 PM by madad58 »

Offline codeFoil

  • Jr. Member
  • *
  • Posts: 13
Re: help needed: input/output using int 0x80
« Reply #3 on: March 11, 2012, 12:26:45 AM »

I'm thinking that a possible cause of the problem is how I use the edx. You mentioned something about the linefeed character so I'm thinking that in GETNUMBER, since I'm asking for 4 digits, I should make edx 5 to include the linefeed character? And when printing out the adjusted numbers should I do the same?
This will work if and only if the user does not enter more characters.  A safer approach would be to
explicitly store a line feed character in your output buffer.

Quote
I'm not too familiar with the details of input/output in nasm... when the program reads in 1234, I now know that it's not the actual number but I'm not sure exactly what it is. Does it read the characters as ASCII? As in 1234 == 31 32 33 34 (hex).
Yes.

Quote
And how does edx affect the input/output size?
In this instance, the EDX register specifies how many bytes you want to read or write.

Quote
I also seem to have difficulty differentiating between [num1] and num1. The way I see it is that num1 is the address where the number is stored and [num1] is like dereferencing the address and getting the value stored there. So when I want to manipulate the actual number (like when shifting), I should use the [num1]. Is that about right?
Correct

Quote
Also, when I looked at sample codes online, I usually see int 80h, not int 0x80... what's the difference between these two and why would my teacher want me to use int 0x80?
The 0x80 notation is compatible with C and several other languages.  The 80h notation is more common in assembly languages.  NASM supports both, and your teacher may or may not have a good reason to prefer the C notation.

With the exception of the missing line feeds, your program does appear to be functioning.  The ASCII values that result from dividing 0x31,0x32,0x32,and 0x34 are control characters and might not have a graphical representation on your system.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: help needed: input/output using int 0x80
« Reply #4 on: March 11, 2012, 02:18:19 AM »
Hi Adam,

Lemme start with the easiest question first, and work backwards...

Quote
Also, when I looked at sample codes online, I usually see int 80h, not int 0x80... what's the difference between these two and why would my teacher want me to use int 0x80?

There's no difference. We could also write it as "int $80", or even "int 128" (decimal). All the same number (bit pattern). Your teacher probably prefers "0x80" because it's compatible with C. The way the boss/teacher prefers is the "right" way. :)

Quote
I also seem to have difficulty differentiating between [num1] and num1. The way I see it is that num1 is the address where the number is stored and [num1] is like dereferencing the address and getting the value stored there. So when I want to manipulate the actual number (like when shifting), I should use the [num1]. Is that about right?

Yup, that's exactly right. There may still be some question of what you need/want, in certain cases, but you understand the difference correctly. Important!

Now we get to the more complicated stuff. In sys_read (from stdin, anyway) edx is the maximum number of characters to read. If you've got space for 4 bytes in the buffer, and 4 in edx, you won't get any more than 4 characters in the buffer, although you might get less. If the pesky user enters more than 4 - including the linefeed - it won't overflow "your" buffer, but the excess will stay in the OS's i/o buffer to screw up your next read, or if you exit the program it'll show up on the command line. I suggested you try "1234ls" - you should see a directory listing. DON"T try "1234rm -fr ." or it'll do real damage! Unix is powerful, and will delete every file on your system if you tell it to. So don't. We really should be flushing that OS buffer after a sys_read...

Since you do have room in the buffer, you could increase edx to 5 and "catch" the linefeed if the user types "1234(enter)" (but "-1234enter" is still going to be too much). If you then made edx 5 in the sys_write, you'd get a newline after the number, which might improve the display. But once we've shifted the characters right or left, the linefeed isn't going to be a linefeed any more, so it isn't going to help that much - you'll just get one more "garbage character".

As I mentioned, sys_read on stdin doesn't return until it gets a linefeed. When it returns, the number of characters/bytes read is in eax. I always say, "including the linefeed", but this isn't always true. If edx were 4, and we saw "1234(enter)", eax would be 4 - which wouldn't include the LF... which would still be in the OS's buffer waiting to cause trouble later. We can use this fact to see if the (OS's) buffer needs flushing. If eax is less than edx, we should be okay. If eax = edx, and the last character in the (our) buffer is a LF (10 decimal, 0xA), we're okay. Otherwise, there's more "junk" which we may want to remove by reading again (and again) until we find the LF. Then we're really done. This may be more "advanced" than you want to get into right now. If eax is less than edx (maximum), you might want to save this somewhere as a guide to how many characters to sys_write later.

sys_write prints however many characters it says in edx, without regard for any zero "terminator", and if there's a (or more) LF(s), it just starts a new line(s)...

As you say, the input is going to be in ascii characters - 31 32 33 34 or maybe 31 32 33 34 0A (all hex). As you can imagine, when we shift this right or left, it isn't going to be ascii characters representing decimal digits any more! If you look at an ascii chart, you'll see that numbers less than 0x20 (32 decimal) are "control characters" not "printable characters". So it isn't a great surprise that "half the number" doesn't display much. I get a "garbage character" there, otherwise my output looks about like your output. That may be "as good as it gets".

As I understand it, the whole point of this assignment is "discuss what needs to be done to get the expected results". There's a clue where he says, "without any conversion...". To do arithmetic on numbers, we need to convert a "string" (I think, in C terms, a "string" has to be zero-terminated, so what we have is just "an array of characters"... but I'll be sloppy and call it a "string") into the number that the characters represent. To display the results, we need to convert the number into a "string" of characters representing the number, and then print that string. I don't know if you're expected to write those conversion routines yourself, or if they will be supplied in some kind of "library". There's some "teacher-supplied code" in the "more than one filename" thread. IMHO it's kinda buggy, but it might give you a general idea what needs to be done...

You mentioned that this assignment is past due. If you can get partial credit for "better late than never", it might be worth trying to clean up the output a little, but I don't think it's ever going to look "good". I'd move on, I think...

Best,
Frank

P.S. Thanks for the input, codeFoil. I'll post anyway, although we say much the same thing. :)