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