<- previous    index    next ->

Lecture 6 Branching and loops

The basic integer compare instruction is  "cmp"
Following this instruction is typically one of:
  JL  label  ; jump on less than  "<"
  JLE label  ; jump on less than or equal "<="
  JG  label  ; jump on greater than ">"
  JGE label  ; jump on greater than or equal ">="
  JE  label  ; jump on equal "=="
  JNE label  ; jump on not equal "!="

After many integer arithmetic instructions
  JZ  label  ; jump on zero
  JNZ label  ; jump on non zero
  JS  label  ; jump on sign plus
  JNS labe;  ; jump on sign not plus

Note: Use 'cmp' rather than 'sub' for comparison.
Overflow can occur on subtraction resulting in sign inversion.

Convert a "C" 'if' statement to nasm assembly ifint.asm
The significant features are:
1) use a compare instruction for the test
2) put a label on the start of the false branch (e.g. false1:)
3) put a label after the end of the 'if' statement (e.g. exit1:)
4) choose a conditional jump that goes to the false part
5) put an unconditional jump to (e.g. exit1:) at the end of the true part

; ifint.asm  code ifint.c for nasm 
; /* ifint.c an 'if' statement that will be coded for nasm */
; #include <stdio.h>
; int main()
; {
;   int a=1;
;   int b=2;
;   int c=3;
;   if(a<b)
;     printf("true a < b \n");
;   else
;     printf("wrong on a < b \n");
;   if(b>c)
;     printf("wrong on b > c \n");
;   else
;     printf("false b > c \n");
;   return 0;
;}
; result of executing both "C" and assembly is:
; true a < b
; false b > c 
	
	global	main		; define for linker
        extern	printf		; tell linker we need this C function
        section .data		; Data section, initialized variables
a:	dd 1
b:	dd 2
c:	dd 3
fmt1:   db "true a < b ",10,0
fmt2:   db "wrong on a < b ",10,0
fmt3:   db "wrong on b > c ",10,0
fmt4:   db "false b > c ",10,0

	section .text
main:	mov	eax,[a]
	cmp	eax,[b]
	jge	false1		; choose jump to false part
	; a < b sign is set
        push    dword fmt1	; printf("true a < b \n"); 
        call    printf	
        add     esp,4
        jmp	exit1		; jump over false part
false1:	;  a < b is false 
        push    dword fmt2	; printf("wrong on a < b \n");
        call    printf
        add     esp,4
exit1:				; finished 'if' statement

	mov	eax,[b]
	cmp	eax,[c]
	jle	false2		; choose jump to false part
	; b > c sign is not set
        push    dword fmt3	; printf("wrong on b > c \n");
        call    printf	
        add     esp,4
        jmp	exit2		; jump over false part
false2:	;  a > b is false 
        push    dword fmt4	; printf("false b :gt; c \n");
        call    printf
        add     esp,4
exit2:				; finished 'if' statement

	mov	eax,0		; normal, no error, return value
	ret			; return 0;



Convert a "C" loop to nasm assembly  loopint.asm
The significant features are:
1) "C" int  is 4-bytes, thus  dd1[1] becomes  dword [dd1+4]
                              dd1[99] becomes  dword [dd1+4*99]

2) "C" int  is 4-bytes, thus  dd1[i]; i++; becomes  add edi,4
   since "i" is never stored, the register  edi  holds "i"

3) the 'cmp' instruction sets flags that control the jump instruction.
   cmp  edi,4*99   is like  i<99
   jnz  loop1      jumps if register  edi  is not  4*99

; loopint.asm  code loopint.c for nasm 
; /* loopint.c a very simple loop that will be coded for nasm */
; #include <stdio.h>
; int main()
; {
;   int dd1[100];
;   int i;
;   dd1[0]=5; /* be sure loop stays 1..98 */
;   dd1[99]=9;
;   for(i=1; i<99; i++) dd1[i]=7;
;   printf("dd1[0]=%d, dd1[1]=%d, dd1[98]=%d, dd1[99]=%d\n",
;           dd1[0], dd1[1], dd1[98],dd1[99]);
;   return 0;
;}
	section	.bss
