Skip to content

汇编与调试第3周复习笔记(含老师样例代码)

1. 本周学习目标

  • 掌握 div 在 8/16/32 位场景下的寄存器约定
  • 会把 C 语言 if、if-else、逻辑与/或 翻译成 cmp + 条件跳转
  • 会写循环输出(矩形星号、金字塔)
  • 理解 ZF 与 jnz/je 的关系

2. 知识点整理

2.1 除法指令 div 的三种常见形式

  1. 16 位 / 8 位
  • 被除数:AX
  • 除数:8 位寄存器或内存
  • 商:AL
  • 余数:AH
  1. 32 位 / 16 位
  • 被除数:DX:AX
  • 除数:16 位寄存器或内存
  • 商:AX
  • 余数:DX
  1. 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位: (dxax)/任意的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位: (edxeax)/任意的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 main

3.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 main

3.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 main

3.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 main

3.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 main

3.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 main

3.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 main

3.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. 复习建议

  1. 先背寄存器约定,再默写 3 种 div 形式。
  2. 把 4.asm 和 5.asm 画流程图,对照 C 代码看短路。
  3. 手写 7.asm,把“空格数/星号数”公式改成 5 层金字塔。