UMBC CMSC 313 -- Sequential Instructions Previous | Next


Sequential Instructions

Different experts classify the instructions in many different way. One system has it as data transfer, I/O, and control. The Intel has many catagories, the General-Purpose Instructions is the first and it is divided into eleven sub-catagories.

Since we are going to cover the entire gauntlet of instructions, we will focus on only part of the General-Purpose. We also are going to focus on structured programming.

Since you are already familiar with that, we can relate to it. This includes most of the C/C++ operators. Since we can only do one "operator" at a time, the operator precedence does not apply. So let's look at them:

Assignment

The easiest of all, is to assign a simple constant. We want to put a constant into a variable, such as i = 12;
Now, let's look at a program that will demonstrate this, but will also show how to use the C printf function.
;; 	 [ The .data section ]		
;; This section is for defining constants, such as filenames or buffer sizes,
;; It includes the initialized variables

section .data
mystring  db 'i is %d',10,0


;; [ The .bss section ]
;; This section is where you declare your uninitialized variables.
;; They look something like this:
;; 
;; 	filename:	resb	255 	; REServe 255 Bytes
;; 	number:		resb	1	    ; REServe 1 Byte

section .bss
i:	resd	1

;; [ The .text section ] The actual code

section .text

;; Declarations for gcc so we can use the standard C libraries
extern printf 

    global main                       ;must be declared for linker (ld)
;; just like main in C -- if linking with gcc, must be main, otherwise does not have to.

main:                                 ;tell linker entry point

;; put your code here 

; give i a value
	mov     dword [i], 12

;Do a printf!
        push    dword [i]         ; one of my integer variables 
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add	esp, 8			  ; clean up the mess


;; The final part of the program must be a call to the operating system to exit
;;; the program.
        mov     ebx,0   ;successful termination of program
        mov     eax,1   ;system call number (sys_exit)
        int     0x80    ;call kernel

;; Notice the file just ends.
The output is:
[~/courses/umbc/CMSC313/spring04/lectures/Lect04]$ sequential
i is 12
[~/courses/umbc/CMSC313/spring04/lectures/Lect04]$ 
Try it and you will see that this is the equivalent of the C program:
#include 

int main( void )
{
	int i;

	i = 12;
	printf("i is %d\n", i );
	return 0;
}
    

Now to explain what went on!

We need to have an initialized variable that will hold the print specification needed by the the printf:

section .data
mystring  db 'i is %d',10,0

There is a declaration of an uninitialized variable named i. When using gcc, an int is four bytes long, so we must declare it as a double word to get a four-byte variable.

section .bss
i:	resd	1

The #include declares the prototype of the functions in stdio.h. We must explicitly declare those as extern, because their definition is external to this file. This is the same as in C/C++.

;; Declarations for gcc so we can use the standard C libraries
extern printf 

We have to give the variable a value. Notice there is a major distinction between the contents of the variable and the address of the variable! When we refer to the contains, we must put the brackets around the name of the variable! Without them, we are referring to the address of the variable!

When C/C++ invoke a function, the arguments to the function must be put on the stack. In the statement:

printf("i is %d\n", i ); There are two arguments, and they are put on the stack in reverse order (right to left): We use the push instruction to put something on the stack. However, the assembler won't help us and we have to specify the size of what we are putting on the stack, word or dword. Right now, we are doing things in 32 bits. This gives us:
        push    dword [i]         ; one of my integer variables 
        push    dword mystring    ; pointer into my data segment 

NOW! Let's invoke (or call) the function:

       call    printf 

Notice that two double words were put on the stack. The programmer must take off everything that was put on the stack! There are a couple of ways to do this. One is to pop the contents into a register:

	pop	eax
	pop	eax
Of course that does not work if we have something in eax that we do not want to lose! Another was is simply adjust the stack pointer so that we no longer point to what is no longer valid.
	add	esp, 8		; 2 doublewords X 4 bytes per double
                            ; word
Boy, that was hard...;-)

And Your Point Is?

You will be modifying that program to do Project 1!

Addition

Remember the instruction format:
label:  mnemonic  dest, src
When we want to add two things together, the sum will be in the dest. The mnemonic is hard -- add. Remember that the dest and src can not both be memory locations. Let's examine three different forms of addition, by converting C to assembly.