dd1:	resd	100
i:	resd	1		; actually unused, kept in register

	section .text
	global main
main:	mov dword [dd1],5	; dd1[0]=5;
	mov dword [dd1+99*4],9	; dd1[99]=9;

	mov edi,4		; i=1; /* 4 bytes */
loop1:	mov dword [dd1+edi],7	; dd1[i]=7;
	add edi,4		; i++; /* 4 bytes */
	cmp edi,4*99		; i<99
	jne	loop1		; loop until i=99
	
        extern	printf		; the C function, to be called
        section .data		; Data section, initialized variables
fmt:    db "dd1[0]=%d, dd1[1]=%d, dd1[98]=%d, dd1[99]=%d",10,0

        section .text           ; Code section, continued
	push	dword [dd1+99*4]	; dd1[99]
        push    dword [dd1+98*4]	; dd1[98]
	push	dword [dd1+4]	; dd1[1]
        push    dword [dd1]	; dd1[0]
        push    dword fmt	; address of format string
        call    printf		; Call C function
        add     esp, 20		; pop stack 5 push times 4 bytes

	mov	eax,0		; normal, no error, return value
	ret			; return 0;
                                ; no registers needed to be saved 
	
	
Previously, integer arithmetic in "C" was converted to
NASM assembly language. The following is very similar
(cut and past) of intarith.asm to intlogic.asm that
shows the "C" operators "&" and, "|" or, "^" xor, "~" not.

intlogic.asm

One significant use of loops is to evaluate polynomials and
convert numbers from one base to another.
(Yes, this is related to project 1 for CMSC 313)

The following program has seven loops.
Loop1 (h1loop) uses Horners method to convert ASCII decimal digits
      to binary, using a sentinal, '.', with 'cmp' and 'je'
      to exit the loop

Loop2 (h2loop) uses Horners method to convert ASCII decimal digits
       using 'edi' as an index, 'ecx' and 'loop' to do the loop.

Loop3 (h3loop) uses Horners method to evaluate a polynomial,
       using 'edi' as an index, 'ecx' and 'loop' to do the loop.

Loop4 (h4loop) uses Horners method, with data order optimized,
      using 'ecx' as both index and loop counter, to get a
      three instruction loop.

Loop5 (h5loop) uses Horners method to evaluate a polynomial
      using double precision floating point. Note 8 byte
      increment and quad word to printf.

Loop6 (h6loop) uses Horners method to evaluate the fractional
      part of a double precision floating point polynomial.
      Note that divide is used in place of multiply and the
      least significant coefficient is used first.

Loop7 (h7loop) uses Horners method to convert ASCII decimal
      fraction to binary. Note that shifting is needed
      because the binary point can not be at the right end
      of the word.

Loop8 (h8loop) just prints 16 bits from the result of Loop7
      as ASCII characters.

Study horner.asm to understand
the NASM coding of the loops.

; horner.asm  Horners method of evaluating polynomials
;
; given a polynomial  Y = a_n X^n + a_n-1 X^n-1 + ... a_1 X + a_0
; a_n is the coefficient 'a' with subscript n. X^n is X to nth power
; compute y_1 = a_n * X + a_n-1
; compute y_2 = y_1 * X + a_n-2
; compute y_i = y_i-1 * X + a_n-i   i=3..n
; thus    y_n = Y = value of polynomial 
;
; in assembly language:
;   load some register with a_n, multiply by X
;   add a_n-1, multiply by X, add a_n-2, multiply by X, ...
;   finishing with the add  a_0
;
; for conversion of decimal to binary, X=10
; 
	extern	printf
	section	.data
decdig:	db	'5','2','8','0','.' ; decimal integer 5280
fmt:	db	"%d",10,0
	
	global	main
	section	.text
