Skip to content

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

1. 本周学习目标

  • 掌握有符号与无符号运算在比较、乘除中的区别
  • 熟悉位运算、移位、循环移位的常用场景
  • 会写 32 位整数转 16 进制/2 进制输出程序

2. 知识点整理

2.1 算术运算

  • add/sub:普通加减
  • mul:无符号乘法
  • imul:有符号乘法
  • div:无符号除法
  • idiv:有符号除法

关键区别:

  • signed 与 unsigned 的解释方式不同
  • 溢出与截断时要观察 OF、CF 与结果寄存器高位

2.2 比较与跳转

有符号跳转:jg、jl、jge、jle、je、jne

无符号跳转:ja、jb、jae、jbe、je、jne

同一比特模式在不同语义下可得相反结论(例如 FFFFh 可看作 -1 或 65535)。

2.3 位运算与移位

  • and/or/xor/not
  • shl/shr/sar
  • rol/ror/rcl/rcr

应用:

  • 掩码清位、置位、翻转
  • 抽取 bit/半字节用于进制转换

3. 老师样例代码(第4周)

3.1 1.asm(知识点总讲义)

asm
1. 算术运算
   add  sub  imul/mul  idiv/div
   
   mov ax, 2
   mov bx, 3
   imul ax, bx ; ax = ax * bx = 2*3 = 0006h
               ; OF=0, CF=0
   
   mov ax, 1234h
   mov bx, 100h
   imul ax, bx ; ax=123400h 无法保存
               ; ax=3400h, OF=1, CF=1
   
   mov ax, 2
   mov bx, 3
   mul bx ; ax * bx = dx、ax
          ; dx=0000h, ax=0006h
          
   
   mov dx, -1
   mov ax, -5 ; dx、ax = 0FFFFFFFBh = -5
   mov bx, 2
   idiv bx ; dx、ax / bx
   
2. 关系运算
   先用cmp比较,再跟随j开头的条件跳转指令实现分支
(1) 跟符号数比较相关的跳转指令
   jg   jl  jge   jle   je   jne
   
(2) 跟非符号数比较相关的跳转指令   
   ja   jb  jae   jbe   je   jne

   mov ax, 0FFFFh ; -1
   mov bx, 1
   cmp ax, bx
   jl ax_is_less_than_bx ; 会发生跳转

   mov ax, 0FFFFh ; 65535
   mov bx, 1
   cmp ax, bx
   jb ax_is_below_bx ; 不会发生跳转


   mov cx, 0
   sub cx, 1; cx=0FFFFh=65535
   cmp cx, 0
   jb done  ; 不会跳转, 因为65535 > 0

3. 二进制运算
   C语言的二进制运算符: &   |   ^    ~   <<    >>
                        与  或 异或  非  左移  右移
   循环左移库函数 _rotl()
   循环右移库函数 _rotr()

   汇编语言的二进制运算指令:
                        and or  xor  not  shl/sal shr/sar
   循环左移指令rol, 循环左移指令ror
   带进位的循环左移指令rcl, 带进位的循环右移指令rcr

   mov ah, 10110110B; 或写成 mov ah, 0B6h
                    ; B是二进制常数的后缀
   mov bh, 01011010B
   and ah, bh       ; ah = ah & bh = 00010010B = 12h
   
   10110110
   01011010 and)
   -------------
   00010010

   mov ah, 10110110B; 或写成 mov ah, 0B6h
                    ; B是二进制常数的后缀
   mov bh, 01011010B
   or ah, bh        ; ah = ah | bh = 11111110B = 0FEh
   
   10110110
   01011010 or)
   -------------
   11111110
    
    
   mov ah, 10110110B; 或写成 mov ah, 0B6h
                    ; B是二进制常数的后缀
   mov bh, 01011010B
   xor ah, bh       ; ah = ah ^ bh = 11101100B = 0ECh
   
   10110110
   01011010 xor)
   -------------
   11101100    
   

   mov ah, 10110110B; 或写成 mov ah, 0B6h
                    ; B是二进制常数的后缀
   not ah           ; ah = 01001001B = 49h

   10110110 not)
   -------------
   01001001
   
   mov ah, 3
   shl ah, 1 ; 左移1位, shl: shift left 逻辑左移
   
   0000 0011 ; 移位前
   0000 0110 ; 移位后, 移出去的原最高位自动进入CF
             ; 本例中, CF=0
             ; 左移1位相当于乘以2

   mov ah, 3
   shr ah, 1 ; 右移1位, shl: shift right 逻辑右移
             ; 右移1位相当于除以2
   
   
   0000 0011 ; 移位前
   0000 0001 ; 移位后, 移出去的原最低位自动进入CF
             ; 本例中, CF=1

   
   ?011 011? ==> 0011 0110 ; 最高位及最低位清零,其余位不变
 设此数在ah中
 and ah, 01111110B ; 01111110B 是mask(掩码), 起过滤作用
 
 ?011 011?
 0111 1110  and)
 ---------------
 0011 0110
 
 
    ?011 011? ==> 1011 0111 ; 最高位及最低位置1,其余位不变
 设此数在ah中
 or ah, 10000001B ; 10000001B 是mask(掩码), 起过滤作用
 
 ?011 011?
 1000 0001  or)
 ---------------
 1011 0111
 
    1011 0110 ==> 0011 0111 ; 最高位及最低位反转,其余位不变
 设此数在ah中
 xor ah, 10000001B ; 10000001B 是mask(掩码), 起过滤作用
 
 1011 0110
 1000 0001  xor)
 ---------------
 0011 0111 
 
 
 异或的可逆性
 设z = x ^ y,则一定有:
 x = z ^ y 且
 y = z ^ x
 举例: 
 x=1011 0110B
 y=1100 0011B xor)
 z=0111 0101B
 
 
 循环左移
 mov al, 10110110B
 rol al, 1; 把AL循环左移1位, rol:rotate left
          ; al=01101101B, CF=1

 mov eax, 12345678h
 rol eax, 4
 0001 0010 0011 0100  0101 0110 0111 1000; 移位前
 0010 0011 0100  0101 0110 0111 1000 0001; 移位后

