ASSEMBLING ON ALPHA PART I
In this first article I will discover how to use functions written in alpha
assembler in a program written in C. The example I give is a rather simple
one. There are many things to know about alpha. This text shows that it is
quite simple to use assembler on alpha.
Introduction
The heart of the alpha architecture is a 64-bit RISC processor with 32 integer
($0 to $31) and 32 floating point registers ($f0 to $f31). Its operation codes
can be classified by the number of its operands:
class opcode
operate opcode Ra,Rb,Rc # Ra operation Rb -> Rc
opcode Ra,number,Rc # Ra operation number (0-255) -> Rc
memory opcode Ra,Disp(Rb) # load/store contents saved in memory address
# Rb + offset Disp in register Ra
branch opcode Ra,label # branch if Ra = true to label
PAL opcode number # opcodes for the operating system
The Usage Convention of register is listed in the following table. Saved
Registers are such whose contents will not be lost if a function is called.
The function will save such registers if it uses them.
int reg Usage Convention Saved
$0 Integer function result No
$1-$8 Conventional scratch regs No
$9-$14 General uses Yes
$15 or $fp Frame pointer Yes
$16-$21 Integer arguments by value No
$22-$25 Conventional scratch regs No
$26 Return address register Yes
$27 Procedure value (pointer) No
$28 or $at reserved for system No
$29 or $gp Global pointer No
$30 or $sp Stack pointer Yes
$31 Zero (not modifiable) n/a
float reg Usage Convention Saved
$f0 floating point function result No
$f1 Imaginary part function result No
$f2-$f9 General uses Yes
$f10-$f15 Conventional scratch regs No
$f16-$f21 Floating point args by value No
$f22-$f30 Conventional scratch regs No
$f31 Zero (not modifiable) n/a
Data Types are specified by suffixes (like q for quadword, l for longword).
Most integer operations only know these two suffixes. Floating point operations
know both: s and t.
Integer Data types:
Type Bits signed range unsigned range
Byte 8 -128 to 127 0 to 255
Word 16 -32768 to 32767 0 to 65535
Longword 32 -2147483648 to 0 to 4294967295
2147483647
Quadword 64 -9228372036854775808 0 to
9228372036854775807 18446744073709551615
Floating Point Data Types:
Type Magnitude Precision
S-floating 1.175 x 10^-38 to 3.403 x 10^38 6 decimal digits
T-floating 2.225 x 10^-308 to 1.798 x 10^308 15 decimal digits
If you want to use 64-bit numbers in the c-programming language (gcc), use
(long) or (long int). (int) is 32 bits long.
The following example was tested on an SX164 with SuSE Alpha Linux 6.3 (Kernel
2.2.13).
The Example
My c-program calls the assembler function div which divides the first argument
given to it by the second one. The arguments will be put in the integer
registers $16 and $17 by convention. So all we have to do is to divide register
$16 by $17. The alpha does not know any division for integer. There is a pseudoopcode
for integer-division but I will show how to convert an integer to a
floating point number, do the division in the floating point registers and
convert it back to integer. Finally the result will be put by convention in
register $0 where the c-program expects it to be.
Compiling the source codes
gcc -c div.s
gcc -o div divide.c div.o
Source of the C-program
/* divide.c */
#include <stdio.h>
int main()
{
long int a,b,c; /* long int is 64 bits long */
a=1111; /* a random number */
b=14; /* second random number */
c=div(a,b); /* div is a function written in assembler code */
/* div returns the value of a / b */
printf("c is %d\n",c);
exit(0);
}
-------------------------------------------------- cut here
Source of the Assembler-Program: div.s
-------------------------------------------------- cut here
.title div divides two arguments and returns the result
.data # Data section
temp1: .quad 0 # temporary variable
temp2: .quad 0 # temporary variable
temp3: .quad 0 # temporary variable
REGS = 1 # How many registers have to be saved
STACK = REGS # this registers will be put on the stack
FRAME = ((STACK*8+8)/16)*16 # Stack size
.text # text section
.align 4
.set noreorder # disallow rearrangements
.globl div # these 3 lines mark the
.ent div # mandatory function
div: # entry
ldgp $gp,0($27) # load the global pointer
lda $sp,-FRAME($sp) # load the stack pointer
stq $26,0($sp) # save our own exit address
.frame $sp,FRAME,$26,0 # describe the stack frame
.prologue 1
stq $16,temp1 # save register $16 (first argument)
stq $17,temp2 # save register $17 (second argument)
ldt $f2,temp1 # load 1st argument in floating point register
ldt $f3,temp2 # load 2nd argument in floating point register
cvtqt $f2,$f2 # convert integer to floating point
cvtqt $f3,$f3 # convert integer to floating point
divt $f2,$f3,$f4 # $f4 <-- $f2 / $f3
cvttq $f4,$f4 # convert floating point to integer
stt $f4,temp3 # store integer
ldq $0,temp3 # load integer in integer register
done: ldq $26,0($sp) # restore exit address
lda $sp,FRAME($sp) # Restore stack level
ret $31,($26),1 # Back to c-program
.end div # Mark end of function
-------------------------------------------------- cut here
|