首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NASM RPN计算器

NASM RPN计算器
EN

Code Review用户
提问于 2016-10-11 19:29:34
回答 1查看 947关注 0票数 3

在过去的几天里,我一直在学习程序集,我制作了一个简单的RPN计算器。

以下是程序的主要逻辑,不包括打印之类的实用程序功能:

代码语言:javascript
复制
%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    eax, esi
    postOp

.multiply:
    preOp
    imul   eax, esi
    postOp

.divide:
    preOp
    div    si
    postOp

.numError:
    mov    eax, badNumMsg
    call   sprint
    mov    eax, [ebp + ebx * 4]
    call   sprintln
    jmp    .end

.fail: 
    ; An invalid character was supplied as input
    push   eax
    mov    eax, badOpMsg
    call   sprint
    ; Infringing character is on the top of the stack
    pop    esi
    mov    eax, [esi]
    call   putchar
    call   newline
    jmp    .end

.invalidExpr:
    mov    eax, badExprMsg
    call   sprintln
    jmp    .end

.success:
    ; If the stack has more than one item in it, 
    ; the supplied expression is invalid
    cmp    ecx, 1
    jne    .invalidExpr

    ; Pop remaining value off the stack and print it
    sub    ecx, 1
    mov    eax, [userInput + ecx * 4]
    call   iprintln

.end:
    call   quit

以下是“实用程序”的内容:

代码语言:javascript
复制
;----------------------------
; int strlen(String message)

strlen:
    push   ebx
    mov    ebx, eax

.nextchar:
    cmp    byte[eax], 0
    jz     .finished
    inc    eax
    jmp    .nextchar

.finished:
    sub    eax, ebx
    pop    ebx
    ret

;----------------------------
; void sprint(String message)

sprint:
    push   edx
    push   ecx
    push   ebx
    push   eax
    call   strlen

    mov    edx, eax ; move string len from eax to edx
    pop    eax      ; restore eax to string pointer

    mov    ecx, eax ; move string pointer to ecx
    mov    ebx, 1   ; 
    mov    eax, 4   ; opcode 4
    int    80h      ; make syscall

    pop    ebx
    pop    ecx
    pop    edx
    ret

;----------------------------
; void sprintln (String message)

sprintln:
    call   sprint    ; print string found at eax
    push   eax       ; preserve eax
    mov    eax, 0Ah
    call   putchar
    pop    eax
    ret

;----------------------------
; void putchar (char)

putchar:
    push   edx 
    push   ebx
    push   ecx
    push   eax     ; eax has character
    mov    edx, 1
    mov    ecx, esp
    mov    ebx, 1
    mov    eax, 4
    int    80h
    pop    eax
    pop    ecx
    pop    ebx
    pop    edx 
    ret

;------------------------------
; newline

newline:
    push   eax
    mov    eax, 0ah
    call   putchar
    pop    eax
    ret

;-----------------------------
; void iprint(int)

iprint:
    push   eax
    push   ecx
    push   edx
    push   esi
    push   edi

    xor    edi, edi
    xor    ecx, ecx
    test   eax, eax
    js     .negate

.divide:
    inc    ecx
    xor    edx, edx
    mov    esi, 10
    idiv   esi
    add    edx, 48
    push   edx
    cmp    eax, 0
    jnz    .divide

    cmp    edi, 1
    je     .printNegative

.print:
    dec    ecx
    mov    eax, esp
    call   sprint
    pop    eax
    cmp    ecx, 0
    jnz    .print
    jmp    .end

.negate:
    mov    edi, 1
    neg    eax
    jmp    .divide

.printNegative:
    push   45d
    inc    ecx
    jmp    .print

.end:
    pop    edi
    pop    esi
    pop    edx
    pop    ecx
    pop    eax
    ret

;-----------------------
; iprintln 

iprintln:
    call   iprint
    call   newline
    ret


;----------------------------
; atoi

atoi:
    push   ecx
    push   esi
    push   ebx

    mov    esi, eax  ; move string pointer to esi as eax will be used for math
    mov    eax, 0
    mov    ecx, 0

    xor    edi, edi  ; negative flag and error flag

    cmp    byte [esi], 45   ; "-"
    je     .negative

.parse:
    xor    ebx, ebx
    mov    bl, [esi + ecx]

    cmp    bl, 10
    je     .terminated
    cmp    bl, 0
    jz     .terminated
    cmp    bl, 48     ; "0"
    jl     .error
    cmp    bl, 57     ; "9"
    jg     .error

    sub    bl, 48
    add    eax, ebx
    mov    ebx, 10
    mul    ebx
    inc    ecx
    jmp    .parse

.negative:
    inc    ecx
    mov    edi, 1     ; make sign negative
    jmp    .parse


.negate:
    neg    eax
    mov    edi, 0     ; because edi is both negative and error, make sure to clear it
    jmp    .end

.error:
    mov    edi, 1
    jmp    .end

.terminated:
    mov    ebx, 10
    div    ebx
    cmp    edi, 1
    je     .negate

.end:
    pop    ebx
    pop    esi
    pop    ecx
    ret


;----------------------------
; void quit()

quit:
    mov    ebx, 0  ; exit code 0
    mov    eax, 1  ; opcode 
    int    80h
    ret

我主要想知道我的代码是否是惯用的。例如,我的寄存器选择是否正确?使用edi (我任意选择的寄存器)来指示解析错误是错误的做法吗?我应该这样使用.bss段吗?人们通常如何记录他们的功能?它们是否指定输入和输出寄存器?

请随时指出,无论如何,我的程序可以改进,包括我如何支持更大的数字和浮点。

编辑:代码现在支持负数。

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-10-11 20:18:19

当我第一次看到call atoi时,我想您应该调用同名的C函数。这让我有点害怕,因为这个函数不应该在严肃的程序中使用,因为它没有提供正确的错误检查。因此,您应该选择另一个名称,特别是在Linux上编程时,在Linux上,您的代码的潜在读者熟悉该函数。

我喜欢在iprint中使用自终止字符串的技巧。此外,把字符按反序排列,然后打印它们的想法也很好,也很简单。如果您必须为每个系统调用进行斗争,那么分配一个12字节的缓冲区将更有效,无论是在内存使用上还是在系统调用数量上。

你是否缩进以支持负数?

您可以将字符串表示为(start,length)元组,而不是像C中那样使用以空结尾的字符串,这样就没有必要使用strlen函数。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/143921

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档