汇编与调试第3周复习笔记(含老师样例代码)
1. 本周学习目标
- 掌握 div 在 8/16/32 位场景下的寄存器约定
- 会把 C 语言 if、if-else、逻辑与/或 翻译成 cmp + 条件跳转
- 会写循环输出(矩形星号、金字塔)
- 理解 ZF 与 jnz/je 的关系
2. 知识点整理
2.1 除法指令 div 的三种常见形式
- 16 位 / 8 位
- 被除数:AX
- 除数:8 位寄存器或内存
- 商:AL
- 余数:AH
- 32 位 / 16 位
- 被除数:DX:AX
- 除数:16 位寄存器或内存
- 商:AX
- 余数:DX
- 64 位 / 32 位(.386)
- 被除数:EDX:EAX
- 除数:32 位寄存器或内存
- 商:EAX
- 余数:EDX
高频错误:
- 忘记在循环除法前清 DX 或 EDX
- 把商和余数寄存器位置写反
2.2 条件分支翻译
- 先 cmp,再用 jg/jl/je/jne 等跳转
- 逻辑与(&&):前一个条件不满足可直接判 false
- 逻辑或(||):前一个条件满足可直接判 true
2.3 循环输出与中断
- AH=2,DL=字符,int 21h 输出单字符
- 一行结束通常输出回车 0Dh + 换行 0Ah
- jnz 依赖 ZF=0 跳转
3. 老师样例代码(第3周)
3.1 1.asm(div 三种用法 + 计算 123 是几位数)
asm
comment @
汇编语言中的除法一共有3种用法:
(1) 16位/8位: ax/任意的8位寄存器或变量=al..ah
mov ax, 123h
mov bl, 10h
div bl ; ax / bl = al..ah, al=12h, ah=03h
(2) 32位/16位: (dx、ax)/任意的16位寄存器或变量=ax..dx
mov dx, 12h
mov ax, 3456h
mov bx, 1000h
div bx ; (dx、ax)/bx = 123456h/1000h = ax..dx,
; ax=0123h, dx=0456h
(3) 64位/32位: (edx、eax)/任意的32位寄存器或变量=eax..edx
mov edx, 0
mov eax, 123456h
mov ebx, 1000h
div ebx ; (edx、eax)/ebx=0000000000123456h/1000h
; eax=123h, edx=456h
;ebx:extended bx, 是一个32位的寄存器
@
; 计算123是几位数
code segment
assume cs:code
main:
mov ax, 123
mov bl, 10
mov cx, 0
again:
div bl ; ax / bl = 123 / 10, al=12, ah=3
add cx, 1
cmp al, 0
je done
mov ah, 0
jmp again
done:
code ends
end main3.2 2.asm(16位除法循环统计位数)
asm
; 计算12345是几位数
code segment
assume cs:code
main:
mov ax, 12345
mov bx, 10
mov cx, 0
again:
mov dx, 0
div bx ; (dx、ax) / bx = 12345 / 10, ax=1234, dx=5
add cx, 1
cmp ax, 0
jne again
;je done
;jmp again
done:
code ends
end main3.3 3.asm(32位除法循环统计位数)
asm
; 计算12345是几位数
.386
code segment use16
assume cs:code
main:
mov eax, 12345
mov ebx, 10
mov cx, 0
again:
mov edx, 0
div ebx ; (edx、eax) / ebx = 12345 / 10, eax=1234, edx=5
add cx, 1
cmp eax, 0
jne again
;je done
;jmp again
done:
code ends
end main3.4 4.asm(逻辑与,双重判断)
asm
code segment
assume cs:code
main:
mov ax, 2
mov bx, -3
cmp ax, 0
jg ax_is_positive
jmp set_cx_0
ax_is_positive:
cmp bx, 0
jg bx_is_positive
set_cx_0:
mov cx, 0
jmp done
bx_is_positive:
mov cx, 1
done:
code ends
end main3.5 5.asm(逻辑或,任一满足即 true)
asm
code segment
assume cs:code
main:
mov ax, 2
mov bx, -3
cmp ax, 0
jg set_cx_1
cmp bx, 0
jg set_cx_1
mov cx, -1
jmp done
set_cx_1:
mov cx, 1
done:
code ends
end main3.6 6.asm(双循环输出 5x3 星号)
asm
;用循环输出5*3个星号
code segment
assume cs:code
main:
mov bx, 3; bx=行数
next_row:
mov cx, 5; cx=每个星号的个数
next_star:
mov ah, 2; ah=2用来指定子函数的编号
mov dl, '*'
int 21h ; 调用0x21号函数集
sub cx, 1
jnz next_star; jump if not zero
;cpu内部有一个FL寄存器,该寄存器的其中一位叫ZF
;当add、sub、cmp等指令使运算结果=0时,ZF会被置1,
;表示zero is true,反之ZF会被清零,表示zero is not true
;jnz是当ZF=0时才跳转
;je是当ZF=1时才跳转
mov ah, 2
mov dl, 0Dh; 或mov dl, 13; 其中0Dh是回车符,即'\r'
int 21h
mov ah, 2
mov dl, 0Ah; 或mov dl, 10; 其中0Ah是换行符,即'\n'
int 21h
sub bx, 1
jnz next_row
mov ah, 4Ch
mov al, 0
int 21h ; 相当于C语言中的函数调用exit(0)
code ends
end main3.7 7.asm(三层金字塔)
asm
comment #
用循环输出一个3层金字塔:
*
***
*****
#
code segment
assume cs:code
main:
mov bx, 1 ; 行号
next_row:
mov cx, bx
add cx, cx
sub cx, 1 ; cx=星号的个数
mov bp, 3 ; 3是金字塔的层数
sub bp, bx ; bp=空格的个数
output_1_space:
cmp bp, 0
je output_1_star
mov ah, 2
mov dl, ' '
int 21h ; 输出一个空格
sub bp, 1
jmp output_1_space
output_1_star:
mov ah, 2
mov dl, '*'
int 21h ; 输出一个星号
sub cx, 1
jnz output_1_star; jnz:jump if not zero
mov ah, 2
mov dl, 0Dh
int 21h; 输出一个回车符'\r'
mov ah, 2
mov dl, 0Ah
int 21h; 输出一个换行符'\n'
add bx, 1
cmp bx, 3
jle next_row
mov ah, 4Ch
mov al, 0
int 21h; 相当于C语言函数调用exit(0), 终止程序的运行
code ends
end main3.8 4.c(逻辑与对照)
c
#include <stdio.h>
main()
{
int a=2, b=-3, c;
if(a > 0)
{
if(b > 0)
c = 1;
else
c = 0;
}
else
c = 0;
/*
if(a > 0 && b > 0)
c = 1;
else
c = 0;
*/
}3.9 5.c(逻辑或对照)
c
main()
{
int a=2, b=-3, c;
if(a > 0 || b > 0)
c = 1;
else
c = -1;
}4. 本周易错点清单
- div 前没清高位寄存器(DX/EDX)
- && 和 || 翻译成跳转时短路逻辑写反
- 字符输出忘了 AH=2
- 换行只写 0Ah,没写 0Dh+0Ah
- 循环计数器减法和跳转位置放错导致死循环
5. 复习建议
- 先背寄存器约定,再默写 3 种 div 形式。
- 把 4.asm 和 5.asm 画流程图,对照 C 代码看短路。
- 手写 7.asm,把“空格数/星号数”公式改成 5 层金字塔。