3.2 2.asm(32位整数转16进制输出)

asm
;把32位整数转化成16进制格式输出
.386
code segment use16
assume cs:code
main:
   mov eax, 12345678h
   mov cx, 8
again:
   rol eax, 4 ; eax = 23456781h
   ;push eax   ; 把eax的值压入堆栈
   mov ebx, eax ; ebx = eax, 备份eax的值到ebx中
   and eax, 1111B; 或写成 and eax, 0Fh
   cmp eax, 10
   jl is_digit
is_alpha:
   sub eax, 10
   add eax, 'A'
   jmp output
is_digit:
   add eax, '0'
output:
   mov ah, 2
   mov dl, al ; 不能写成mov dl, eax; 因为操作数必须等宽
   int 21h
   mov eax, ebx ; 恢复eax的值
   ;pop eax      ; 从堆栈中弹出eax即恢复eax
   sub cx, 1
   jnz again
   mov ah, 4Ch
   mov al, 0
   int 21h
code ends
end main

3.3 3.asm(32位整数转二进制输出)

asm
;把32位整数转化成二进制格式输出
.386
code segment use16
assume cs:code
main:
   mov eax, 12345678h
   mov cx, 32
again:   
   rol eax, 1 ; eax = 23456781h
   ;push eax   ; 把eax的值压入堆栈
   mov ebx, eax ; ebx = eax, 备份eax的值到ebx中
   and eax, 1
   add eax, '0'
output:
   mov ah, 2
   mov dl, al ; 不能写成mov dl, eax; 因为操作数必须等宽
   int 21h
   mov eax, ebx ; 恢复eax的值
   ;pop eax      ; 从堆栈中弹出eax即恢复eax
   sub cx, 1
   jnz again
   mov ah, 4Ch
   mov al, 0
   int 21h
code ends
end main

3.4 imul.asm(有符号乘法补充)

asm
.386
code segment use16
assume cs:code
main:
   mov ax, 1234h
   mov bx, 100h
   imul ax, bx
   
   mov ax, 1234h
   mov bx, 100h
   imul bx

   mov ax, -2
   mov bx, 2
   imul bx
code ends
end main

3.5 idiv.asm(有符号除法补充)

asm
.386
code segment use16
assume cs:code
main:
   mov dx, -1
   mov ax, -5 ; dx、ax = 0FFFFFFFBh = -5
   mov bx, 2
   idiv bx ; dx、ax / bx
           ; ax=0FFFEh=-2, dx=0FFFFh=-1
code ends
end main

4. 本周易错点清单

  • 有符号比较误用 jb/ja
  • 无符号比较误用 jl/jg
  • 抽 bit/半字节时忘记备份原寄存器
  • mov dl, eax 这样的宽度不匹配写法
  • 只会背指令,不会结合场景(掩码、输出)

5. 复习建议

  1. 把 1.asm 里的每条位运算都手算一遍二进制结果。
  2. 口算 FFFFh 与 1 比较时,signed/unsigned 两种结论。
  3. 重新默写 2.asm:不用看代码也能写出 16 进制输出流程。