RPN FPU Calculator
Just for fun, I wrote a RPN (Reverse Polish Notation) calculator in x86 assembler. It uses the FPU (Floating-Point Unit) for all the calculations, and some common C-library calls for input/output.
It only supports the basic arithmetic operations like addition, subtraction, multiplication and division, and only operates on integers. I have tried to emulate the basic behaviour of the common dc Unix command-line RPN calculator, so it will print the result when getting a 'p', and quit on a 'q'. Also be aware that it expects each operator or operand on a single line by itself.
It is written using AT&T syntax and will probably only assemble/link on Linux x86/i386 platforms. Check out the code:
/*
* Assembling and linking commands:
* as -o fpu-rpn.o fpu-rpn.s
* ld -dynamic-linker /lib/ld-linux.so.2 -lc -o fpu-rpn fpu-rpn.o
*/
.section .data
output_format:
.asciz "%d\n"
error_input:
.asciz "Error: Unknown input.\n"
error_stack:
.asciz "Error: Stack empty.\n"
.section .bss
.lcomm input, 16
.lcomm buffer, 4
.lcomm status, 2
.section .text
.globl _start
_start:
finit
read_input:
pushl stdin
pushl $16
pushl $input
call fgets
addl $12, %esp
/* End if EOF is reached (fgets returns NULL). */
cmpl $0, %eax
je end
/* Extract first character of returned input string. */
movl (%eax), %ebx
/* Clear any errors on the FPU each round. */
fclex
/* Switch-case like check on the input. */
cmpb $0x71, %bl # 'q'
je end
cmpb $0x70, %bl # 'p'
je display_result
cmpb $0x2B, %bl # '+'
je addition
cmpb $0x2D, %bl # '-'
je subtraction
cmpb $0x2A, %bl # '*'
je multiplication
cmpb $0x2F, %bl # '/'
je division
/* Check if between 0-9 to determine if it's a number. */
cmpb $0x30, %bl # '0'
jl input_error
cmpb $0x39, %bl # '9'
jg input_error
/* Convert number from string and push onto FPU's stack. */
pushl %eax
call atoi
addl $4, %esp
movl %eax, buffer
filds buffer
jmp read_input
input_error:
push stdout
push $error_input
call fputs
addl $8, %esp
jmp read_input
addition:
faddp
jmp check_stack
subtraction:
fsubrp
jmp check_stack
multiplication:
fmulp
jmp check_stack
division:
fdivrp
jmp check_stack
check_stack:
fstsw status
testw $0b1000000, status /* Test for "Stack Fault" flag. */
jz read_input
fdecstp /* Decrement stack to avoid displaying the fake result. */
push stdout
push $error_stack
call fputs
addl $8, %esp
jmp read_input
display_result:
/* Display the value at the top of the FPU's stack. */
fistpl buffer
fstsw status
testw $0b1000000, status
jnz read_input /* Just do the check again to receive error messsage. */
pushl buffer
pushl $output_format
call printf
addl $8, %esp
jmp read_input
end:
/* Tell the Linux kernel to end this process. */
movl $1, %eax # exit()
movl $0, %ebx
int $0x80