现在位置: 首页 > 汇编语言 > 正文

汇编语言 - 数字处理

在汇编语言中处理数字涉及到进制转换、ASCII 转换和算术运算的结合。本章详细讲解如何在程序中输入和输出各种进制的数字。


数字的表示方式

计算机内部所有数字都以二进制存储,但程序中可以用不同进制表示:

进制基数NASM 写法数字范围(32位)
二进制20b1010101010101010b0 和 1
八进制80o5252o0-7
十进制10420-9
十六进制160x2A2Ah0-9, A-F

ASCII 与数字的关系

在屏幕上输入和输出的都是 ASCII 字符,而非二进制数值。

理解字符与数值的转换是汇编数字处理的核心:

字符ASCII 值对应的数值
'0'0x30(48)0
'1'0x31(49)1
'9'0x39(57)9
'A'0x41(65)10(十六进制)
'F'0x46(70)15(十六进制)
'a'0x61(97)10(十六进制)

字符转数字:数值 = ASCII码 - '0'(即 ASCII码 - 0x30

数字转字符:ASCII码 = 数值 + '0'(即 数值 + 0x30


输入数字(字符串转数值)

用户输入的是字符序列,需要转换为二进制数值才能用于计算:

实例

; 文件路径:string_to_int.asm
; 将十进制数字字符串转换为整数值

section .data
    input_str db '12345', 0      ; 输入字符串 "12345"
    input_len dd 5               ; 字符串长度

section .bss
    result_val resd 1            ; 存放转换后的数值

section .text
    global _start

_start:
    ; 转换算法:result = 0
    ; result = result * 10 + (当前字符 - '0')
    mov esi, input_str           ; esi 指向输入字符串
    mov ecx, [input_len]         ; ecx = 循环次数
    mov eax, 0                   ; eax = 累加结果

convert_loop:
    mov ebx, 10
    mul ebx                      ; eax = eax * 10
    ; mul ebx: edx:eax = eax * ebx

    mov bl, [esi]                ; 读取当前字符
    sub bl, '0'                  ; 转为数值:字符 - '0'
    movzx ebx, bl                ; 零扩展 ebx(bl -> ebx)
    add eax, ebx                 ; eax = eax + 当前位数值

    inc esi                      ; 移动到下一个字符
    loop convert_loop
    ; eax 现在 = 12345

    mov [result_val], eax        ; 保存结果

    mov eax, 1
    mov ebx, 0
    int 0x80

算法解析:以 "12345" 为例:
第1轮:result = 0×10 + 1 = 1
第2轮:result = 1×10 + 2 = 12
第3轮:result = 12×10 + 3 = 123
...最终得到 12345。


输出数字(数值转字符串)

将二进制数值转为可显示的十进制字符串:

实例

; 文件路径:int_to_string.asm
; 将整数值转换为十进制字符串输出

section .data
    number dd 12345              ; 要输出的数字
    newline db 0xA

section .bss
    output_buf resb 12           ; 输出缓冲区(最大 32 位整数是 10 位 + 符号 + null)

section .text
    global _start

_start:
    mov eax, [number]            ; 加载数字
    mov edi, output_buf + 11     ; edi 指向缓冲区末尾
    mov byte [edi], 0            ; null 终止符(方便调试)
    dec edi

    mov ebx, 10                  ; 除数 = 10
    mov ecx, 0                   ; 位数计数器

convert_digit:
    mov edx, 0                   ; 清零 edx(div 需要 edx:eax)
    div ebx                      ; eax = eax/10, edx = eax%10
    add dl, '0'                  ; 余数转字符
    mov [edi], dl                ; 存入缓冲区(从后往前)
    dec edi                      ; 缓冲区指针前移
    inc ecx                      ; 位数 +1
    cmp eax, 0                   ; 商是否为 0?
    jne convert_digit            ; 否,继续循环

    ; 现在 edi+1 指向第一个有效数字字符
    inc edi                      ; 修正指针,指向字符串开头

    ; 计算有效字符长度
    ; output_buf + 11 - edi 即有效长度

    ; 输出数字
    mov eax, 4
    mov ebx, 1
    mov ecx, edi                 ; 指向转换后的字符串
    ; 计算长度
    mov edx, output_buf + 11
    sub edx, edi                 ; edx = 缓冲区末尾 - 字符串开头
    int 0x80

    ; 输出换行
    mov eax, 4
    mov ebx, 1
    mov ecx, newline
    mov edx, 1
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

运行结果:

$ nasm -f elf32 int_to_string.asm -o int_to_string.o
$ ld -m elf_i386 int_to_string.o -o int_to_string
$ ./int_to_string
12345

十六进制输入输出

处理十六进制需要同时处理 0-9 和 A-F/a-f 的转换:

实例

; 文件路径:hex_output.asm
; 将数值以十六进制格式输出

section .data
    number dd 0x1A2B3C4D         ; 要输出的值
    hex_prefix db '0x'
    hex_prefix_len equ $ - hex_prefix
    newline db 0xA

section .bss
    hex_buf resb 10              ; 8 个十六进制位 + 前缀 + null

section .text
    global _start

_start:
    ; 输出前缀 "0x"
    mov eax, 4
    mov ebx, 1
    mov ecx, hex_prefix
    mov edx, hex_prefix_len
    int 0x80

    mov eax, [number]            ; 要转换的数值
    mov edi, hex_buf + 8         ; 缓冲区末尾(8 个十六进制位)
    mov ecx, 8                   ; 循环 8 次(32 位 = 8 个十六进制位)

hex_loop:
    mov edx, 0                   ; 准备除法
    mov ebx, 16                  ; 除以 16
    div ebx                      ; 现在:eax 除以 ebx
    ; 注意:这里 div ebx 实际是 edx:eax / ebx
    ; 余数在 edx(0-15),商在 eax
    mov ebx, eax                 ; 保存商

    ; 将余数转为十六进制字符
    mov al, dl
    cmp al, 10
    jl  digit_09                 ; 0-9
    add al, 'A' - 10             ; A-F
    jmp store_char

digit_09:
    add al, '0'                  ; 0-9

store_char:
    dec edi
    mov [edi], al
    mov eax, ebx                 ; 恢复商
    loop hex_loop

    ; 移除了loop,直接使用递减方式

    ; 输出十六进制数字(8 位)
    mov eax, 4
    mov ebx, 1
    mov ecx, hex_buf
    mov edx, 8
    int 0x80

    ; 输出换行
    mov eax, 4
    mov ebx, 1
    mov ecx, newline
    mov edx, 1
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

完整示例:简单的加法计算器

从键盘输入两个一位数,计算它们的和并输出:

实例

; 文件路径:simple_calc.asm
; 输入两个一位数(0-9),计算和并输出

section .data
    prompt1 db 'Enter first number (0-9): '
    prompt1_len equ $ - prompt1
    prompt2 db 'Enter second number (0-9): '
    prompt2_len equ $ - prompt2
    result_msg db 'Sum = '
    result_msg_len equ $ - result_msg
    newline db 0xA

section .bss
    num1 resb 2                  ; 第一个数的输入(1位+换行)
    num2 resb 2                  ; 第二个数的输入
    result_str resb 3            ; 结果字符串(最多2位+换行)

section .text
    global _start

_start:
    ; 提示输入第一个数
    mov eax, 4
    mov ebx, 1
    mov ecx, prompt1
    mov edx, prompt1_len
    int 0x80

    ; 读取第一个数
    mov eax, 3
    mov ebx, 0
    mov ecx, num1
    mov edx, 2
    int 0x80

    ; 提示输入第二个数
    mov eax, 4
    mov ebx, 1
    mov ecx, prompt2
    mov edx, prompt2_len
    int 0x80

    ; 读取第二个数
    mov eax, 3
    mov ebx, 0
    mov ecx, num2
    mov edx, 2
    int 0x80

    ; 计算和
    mov al, [num1]               ; 读取第一个数(ASCII 字符)
    sub al, '0'                  ; 转为数值
    mov bl, [num2]               ; 读取第二个数
    sub bl, '0'                  ; 转为数值
    add al, bl                   ; 求和

    ; 将结果转为字符串
    mov ah, 0                    ; 准备除法
    mov bl, 10                   ; 除以 10
    div bl                       ; al = 十位数, ah = 个位数

    ; 保存结果
    add al, '0'                  ; 十位转字符
    mov [result_str], al         ; 存十位
    add ah, '0'                  ; 个位转字符
    mov [result_str + 1], ah     ; 存个位
    mov byte [result_str + 2], 0xA  ; 换行

    ; 输出结果信息
    mov eax, 4
    mov ebx, 1
    mov ecx, result_msg
    mov edx, result_msg_len
    int 0x80

    ; 输出计算结果(可能 1 或 2 位)
    mov eax, 4
    mov ebx, 1
    mov ecx, result_str
    cmp byte [result_str], '0'
    je  output_one_digit        ; 如果十位是 '0',只输出个位
    mov edx, 3                  ; 2 位 + 换行
    jmp do_output

output_one_digit:
    inc ecx                     ; 跳过十位的 '0'
    mov edx, 2                  ; 1 位 + 换行

do_output:
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

运行结果:

$ nasm -f elf32 simple_calc.asm -o simple_calc.o
$ ld -m elf_i386 simple_calc.o -o simple_calc
$ ./simple_calc
Enter first number (0-9): 7
Enter second number (0-9): 8
Sum = 15

在汇编中处理数字输入输出需要对 ASCII 编码和进制转换有清晰的理解。数字转字符串(int to string)和字符串转数字(string to int)是最基本的两个工具函数,建议写好后保存为模板,后续可直接复用。