1

64 ビット Linux で NASM を使用してアセンブリを学習しようとしています。

2つの数字を読み込んで加算するプログラムを作ることができました。私が最初に気付いたのは、プログラムが 1 桁の数字 (および結果) でしか機能しないということです。

; Calculator

SECTION .data
    msg1    db  "Enter the first number: "
    msg1len equ $-msg1
    msg2    db  "Enter the second number: "
    msg2len equ $-msg2
    msg3    db  "The result is: "
    msg3len equ $-msg3

SECTION .bss
    num1    resb    1
    num2    resb    1
    result  resb    1

SECTION .text
    global main

main:
    ; Ask for the first number
    mov EAX,4
    mov EBX,1
    mov ECX,msg1
    mov EDX,msg1len
    int 0x80

    ; Read the first number
    mov     EAX,3
    mov     EBX,1
    mov ECX,num1
    mov EDX,2
    int 0x80

    ; Ask for the second number
    mov EAX,4
    mov EBX,1
    mov ECX,msg2
    mov EDX,msg2len
    int 0x80

    ; Read the second number
    mov     EAX,3
    mov     EBX,1
    mov ECX,num2
    mov EDX,2
    int 0x80

    ; Prepare to announce the result
    mov EAX,4
    mov EBX,1
    mov ECX,msg3
    mov EDX,msg3len
    int 0x80

    ; Do the sum
    ; Store read values to EAX and EBX
    mov EAX,[num1]
    mov EBX,[num2]

    ; From ASCII to decimal
    sub EAX,'0'
    sub EBX,'0'

    ; Add
    add EAX,EBX

    ; Convert back to EAX
    add EAX,'0'

    ; Save the result back to the variable
    mov [result],EAX

    ; Print result
    mov EAX,4
    mov EBX,1
    mov ECX,result
    mov EDX,1
    int 0x80

ご覧のとおり、最初の数値用に 1 バイト、2 番目の数値用に 1 バイト、結果用にもう 1 バイトを予約しています。これはあまり柔軟ではありません。任意の大きさの数字で足し算をしたいです。

これにどのようにアプローチすればよいですか?

4

1 に答える 1

4

まず、64 ビット プログラムではなく、32 ビット プログラムを生成します。Linux 64 ビットは、32 ビット プログラムが静的にリンクされているか (これはあなたの場合です)、32 ビット共有ライブラリがインストールされていれば実行できるため、問題はありません。

あなたのプログラムには本当のバグがあります: RAM の 1 バイト フィールドから "EAX" レジスタを読み書きしています:

mov EAX, [num1]

これは通常、リトル エンディアン コンピューター (x86) で機能します。ただし、読み取りたいバイトがプログラムの最後のメモリ ページの最後にある場合は、バス エラーが発生します。

さらに重要なのは書き込みコマンドです。

mov [result], EAX

このコマンドは、「result」変数に続く 3 バイトのメモリを上書きします。プログラムを追加のバイトで拡張する場合:

num1 resb 1
num2 resb 1
result resb 1
newVariable1 resb 1

これらの変数を上書きします! プログラムを修正するには、完全な EAX レジスタの代わりに AL (および BL) レジスタを使用する必要があります。

mov AL, [num1]
mov BL, [num2]
...
mov [result], AL

プログラムのもう 1 つの発見は、ファイル ハンドル #1 から読み込んでいるということです。これが標準出力です。プログラムは、ファイル ハンドル #0 (標準入力) から読み取る必要があります。

mov EAX, 3 ; read
mov EBX, 0 ; standard input
...
int 0x80

しかし、実際の質問に対する答えは次のとおりです。

C ライブラリ関数 (fgets() など) は、バッファリングされた入力を使用します。このようにすると、最初は少し複雑になるため、一度に 1 バイトずつ読み取ることもできます。

「C のような高級言語を使ってこの問題を解決するにはどうすればよいか」という方法を考えます。アセンブラ プログラムでライブラリを使用しない場合は、システム コール (セクション 2 のマニュアル ページ) のみを関数として使用できます (たとえば、「fgets()」は使用できず、「read()」のみを使用できます)。

あなたの場合、標準入力から数値を読み取る C プログラムは次のようになります。

int num1;
char c;
...
num1 = 0;
while(1)
{
    if(read(0,&c,1)!=1) break;
    if(c=='\r' || c=='\n') break;
    num1 = 10*num1 + c - '0';
}

ここで、アセンブラ コードについて考えるかもしれません (私は通常、別の構文を持つ GNU アセンブラを使用しているため、このコードにはいくつかのバグが含まれている可能性があります)。

c resb 1
num1 resb 4

...

    ; Set "num1" to 0
  mov EAX, 0
  mov [num1], EAX
    ; Here our while-loop starts
next_digit:
    ; Read one character
  mov EAX, 3
  mov EBX, 0
  mov ECX, c
  mov EDX, 1
  int 0x80
    ; Check for the end-of-input
  cmp EAX, 1
  jnz end_of_loop
    ; This will cause EBX to be 0.
    ; When modifying the BL register the
    ; low 8 bits of EBX are modified.
    ; The high 24 bits remain 0.
    ; So clearing the EBX register before
    ; reading an 8-bit number into BL is
    ; a method for converting an 8-bit
    ; number to a 32-bit number!
  xor EBX, EBX
    ; Load the character read into BL
    ; Check for "\r" or "\n" as input
  mov BL, [c]
  cmp BL, 10
  jz end_of_loop
  cmp BL, 13
  jz end_of_loop
    ; read "num1" into EAX
  mov EAX, [num1]
    ; Multiply "num1" with 10
  mov ECX, 10
  mul ECX
    ; Add one digit
  sub EBX, '0'
  add EAX, EBX
    ; write "num1" back
  mov [num1], EAX
    ; Do the while loop again
  jmp next_digit
    ; The end of the loop...
end_of_loop:
    ; Done

桁数の多い 10 進数を書くのはより困難です。

于 2013-09-11T05:24:45.010 に答える