汇编基础

汇编基础

写在前面

拖更说明:
本人是鸽子精
前一段出去玩了,现在回来了要好好学习了

条件处理

复习-CPU标志位

情况 描述
操作结果为0 零标志位1
操作导致最高位进位 进位标志位1
符号标志位是操作数的最高位的副本,表示负数 符号标志位1
操作超出有符号目的操作数的范围 溢出标志位1
指令使目标操作数低字节中有偶数个1 奇偶标志位1

VS中PE=1表示偶校验,PE=0表示奇校验

布尔和比较指令

符号 逻辑运算
AND 按位与
OR 按位或
XOR 按位异或
NOT 按位非(反码)

Q:编写一条指令,用16位操作数清除AX的高8位而低8位不变
K:这个是and的常见用处之一,我们不想要那些位置可以让其和0与,结果就是0,保留哪一位就与1与
A:and ax, 00FFh

  • AND会清除溢出和进位标志位,并根据目标操作数的值来修改符号标志位,零标志位,奇偶标志位

Q:编写一条指令,用16位操作数使AX得高8位置1,低8位不变
K:类似AND,我们想置1就与1或,想保留就与0或
A:or ax, FF00h

  • OR指令总是清除进位和溢出标志位,并根据目标操作数的值来修改符号标志位,零标志位,奇偶标志位。我们可以将一个数和自己(或0)OR, 来获取该数值的信息
    or al, al 可能结果:
零标志位 符号标志位 AL中的值
清0 清0 大于0
置1 清0 等于0
清0 置1 小于0

Q:编写一条指令,不使用NOT,使eax中的所有位取反
A: xor eax, 0FFFFFFFFh
K:在x86处理器中,当按位操作或者算数操作的目标操作数最低字节为偶校验时,奇偶标志位置1(即结果中1的个数为偶数时置1),我们让一个数和0异或既不会改变该数的值,又能通过观察标志位获取该数是奇校验还是偶校验

Q:编写指令实现如下功能:当EAX的32位值为偶数时将零标志位置1,反之置0
K:TEST指令用于两个操作数的对应位AND, 并根据结果设置标志位,TEST不修改目的操作数
A:test eax, 00000001h

Q:编写一条指令将AL中的大写字母转换为小写字母,如果AL包含小写字母,不修改AL
K:大小写的字母的ASCII码只有位5不同
A:or al, 00100000b

置位和清除标志位

CMP指令执行从目的操作数中减去源操作数的隐含减法操作,而且不修改任何操作数

1
2
3
4
5
6
7
8
9
mov ax, 5
cmp ax, 10 ; ZF=0,CF=1(借位)

mov ax, 1000
mov cx, 1000
cmp cx, ax ; ZF=1,CF=0

mov si, 105
cmp si, 0 ; ZF=0,CF=0

当比较的是有符号数,SF≠CF时为小于(目标数小于源操作数),SF=CF时为大于
下面是一些修改标志位的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
test al, 0    ; ZF=1
and al, 0 ; ZF=1
or al, 1 ; ZF=0

or al, 80h ;(-128) SF=1
and al, 7Fh ; SF=0

stc ; CF=1
clc ; CF=0

mov al, 7Fh
inc al ; 溢出标志位为1,就把两个正数相加得到负数,OF=1
or eax, 0 ; OF=0

条件跳转

Q:下面代码会跳转到target吗?

1
2
3
mov ax, 8109h
cmp ax, 26h
jg target

A:不会, 8109h为负数
K:关于跳转指令,下面列出全部:

  • 特定标志位
助记符 说明 标志位或寄存器
JZ 为0跳转 ZF=1
JNZ 非0跳转 ZF=0
JC 进位跳转 CF=1
JNC 无进位跳转 CF=0
JO 溢出跳转 OF=1
JNO 无溢出跳转 OF=0
JS 有符号跳转 SF=1
JNS 无符号跳转 SF=0
JP 偶跳转 PF=1
JNP 奇跳转 PF=0
  • 比较跳转

相等

助记符 说明
JE 相等跳转
JECX ECX=0跳转

无符号数

助记符 说明
JA 大于跳转
JAE 大于等于跳转
JB 小于跳转
JBE 小于等于跳转

有符号数

助记符 说明
JG 大于跳转
JGE 大于等于跳转
JL 小于跳转
JLE 小于等于跳转

例子:循环直到按下按键

1
2
3
4
5
6
7
8
9
.data
char BYTE ?
.code
L1:
mov eax, 10
call Delay ; 创建10s延迟
call ReadKey ; 检查按键,ReadKey只有在发现有按键时才清除0标志位
jz L1 ; 没按键时循环
mov char, AL ; 有按键时读入AL(以ASCII码)

条件循环指令

Q:(判断)
1.当且仅当零标志位被清除时,LOOPE指令会跳转到标号
2.32位模式下,当ECX大于零且零标志位被清除时LOOPNZ指令才会跳转
3.LOOPZ指令的标号必须在距离其后-128~127字节范围之内
A:F T T
K:
LOOPZ和LOOPE与LOOP基本相同,但需要零标志位为1才会跳转(ZF=1),同时其不影响标志位
LOOPNZ和LOOPNE完全一样,当ECX中无符号值大于l零(减1操作后)且零标志位为0时才跳转