main:	push	ebp		; save ebp
        mov	ebp,esp		; ebp is callers stack
	push	ebx
	push	edi		; save registers
	

; method 1, using a "sentinel" e.g. '.'
	
	mov	eax,0		; accumulate value here
	mov	al,[decdig]	; get first ASCII digit
	sub	al,48		; convert ASCII digit to binary
	mov	edi,1		; subscript initialization
h1loop:	mov	ebx,0		; clear register (upper part)
	mov	bl,[decdig+edi]	; get next ASCII digit
	cmp	bl,'.'		; compare to decimal point
	je	h1fin		; exit loop on decimal point
	sub	bl,48		; convert ASCII digit to binary
	imul	eax,10		; * X     (ignore edx)
	add	eax,ebx		; + a_n-i
	inc	edi		; increment subscript
	jmp	h1loop
h1fin:
	push	dword eax	; print eax
	push	dword fmt	; format %d
	call	printf
	add	esp,8		; restore stack

; method 2, using a count
	
	mov	eax,0		; accumulate value here
	mov	al,[decdig]	; get first ASCII digit
	sub	al,48		; convert ASCII digit to binary
	mov	edi,1		; subscript initialization
	mov	ecx,3		; loop iteration count initialization
h2loop:	mov	ebx,0		; clear register (upper part)
	mov	bl,[decdig+edi]	; get next ASCII digit
	sub	bl,48		; convert ASCII digit to binary
	imul	eax,10		; * X     (ignore edx)
	add	eax,ebx		; + a_n-i
	inc	edi		; increment subscript
	loop	h2loop		; decrement ecx, jump on non zero

	push	dword eax	; print eax
	push	dword fmt	; format %d
	call	printf
	add	esp,8		; restore stack

; evaluate a polynomial, X=7, using a count

	section	.data
a:	dd	2,5,-7,22,-9	; coefficients of polynomial, a_n first
X:	dd	7
	section	.text
	mov	eax,[a]		; accumulate value here, get coefficient a_n
	mov	edi,1		; subscript initialization
	mov	ecx,4		; loop iteration count initialization, n
h3loop:	imul	eax,[X]		; * X     (ignore edx)
	add	eax,[a+4*edi]	; + a_n-i
	inc	edi		; increment subscript
	loop	h3loop		; decrement ecx, jump on non zero

	push	dword eax	; print eax
	push	dword fmt	; format %d
	call	printf
	add	esp,8		; restore stack

; evaluate a polynomial, X=7, using a count as index
; optimal organization of data allows a three instruction loop
	
	section	.data
aa:	dd	-9,22,-7,5,2	; coefficients of polynomial, a_0 first
	section	.text
	mov	eax,[aa+4*4]	; accumulate value here, get coefficient a_n
	mov	ecx,4		; loop iteration count initialization, n
h4loop:	imul	eax,[X]		; * X     (ignore edx)
	add	eax,[aa+4*ecx-4]; + aa_n-i
	loop	h4loop		; decrement ecx, jump on non zero

	push	dword eax	; print eax
	push	dword fmt	; format %d
	call	printf
	add	esp,8		; restore stack

; evaluate a double floating polynomial, X=7.0, using a count as index
; optimal organization of data allows a three instruction loop
	
	section	.data
af:	dq	-9.0,22.0,-7.0,5.0,2.0	; coefficients of polynomial, a_0 first
XF:	dq	7.0
Y:	dq	0.0
N:	dd	4
fmtflt:	db	"%e",10,0

	section	.text
	mov	ecx,[N]		; loop iteration count initialization, n
	fld	qword [af+8*ecx]; accumulate value here, get coefficient a_n
h5loop:	fmul	qword [XF]	; * XF
	fadd	qword [af+8*ecx-8] ; + aa_n-i
	loop	h5loop		; decrement ecx, jump on non zero

	fstp	qword [Y]	; store Y in order to print Y
	push	dword [Y+4]	; print Y (must be two parts of quadword)
	push	dword [Y]	; print Y
	push	dword fmtflt	; format %e
	call	printf
	add	esp,12		; restore stack


