<- previous index next ->
Pass an array and change the array in assembly language.
Be safe, use a header file, .h, in the "C" code.
test_call1_64.c
test_call1_64.s
call1_64.h
call1_64.c
call1_64.s
call1_64.asm
test_call1_64.out
// test_call1_64.c test call1_64.asm
// nasm -f elf64 -l call1_64.lst call1_64.asm
// gcc -m64 -o test_call1_64 test_call1_64.c call1_64.o
// ./test_call1_64 > test_call1_64.out
#include "call1_64.h"
#include <stdio.h>
int main()
{
long int L[2];
printf("test_call1_64.c using call1_64.asm\n");
L[0]=1;
L[1]=2;
printf("address of L=L[0]=%ld, L[1]=%ld \n", &L, &L[1]);
call1_64(L); // add 3 to L[0], add 4 to L[1]
printf("L[0]=%ld, L[1]=%ld \n", L[0], L[1]);
return 0;
}
; call1_64.asm a basic structure for a subroutine to be called from "C"
;
; Parameter: long int *L
; Result: L[0]=L[0]+3 L[1]=L[1]+4
global call1_64 ; linker must know name of subroutine
extern printf ; the C function, to be called for demo
SECTION .data ; Data section, initialized variables
fmt1: db "rdi=%ld, L[0]=%ld", 10, 0 ; The printf format, "\n",'0'
fmt2: db "rdi=%ld, L[1]=%ld", 10, 0 ; The printf format, "\n",'0'
SECTION .bss
a: resq 1 ; temp for printing
SECTION .text ; Code section.
call1_64: ; name must appear as a nasm label
push rbp ; save rbp
mov rbp, rsp ; rbp is callers stack
push rdx ; save registers
push rdi
push rsi
mov rax,rdi ; first, only, in parameter
mov [a],rdi ; save for later use
mov rdi,fmt1 ; format for printf debug, demo
mov rsi,rax ; first parameter for printf
mov rdx,[rax] ; second parameter for printf
mov rax,0 ; no xmm registers
call printf ; Call C function
mov rax,[a] ; first, only, in parameter, demo
mov rdi,fmt2 ; format for printf
mov rsi,rax ; first parameter for printf
mov rdx,[rax+8] ; second parameter for printf
mov rax,0 ; no xmm registers
call printf ; Call C function
mov rax,[a] ; add 3 to L[0]
mov rdx,[rax] ; get L[0]
add rdx,3 ; add
mov [rax],rdx ; store sum for caller
mov rdx,[rax+8] ; get L[1]
add rdx,4 ; add
mov [rax+8],rdx ; store sum for caller
pop rsi ; restore registers
pop rdi ; in reverse order
pop rdx
mov rsp,rbp ; restore callers stack frame
pop rbp
ret ; return
A small change to use a double array, floating point
test_callf1_64.c
callf1_64.h
callf1_64.c
callf1_64.asm
test_callf1_64.out
; callf1_64.asm a basic structure for a subroutine to be called from "C"
;
; Parameters: double *L
; Result: L[0]=L[0]+3.0 L[1]=L[1]+4.0
global callf1_64 ; linker must know name of subroutine
extern printf ; the C function, to be called for demo
SECTION .data ; Data section, initialized variables
fmt1: db "rdi=%ld, L[0]=%e", 10, 0 ; The printf format, "\n",'0'
fmt2: db "rdi=%ld, L[1]=%e", 10, 0 ; The printf format, "\n",'0'
a3: dq 3.0 ; 64-bit variable a initialized to 3.0
a4: dq 4.0 ; 64-bit variable b initializes to 4.0
SECTION .bss
a: resq 1 ; temp for saving address
SECTION .text ; Code section.
callf1_64: ; name must appear as a nasm label
push rbp ; save rbp
mov rbp, rsp ; rbp is callers stack
push rdx ; save registers
push rdi
push rsi
mov rax,rdi ; first, only, in parameter
mov [a],rdi ; save for later use
; mov rdi,fmt1 ; format for printf debug, demo
; mov rsi,rax ; first parameter for printf
; movq xmm0, qword [rax] ; second parameter for printf
; mov rax,1 ; one xmm registers
; call printf ; Call C function
; mov rax,[a] ; first, only, in parameter, demo
; mov rdi,fmt2 ; format for printf
; mov rsi,rax ; first parameter for printf
; movq xmm0, qword [rax+8] ; second parameter for printf
; mov rax,1 ; one xmm registers
; call printf ; Call C function
mov rax,[a] ; add 3.0 to L[0]
fld qword [rax] ; load L[0] (pushed on flt pt stack, st0)
fadd qword [a3] ; floating add 3.0 (to st0)
fstp qword [rax] ; store into L[0] (pop flt pt stack)
fld qword [rax+8] ; load L[1] (pushed on flt pt stack, st0)
fadd qword [a4] ; floating add 4.0 (to st0)
fstp qword [rax+8] ; store into L[1] (pop flt pt stack)
pop rsi ; restore registers
pop rdi ; in reverse order
pop rdx
mov rsp,rbp ; restore callers stack frame
pop rbp
ret ; return
Here is another basic subroutine (function, procedure, etc)
Note passing parameters.
Note saving and restoring the callers registers.
(Yes, this is needed for CMPE 310 project 2)
Now, to pass more arguments, call2_64.c
can be implemented as call2_64.asm
Both tested using test_call2_64.c
Using prototype call2_64.h
Output is test_call2_64.out
Note passing arrays including strings is via address,
passing scalar values is via passing values.
; call2_64.asm code call2_64.c for nasm for test_call2_64.c
; // call2_64.c a very simple loop that will be coded for nasm
; void call2_64(long int *A, long int start, long int end, long int value)
; {
; long int i;
;
; for(i=start; i<=end; i++) A[i]=value;
; return;
; }
;
; execution output is dd1[0]=5, dd1[1]=7, dd1[98]=7, dd1[99]=9
section .bss
i: resd 1 ; actually unused, kept in register rax
section .text
global call2_64 ; linker must know name of subroutine
call2_64: ; name must appear as a nasm label
push rbp ; save rbp
mov rbp, rsp ; rbp is callers stack
push rax ; save registers (overkill)
push rbx
push rcx
push rdx
; know address or value from prototype
; mov rdi,rdi ; get address of A into rdi (default)
mov rax,rsi ; get value of start
mov rbx,rdx ; get value of end
mov rdx,rcx ; get value of value
loop1: mov [8*rax+rdi],rdx ; A[i]=value;
add rax,1 ; i++;
cmp rax,rbx ; i<=end
jle loop1 ; loop until i=end
pop rdx ; in reverse order
pop rcx
pop rbx
pop rax
mov rsp,rbp ; restore callers stack frame
pop rbp
ret ; return
A simple program with a simple function,
called and written in the same .asm file
intfunct_64.asm
; intfunct_64.asm this is a main and a function in one file
; call integer function long int sum(long int x, long int y)
; compile: nasm -f elf64 -l intfunct_64.lst intfunct_64.asm
; link: gcc -m64 -o intfunct_64 intfunct_64.o
; run: ./intfunct_64 > intfunct_64.out
; view: cat intfunct_64.out
; result: 5 = sum(2,3)
extern printf
section .data
x: dq 2
y: dq 3
fmt: db "%ld = sum(%ld,%ld)",10,0
section .bss
z: resq 1
section .text
global main
main: push rbp ; set up stack
mov rdi, [x] ; pass arguments for sum
mov rsi, [y]
call sum ; coded below
mov [z],rax ; save result from sum
mov rdi, fmt ; print
mov rsi, [z]
mov rdx, [x] ; yes, rdx comes before rcx
mov rcx, [y]
mov rax, 0 ; no float or double
call printf
pop rbp ; restore stack
mov rax,0
ret
; end main
sum: ; function long int sum(long int x, long int y)
; so simple, do not need to save anything
mov rax,rdi ; get argument x
add rax,rsi ; add argument y, x+y result in rax
ret ; return value in rax
; end of function sum
; end intfunct_64.asm
A simple demonstration of using a double sin(double x) function
from the "C" math.h fltfunct_64.asm
Note xmm 128-bit registers are used to pass parameters.
; fltfunct_64.asm call math routine double sin(double x)
; compile: nasm -f elf64 fltfunct_64.asm
; link: gcc -m64 -o fltfunct_64 fltfunct_64.o -lm # needs math library
; run: ./fltfunct_64 > fltfunct_64.out
; view: cat fltfunct_64.out
extern sin ; must extern all library functions
extern printf
section .data
x: dq 0.7853975 ; Pi/4 = 45 degrees
y: dq 0.0 ; should be about 7.07E-1
fmt: db "y= %e = sin(%e)",10,0
section .text
global main
main: push rbp ; set up stack
movq xmm0, qword [x] ; pass argument to sin()
call sin ; all "C" math uses double
movq qword [y], xmm0 ; save result
mov rdi, fmt ; print
movq xmm0, qword [y]
movq xmm1, qword [x]
mov rax,2 ; 2 doubles
call printf
pop rbx ; restore stack
mov rax,0 ; no error return
ret ; return to operating system
; result: y= 7.071063e-01 = sin(7.853975e-01)
Now, if you want to see why I teach Nasm rather than gas,
See fltfunct_64.c
// fltfunct_64.c call math routine double sin(double x)
#include <stdio.h>
#include <math.h>
int main()
{
double x = 0.7853975; // Pi/4 = 45 degrees
double y;
y = sin(x);
printf("y= %e = sin(%e)\n", y, x);
return 0;
}
See gas assembly language, gcc -m64 -S fltfunct_64.c makes fltfunct_64.s
.file "fltfunct_64.c"
.section .rodata
.LC1:
.string "y= %e = sin(%e)\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movabsq $4605249451321951854, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
call sin
movsd %xmm0, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, -16(%rbp)
movq -8(%rbp), %rdx
movq -16(%rbp), %rax
movq %rdx, -24(%rbp)
movsd -24(%rbp), %xmm1
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
movl $.LC1, %edi
movl $2, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.2 20140120 (Red Hat 4.8.2-16)"
.section .note.GNU-stack,"",@progbits
And a final example of a simple recursive function, factorial,
written in optimized assembly language following the "C" code.
main test_factorial_long.c
"C" version factorial_long.c
"C" header factorial_long.h
nasm code factorial_long.asm
output test_factorial_long.out
// test_factorial_long.c the simplest example of a recursive function
// a recursive function is a function that calls itself
// external
// long int factorial_long(long int n) // n! is n factorial = 1*2*...*(n-1)*n
// {
// if( n <= 1 ) return 1; // must have a way to stop recursion
// return n * factorial_long(n-1); // factorial calls factorial with n-1
// } // n * (n-1) * (n-2) * ... * (1)
#include "factorial_long.h"
#include <stdio.h>
int main()
{
printf("test_factorial_long.c using long int, note overflow\n");
printf(" 0!=%ld \n", factorial_long(0)); // Yes, 0! is one
printf(" 1!=%ld \n", factorial_long(1l)); // Yes, "C" would convert 1 to 1l
printf(" 2!=%ld \n", factorial_long(2l)); // because of function prototype
printf(" 3!=%ld \n", factorial_long(3l)); // coming from factorial_long.h
printf(" 4!=%ld \n", factorial_long(4l));
printf(" 5!=%ld \n", factorial_long(5l));
printf(" 6!=%ld \n", factorial_long(6l));
printf(" 7!=%ld \n", factorial_long(7l));
printf(" 8!=%ld \n", factorial_long(8l));
printf(" 9!=%ld \n", factorial_long(9l));
printf("10!=%ld \n", factorial_long(10l));
printf("11!=%ld \n", factorial_long(11l));
printf("12!=%ld \n", factorial_long(12l));
printf("13!=%ld \n", factorial_long(13l));
printf("14!=%ld \n", factorial_long(14l));
printf("15!=%ld \n", factorial_long(15l));
printf("16!=%ld \n", factorial_long(16l));
printf("17!=%ld \n", factorial_long(17l));
printf("18!=%ld \n", factorial_long(18l));
printf("19!=%ld \n", factorial_long(19l));
printf("20!=%ld \n", factorial_long(20l));
printf("21!=%ld \n", factorial_long(21l)); /* expect a problem with */
printf("22!=%ld \n", factorial_long(22l)); /* integer overflow */
return 0;
}
/* output of execution, with comments, is:
test_factorial_long.c using long int, note overflow
0!=1
1!=1
2!=2
3!=6
4!=24
5!=120
6!=720
7!=5040
8!=40320
9!=362880
10!=3628800
11!=39916800
12!=479001600
13!=6227020800
14!=87178291200
15!=1307674368000
16!=20922789888000
17!=355687428096000
18!=6402373705728000
19!=121645100408832000
20!=2432902008176640000
21!=-4249290049419214848 wrong and obvious if you check your results
22!=-1250660718674968576
*/
; factorial_long.asm test with test_factorial_long.c main program
; // factorial_long.c the simplest example of a recursive function
; // a recursive function is a function that calls itself
; long int factorial_long(long int n) // n! is n factorial = 1*2*...*(n-1)*n
; {
; if( n <= 1 ) return 1; // must have a way to stop recursion
; return n * factorial_long(n-1); // factorial calls factorial with n-1
; } // n * (n-1) * (n-2) * ... * (1)
; // note: "C" makes 1 be 1l long
global factorial_long
section .text
factorial_long: ; extremely efficient version
mov rax, 1 ; default return
cmp rdi, 1 ; compare n to 1
jle done
; normal case n * factorial_long(n-1)
push rdi ; save n for multiply
sub rdi, 1 ; n-1
call factorial_long ; rax has factorial_long(n-1)
pop rdi
imul rax,rdi ; ??
; n*factorial_long(n-1) in rax
done: ret ; return with result in rax
; end factorial_long.asm
Need more RAM?
4GB RAM
<- previous index next ->