Q:看下面一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data
array SWORD -3, -6, -1, -10, 10, 30, 40, 4
sentinel SWORD 0
.code
mov esi, OFFSET array
mov ecx, LENGTHOF array
L1:
test WORD PTR [esi], 8000h
pushfd
add esi, TYPE array
popfd
loopnz L1
jnz quit
sub esi, TYPE array
quit:

这个程序如果找到一个非负数,ESI会指向该负数
popfd,pushfd用于将EFLAG寄存器(保存cpu标志位)压栈
未找到时ESI指向sentinel,为数组的下一块内存
Q1:修改代码实现寻找第一个负数
A1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.data
array SWORD 3, 6, 1, 10, -10, -30, -40, -4
sentinel SWORD 0
.code
mov esi, OFFSET array
mov ecx, LENGTHOF array
L1:
test WORD PTR [esi], 8000h ; 和1000000000000000b AND
pushfd ; 负数的时候ZF=0
add esi, TYPE array
popfd
loopz L1
jz quit ; ZF=1跳转,未找到负数
sub esi, TYPE array
quit:

Q2: 紧靠一个标志记值来处理能不能发现正数?如果去掉会咋样?
A2:紧靠一个不够,仅能判断非负;去掉的话循环不会中间结束,会一直转到最后一位,因为add esi这句会影响cpu的标志位

条件结构

if

  • 单ifs

if(op1==op2){x=1;y=2;}

1
2
3
4
5
6
mov eax, op1
cmp eax, op2
jne L1 ;不相等,跳过后续指令
mov x, 1
mov y, 2
L1:
  • 分支if

if (op1 > op2)
    call routine1
else
    call rountine2

1
2
3
4
5
6
7
8
mov eax, op1
cmp eax, op2
jg A1
call rountine2
jmp A2
A1:
call rountine1
A2: ;后面的语句
  • 嵌套if

if (op1 == op2){
    if (X>Y)
        call rountine1
    else
        call routine2
else
    call rountine3
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mov eax, op1
cmp eax, op2
jne L2

; 处理内层
mov eax, x
cmp eax, y
jg L1
call rountine2
jmp L3
L1:
call rountine1
jmp L3
L2:
call rountine3
L3:

while

while( val1 < val2 ){
    val1++
}

1
2
3
4
5
6
7
8
9
mov eax, val1
beginWhile:
cmp eax, val2
jnl endWhile
inc eax
jmp beginWhile
endWhile:
mov val1, eax ; 保存新的值

有限状态机

Q:有限状态机是哪种数据结构类型的特殊应用?其示意图中的节点和边分别表示什么?
A:有限状态机(FSM)是有向图的更一般结构的特例,每个节点表示一个程序的状态,每个边表示从一个状态到另一个状态
状态机示意图

例子:验证有符号整数的输入

验证有符号整数过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
;Finite.asm
INCLUDE Irvine32.inc

enter_key = 13

.data
InvalidInputMsg BYTE "Invalid Input", 13, 10, 0

.code
main PROC
call Clrscr ; 清除控制台窗口

StateA:
call Getnext ; 读取下一个字符,送入AL
cmp al, '+' ; 前置"+"
je StateB
cmp al, '-' ; 前置"-"
je StateB
call IsDigit ; 如果AL包含数字, 则ZF=1
jz StateC
call DisplayErrorMsg ; 提示非法输入
jmp Quit

StateB:
call Getnext
call IsDigit
jz StateC
call DisplayErrorMsg
jmp Quit

StateC:
call Getnext
call IsDigit
jz StateC
cmp al, enter_key ; 是否按下Enter键
je Quit ; 是,退出
call DisplayErrorMsg ; 否,提示非法输入
jmp Quit

Quit:
call Crlf ; 将光标移到到下一行
call WaitMsg
exit
main ENDP

Getnext PROC
;------------------------
; 从标准输入获取一个字符
; 无参数
; 字符保存在AL中
;------------------------
call ReadChar
call WriteChar
ret
Getnext ENDP

DisplayErrorMsg PROC
;----------------------
; 显示一个错误消息
; 无参数
; 无返回值
;----------------------
push edx ; 保存一下原来的值
mov edx, OFFSET InvalidInputMsg
call WriteString
pop edx
ret
DisplayErrorMsg ENDP

END main

其中IsDigit的源码如下:
相当简洁的写法, 可以供参考学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
IsDigit PROC
;----------------------------------
; 确定AL中是否为10进制数
; 参数: AL=字符
; 返回: 若AL为有效十进制数, ZF=1;否则ZF=0
;----------------------------------
cmp al, '0'
jb ID1 ; 比0小结束
cmp al, '9'
ja ID1 ; 比9大也结束
test ax, 0 ; 设置ZF=1
ID1:ret
IsDigit ENDP

条件控制流伪指令

32位模式下,MASM包含一些高级条件控制流伪指令,这有助于简化编写条件语句,但这些伪指令不能用于64位,这些伪指令有**.IF**, .ELSE, .ELSEIF, .ENDIF.WHILE,.REPEAT

Author

Ctwo

Posted on

2019-07-29

Updated on

2020-10-25

Licensed under

Comments