应用标志位寄存器的学习在8086汇编中
标志位寄存器其作用就是以其不同的标志位来支持更高级的指令,使得程序员的操作更为方便。关于标志位寄存器的知识点:区分CF标志位(进位标志)与OF标志位(溢出标志)
1、adc与sbb指令:
CF标志位用在加减法的进位与借位操作上:
adc指令(add carry):带进位加法
adc ax,bx ==> (ax)=(ax)+(bx)+CF
eg:1E F000 1000H + 20 1000 1EF0H
要求ax、bx、cx分别存放结果的高16位,中间16位,低十六位。
mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H
sbb指令:带借位减法
sbb ax,bx ==> (ax)=(ax)-(bx)-CF
eg:003E 1000H - 0020 2000H
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H
应用:大数的加法运算:
;大数加法:两个128位数据进行相加
;2017年5月6日15:51:04
;Author:Kangruojin
;Version:v1.1
;Mail:mailbox_krj@163.com
;规则:将ds:[si]指向第一个数的存储单元,ds:[di]指向第二个数的存储单元
;由低地址到高地址单元依次存放128位数据由低到高的各个字(总共8个字),
;结果存放在在第一个数的存储空间中
assume cs:code
data segment
db 0FEH,0EDH,0A2H,0C3H,0D4H,0E5H,0F6H,0CCH,0CFH,0EDH,012H,064H,0FFH,01DH,0AAH,033H
;db 16 dup (0FFH) ;ds:[0]开始存储第一个操作数2^128-1
dw 0 ;存最终的进位
db 0FFH,01DH,0AAH,033H,0CFH,0EDH,012H,064H,0D4H,0E5H,0F6H,0CCH,0FEH,0EDH,0A2H,0C3H
;db 16 dup (0FFH) ;ds:[130]开始存储第二个操作数2^128-1
data ends
code segment
start:
mov ax,data
mov ds,ax
call add_128bit
mov ax,4C00H
int 21H
;========================================
add_128bit:
mov si,0
mov di,18
sub ax,ax ;将CF设置为0
mov cx,8
add_loop:
mov ax,ds:[si]
adc ax,ds:[di]
mov ds:[si],ax
inc si
inc si
inc di
inc di ;注意:不能将两个inc di和两个inc si合并为一个add si,2与add di,2.因为这种操作会对CF产生影响
loop add_loop
mov ax,0
adc ax,0 ;最后这一步是要将最终有可能存在的进位OF保存起来
mov ds:[si],ax
ret
;========================================
code ends
end start
测试结果:
2、CMP指令:
CMP指令(compare比较指令):不保存结果到寄存器或者内存单元中。只影响标志位。(虽然不保存最终结果,但是会将中间结果暂存在CPU内部的暂存器上)
①无符号数比较(检测zf和cf):
eg:cmp ax,bx
zf=1,说明(ax)=(bx)
cf=1,说明(ax)<(bx)
zf=0并且cf=0,说明(ax)>(bx)
如果zf=1说明(ax)-(bx)=0为真,即(ax)=(bx),其它解释相似。
②有符号数比较(检测zf、sf和of):
eg:cmp ah,bh
sf=1并且of=0,说明(ah)<(bh)
sf=1并且of=1,说明(ah)>(bh)
sf=0并且of=1,说明(ah)<(bh)
sf=0并且of=0,说明(ah)≥(bh)
zf=0,说明(ah)=(bh)
test reg/mem,reg/imm
与cmp指令相似,只不过两个操作数进行与操作,不保存结果值,但影响标志寄存器的值,经常用来测试某位是不是1。
3、条件转移指令:
jcxz属于条件转移指令,但是它监测的是cx的值,而不是标志位寄存器。下面的条件转移指令属于根据检测不同标志位的值来决定是否转移的条件转移指令。并且这些条件跳转指令与cmp、test指令是配合使用的,没有cmp、test的话,这些指令没有直接意义。
比如:
对于C语言的:
short a=5,b=3;
if(a>b) ;if(a<=b)
a=a+b;
else
a=a-b;
类似的对于汇编来说可能就是:
mov ax,5
mov bx,3
cmp ax,bx
ja calc ;大于就跳转到加法
sub ax,bx ;没有跳转到加法就执行该指令
jmp short over ;结束
calc:
add ax,bx
over:
...
与标志位有关的跳转指令(五个标志位:cf、of、pf、sf、zf)这其中包含有符号与无符号的操作:
je、jz 两数相等(ZF == 1)则跳转
jne、jnz 两数不等(ZF == 0)则跳转js 结果为负数跳转(SF == 1)
jns 结果为正数跳转(SF == 0)jp、jpe 结果的1的个数为偶数则跳转(PF == 1)
jnp、jpo 结果的1的个数为奇数则跳转(PF == 0)
注意:PF值只受低八位影响,odd奇数,even偶数jo 溢出则跳转(OF == 1)
jno 不溢出则跳转(OF == 0)无符号数:
jb、jnae 小于则跳转(CF == 1)
jnb、jae 不小于则跳转(CF == 0)
jbe、jna 小于等于则跳转(CF == 1 || ZF == 1)
jnbe、ja 大于则跳转(CF == 0 && ZF == 0)有符号数:
jl、jnge 小于则跳转(SF != OF)
jnl、jge 不小于则跳转(SF == OF)
jle、jng 小于等于则跳转(ZF == 1 || OF != SF)
jnle、jg 大于则跳转(ZF == 0 && SF == OF)j:jmp跳转
z:zero零
e:equal等于
n:not非,不是
s:signel符号
p:parity奇偶校验
o:odd奇数
e:even偶数
o:overflow溢出
b:below低于
a:above高于
l:lesser小于
g:greater大于
简单应用:
;2017-5-6 20:22:30
;Mail:mailbox_krj@163.com
;Author:Kangruojin
;Version:v1.1
;统计数据段中,值与8的关系:
;将值为8的个数存放到ax中
;将值大于8的个数存放到bx中
;将值小于8的个数存放到dx中
assume cs:code
data segment
db 8,12,8,4,7,8,2,8,9,8,3,1,8,16,8,15
data ends
code segment
start:
mov ax,data
mov ds,ax
call compare_eight
mov ax,4C00H
int 21H
;==============================
compare_eight:
mov si,0
mov ax,0
mov bx,0
mov dx,0
mov cx,16
again:
cmp byte ptr ds:[si],8
jne notequal ;不等于则跳过ax自加
inc ax ;统计等于8的个数
jmp short over ;该步统计了就直接跳到over,进行下一个
notequal:
jnb notless
inc bx ;统计小于8的个数
jmp short over ;该步统计了就直接跳到over,进行下一个
notless:
inc dx ;统计大于8的个数
over:
inc si
loop again
ret
code ends
end start
测试:
4、movs、stos、rep、cld、std指令:
执行一次movsb等价于:
mov al ds:[si]
mov es:[di],al ;al只是一个中间temp,不一定是al
;df为0时自加,df为1时自减
inc si ;dec si
inc di ;dec di
执行一次movsw等价于:
mov ax ds:[si]
mov es:[di],ax ;ax只是一个中间temp,不一定是ax
;df为0时加,df为1时减
add si,2 ;sub si,2
add di,2 ;sub di,2
上面两个指令能自动加减si和di是因为检测了df标志位,如果df标志位为0则自加si和di,如果df标志位为1则自减si和di。
而设置df标志位的指令为:
cld:置为0,clear df
std:置为1,set df
rep指令:重复指令,ECX(CX)中存放重复的次数:每执行一次重复的指令,ecx自减一次,减到0则结束
rep指令与movsb/movsw配合使用:
rep movsb
//等价于
s:movsb
loop s
rep movsw
//等价于
s:movsw
loop s
所以说用rep、movs、cld/std指令的配合,在设定cx以后就可以自动完成我们自己用循环对ds到es的数据复制。
eg:将data段的前16个字节内容复制到后十六个字节
mov ax,data
mov ds,ax
mov es,ax
mov si,0
mov di,16
mov cx,16
s:
mov al,ds:[si]
mov es:[di],al
inc si
inc di
loop s
;用我们新的方式做:简洁了许多
mov ax,data
mov ds,ax
mov es,ax
mov si,0
mov di,16
mov cx,16
cld
rep movsb
stos指令与movs指令类似(2017年7月31日17:49:01新增笔记):
STOS:执行一次edi、esi的值会自动增加/减小1/2/4
STOS <==> STOS DWORD PTR ES:[EDI] ==> MOV ES:[EDI],EAX
STOSD<==> STOS DWORD PTR ES:[EDI] ==> MOV ES:[EDI],EAX
STOSW<==> STOS WORD PTR ES:[EDI] ==> MOV ES:[EDI],AX
STOSB<==> STOS BYTE PTR ES:[EDI] ==> MOV ES:[EDI],ALMOVS(两边均为内存的特殊指令):执行一次edi、esi的值会自动增加/减小1/2/4
MOVS <==> MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
MOVSD<==> MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
MOVSB<==> MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
MOVSW<==> MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
pushf与popf指令:
pushf:将标志寄存器的内容入栈
popf:将栈中的值出栈到标志寄存器中
pushad:将通用寄存器全部依次压栈(保存现场)
popad:将栈顶开始的元素依次出栈通用寄存器中(恢复现场)
为直接访问标志寄存器提供了便利的方式。想使用哪一位就直接将其他位“and 0”去掉。
推荐阅读
-
应用标志位寄存器的学习在8086汇编中
-
NeurIPS 2022 | 最强斗地主AI!网易互娱AI Lab提出基于完美信息蒸馏的方法-完美信息蒸馏(PTIE) 在斗地主游戏中,非完美信息的引入主要是由于三位玩家均不能看到别人的手牌,对于任意一位玩家而言,仅可知道其余两位玩家当前手牌的并集,而难于精准判断每位玩家当前手牌。完美信息蒸馏的思路是针对这种非完美问题,构建一个第三方角色,该角色可以看到三位玩家的手牌,该角色在不告知每位玩家完美信息的情况下通过信息蒸馏的方式引导玩家打出当前情况下合理的出牌。 以强化学习常用的 Actor-Critic 算法为例,PTIE 在 Actor-Critic 算法的应用中可以利用 Critic 的 Value 输出作为蒸馏手段来提升 Actor 的表现。具体而言即在训练中 Critic 的输入为完美信息(包含所有玩家的手牌信息),Actor 的输入为非完美信息(仅包含自己手牌信息),此种情况下 Critic 给予的 Value 值包含了完美信息,可以更好地帮助 Actor 学习到更好的策略。 从更新公式上来看,正常的 Actor-Critic 算法 Actor 更新的方式如下: 在 PTIE 模式下,对于每个非完美信息状态 h,我们可以在 Critic 中构建对应的完美信息状态 D(h),并用 Critic 的输出来更新 Actor 的策略梯度,从而达到完美信息蒸馏的效果。 PTIE 框架的整体结构如下图所示: 无论是训练还是执行过程中智能体都不会直接使用完美信息,在训练中通过蒸馏将完美信息用于提升策略,从而帮助智能体达到一个更高的强度。 PTIE 的另一种蒸馏方式是将完美信息奖励引入到奖励值函数的训练中,PerfectDou 提出了基于阵营设计的完美信息奖励 node reward,以引导智能体学习到斗地主游戏中的合作策略,其定义如下: 如上所示,完美信息部分 代表 t 时刻地主手牌最少几步可以出完,在斗地主游戏中可以近似理解为是距游戏获胜的距离, 代表 t 时刻地主阵营和农民阵营距游戏获胜的距离之差, 为调节系数。通过此种奖励设计,在训练时既可以一定程度地引入各玩家的手牌信息(出完的步数需要知道具体手牌才能计算),同时也鼓励农民以阵营的角度做出决策,提升农民的合作性。 特征构建: PerfectDou 针对牌类游戏的特点主要构建了两部分特征:牌局状态特征和动作特征。其中牌局状态特征主要包括当前玩家手牌牌型特征、当前玩家打出的卡牌牌型特征、玩家角色、玩家手牌数目等常用特征,动作特征主要用于刻画当前状态下玩家的所有可能出牌,包括了每种出牌动作的牌型特征、动作的卡牌数目、是否为最大动作等特征。 牌型特征为 12 * 15 的矩阵,如下图所示: 该矩阵前 4 行代表对应每种卡牌的张数,5-12 行代表该种卡牌的种类和对应位置。 网络结构和动作空间设计 针对斗地主游戏出牌组合数较多的问题,PerfectDou 基于 RLCard 的工作上对动作空间进行了简化,对占比最大的两个出牌牌型:飞机带翅膀和四带二进行了动作压缩,将整体动作空间由 27472 种缩减到 621 种。 PerfectDou 策略网络结构如下图所示: 策略网络结构同样分为两部分:状态特征部分和动作特征部分。 在状态特征部分,LSTM 网络用于提取玩家的历史行为特征,当前牌局状态特征和提取后的行为特征会再通过多层的 MLP 网络输出当前的状态信息 embedding。 在动作特征部分,每个可行动作同样会经过多层 MLP 网络进行编码,编码后的动作特征会与其对应的状态信息 embedding 经过一层 MLP 网络计算两者间的相似度,并经由 softmax 函数输出对应的动作概率。 实验结果
-
ARM汇编(2)(指令)-跳转指令B与BL都可以使程序跳转到指定的地址执行程序。指令BL的作用是跳转的同时将下一条指令的地址复制到R14(即返回地址连接寄存器LR)寄存器中。需要注意的是,这两条指令和目标地址处的指令都要属于ARM指令集。两条指令都可以根据CPSR中的条件标志位的值决定指令是否执行。 MOVEQ PC, LR B LAB1 (1)指令格式 B {L} {<cond>} <target_address> (2)指令的例子 循环10次的例子 MOV R1, #0 BL MOV R2, #1 ...... LAB1: ADD R1, R1, #1 CMP R1, #10 @带连接的分支 load_new_format: BL switch_screen_mode BL get_screen_info BL load_palette new_loop: MOV R1, R5 BL read_byte CMP R0, #255 BLEQ read_loop STRB R0, [R2, #1]! Load/Store指令 *LDR指令 (1)指令语法格式 LDR指令用于从内存中将一个32位的字读取到目标寄存器。 指令的编码格式如图所示。 LDR指令编码格式 LDR{<cond>} <Rd>,<addr_mode> (2)指令举例 LDR r1,[r0,#0x12] ;将r0+12地址处的数据读出,保存到r1中(r0的值不变) LDR r1,[r0] ;将r0地址处的数据读出,保存到r1中(零偏移) LDR r1,[r0,r2] ;将r0+r2地址的数据读出,保存到r1中(r0的值不变) LDR r1,[r0,r2,LSL #2] ;将r0+r2×4地址处的数据读出,保存到r1中(r0,r2的值不变) LDR Rd,label ;label为程序标号,label必须是当前指令的±4KB范围内 LDR Rd,[Rn],#0x04 ;Rn的值用作传输数据的存储地址。在数据传送后将偏移量0x04与 Rn相加,结果写回到Rn中。Rn不允许是r15 注意:(1)地址对齐问题:大多数情况下,必须保证用于32位传送的地址是32位对齐的。 (2)LDR有两种形式,一种是指令,一种是伪指令,使用LDR的伪指令时,在第二个操作数前加"=" *STR指令用于将一个32位的字写入到指令中指定的内存单元 (1) 指令的语法格式 STR {<cond>} <Rd>, <addr_mode> (2) 指令举例 LDR/STR指令用于对内存变量的访问、内存缓冲区数据的访问、查表、外围部件的控制操作等。 ① 变量访问 NumCount EQU 0x40003000 ;定义变量NumCount LDR R0,=NumCount ;使用LDR伪指令装载NumCount的地址到R0 LDR R1,[R0] ;取出变量值 ADD R1,R1,#1 ;NumCount=NumCount+1 STR R1,[R0] ;保存变量 单数据交换指令 单数据交换指令是Load/Store指令的一种特例,它把一个内存单元中的内容与寄存器中的内容进行交换,交换指令是一个原子操作,也就是说,在连续的总线操作中读/写一个存储单元,在操作期间阻止其他任何指令对该存储单元的读/写。 SWP指令一般有两种形式: (1), SWP 字交换 tmp=mem32[Rn]; mem32[Rn] = Rm; Rd = tmp 指令的格式: SWP {<cond>} <Rd>, <Rm>, [<Rn>] SWP R1, R1, [R0] ;将R1的内容与R0指向的存储单元内容进行交换。 (2), SWPB 字节交换 状态寄存器传输指令 ARM指令集提供了两条指令,用于读写程序状态寄存器,MRS指令用于把CPSR或SPSR的值传送到一个寄存器中;MSR相反,把一个寄存器的内容传送到CPSR或SPSR中,这两条指令结合起来,可用于对CPSR和SPSR进行读/写操作。 MRS 把程序状态寄存器的值传送给一个通用寄存器, Rd=SPSR MSR 把通用寄存器的值传送给程序状态寄存器或把一个立即数传送给程序状态寄存器 (1)MRS指令 在ARM指令集中,只有MRS指令可以 将状态寄存器中的值读取到通用寄存器中。 格式: MRS {<cond>} Rd, CPSR/SPSR 其中,Rd为目标寄存器,Rd不允许为程序计数器(R15)。 (2) MSR指令 在ARM指令集中,只有MSR指令可以直接设置 状态寄存器的值 格式: MSR {<cond>} SPSR/CPSR , #immed Msr {<cond>} CPSR/SPSR , Rm 3,LDM和STM的配对规则 LDMFD--STMFD LDMED--STMED LDMFA--STMFA LDMEA--STMEA LDMIA--STMDB LDMIB--STMDA LDMDA--STMIB LDMDB--STMIA 指令代码如下: .global _start_start: