A NASM (x86-64) assembly program to add two integer inputs from the user, using custom ASCII-to-integer and integer-to-ASCII conversion functions.
The program was written using the win64 version, but run using WSL (Ubuntu) and will not work on pure Windows CMD or PowerShell without modification.
When compiled and run, the program will prompt the user to enter a number. The number is converted from ASCII to an integer value by calling a function ascii_to_int. This is stored and a second number is requested, going through the same process. The two numbers are added and this value is converted back to an ASCII value by the function int_to_ascii before being printed to the CLI.
The ascii_to_int functions loops through each digit in an ASCII value from left to right, converting each digit to its integer value. The integer value of the digit is added to a buffer where each loop multiplies the buffer by ten so that the new digit is always added to the empty 'ones column'.
The int_to_ascii function has two loops. The first divides the integer value by ten and converts the remainder digit to an ASCII value before appending it to the buffer. In this manner the digits in the integer value are converted to ASCII digits from right to left and stored in the buffer. This leaves the digits in reverse order so the second loop in the function reverses the order of the buffer before returning to the caller.
- Windows Subsystem for Linux (Ubuntu)
- NASM (win64)
ld(GNU linker, available viabuild-essential)
To compile and run the program using the Windows Subsystem for Linux (ubuntu) use the following commands.
nasm -f elf64 basic_addition.asm
ld -o AdditionProg basic_addition.o
./AdditionProgInitialised Data
- Contains messages to be printed in the CLI.
equ $-sets the length of the messages.0dh,0ahcreates a newline in the terminal when printing.
Uninitialised Data
- Reserving space for the two numbers, the total number, and a buffer for converting to and from ASCII.
- The program is started and prints the prompt for the first number.
- This is read using
syscallbefore theascii_to_intfunction is called to convert the number from ASCII to an integer value. - The conversion is done in the
EAXregister, which is the bottom 32 bits ofRAX.RAXis the equivalent 64-bit register in x86-64; however,EAXwas used to store numbers because it is not anticipated that the numbers will require more than 32-bits within the scope of this project. - The number is stored in the reserved uninitialised data space
num1before the process is repeated for the second number. num1is moved back into theEAXregister andnum2is then added to it, before the resultant number is moved to the reservedtotalspace.- The totals message is printed before the function
int_to_asciiis called to convert the integer total into ASCII. - The
int_to_asciifunction returns the length of the string in theRAXregister. This is used to reserve the number of bytes needed inRDXbefore settingRAXto 1 forsys_write. - The total is printed followed by a newline.
- The program exits.
- The function
ascii_to_int:does what it says on the tin and converts the ASCII input to integers. RCXandRAXare set to 0.RCXwill be used as a counter whileRAXwill be the buffer where the results are stored.- The conversion is done in a loop,
convert_loop:, converting each digit from left to right. - The loop takes the digit at the pointed to address (start of buffer address
RSI+ counterRCX) and usesmovzxto move it toRBX, adding zeros to match the larger destination size. blis the bottom byte ofRBXand contains the digit to be converted. The function checks if it is a newline,0x0A, escaping the loop if it is, and continuing with the loop if not.- Remaining in the
RBXregister, 48 ('0') is then subtracted from the value ofblto convert from the ASCII decimal value to the matching integer value. - Before adding the value from the
RBXregister, the current value inRAXis multiplied by 10. This effectively moves the digits one place to the left, so that a digit in the 'ones column' moves to the 'tens column'. - The value in
RBXis then added toRAX, effectively being added to the 'ones column'. - The counter
RCXis incremented before the function loops and returns to the caller once a newline is detected. - The final integer value is in the
EAXportion of theRAXregister and will be moved to the relevant reserved data space after the function returns to the caller.
- The function
int_to_ascii:converts from integer values to ASCII and is used to prepare the total for printing. - Prior to the function being called, the
totalinteger value is moved into theEAXregister, which is the bottom half of theRAXregister. RCXis set to 0 as a counter whileRSIis pointed to the start of the buffer.
Convert to ASCII Loop
- Once again, the function uses a loop for the conversion,
convert_to_ascii:. - During each loop
RDXis used as a scratch register so is cleared ready for use. - The
RBXvalue is set to 10 and used to divide the integer value inRAXby 10 with the remainder being stored in RDX. - The remainder is whatever value was in the 'ones column' before division and is converted to an ASCII value by adding 48 (
'0') before being stored in the next location in the buffer (start pointRSI+ counterRCX). - The counter is then incremented and a comparison done to see if
RAXnow equals 0, at which point theconvert_to_ascii:loop will exit. - In this manner, each digit of the
totalinteger is converted to its respective ASCII value and appended to the buffer,RSI, from right to left. This results in the number in the buffer being listed in reverse. i.e. 12345 would be listed in the buffer as 54321. - Thus, it is necessary to reverse the order of the digits listed in the buffer and this is done by the second loop,
reverse_loop, in theint_to_ascii:function.
Reverse Loop
- Prior to starting the loop,
RDIis pointed to the last digit in theRSIbuffer using the counterRCXanddecinstruction. - Beginning the loop, there is a comparison to check that the end of the number has not been reached.
- The loop works in from the ends of the number:
- The first digit in the buffer (at the address of
RSI) is moved to the lowest byte,al, ofRAX - Then the last digit in the buffer(at the address of
RDI) is moved to the lowest byte,bl, ofRBX. - The digit in
blis then moved into theRSIaddress location, becoming the first digit in the buffer. - The digit in
alis then moved into theRDIaddress location, becoming the last digit in the buffer. RSIandRDIare incremented and decremented respectively so that they point to the second digit and the second to last digit.
- The first digit in the buffer (at the address of
- The loop repeats until
RSI>=RDI. i.e. the middle of the number has been reached. - At this point the loop exits and the function returns to caller with the ASCII number stored in the buffer
- This looping process is illustrated below for clarity.
| Loop | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | Explanation |
|---|---|---|---|---|---|---|
| Start | 5 | 4 | 3 | 2 | 1 | Reverse number in buffer.RSI points to byte 1 and RDI to byte 5. |
| First | 1 | 4 | 3 | 2 | 5 | Number in buffer after first loop.RSI now points to byte 2 and RDI to byte 4. |
| Second | 1 | 2 | 3 | 4 | 5 | Number in buffer after second loop.RSI and RDI both point to byte 3. |
| Third | 1 | 2 | 3 | 4 | 5 | RSI = RDI so exit loop |
The program converts a user input from ASCII to an integer. This means that any ASCII digits entered are converted to their decimal value less 48 (the offset of the numeric digits from 0). i.e. m would be read as 61 by the program instead of an error being thrown. Thus, any input will return a value, even if the input was not numeric.
Furthermore, if a user were to enter a float, the program would fail to recognise it, and as such a much larger number would be output, with the decimal point, ., being converted to 254 due to wraparound. e.g. With the current program, 1.0 would have an integer value of 2640.