HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

NASM RPN Calculator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
calculatorrpnnasm

Problem

I've been learning Assembly over the past few days, and I've made a simple RPN calculator.

Here is the main logic of the program, excluding utility functions such as printing:

```
%include "utilities.s"

SECTION .data
badOpMsg db "Unknown character: ", 0h
badNumMsg db "Error parsing number: ", 0h
badExprMsg db "Invalid expression supplied", 0h

SECTION .bss
userInput resw 500

SECTION .text
global _start

%macro preOp 0
cmp ecx, 2
jl .invalidExpr
; pop two values off of the "stack" and place them into esi and eax
dec ecx
mov esi, [userInput + ecx * 4]
dec ecx
mov eax, [userInput + ecx * 4]
%endmacro

%macro postOp 0
; put eax back onto the "stack" and return to the end of the parse loop
mov [userInput + ecx * 4], eax
inc ecx
jmp .endloop
%endmacro

_start:
mov ebp, esp
xor ecx, ecx ; parsed stack index
mov ebx, 2 ; argument list index

.parseLoop:
mov eax, [ebp + ebx * 4]

; Hack to allow numbers prefixed with '-'
; If current argument has a length of 1, treat it as an op/number
; otherwise treat it as a number
cmp byte [eax + 1], 0
jnz .parseNum

cmp byte [eax], 43 ; "+"
je .add
cmp byte [eax], 45 ; "-"
je .subtract
cmp byte [eax], 47 ; "/"
je .divide
cmp byte [eax], 120 ; "x"
je .multiply
cmp byte [eax], 48 ; "0"
jl .fail
cmp byte [eax], 57 ; "9"
jg .fail

.parseNum:
; otherwise, add number to the stack
; use edi to indicate atoi error
xor edi, edi
call atoi
cmp edi, 1
je .numError

mov [userInput + ecx * 4], eax
inc ecx

.endloop:
inc ebx

cmp ebx, [ebp]
jle .parseLoop

jmp .success

.add:
preOp
add eax, esi
postOp

.subtract:
preOp
sub

Solution

When I first saw the call atoi, I thought you would call the C function of the same name. That frightened me a bit, because that function should not be used in serious programs, since it doesn't provide proper error checking. So you should choose another name, especially when programming on Linux, where potential readers of your code are familiar with that function.

I like the trick with the self-terminating string in iprint. Also, the idea of pushing the characters in reverse order and then printing them is nice and simple. If you had to fight for every system call, allocating a single 12-byte buffer would be more efficient, both in the memory usage and in the number of system calls.

Do you indent to support negative numbers?

Instead of using null-terminated strings like in C, you could represent a string as (start, length) tuple, which would make the strlen function unnecessary.

Context

StackExchange Code Review Q#143921, answer score: 3

Revisions (0)

No revisions yet.