This is a simple project that combine the power of Assembly
language with the power of C
language. In this project I have create a basic calculator with the four fundamental operations, namely sum, subtract, multiplication and division. Obviously this is not a large-scale production project, but it represents a tool for educational purposes. In fact, in this project three versions of Assembly
language were implemented and used: AT&T
, Intel
and ARM
.
Warning
This code has only been tested on Manjaro Linux
(for AT&T Assembly
and Intel Assembly
) and Raspberry Pi 3 Model B
(for ARM Assembly
based on aarch64
run on Raspbian
operating system). If you experience problems using other operating systems, such as Windows
or macOS
, make sure you have the appropriate skills or risk damaging your equipment.
Download the repository to your computer using the following command:
git clone https://github.com/AntonioBerna/call-assembly-from-c.git
once we are inside the project folder we can use one of the following programs:
./build.sh ATT
# or
./build.sh intel
# or
./build.sh arm
# or
./build.sh clean
in fact, leaving aside the last command which is used to eliminate the final executables, the first two commands represent the type of Assembly
that is used and therefore combined with the C
language. Obviously we are not talking about architecture, but only and exclusively about syntax preferences. In fact, the dear AT&T Assembly
has for each instruction it uses a syntax of the type:
istrX %source, %destination
where X
represents the number of bytes of registers that will be used as source and destination. In particular we can choose between b
(1 byte = 8 bits), l
(2 bytes = 16 bits), w
(4 bytes = 32 bits) and q
(8 bytes = 64 bits). This type of Assembly
is a classic but can sometimes be cumbersome. In fact, for this very reason many people prefer to use or read the Intel Assembly
:
istr source, destination
As we can see, by not having to specify the number of bytes/bits, by not having to specify the %
symbol and by not having to specify the $
symbol for immediate values, this syntax is simpler and more pleasant.
Finally the syntax of ARM Assembly
is very different from the previous ones and appears, in the simplest case, as follows:
istr destination, operand1, operand2
in fact we note the presence of 3 parameters. However, in this type of Assembly
we can also use 2 parameters, based on the type of instruction we want to use.
Once the command has been chosen, the executable file will be created inside the build
directory. Therefore we can run our code using the following command:
./build/ATT-calculator
# or
./build/intel-calculator
# or
./build/arm-calculator
getting the following message:
Usage: ./build/ATT-calculator [add|sub|mul|div|test] [x] [y]
# or
Usage: ./build/intel-calculator [add|sub|mul|div|test] [x] [y]
# or
Usage: ./build/arm-calculator [add|sub|mul|div|test] [x] [y]
Then simply follow the instructions in the message obtained to use the program correctly.
Note
I would like to point out that there is also the test
option which allows us to execute a function to test some very simple operations.
The System V Application Binary Interface (ABI)
is a set of conventions used on Unix-like operating systems, such as Linux
and Solaris
, to define how functions should be called and how data should be passed between functions in a compiled program. System V ABI
calling conventions for 64 bit systems include:
-
Return Register: The return value of a function is stored in the
RAX
register. -
Parameter passing: The first six integers or pointers are passed into registers
RDI
,RSI
,RDX
,RCX
,R8
andR9
. The other parameters are passed onto thestack
, in order from left to right. -
Saving registers: The called function must save the
RBX
,RSP
,RBP
,R12
,R13
,R14
, andR15
registers if it uses them and restore them before returning control to the caller. -
Stack alignment: The
stack
must always be aligned to a multiple of 16 bytes at the start of a function. -
Data Structure Conventions: Data structures smaller than 16 bytes and unions are passed into registers if possible. Data structures larger than 16 bytes are passed by reference.
-
Management of local variables: Local variables are usually allocated on the
stack
, moving the stack pointer (RSP
). -
Handling function calls: The calling function is responsible for cleaning up the
stack
after the call, removing passed parameters.
These conventions are designed to maximize the efficiency and interoperability of programs on operating systems that adopt the System V ABI
for 64 bit architectures. They ensure that functions can communicate consistently and that code can be effectively optimized by the compiler.