Skip to content

输入输出 笔记

汇编没有 printf / scanf,一切 I/O 都通过 int 21h(DOS 中断) 来完成。
核心套路:把功能号放 ah,把参数放指定寄存器,然后 int 21h


一、单个字符

1.1 输出一个字符

asm
mov ah, 02h       ; 功能号:输出字符
mov dl, 'A'       ; dl = 要输出的字符(ASCII 码)
int 21h           ; 屏幕显示 A

输出换行:

asm
mov ah, 02h
mov dl, 0Dh       ; 回车 CR
int 21h
mov ah, 02h
mov dl, 0Ah       ; 换行 LF
int 21h

1.2 输入一个字符

asm
mov ah, 01h       ; 功能号:输入字符(会回显到屏幕)
int 21h           ; 等待用户按键
                  ; 按下后 al = 输入的字符(ASCII 码)

输入完后字符在 al 里。比如用户按了 3al = 33h(字符 '3' 的 ASCII)。


二、字符串

2.1 输出字符串

字符串必须以 $ 结尾($ 本身不会显示):

asm
data segment
msg db "Hello, world!", 0Dh, 0Ah, '$'   ; 字符串 + 回车换行 + 结束符
data ends

code segment
assume cs:code, ds:data
main:
    mov ax, data
    mov ds, ax        ; ★ 别忘了初始化 ds

    mov ah, 09h       ; 功能号:输出字符串
    mov dx, offset msg; dx = 字符串首地址
    int 21h           ; 屏幕显示 Hello, world!

    mov ah, 4Ch
    int 21h
code ends
end main

要点

  • 字符串用 $ 结尾,不是 0!
  • 用之前一定要 mov ds, ax 初始化数据段

2.2 输入字符串

需要提前准备一个 缓冲区 buf

buf 结构:
  第 0 字节:最多允许输入几个字符(你来定)
  第 1 字节:实际输入了几个字符(系统填)
  第 2 字节起:输入的字符内容
asm
data segment
buf db 20          ; 最多输入 20 个字符
    db 0           ; 实际输入长度(系统填)
    db 20 dup(0)   ; 存放输入内容的空间
data ends

code segment
assume cs:code, ds:data
main:
    mov ax, data
    mov ds, ax

    mov ah, 0Ah       ; 功能号:输入字符串
    mov dx, offset buf
    int 21h            ; 等待用户输入,回车结束

    ; 输入完成后:
    ; buf+1 = 实际输入的字符数
    ; buf+2 开始 = 输入的字符

    mov ah, 4Ch
    int 21h
code ends
end main

三、数字输入输出(重点!)

DOS 中断只能处理字符,不能直接输入输出数字。所以:

  • 输出数字 = 把数字转成一个个字符再逐个输出
  • 输入数字 = 把输入的字符转成数字

3.1 输出一个数字

思路:不断除以 10,取余数 + '0' 变成字符,因为先取到的是个位,所以用栈倒序输出

asm
; ---- 输出 ax 中的无符号数(ax = 12345)----
    mov ax, 12345
    mov bx, 10        ; 除数
    xor cx, cx         ; cx = 计数器(记录有几位数)

split:
    xor dx, dx         ; 清零 dx(16位除法要 dx:ax)
    div bx             ; ax = ax/10(商),dx = ax%10(余数=当前最低位)
    push dx            ; 把这一位压栈
    inc cx             ; 位数 +1
    cmp ax, 0
    jne split          ; 商不为 0 就继续拆

print:
    pop dx             ; 弹出一位(从高位到低位)
    add dl, '0'        ; 数字 → ASCII 字符
    mov ah, 02h
    int 21h            ; 输出这个字符
    loop print         ; cx 次

如果要输出有符号数,先判断正负:负数就先输出 '-',再对 ax 取反(neg ax)后按无符号输出。

3.2 输入一个数字

思路:逐个读取字符,每读一个就 结果 = 结果 × 10 + (字符 - '0')

asm
; ---- 从键盘读入一个无符号数 → 结果存在 bx ----
    xor bx, bx         ; bx = 0(累加结果)

read_digit:
    mov ah, 01h
    int 21h            ; al = 输入字符
    cmp al, 0Dh        ; 回车?
    je read_done       ; 是就结束输入

    sub al, '0'        ; ASCII → 数字('3' - '0' = 3)
    xor ah, ah          ; ax = al(零扩展)

    ; bx = bx * 10 + ax
    push ax
    mov ax, bx
    mov cx, 10
    mul cx             ; dx:ax = bx * 10
    mov bx, ax
    pop ax
    add bx, ax

    jmp read_digit
read_done:
    ; 此时 bx = 输入的数字

四、常用输出小模板

输出空格

asm
mov ah, 02h
mov dl, ' '
int 21h

输出换行

asm
; 可以写成一个小函数反复调用
newline:
    mov ah, 02h
    mov dl, 0Dh
    int 21h
    mov ah, 02h
    mov dl, 0Ah
    int 21h
    ret

输出十六进制数(al 的值)

asm
; ---- 输出 al 的高 4 位和低 4 位(两位十六进制)----
    mov bl, al         ; 备份 al
    shr al, 4          ; 取高 4 位
    call print_hex_digit
    mov al, bl
    and al, 0Fh        ; 取低 4 位
    call print_hex_digit
    ; ...

print_hex_digit:       ; al = 0~15,输出对应十六进制字符
    cmp al, 10
    jb is_digit
    add al, 'A' - 10   ; 10~15 → 'A'~'F'
    jmp do_print
is_digit:
    add al, '0'        ; 0~9 → '0'~'9'
do_print:
    mov dl, al
    mov ah, 02h
    int 21h
    ret

五、程序退出

每个程序最后都要有:

asm
mov ah, 4Ch        ; 功能号:返回 DOS
mov al, 0          ; 返回码(一般写 0)
int 21h

简写:mov ax, 4C00h + int 21h


六、速查表

功能ah参数返回值说明
输入字符01hal = 字符带回显
输出字符02hdl = 字符
输出字符串09hds:dx → 字符串($ 结尾)
输入字符串0Ahds:dx → bufbuf+1 = 长度buf 第0字节 = 最大长度
程序退出4Chal = 返回码