i = 3 + 4;

	mov	dword [i], 3      ; since we are placing i, we can use it temporarily.
	add     dword [i], 4

i = a + 2;

        mov	eax, [a]
	add	eax, 2
 	mov     dword [i], eax

i = a + b;

        mov	eax, [a]
	add	eax, [b]
 	mov     dword [i], eax

Subtraction

The mnemonic is sub.

Increment

Just as there is an increment in C/C++, there is one in assembly language. (Actually, there are two version in C/C++, pre-increment and post-increment, but we can only do one thing at a time, so we don't use pre/post, we write the instruction before or after the expression.) The mnemonic is inc and it only has one operand. So the statement i++; is
        inc     dword [i]	

Decrement

The mnemonic is dec
        dec     dword [i]

It Really Works!

The proof of the pudding is in the eating. Here is the program that does what we have been talking about!
;; 	 [ The .data section ]		
;; This section is for defining constants, such as filenames or buffer sizes,
;; It includes the initialized variables

section .data
mystring  db 'i is %d',10,0
a         dd 50
b         dd 100


;; [ The .bss section ]
;; This section is where you declare your uninitialized variables.
;; They look something like this:
;; 
;; 	filename:	resb	255 	; REServe 255 Bytes
;; 	number:		resb	1	; REServe 1 Byte

section .bss
i:	resd	1

;; [ The .text section ] The actual code

section .text

;; Declarations for gcc so we can use the standard C libraries
extern puts
extern printf 
extern scanf

    global main                       ;must be declared for linker (ld)
;; just like main in C -- if linking with gcc, must be main, otherwise does not have to.

main:                                 ;tell linker entry point

;; put your code here 

;
; Assignment
;

;i = 12;
	mov     dword [i], 12
        push    dword [i]         ; one of my integer variables 
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add	esp, 8
;
; Addition
;
;i = 3 + 4;
	mov	dword [i], 3      ; since we are placing i, we can use it temporarily.
	add     dword [i], 4

        push    dword [i]         ; one of my integer variables 
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add	esp, 8

;i = a + 2;
        mov	eax, [a]
	add	eax, [b]
 	mov     dword [i], eax

        push    dword [i]         ; one of my integer variables 
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add 	esp, 8

;i = a + b;	
        mov	eax, [a]
	add	eax, [b]
 	mov     dword [i], eax

        push    dword [i]         
        push    dword mystring    
        call    printf 
	add	esp, 8
; i++;
        inc     dword [i]
         
        push	dword [i]
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add	esp, 8
; i--;
        dec     dword [i]
         
        push	dword [i]
        push    dword mystring    ; pointer into my data segment 
        call    printf 
	add 	esp, 8

;; The final part of the program must be a call to the operating system to exit
;;; the program.
        mov     ebx,0   ;successful termination of program
        mov     eax,1   ;system call number (sys_exit)
        int     0x80    ;call kernel

;; Notice the file just ends.

Wait! This is too easy!

Yes, it is. I forgot to tell you the bad news. The size of the value must fit into the datatype. Suppose we have a datatype that holds one digit, zero through nine. If we add three and four, the answer is seven. Fits fine.

If we add nine and nine, we get.......trouble. It does not fit. But if we simply ignore the carry, then the answer is 8 and it fits.....Not a perfect solution. Well, all we need is a place to store the carry and it is OK. That is where the carry flag comes into play! In our case of 9 + 9, we get 8 with the carry going into the carry flag (CF). Think about how we did math in the third grade, one column at a time, with a carry.

Sometimes, we need to know if the answer is positive or negative! Every math operation copies the most significant bit into the Sign Flag (SF). Then all we have to do is check the SF to get that information. Nice and easy.

What if the number is zero? This is a special condition that can do a lot for us. Every math operation sets the Zero Flag (ZF) according to the results of the operation. If the answer is zero, the ZF is set, otherwise it is cleared. One way that we use it is to check for equality. If src - dest is zero, they are equal! We now have a test for equality! We will return to this later.


Previous | Next

©2004, Gary L. Burt