; Convert the fractional polynomial, Y = a_-1 X^-1 + a_-2 X^-2 + ...
; This must be performed using divide in reverse order.
; compute y_1 = a_-n / X + a_-n+1
; compute y_2 = y_1 / X + a_-n+2
; compute y_i = y_i-1 / X + a_-n+i   i=3..n
; thus    y_n = Y_n-1 / X  = Y = value of polynomial 
; Using the coefficients above a_-1 = -9.0 (first)
; a_-2 = 22.0, a_-3 = -7.0, a_-4 = 5.0, a_-5 = 2.0
; N=4 (not 5) because the the first term is outside the loop

	mov	ecx,[N]		; loop iteration count initialization, n
	fld	qword [af+8*ecx]; accumulate value here, get  a_-n-1 = 2.0
h6loop:	fdiv	qword [XF]	; * XF
	fadd	qword [af+8*ecx-8] ; + aa_n-i
	loop	h6loop		; decrement ecx, jump on non zero
	fdiv	qword [XF]	; extra divide for fractional terms
	
	fstp	qword [Y]	; store Y in order to print Y
	push	dword [Y+4]	; print Y (must be two parts of quadword)
	push	dword [Y]	; print Y
	push	dword fmtflt	; format %e
	call	printf
	add	esp,12		; restore stack


; Convert the fractional part, a_-1 X^-1 + a_-2 X^-2 + ...
; This must be performed using "fixed point" arithmetic.
; The implied binary point is 16-bits from LSB.
	
	section	.data
fracdig:db	'.','1','2','3','4' ; decimal fraction .1234
fmth:	db	"%X",10,0
ten:	dd	10
eaxsave:dd	0
	
	global h7loop		; for debugging loop  "break h7loop"
	section	.text
	mov	eax,0		; accumulate value here
	mov	al,[fracdig+4]	; get last ASCII digit
	sub	al,48		; convert ASCII digit to binary
	shl	eax,16		; move binary point
	mov	ecx,3		; loop iteration count initialization
h7loop:	mov	edx,0		; must clear upper dividend
	idiv	dword [ten]	; quotient in eax
	mov	ebx,0		; clear register (upper part)
	mov	bl,[fracdig+ecx]; get next previous ASCII digit
	sub	bl,48		; convert ASCII digit to binary
	shl	ebx,16		; move binary point 16-bits
	add	eax,ebx		; + a_n-i
	loop	h7loop		; decrement ecx, jump on non zero
	mov	edx,0		; must clear upper dividend
	idiv	dword [ten]	; final divide
	
	mov	[eaxsave],eax	; save eax, printf destroys it
	push	dword eax	; print eax
	push	dword fmth	; format %X (look at low 16-bits)
	call	printf
	add	esp,8		; restore stack

; print the bits in eaxsave:
	section	.bss
abits:	resb	17		; 16 characters plus zero terminator
	section	.data
fmts:	db	"%s",10,0
	section	.text
	mov	eax,[eaxsave]	; restore eax
	ror	eax,1		; get bottom bit in top of eax
	mov	ecx,16		; for printing 16 bits
h8loop:	mov	edx,0		; clear edx ready for a bit
	shld	edx,eax,1	; top bit of eax into edx
	add	edx,48		; make it ASCII
	mov	[abits+ecx-1],dl ; store character
	ror	eax,1		; next bit into top of eax
	loop	h8loop		; decrement ecx, jump non zero
	
	mov	byte [abits+16],0 ; end of "C" string
	push	dword abits	; string to print
	push	dword fmts	; "%s"
	call	printf
	add	esp,8
	

	pop	edi
	pop	ebx
	mov	esp,ebp		; restore callers stack frame
	pop	ebp
	ret			; return

; output from execution:
; 5280
; 5280
; 6319
; 6319
; 6.319000e+03
; -8.549414e-01
; 1F97
; 0001111110010111


    <- previous    index    next ->

Other links

Go to top