汇编angularjs 指令详解程序,看不懂,求详细翻译,js,movsxd,cmp,jnp,short near ptr等等,越详细越好,小白

汇编指令程序,看不懂,求详细翻译,js,movsxd,cmp,jnp,short near ptr等等,越详细越好,小白_百度知道
汇编指令程序,看不懂,求详细翻译,js,movsxd,cmp,jnp,short near ptr等等,越详细越好,小白
segment byte public 'CODE' use64
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
[rbx], ecx
short near pt...
我有更好的答案
为什么还要用长的,要知道有些对程序常度有要求的场合,明明在段内用段内跳可以节省字节缩短指令长度,长跳转,段间跳,段内跳汇编特点是贴近机器,更简练,所以指令就分很细有短跳转
采纳率:67%
为您推荐:
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)查看: 1754|回复: 10
【搬运】弄了一片汇编指令大全 准备打印到口袋每天看一遍 分享给大家
&&离线&该用户从未签到
马上注册,深入学习!
才可以下载或查看,没有帐号?
8080汇编手册数据传输指令
──────────────────────────────
它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据。
1。 通用数据传送指令。
MOV 传送字或字节。
MOVSX 先符号扩展,再传送。
MOVZX 先零扩展,再传送。
PUSH 把字压入堆栈。
POP 把字弹出堆栈。
PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈。
POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈。
PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈。
POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈。
BSWAP 交换32位寄存器里字节的顺序
XCHG 交换字或字节。( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
CMPXCHG 比较并交换操作数。( 第二个操作数必须为累加器AL/AX/EAX )
XADD 先交换再累加。( 结果在第一个操作数里 )
XLAT 字节查表转换。
── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即
0-FFH); 返回 AL 为查表结果。 ( [BX+AL]-&AL )
2。 输入输出端口传送指令。
IN I/O端口输入。 ( 语法: IN 累加器, {端口号│DX} )
OUT I/O端口输出。 ( 语法: OUT {端口号│DX},累加器 )
输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,
其范围是 0-65535。
3。 目的地址传送指令。
LEA 装入有效地址。
例: LEA DX,把偏移地址存到DX。
LDS 传送目标指针,把指针内容装入DS。
例: LDS SI,把段地址:偏移地址存到DS:SI。
LES 传送目标指针,把指针内容装入ES。
例: LES DI,把段地址:偏移地址存到ES:DI。
LFS 传送目标指针,把指针内容装入FS。
例: LFS DI,把段地址:偏移地址存到FS:DI。
LGS 传送目标指针,把指针内容装入GS。
例: LGS DI,把段地址:偏移地址存到GS:DI。
LSS 传送目标指针,把指针内容装入SS。
例: LSS DI,把段地址:偏移地址存到SS:DI。
4。 标志传送指令。
LAHF 标志寄存器传送,把标志装入AH。
SAHF 标志寄存器传送,把AH内容装入标志寄存器。
PUSHF 标志入栈。
POPF 标志出栈。
PUSHD 32位标志入栈。
POPD 32位标志出栈。
二、算术运算指令
──────────────────────────────
ADD 加法。
ADC 带进位加法。
INC 加 1。
AAA 加法的ASCII码调整。
DAA 加法的十进制调整。
SUB 减法。
SBB 带借位减法。
DEC 减 1。
NEC 求反(以 0 减之)。
CMP 比较。(两操作数作减法,仅修改标志位,不回送结果)。
AAS 减法的ASCII码调整。
DAS 减法的十进制调整。
MUL 无符号乘法。
IMUL 整数乘法。
以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM 乘法的ASCII码调整。
DIV 无符号除法。
IDIV 整数除法。
以上两条,结果回送:
商回送AL,余数回送AH, (字节运算);
或 商回送AX,余数回送DX, (字运算)。
AAD 除法的ASCII码调整。
CBW 字节转换为字。 (把AL中字节的符号扩展到AH中去)
CWD 字转换为双字。 (把AX中的字的符号扩展到DX中去)
CWDE 字转换为双字。 (把AX中的字符号扩展到EAX中去)
CDQ 双字扩展。 (把EAX中的字的符号扩展到EDX中去)
三、逻辑运算指令
────────────────────────────
AND 与运算。
OR 或运算。
XOR 异或运算。
NOT 取反。
TEST 测试。(两操作数作与运算,仅修改标志位,不回送结果)。
SHL 逻辑左移。
SAL 算术左移。(=SHL)
SHR 逻辑右移。
SAR 算术右移。(=SHR)
ROL 循环左移。
ROR 循环右移。
RCL 通过进位的循环左移。
RCR 通过进位的循环右移。
以上八种移位指令,其移位次数可达255次。
移位一次时, 可直接用操作码。 如 SHL AX,1。
移位&1次时, 则由寄存器CL给出移位次数。
如 MOV CL,04
SHL AX,CL
四、串指令
───────────────────────────
DS:SI 源串段寄存器 :源串变址。
ES:DI 目标串段寄存器:目标串变址。
CX 重复次数计数器。
AL/AX 扫描值。
D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量。
Z标志 用来控制扫描或比较操作的结束。
MOVS 串传送。
( MOVSB 传送字符。 MOVSW 传送字。 MOVSD 传送双字。 )
CMPS 串比较。
( CMPSB 比较字符。 CMPSW 比较字。 )
SCAS 串扫描。
把AL或AX的内容与目标串作比较,比较结果反映在标志位。
LODS 装入串。
把源串中的元素(字或字节)逐一装入AL或AX中。
( LODSB 传送字符。 LODSW 传送字。 LODSD 传送双字。 )
STOS 保存串。
是LODS的逆过程。
REP 当CX/ECX&&0时重复。
REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX&&0时重复。
REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX&&0时重复。
REPC 当CF=1且CX/ECX&&0时重复。
REPNC 当CF=0且CX/ECX&&0时重复。
五、程序转移指令
──────────────────────────
1&无条件转移指令 (长转移)
JMP 无条件转移指令
CALL 过程调用
RET/RETF过程返回。
2&条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1 JA/JNBE 不小于或不等于时转移。
JAE/JNB 大于或等于转移。
JB/JNAE 小于转移。
JBE/JNA 小于或等于转移。
以上四条,测试无符号整数运算的结果(标志C和Z)。
JG/JNLE 大于转移。
JGE/JNL 大于或等于转移。
JL/JNGE 小于转移。
JLE/JNG 小于或等于转移。
以上四条,测试带符号整数运算的结果(标志S,O和Z)。
JE/JZ 等于转移。
JNE/JNZ 不等于时转移。
JC 有进位时转移。
JNC 无进位时转移。
JNO 不溢出时转移。
JNP/JPO 奇偶性为奇数时转移。
JNS 符号位为 &0& 时转移。
JO 溢出转移。
JP/JPE 奇偶性为偶数时转移。
JS 符号位为 &1& 时转移。
3&循环控制指令(短转移)
LOOP CX不为零时循环。
LOOPE/LOOPZ CX不为零且标志Z=1时循环。
LOOPNE/LOOPNZ CX不为零且标志Z=0时循环。
JCXZ CX为零时转移。
JECXZ ECX为零时转移。
4&中断指令
INT 中断指令
INTO 溢出中断
IRET 中断返回
5&处理器控制指令
HLT 处理器暂停, 直到出现中断或复位信号才继续。
WAIT 当芯片引线TEST为高电平时使CPU进入等待状态。
ESC 转换到外处理器。
LOCK 封锁总线。
NOP 空操作。
STC 置进位标志位。
CLC 清进位标志位。
CMC 进位标志取反。
STD 置方向标志位。
CLD 清方向标志位。
STI 置中断允许位。
CLI 清中断允许位。
六、伪指令
───────────────────────────
DW 定义字(2字节)。
PROC 定义过程。
ENDP 过程结束。
SEGMENT 定义段。
ASSUME 建立段寄存器寻址。
ENDS 段结束。
END 程序结束。
一.机械码,又称机器码.
ultraedit打开,编辑exe文件时你会看到
许许多多的由0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F组成的数码,这些数码
就是机器码.
修改程序时必须通过修改机器码来修改exe文件.
二.需要熟练掌握的全部汇编知识(只有这么多)
不大容易理解,可先强行背住,混个脸儿熟,以后慢慢的就理解了
cmp a,b 比较a与b
mov a,b 把b的值送给a
ret 返回主程序
nop 无作用,英文“no operation”的简写,意思是“do nothing”(机器码90)***机器码的含义参看上面
(解释:ultraedit打开编辑exe文件时你看到90,等同于汇编语句nop)
call 调用子程序
je 或jz 若相等则跳(机器码74 或0F84)
jne或jnz 若不相等则跳(机器码75或0F85)
jmp 无条件跳(机器码EB)
jb 若小于则跳
ja 若大于则跳
jg 若大于则跳
jge 若大于等于则跳
jl 若小于则跳
jle 若小于等于则跳
三.常见修改(机器码)
74=&75 74=&90 74=&EB
75=&74 75=&90 75=&EB
75-&90(相应的机器码修改)
jnz -& jmp
75 -& EB(相应的机器码修改)
75-&74 (正常) 0F 85 -& 0F 84(特殊情况下,有时,相应的机器码修改)
四.两种不同情况的不同修改方法
1.修改为jmp
je(jne,jz,jnz) =&jmp相应的机器码EB (出错信息向上找到的第一个跳转)jmp的作用是绝对跳,无条件跳,从而跳过下面的出错信息
xxxxxxxxxxxx 出错信息,例如:注册码不对,sorry,未注册版不能...,&Function Not Avaible in Demo& 或 &Command Not Avaible& 或 &Can't save in Shareware/Demo&等 (希望把它跳过,不让它出现)
xxxxxxxxxxxx 正确路线所在
2.修改为nop
je(jne,jz,jnz) =&nop相应的机器码90 (正确信息向上找到的第一个跳转) nop的作用是抹掉这个跳转,使这个跳转无效,失去作用,从而使程序顺利来到紧跟其后的正确信息处
xxxxxxxxxxxx 正确信息,例如:注册成功,谢谢您的支持等(我们希望它不被跳过,让它出现,程序一定要顺利来到这里)
xxxxxxxxxxxx 出错信息(我们希望不要跳到这里,不让它出现)它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.
1. 通用数据传送指令.
MOV 传送字或字节.
MOVSX 先符号扩展,再传送.
MOVZX 先零扩展,再传送.
PUSH 把字压入堆栈.
POP 把字弹出堆栈.
PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
BSWAP 交换32位寄存器里字节的顺序
XCHG 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
XADD 先交换再累加.( 结果在第一个操作数里 )
XLAT 字节查表转换.
── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即
0-FFH); 返回 AL 为查表结果. ( [BX+AL]-&AL )
2. 输入输出端口传送指令.
IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,
其范围是 0-65535.
3. 目的地址传送指令.
LEA 装入有效地址.
例: LEA DX,把偏移地址存到DX.
LDS 传送目标指针,把指针内容装入DS.
例: LDS SI,把段地址:偏移地址存到DS:SI.
LES 传送目标指针,把指针内容装入ES.
例: LES DI,把段地址:偏移地址存到ESI.
LFS 传送目标指针,把指针内容装入FS.
例: LFS DI,把段地址:偏移地址存到FSI.
LGS 传送目标指针,把指针内容装入GS.
例: LGS DI,把段地址:偏移地址存到GSI.
LSS 传送目标指针,把指针内容装入SS.
例: LSS DI,把段地址:偏移地址存到SSI.
4. 标志传送指令.
LAHF 标志寄存器传送,把标志装入AH.
SAHF 标志寄存器传送,把AH内容装入标志寄存器.
PUSHF 标志入栈.
POPF 标志出栈.
PUSHD 32位标志入栈.
POPD 32位标志出栈.
二、算术运算指令
───────────────────────────────────────
ADC 带进位加法.
AAA 加法的ASCII码调整.
DAA 加法的十进制调整.
SBB 带借位减法.
NEC 求反(以 0 减之).
CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
AAS 减法的ASCII码调整.
DAS 减法的十进制调整.
MUL 无符号乘法.
IMUL 整数乘法.
以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM 乘法的ASCII码调整.
DIV 无符号除法.
IDIV 整数除法.
以上两条,结果回送:
商回送AL,余数回送AH, (字节运算);
或 商回送AX,余数回送DX, (字运算).
AAD 除法的ASCII码调整.
CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)
三、逻辑运算指令
───────────────────────────────────────
AND 与运算.
OR 或运算.
XOR 异或运算.
TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
SHL 逻辑左移.
SAL 算术左移.(=SHL)
SHR 逻辑右移.
SAR 算术右移.(=SHR)
ROL 循环左移.
ROR 循环右移.
RCL 通过进位的循环左移.
RCR 通过进位的循环右移.
以上八种移位指令,其移位次数可达255次.
移位一次时, 可直接用操作码. 如 SHL AX,1.
移位&1次时, 则由寄存器CL给出移位次数.
如 MOV CL,04
四、串指令
───────────────────────────────────────
DS:SI 源串段寄存器 :源串变址.
ESI 目标串段寄存器:目标串变址.
CX 重复次数计数器.
AL/AX 扫描值.
D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
Z标志 用来控制扫描或比较操作的结束.
MOVS 串传送.
( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
CMPS 串比较.
( CMPSB 比较字符. CMPSW 比较字. )
SCAS 串扫描.
把AL或AX的内容与目标串作比较,比较结果反映在标志位.
LODS 装入串.
把源串中的元素(字或字节)逐一装入AL或AX中.
( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
STOS 保存串.
是LODS的逆过程.
REP 当CX/ECX&&0时重复.
REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX&&0时重复.
REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX&&0时重复.
REPC 当CF=1且CX/ECX&&0时重复.
REPNC 当CF=0且CX/ECX&&0时重复.
五、程序转移指令
───────────────────────────────────────
1&无条件转移指令 (长转移)
JMP 无条件转移指令
CALL 过程调用
RET/RETF过程返回.
2&条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1&OP2 )
JA/JNBE 不小于或不等于时转移.
JAE/JNB 大于或等于转移.
JB/JNAE 小于转移.
JBE/JNA 小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
JG/JNLE 大于转移.
JGE/JNL 大于或等于转移.
JL/JNGE 小于转移.
JLE/JNG 小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
JE/JZ 等于转移.
JNE/JNZ 不等于时转移.
JC 有进位时转移.
JNC 无进位时转移.
JNO 不溢出时转移.
JNP/JPO 奇偶性为奇数时转移.
JNS 符号位为 &0& 时转移.
JO 溢出转移.
JP/JPE 奇偶性为偶数时转移.
JS 符号位为 &1& 时转移.
3&循环控制指令(短转移)
LOOP CX不为零时循环.
LOOPE/LOOPZ CX不为零且标志Z=1时循环.
LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
JCXZ CX为零时转移.
JECXZ ECX为零时转移.
4&中断指令
INT 中断指令
INTO 溢出中断
IRET 中断返回
5&处理器控制指令
HLT 处理器暂停, 直到出现中断或复位信号才继续.
WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
ESC 转换到外处理器.
LOCK 封锁总线.
NOP 空操作.
STC 置进位标志位.
CLC 清进位标志位.
CMC 进位标志取反.
STD 置方向标志位.
CLD 清方向标志位.
STI 置中断允许位.
CLI 清中断允许位.
六、伪指令
───────────────────────────────────────
DW 定义字(2字节).
PROC 定义过程.
ENDP 过程结束.
SEGMENT 定义段.
ASSUME 建立段寄存器寻址.
ENDS 段结束.
END 程序结束
★★★★★ 热心人,佛祖保佑你事事顺利 ,财源滚滚!!!
★★★★★ 热心人,佛祖保佑你事事顺利 ,财源滚滚!!!
吃水不忘引水人,学习中!
不错,不错,评个分!
评分,不会扣掉你的分!给楼主评分,你零损失!
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
先介绍简单的就是有消息框的。
1.函数断点法
如直接OD下断点 bp MessageBoxA&&当然这个只是一个函数。举个例子而已。后边会有一个专门的总结方便下断点。
2.F12堆栈回溯法(F12暂停法)
(1)F12堆栈回溯法
当出现提示框时在OD按下F12暂停程序。然后点击工具栏的 “K”&&或者& &查看---调用堆栈---找到最接近入口的地址如401C22
回车找到该代码“push ebp” 十六进制“55”
(2)F12暂停法【这个才是真正地F12暂停法】
1 od 下运行程序,F12 暂停;
2 View菜单中选击Windows项,在打开的窗口中可以从Title栏看到目标按钮,从而找到它的Handle(xxxxxxxx) ;
对不同平台生成的程序,分别处理:
一、VB, Delphi, CBuilder 程序:
3 在CallWindowProcA入口下条件断点: [esp+8]==xxxxxxxx && [esp+0c]==202;
4 F9继续程序,点击目标按钮,程序中断;
5 Alt+F4,在代码段(.text)上下访问断点;
6 F9执行程序,程序中断,
& &1). VB程序中断在下面代码
& &&&PUSH DWORD PTR DS:[EAX+EBX]& && && && &&&; yyyyyyy
& &&&上面[EAX+EBX]的值(yyyyyy)就是我们要找的位置。
& &2). Delphi, CBuilder 的程序
& &&&程序直接断在我们要找的位置。
借moon一句,这姑妄称作CallWindowProcA条件断点加上code段内存断点法吧。
二、VC程序
又分MFC和Win32两种情况,二者相同之处:
3 在IsDialogMessageW入口下条件断点: [[esp+8]]==xxxxxxxx && [[esp+8]+4]==202
4 F9继续程序,点击目标按钮,程序中断;
5 Alt+F4,在代码段上下访问断点;
6 F9执行程序,程序中断, 注意这里虽然中断在code段,但却不是处理Button点击事件的代码处
&&对Win32程序,只需要按几下F7,当回到User32.dll领空后再重复一次第 5、6步就可以了;
&&而对于MFC程序,我们不得不多次重复这样的操作:单步回到MFC领空,再第 5、6步。好在已经看到大陆啦!
类比,这就叫IsDialogMessageW条件断点加上code段内存断点法了
3.关键字符串法
无插件(反汇编窗口右键---查找---所有参考字符串文本【可能翻译不一样大概就是这样吧】)
有插件(插件---中文搜索---查找U码【unicode】(查找A码【ascii】)
然后ctrl+f 查找你要的关键字&&然后回车&&找到“push ebp”&&也就是十六进制的“55”&&事件头下断点
4.按钮事件法
要熟悉各个语言的按钮事件以下是我总结的
(1)Delphi和BC++程序
OD载入后,CTRL+G,转到处
然后就CTRL+B,查找特征码740E8BD38B83????????FF93????????
然后就在下面的CALL处下断吧.
下面的工作就是不断的CTRL+L继续查找和F2下断了.
(2)VB程序
OD载入后,CTRL+B,816C24
然后,就在下面的JMP处F2下断,下完后CTRL+L,如果还有,就继续下断点.
下完断点后,运行程序,点击相应的按纽,OD就会断下来了
(3)易语言
【1.老版本的易语言】OD载入后,就F9运行程序吧,当程序运行后,ALT+E
选中易语言的核心库krnln,双击进去
然后CTRL+B,查找FF 55 FC
【2.新版本的易语言】OD载入后,CTRL+G,转到处
然后就CTRL+B,查找特征码:FF 55 FC 5F 5E 89 5D F4
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
不错,不错,评个分!
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
感谢分享,眼睛都看花了
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&签到天数: 39 天该用户今日未签到
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
不怎么通俗
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&签到天数: 15 天该用户今日未签到
谢谢分享 对新手来说是很使用的省去了一些时间
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
刻苦钻研啊!这是要超恒大的节奏啊
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&签到天数: 4 天该用户今日未签到
谢谢楼主慷慨
【灌水警告】您的回复有灌水嫌疑或明显灌水行为,请注意!
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
&&离线&该用户从未签到
谢谢分享 收藏了
关注PC软件安全与移动软件安全领域。方法,使软件开发者能够更好的弥补软件缺陷,修复软件漏洞,提升,将损失降为最低。大量的,使软件开发者与代码逆向分析爱好者受益颇多,因此被连连称赞。保护开发者的利益与版权是我们持之以恒的动力!学破解论坛将竭尽全力为软件安全领域献出微薄之力!
本站中所有被研究的素材与信息全部来源于互联网,版权争议与本站无关。本站所发布的任何软件的逆向分析文章、逆向分析视频、补丁、注册机和注册信息,仅限用于学习和研究软件安全的目的。全体用户必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。学习逆向分析技术是为了更好的完善软件可能存在的不安全因素,提升软件安全意识。所以您如果喜欢某程序,请购买注册正版软件,获得正版优质服务!不得将上述内容私自传播、销售或者其他任何非法用途!否则,一切后果请用户自负!
站长邮箱:SharkHeng#iCloud.com[#改@]
站长微信号:SharkHeng|||||||
( 京公网安备 37号 |
京ICP备号 )&开始学习汇编语言,对相关的所学知识做个总结,希望对自己可以有所提高。
1、在计算机中数的表示方式
因为计算机中只能存储二进制数,所以一般都是通过二进制直接进行存储,但是为了方便阅读和程序员的编码简单化,就出现了八进制、十进制、十六进制,一般在汇编的学习过程中以二、十、十六进制为主。
四种数据的表示形式符号是:B(二进制)、O(八进制),D(十进制),H(十六进制)
二进制、八进制、十六进制转化为十进制都是通过数值为乘以权值,然后求和得出;
十进制转换为相应的进制都是通过除以相应的基数取余后,逆序达到相关的表示法。
2、在计算中通常通过数值的原码、反码、补码来进行表示。
对于无符号数,讨论三码没有什么意义;
对于有符号数,一般第一位代表的是符号位,0代表+,1代表—,同时在表示的过程中,因为存储器的位数可能存在数据为的扩展,一般都是扩展符号位。
对于正整数而言,原码=反码=补码,符号扩展位为0;
对于负整数而言,原码 按位取反 = 反码,反码+1 = 补码,符号位保证不变。
求负数的补码一般有两种方法:
(1)先写出其绝对值的原码,然后把符号位也参加在内进行取反,在加1即可。
(2)按照上面给出负数的求补即可。
补码的相关规则
(a+b)补 = (a)补+(b)补
(a-b)补 = (a)补-(b)补
3、对8086体系结构中寄存器的认识
(1)存在14个16位的寄存器和8个8位寄存器
通用寄存器包括如下几类
通用寄存器:传送、暂存数据和接受相关的运算结果。
1、16位数据寄存器,保存操作数和操作结果,缩短了访问内存的时间和并且不会占用相关的系统总数
AX(累加器、被除数的低16位和除法结果)&&&&&&&&&&&&&&&& = AH + AL(8位)&两个独立的寄存器,下面的相同&
BX(基址寄存器)&&&&&&&& = BH + BL(8位)&用于基址寻址,唯一一个作为存储器指针使用的寄存器&
CX(字符串操作、控制循环次数)&&&&&&&&&&&&&&&&&&&& = CH + CL (移位的时候使用,保存移位的位数)(8位)
DX(32位乘除法,存放被除数的高16位,或者保留余数,AX保留结果) = DH + DL(8位)
8个八位寄存器
AH 、AL 、BH、 BL、 CH、 CL、 DH、 DL
指针寄存器:存储某个存储单元的地址或者是一段存储单元的起始地址
2、16位指针寄存器
BP(基址指针 base point& 堆栈数据区基址的偏移 )
SP(堆栈指针 stack pointer &push pop 指令的时候使用,保存栈顶地址&)&
上面两个指针一般是和SS合用
3、16位变值寄存器&一般在字符串操作的时候用的比较多&
DI (目的地址 destination )
SI(源地址 source)
&上面的两个寄存器一般是和DS、ES合用
控制寄存器
IP(指令指针)&下一条指令的地址,但是不代表是下次将会执行的指令&
在计算机的组成原理中还有PC(程序计数器,始终指向下一条将要执行的指令,有时候PC和IP的内容相同,有时候又不同,个人理解?)
FLAG(标志寄存器),其中包含了9个标志,主要反映存储器的状态和相关的运算状态
前6个运算结果标志,后3个控制标志
0 CF (carry flag ) 进位标志& 反映运算是否产生进位或者借位&加法和减法&
2 PF (parity flag ) 奇偶标志& 反映运算结果中1的个数是奇数还是偶数(偶数则置为1)
4 AF(assist flag ) 辅助标志 在字节操作中,发生低半字节向高半字节进位或借位;在字操作中,低半字向高半字进位或者借位
6 ZF(zero flag ) 零标志 反映运算结果是否为0
7 SF(signed flag )符号标志 反映运算结果的符号位,与运算结果的最高位相同
11 OF(over flag) 溢出标志 反映有符号数加减运算是否引起溢出
8 TF(trace flag ) 跟踪标志 置为1后,cpu进入单步方式。主要用于调试,cpu执行一条指令后被中断
9 IF(interrupt flag)中断标志 决定CPU是否相应外部可屏蔽中断请求,1则响应,0则不响应
10 DF(direction flag) 方向标志 决定串操作指令执行时有关指针寄存器调整方向,为1,则串操作指令按减方式改变有关寄存器的值,反之则用加方式
16位段寄存器&寻址1M的物理地址空间的时候需要使用它,在计算机的内存中代码&指令&和数据是放在不同的存储空间中&
放操作的数据
DS(数据段,一般配合bx作为偏移 data segment)
ES(附加段 extend segment)
CS(代码段,一般和IP连用 code segment)
保存相关的寄存器值等,放在这里方便函数返回的时候在恢复现场
SS(堆栈段 stack segment)
2、地址分段和寻址&
一、明确地址分段的原因
&&&& 因为在8086中CPU的地址线是20位,那么实际可用的最大物理地址空间是1MB,但是因为寄存器都是只有16位和8位之分,最大寻址范围是64KB,为了寻找到所有的物理地址,需要对物理地址空间进行分段。
分段一般是由段首地址+段内偏移地址组成。
但是对于段的首地址不是随意乱取,通常都以“小段的起始地址为主”
“小段”即是在物理地址中从00000H开始,每16个字节而划分的,那么整个物理地址空间就可以划分为64K个小段,且首地址的最后四位均为0(用二进制表示时),所以是16的倍数。
&& 进行分段后,段与段之间就会有重叠、相邻、不会相干的现象产生。
一般物理地址= 段首地址*16+段内偏移地址。
前者为物理地址,后者断首地址:偏移地址为逻辑地址。所以一个物理地址可能对应多个逻辑地址的表示。
二、寻址方式
(1)汇编代码是由两部分组成:操作码+操作数
一般操作码在相应的机器指令体系中有相关的表示,但是操作数的存储就会不同了。
操作数存储在如下地方:
一、直接在汇编代码中:那么这种寻址方式就是立即数寻址&& mov ax, 1234H
二、存放在寄存器中:那么这种寻址方式就是寄存器寻址& mov ax,bx
三、存放在内存中,那么这种寻址方式就比较多了
寻址方式:(以源操作数的寻址为例)
1、立即数寻址& mov ax,1234H
2、寄存器寻址 mov ax,bx
3、直接寻址&&& mov ax,【1234H】& (ax) = (ds*16+1234H)
4、寄存器间接寻址
mov& ax,【bx】& (ax) = (ds*16+bx)
mov&&ax,【BP】 (ax)=(ss*16+bp)
因为bp的默认是通过ss来寻址,不过也可以通过段地址前缀来进行强加了
mov ax,ds:[BP]& (ax) = (ds*16+bp)
5、寄存器相对寻址
mov ax,[bx+1234H]& (ax) = (ds*16+bx+1234H)
也可以表示为
mov ax,1234H【bx】
同4,也存在这样的关系
6、基址变址寻址
mov ax,【bx+si】 (ax)= (ds*16+bx+si) &bx是基址寄存器,默认是和ds合用&
也可以表示为
mov ax,【bx】【si】或者是mov ax,【si】【bx】
7、相对基址变址寻址
mov ax,【bx+si+1234H】 (ax)=(ds*16+bx+si+1234H)
因为对于其中的很多寄存器都是可以变换的,所以不在这里一一列举
但是对于以上7中寻址方式到底应该在什么情况下进行使用还需要进一步的学习。
汇编代码是由两部分组成:操作码(mov)+操作数,既然有操作数的参与,那么对于操作数必然需要存储。
在计算机中,对于操作数的存取至少有两种方式:寄存器和存储器,那么相对而言就产生了各种寻找操作数的方式,本文一一介绍
1、立即寻址方式
操作数就包含在指令代码中,它作为指令的一部分跟在操作码后放在代码段(CS)中。
这种操作数被称作立即数,立即数可以是8位的也可以是16位的。
如果立即数是16位的就按照“高高低低”的原则存储&主要是针对寄存器,与存储单元的大端和小端没有关系&
指令示例:mov ax,1234H&H代表的是16位&
AH = 12, AL=34
但是汇编指令在代码段中的存储方式需要依据存储器的大小端格式来定。
2、寄存器寻址方式
操作数放在CPU内部的寄存器中,指令指定寄存器的编号。
对于16位的操作数,寄存器可以如下:
数据寄存器(AX/BX/CX/DX)
地址寄存器(DI/SI)
指针寄存器(SP/BP)
对于8位的操作数,可以是第一节的8个8位的寄存器
因为操作数在寄存器中,不需要占有系统总线,访问速度快。
指令示例:mov ax,bx
如果执行前:(AX) = 3064H,(BX)=1234H,指令执行后
(AX)= 1234H
3、直接寻址方式
指令直接包含操作数的有效地址(偏移地址)
操作数的有效地址一般存储在代码段中,操作数放在数据段中,默认的是ds段
所以操作数的地址由DS加上偏移地址得到有效地址,取出有效地址中的数据进行操作。
指令示例:
MOV AX,[1234H]
假设DS = 4567H,内存中(468A4H) = 0001H,那么有效地址 = (4567H)*16+(1234H) = (468A4H)
那么寄存器AX = 0001H
因为默认的是DS寄存器,其实也可以指定前缀寄存器,即所谓的段超越前缀
MOV AX, SS:[1234]H
AX = SS*16+(1234H)
4、寄存器间接寻址方式
操作数在寄存器中,操作数的有效地址在SI\DI\BX(DS),&&&&&&& BP(SS)
在这四个寄存器之一种,一般情况下如果有效地址在SI、DI、 BX中,则默认段地址是DS
如果在BP中,则默认是SS
不过和上面一样,指令中也可以指定段超越前缀来取得其他段中的数据
指令示例:
MOV AX,[SI] 引用的段寄存器是DS
MOV AX,ES:[SI],引用的段寄存器是ES
MOV [BP],AX,引用的段寄存器是SS
5、寄存器相对寻址方式
操作数放在存储器中,操作数的有效地址是一个基址寄存器(BX、BP)或者是变址寄存器的(SI 、DI)内容加上指令中给定的8位
或者是16位的位移偏移量之和
其中和上面的一样,引用段地址的时候,BX、SI、DI的段地址寄存器是DS,BP的是SS,不过也可以采用段地址超前缀。
其中给定的8位或者是16位采用补码形式表示。如果偏移量是8位,则需要进行符号扩展到16位
MOV AX,[BX+1234H] ,引用段寄存器是DS,有效物理地址=DS*16+BX+1234H,(AX)=(DS*16+BX+1234H)
MOV [BP+1234H],AX,引用段寄存器是SS
MOV ES:[SI+1234H],AX,引用段寄存器是ES
有时候指令也可以表示如左:MOV AX,1234H[BX]等。
6、基址加变址寻址方式
操作数在存储器中,操作数的有效地址由基址寄存器(BX、BP)的内容与变址寄存器(DI、SI)之一的内容相加
如果有BP则段寄存器是SS,其他的是DS
指令示例:
MOV AX,[BX][DI] 等价于 MOV AX,[BX+DI]
有效地址 = DS*16+BX+DI&,(AX) =(有效地址)
同时也允许段超越前缀
MOV DS:[BP+SI],AX
寻址方式适合于数组操作,利用基址存储数组的首地址,变址存储元素的相对地址
7、相对基址加变址寻址方式
操作数在存储器中,操作数的有效地址由基址寄存器之一的内容+变址寄存器之一的内容+8位或者是16位的偏移量之和。
段寄存器和相关的符号扩展同前面。
如果得到的有效地址超过FFFFH时,取64K的模。
大多数指令既可以处理字数据,也可以处理字节数据。
算术运算和逻辑运算不局限于寄存器,存储器操作数也可以直接参加算术逻辑运算。
指令系统分为如下六个功能组:
(1)数据传送
(2)算术运算
(3)逻辑运算
(5)程序控制
(6)处理器控制
指令的一般格式分为四个部分
[标号:] 指令助记符 [操作数1][,操作数2][;注释]
指令是否带有操作数完全取决于指令
标号的使用取决于程序的需要,但是不被汇编程序识别,与指令系统无关。
标号有点类似于中的goto语句中的标号,做为一个偏移。
指令助记符代表操作码,从二进制的操作码到助记符的一个翻译过程。
功能组一:数据传送指令
1、传送指令格式:
MOV DST(目的操作数),SRC(源操作数)
源操作数:累加器(AX),寄存器,存储单元和立即数
目的操作数:不能是立即数
操作后不改变源操作数。
(1)CPU内部之间的传送(两个都是寄存器)
源操作数和目的操作数两个不能都是段寄存器
代码段(CS)不能作为目的操作数
指令指针(IP)既不能作为源操作数也不能作为目的操作数
(2)立即数送通用寄存器和存储单元
MOV VARB,-1;VARB是变量名,代表一个存储单元
MOV VARW,3456H;VARW是一个字变量
MOV [SI],6543H
主要:立即数不能直接传送给段寄存器
&&&&&&&&&&& 立即数不能是目的操作数
(3)寄存器与存储器之间的数据传送
MOV AX,VARW ;VARW是一个字变量,为直接寻址
MOV BH,[DI];为寄存器间接寻址
MOV DI,ES:[SI+3];为寄存器相对寻址,段超越前缀
MOV VARB,DL;为寄存器直接寻址
MOV DS:[BP],DI;寄存器基址变址寻址
MOV VARW,DS;寄存器直接寻址
MOV ES,VARW;直接寻址
源操作数和目的操作数类型一样(byte和word等),除了串操作外
不能同时是存储器操作数,两个操作数必须有一个寄存器除立即寻址以外。
如果需要在两个存储单元中进行数据传送,可以利用一个寄存器过渡
MOV AX,VARW1
MOV VARW2,AX
实现了VARW1-&VARW2的数据传送。
操作数不能同时为段寄存器,那么同上也可以进行过渡。
MOV BX,OFFSET TABLE
把TABLE的偏移地址送到BX寄存器中,其中OFFSET为属性操作符。
传送指令不影响FLAG寄存器
2、交换指令
利用交换指令可以方便的实现通用寄存器之间或者是与存储器之间的数据交换
指令格式:
XCHAG OPRD1,OPRD2
此指令把操作数OPRD1与OPRD2的内容进行交换必须保证数据类型的一致。
通过上面的分析,操作指令中必须有一个寄存器,并且存储器之间,段寄存器之间不能直接通过MOV进行操作。
XCHAG AL,AH
XCHAG SI,BX
OPRD可是通用寄存器和存储单元,但是不能包括段寄存器&一定要通过通用寄存器来交换&
还不能有立即数,可以采用各种寄存器和存储器的寻址方式。
指令示例:
XCHAG BX,[BP+SI]; 基址加变址寻址方式,基址寄存器和存储器的数据呼唤[SS]
此指令不影响FLAG
3、地址传送指令
(1)指令LEA( load effective Address)
传送有效地址指令,格式如下:
LEA REG,OPRD
该指令把操作数OPRD的有效地址传送到操作数REG中。
操作数OPRD必须是一个存储器操作数
操作数REG必须是一个16位的通用寄存器(AX BX CX DX BP SP DI SI)
操作的结果是把偏移地址送给REG,记住不是物理地址,是偏移地址
指令示例:
LEA AX, BUFFER&& [AX]=BUFFER
LEA DS,[BS+SI]&&&&& [DS] = BS+SI
LEA SI,[BP+DI+4]& [SI] = BP+DI+4
(2)LDS( Load pointer into DS)
段值和段内偏移构成32位的地址指针。
该指令传送32为地址指针,其格式如下:
LDS REG, OPRD
执行操作: (REG) &- (SRC)
&&&&&&&&&&&&&&&&&&&&&& (DS) &- (SRC+2)
该指令把操作数OPRD作为基址所含的一个32位的内存中的内容前两个字节送到REG中,后两个字节送到数据段寄存器DS
操作数OPRD必须是一个32为的存储器操作数,
操作数REG可以是一个16位的通用寄存器,但实际使用的往往是变址寄存器或者是指针寄存器。
(3)LES(Load pointer into ES)
操作和上面的完全相同。
4、堆栈指令
在系统中,堆栈实际是一段随机访问RAM区域。
称为栈底的一端地址较大,称为栈顶的一端地址较小。
堆栈的段值在堆栈寄存器SS中
堆栈的指针寄存器SP始终指向栈顶
堆栈是以“后进先出”方式工作
堆栈的存取必须以字为单位(16bit = 2Btye)
堆栈的指令分为如下两种:
(1)进栈指令PUSH
格式如下:PUSH SRC(源操作数)
该指令把源操作数SRC压栈。
执行过程是:先把栈顶指针SP值减2,SP = SP-2
再把SRC中的值放入SP所指的栈顶中即 [SS*16+SP] = [SRC]
SRC可以是通用寄存器和段寄存器,也可以是字存储单元
(2)出栈指令POP
格式如下:POP DST(目的操作数)
该指令把栈顶的元素放到DST中,然后把SP加2
执行过程如下:先把堆栈指针SP指的数据放到DST中,【DST】=【SS*16+SP】
再使SP = SP&+ 2
DST可以是通用寄存器和段寄存器(但是CS除外),也可以是字存储单元。
(1)上面两条指令PUSH和POP只能是字操作
(2)可以使用除立即寻址外的其他任何方式
(3)POP指令不允许使用CS寄存器
此两条指令不影响FLAG标志位
利用这两条指令可以是实现两个段寄存器的数据交换
例如:实现DS、ES的数据交换
在汇编的过程中,堆栈操一般实现“现场保存”和“现场恢复”,作为参数的传递缓冲区等。
数据交换有三种方式:
传送指令、交换指令、堆栈指令
举例:交换DS、AX的数据
利用传送指令
利用交换指令
XCHG AX,DS&不能同时是段寄存器&
利用堆栈操作指令如上面的示例。
5、标志操作指令
(1)标志传送指令
1、LAHF(Load AH Flags)
把FLAG寄存器的低八位送到AH,即把CF PF AF ZF SF送到AH中。
不影响标志寄存器。
2、SAHF(Store AH into Flags)
把AH寄存器的八位传送到FLAG寄存器的低八位中,刚好和上面的指令作用相反。
影响标志寄存器。但是不影响8-15中的标志位。
3、PUSHF和POPF
把FLAG的标志寄存器压入和压出。
可以通过他们的操作来改变FLAG中的标志位的值。主要可以改变TF标志。
1、加法指令ADD
格式:ADD OPRD1,OPRD2
(OPRD1) = (OPRD1)+(OPRD2)
例如:MOV AX,7896H;&& AX=7896H
即AH = 78H, AL=96H;各个标志寄存位保持不变
ADD AL,AH&& ;AL=0EH,AH = 78H,即AX = 780EH(1110)
此时如果FLAG寄存器的值分别为
CF = 1, ZF = 0,SF = 0,OF = 0,AF = 0,PF = 0
继续操作如下:
ADD DX,0F0F0H
执行前(DX) = 4652H,执行后(DX)=3742H,ZF=0,SF=0,CF=1,OF=0
如果执行如下操作:
ADD AX,4321H
执行前(AX)=62A0H,执行后(AX)=A5C1H SF=1,ZF=0,CF=0,OF=1
讲解一下执行的过程,为什么FLAG标志位的状态发生了变化。
在寄存器中一般数值都是用补码表示,最高位代表符号位。
但是在加法指令中,是不区分操作数的符号位的,因为补码的表示完全避开了这个符号位的概念&在下一篇目录中会有说明&,符号的概念只在编程语言级别才有区分(针对加法和减法)
根据上面的分析可以知道:加法指令影响标志位。
PF标志位表示结果包含的1的个数,如果为偶数个则为1,如果是奇数个则为0
2、带进位的加法指令ADC( ADD with carry )
ADC,OPRD1,OPRD2
OPRD1 = OPRD1+OPRD2+CF
例如:下列指令序列执行了两个双精度的加法。(双精度32位)
设目的操作数放在DX和AX寄存器中,其中DX存放高位字,AX存放低位字
源操作数放在BX,CX中,其中BX存放高位字
(DX) = 0002H (AX) = 0F365H
(BX) = 0005H (CX) = 0E024H
指令序列如下:
执行第一条指令后:
AX = 0D389H(1001),SF=1, ZF=0,CF=1,OF=0
执行第二条指令后:
DX = 0),SF=0,& ZF=0, CF=0, OF=0
从上面的例子可以看到:
为了实现双精度数的加法,必须用两条指令来完成,低位和低位相加,
产生进位,然后再使用ADC。
另外带符号的双进度数的溢出,需要根据ADC指令的OF来判断
ADD指令的OF没有作用。
通过上面例子可以看出:影响FLAG
3、加1指令INC(INCrement)
加1指令的格式如下:
OPRD = OPRD + 1;
操作数可以是通用寄存器,也可以是存储单元
这条指令的执行结果影响标志位ZF,SF,OF,PF和AF,但它不影响CF
该指令主要用于调整地址指针和计数器。
两个数相加,如果最高有效位不同,那么肯定不会发生溢出,即OF=0,但是会有进位,CF的值根据是否有进位来判断
如果最高有效位相同,相加的结果的最高有效位如果与操作数相反,那么肯定有溢出了,则OF=1,证明发生了错误,CF的值根据是否有进位来判断
在高级的编程语言层面,这个就作为“截断”进行处理,然后可以根据OF和CF的值来判断结果是错误,还是正确。
如果OF = 1,则结果肯定是错误,不用理会CF
如果OF = 0,则结果可能是正确的,如果CF=0,则正确,如果CF=1,则发生了进位,结果被“截断”
4、减法指令SUB(SUBtraction)
格式如下:
SUB OPRD1,OPRD2
执行的操作:(OPRD1) = (OPRD1)-(OPRD2)
SUB [SI+14H],0136H
指令执行前 (DS) = 3000H, (SI)=0040H
(300054H) = 4336H
指令执行后 (30054H) = 4200H
SF=0, ZF=0, CF=0, OF=0
例子如下:
SUB DH,[BP+4]
指令执行前
(DH)=41H,(SS)=0000H,(BP)=00E4H,(000E8H)=5AH
指令执行后
(DH)=E1H,(SS)=0000H,(BP)=00E4H,(000E8H)=5AH
SF=1, ZF=0, CF=1&借位&, OF=0
5、带借位的减法SBB( SuBtrace with Borrow)
与带借位的加法刚好相反
SUC OPRD1,OPRD2
OPRD1 = OPRD1-OPRD2-CF
该条指令主要用于多字节想减的场合。
6、减1指令 DEC(DECrement)
格式如下:
(OPRD) = (OPRD)-1
DEC VARB& ;VARB是字节变量
操作数OPRD可以是通用寄存器,也可以是存储单元。
减1指令,在相减的时候,把操作数作为一个无符号数对待。
这条指令执行的结果影响ZA,OF,PF,SF,AF但是不影响CF
该条指令主要用于调整地址指针和计数器。
两个操作数相减,如果最高有效位相同,则不会发生溢出,则OF=0,CF根据是否借位来判断,如果CF=1,则借位,如果CF=0,则没有借位。
如果最高有效位不同,且操作的结果和减数的最高有效位相同,则OF=1,CF根据是否借位来判断,如果CF=1,则借位,如果CF=0,则没有借位
7、取补指令NEG(NEGate)
格式如下:
这条指令对操作数取补,就是用零减去操作数OPRD,在把结果送到OPRD中
(OPRD) = -(OPRD)
操作数可以是通用寄存器,也可以是存储单元
此指令的执行结果影响CF/ZF/SF/OF/AF和PF
操作数为0时,求补的运算结果是CF=0,其它情况则均为1,都是借位操作
如果在字节操作的时候对-128取补,或在字操作的时候对-32768取补,则操作数不变,但是OF被置为1,
8、比较指令CMP(CoMPare)
格式如下:
CMP OPRD1,OPRD2
这条指令完成操作数OPRD1减去OPRD2,运算结果不送到OPRD1,
但是影响标志CF/ZF/SF/OF/AF和PF
记住双操作数中至少有一个寄存器。
比较指令主要用于比较两个数的关系,是否相等,谁大谁小
执行了比较指令后,可以根据ZF是否置位,来判断两者是否相等
如果两者都是无符号数,则可以根据CF判断大小,如果借位了则前者小
如果两个都是有符号数,则可以根据SF和OF判断大小,
若为无符号数,则根据CF判断大小:
若CF = 1,则OPRD1 & OPRD2
若CF = 0,则OPRD1 &=& OPRD2,如果ZF不等于1则是大于
若为有符号数,则根据SF和OF判断大小:
若SF = 1, OF = 1,则OPRD1 & OPRD2,说明发生了溢出,相减后为负数1(正数-负数)
若SF = 1, OF = 0,则OPRD1 & OPRD2,说明没有发生溢出,相减后为负数
若SF = 0, OF = 1,则OPRD1 & OPRD2,说明发生了溢出,相减后为正数,但是发生了溢出(负数-正数)
若SF = 0, OF = 0,则OPRD1 & OPRD2,说明正常操作,且结果为正数
在汇编指令中,是不区分有符号数和无符号数,但是汇编指令中对于加减法指令是不区分有符号数和无符号数的。
但是乘除法指令是区分有符号数和无符号数。
一、只有一个标准!
在汇编语言层面,声明变量的时候,没有 signed&& 和&& unsignde
之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db
-20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db
是分配一个字节,那么一个字节能表示的有符号整数范围是:-128
~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236
的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00
EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC
,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC
,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如
-20 那么汇编后的结果是正确的;如果你输入 236
那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)
二、存在两套指令!
第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul
div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
举例来说:
内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2
。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE
当作有符号数就是:-18 ,无符号数就是 238 。所以,add
一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472。
三、可爱又可怕的c语言。
为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的
signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl
也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)
C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,之类就太远了)。当初K&R 版的c就是高级一点的汇编……:-)
C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:
#include &stdio.h&&
#include &string.h&
int main()
int x = 2;&
char * str = "abcd";&
int y = (x - strlen(str) ) / 2;
printf("%d\n",y);
结果应该是 -1 但是却得到: 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
观察编译后的代码,除法指令为 div ,意味无符号除法。
解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2;
强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令
imul ,idiv 等得到的结果,与用
无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)
为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)
在前面一节谈到,在汇编中对于加法和减法指令是没有所谓的有符号数加法和有符号数减法,统一通过补码进行运算,然后再根据标识寄存器的相关标识位进行判断,来辨别运算结果是否正确,主要是以OF,SF和CF的对比来判断。
但是乘法指令和除法指令区分了相关的有符号操作和无符号操作,因为在运算的结果中需要进行符号位的扩展。
乘法指令和除法指令都分为字节和字的操作,如果是两个8bit位的操作数,则结果存放在AX中,如果是两个16bit的操作数,则操作数和结果放在AX和DX中。
(1)无符号数的乘法指令
指令格式如下:
如果OPRD是8bit位的无符号数,那么有一个隐藏数在AL中,最后的结果放在AX中。
如果OPRD是16bit位的无符号数,那么有一个隐藏数在AX中,最后的结果是低字放在AX中,高字放在DX中。
对于标志为的影响比较特殊:
如果高半部分不为0,则CF = 1,OF=1(说明操作结果有效),如果为0则CF=OF=0,说明CF和OF是对高半部分进行记录,但是对其它FLAG位的影响没有定义。
(2)有符号数的乘法指令
格式如下:
如果OPRD是8bit位的无符号数,那么有一个隐藏数在AL中,最后的结果放在AX中。
如果OPRD是16bit位的无符号数,那么有一个隐藏数在AX中,最后的结果是低字放在AX中,高字放在DX中。
对于标志为的影响比较特殊:
如果高半部分是低半部分的符号扩展,则CF =OF=0(说明操作结果有效),如果不是符号扩展则CF=OF=1,说明CF和OF是对高半部分进行记录,但是对其它FLAG位的影响没有定义。
乘法指令适应于我们前面介绍的所有寻址方式。
(3)无符号的除法指令
格式如下:
如果OPRD是8bit位的无符号数,那么有一个隐藏数在AX中,最后的结果AL保存商,AH保存余数。
如果OPRD是16bit位的无符号数,那么有隐藏数在AX、Dx中,其中AX保存操作数的低字,DX保存操作数的高字,最后的结果是商放在AX中,余数放在DX中。
除法指令是状态标志没有意义,结果可能产生溢出,溢出时8086CPU中就产生编号为0的内部中断.实用中应该考虑这
(4)有符号数的除法指令
指令格式如下:
整个的操作过程和DIV一样,没有什么比较特殊的地方
汇编指令中的移位操作分为算术移位和逻辑移位
一般在进行左移操作的时候,算术移位和逻辑移位的处理过程都比较简单:移除左边的最高位,最低位补零
但是在进行右移操作的时候,算术移位移除右边的数字然后左边的最高位进行符号扩展,不过逻辑移位就是补零,则个需要注意一点。
对于需要进行左移和右移的操作,一般都是需要指定移动位数M,如果M=1则可以直接以立即数给出,如果移位超过1则需要把移位放在CL中。
移位操作主要分为如下几个指令:
SAL OPRD,M 算术左移
SHL OPRD,M 逻辑左移
SAR OPRD,M 算术右移
SHR OPRD,M 逻辑右移
循环移位没有符号位的扩展等性质
ROL OPRD,M 循环左移&如果操作数为Nbit位,则移动N次后可以还原&
ROR OPRD,M 循环右移
RCL OPRD,M 带进位的循环左移&CF作为循环移动的一部分,需要移动N+1次才可以复位&
RCR OPRD,M 带进位的循环右移
一般移位操作都是和逻辑运算结合进行操作数的结合与分解运算
右移操作一般是把最高位移动到CF中
带进位的循环移位操作也是对CF进行了操作,对其他标志位的影响根据相关性质来决定。
在汇编指令中跳转指令分为两种,一种是无条件跳转指令,一种是有条件跳转指令。
对于前者无条件跳转指令有点类似于高级语言C中的goto语句,goto标志符,无跳转指令的格式也是类似JMP 标号;
对于有条件跳转指令通常都是根据FLAG寄存器的相关状态值SF,OF,AF,PF,CF是否被设置为1或者是0来进行跳转的选择,这个就可以实现相关的分支语句。类似于高级语言中的if等。
(1)无条件跳转指令JMP
基本格式如下:
JMP 标号;
因为在中我们一般对程序进行分段处理,那么在不同的段就会设置不同的CS寄存器,执行不同指令的过程中实质是设置CS与IP寄存器的值,然后CPU以此来进行指令的取出,由此对于跳转指令我们就分为段内跳转和段间跳转,前者是在一个代码段中,后者是实现不同的代码段的跳转。
首先说一下段间无条件跳转。
段间的无条件跳转的实现原理是:汇编器根据JMP后面设置的标号,计算出标号对应的段内偏移与此时IP寄存器中的值得差值,然后让IP加上该差值,实质就是设置IP的值为该标号对应的段内偏移值。
根据差值所占位的大小我们又分为:无条件段内近转移和无条件段内短转移。
对于前者,偏移与IP的差值大小只占2个字节,后者占1个字节
指令格式分别如下:
无条件段内短转移:JMP SHORT 标号
无条件段内近转移:JMP NEAR PTR 标号
对于这个PTR什么时候需要添加我也不是很清楚,到后面的学习过程中明了后在进行修改。
对于无条件转移指令,此时的IP值是通过标号直接设置的,在汇编器解析的时候进行设置,但是有时候我们可以把需要设置的IP值放到通用寄存器或者是存储器中,那么这样就可以实现无条件段内间接跳转指令。
指令的格式如下:
其中OPRD可以是通用寄存器,也可是存储单元,寻址方式除了是立即数寻址外,可以是其它的寻址方式。
JMP AX;JMP [AX];JMP WORD PTR [1234H]等
(2)无条件段间跳转
所谓的无条件段间跳转就是通过相关的操作直接设置CS和IP寄存器的值,使得执行不同代码段中的代码
指令格式和上面的大同小异,但是汇编器在进行解析的时候会设置CS和IP的值。
指令分为两种:一种是直接跳转
JMP FAR PTR 标号
一种是间接跳转,通过直接寻址的方式,把存储器中的低字放到IP中,高字放到CS中,指令格式如下
JMP DWORD PTR OPRD
例如:JMP DWORD PTR [1234H],则执行后IP = DS*16+[1234H],CS = DS*16+[1236H]
总结了一些JMP跳转指令的相关格式:
jmp 16位寄存器
以16位寄存器的值改变IP
jmp&段地址:偏移地址
以立即数改变段地址和偏移地址
jmp short&标号
以标号地址后第一个字节的地址来改变IP,实际上这个功能可以作如下描述:
(IP)=(IP)+8bit位移
8bit位移指的是从jmp指令后第一个字节开始算起
jmp short sign
段内短转移
对IP的修改范围是-128-&127,实际是编译器根据当前IP指针的指向来计算到底偏移多少个字节来指向下一条指令,下面这段代码就会出编译错误
jmp short s
dw 200 dup(2)
s: mov ax,4
因为跳转超过了范围
jmp near ptr&标号
以标号地址后第一个字的地址来改变IP,
实际上这个功能可以作如下描述:
(IP)=(IP)+16bit位移
16bit位移指的是从jmp指令后第一个字节开始算起
jmp near ptr sign
段内近转移
对IP的修改范围是-3
jmp far ptr标号
以标号的段地址和指令地址同时改变CS和IP
jmp far ptr sign
jmp word ptr&内存地址
以内存地址单元处的字修改IP,内存单元可以以任何合法的方式给出
jmp word ptr ds:[si]
jmp word ptr ds:[0]
jmp word ptr [bx]
jmp word ptr [bp+si+idata]
jmp dword ptr内存地址
以内存地址单元处的双字来修改指令,高地址内容修改CS,低地址内容修改IP,内存地址可以以任何合法的方式给出
jmp dword ptr [bx]
s1 segment
dw&0a0bh,&0c0dh
jmp dword ptr ds:[0]
对于JMP 段地址:偏移地址,并不是所有的MASM都支持的,需要依据实际的形式来判断。
上面我们看到了段内的无条件跳转指令,但是和很多高级语言进行对比,我们在很多时候都是通过条件的判断来决定是否需要进行跳转,同样在汇编指令中也提供了相关的条件跳转指令,我们现在一一进行介绍:
明确一下,在汇编指令中N代表的是否。同时进行条件跳转的指令都是段内跳转,因此有短和近跳转了撒!
(1)根据标识FLAG寄存器来判断是否需要进行跳转
我们根据前面的需要知道相关的算术运算、逻辑运算、移位运算(部分指令会影响CF)都会影响FLAG寄存器中的部分标识位的值,那么根据这些标志位我们可以判断是否需要进行跳转,譬如CMP
AX,BX是判断AX和BX的大小,那么通过相关设置的标志位我们就可以判断是AX大还是BX大,然后决定是否需要转移嘛,这就是所谓的分支语句了撒!
对于有符号数分大于(G-great),等于(E-equal),小于(L-light)
对于无符号数分为大于(A),等于(E),小于(B)
根据标志位跳转的指令:
JE&;等于则跳转
JNE&;不等于则跳转
0 则跳转&(ZF)
JNZ&;不为 0 则跳转&
JS&;为负则跳转&(SF)
JNS&;不为负则跳转&
JC&;进位则跳转&(CF)
JNC&;不进位则跳转&
JO&;溢出则跳转(OF)
JNO&;不溢出则跳转
JA&;无符号大于则跳转&
JNA&;无符号不大于则跳转&
JAE&;无符号大于等于则跳转&
JNAE&;无符号不大于等于则跳转&
JG&;有符号大于则跳转&
JNG&;有符号不大于则跳转&
JGE&;有符号大于等于则跳转
JNGE&;有符号不大于等于则跳转
JB&;无符号小于则跳转&
JNB&;无符号不小于则跳转
JBE&;无符号小于等于则跳转&
JNBE&;无符号不小于等于则跳转&
JL&;有符号小于则跳转&
JNL&;有符号不小于则跳转&
JLE&;有符号小于等于则跳转
JNLE&;有符号不小于等于则跳转&
JP&;奇偶位置位则跳转&(PF)
JNP&;奇偶位清除则跳转
JPE&;奇偶位相等则跳转&
JPO&;奇偶位不等则跳转&
跳转相关的标志位:
11109876543210
OFDFIFTFSFZF&AF&PF&CF
通过上面的跳转指令我们就可以实现简单的分支和循环,例如
MOV CX,10H
&&&& ........
&&&& DEC CX
&&&& JNZ NEXT
实现的是执行NEXT中的代码段10次
但是通过自己手动的写相关的循环语句有时候很复杂,增加了编码的难度,因此在汇编指令中有了如下的专门的循环指令,如下所示:
LOOP = CX不为零的时候进行跳转
LOOPE/LOOPZ = CX不为零并且相等的时候跳转
LOOPNE/LOOPNZ = CX不为零并且不相等的时候跳转
LCXZ& CX为零的时候跳转
通过相关的循环+跳转语句就可以实现高级语言中的分支语句和循环语句的执行了
程序设计语言是实现人机交换信息(对话)的最基本工具,可分为机器语言、汇编语言和高级语言。本章重占介绍汇编语言。
  (1)汇编语言是一种面向机器的程序设计语言,其基本内容是机器语言的符合化描述;
  (2)通常汇编语言的执行语句与机器语言的执行指令是一一对应的;
  (3)汇编语言允许程序直接使用寄存器,标志等微处理器芯片内部的特性;
  (4)同高级语言程序相比,与其等效的汇编语言执行速度要块,目标代码所占的内存要少;
  (5)汇编语言是系统软件和实时控制系统程序员必须掌握的。
  1.机器语言
  机器语言用二进制编码表示每条指令,它是计算机能只别和执行的语言。用机器语言编写的程序称为机器语言程序或指令程序(机器码程序)。因为机器只能直接识别和执行这种机器码程序,所以又称它为目标程序。显然,用机器语言缩写程序不易记忆、不易查错与不易修改。为了克服机器语言的上述缺点,可采用有一定含义的符号即指令助记符来表示指令。一般都采用某些有关的英文单词的缩写,这样就出现了另一种程序语言――汇编语言。
  2.汇编语言
  汇编语言是用指令的助记符、符号地址、标号等来表示指令的程序语言,简称符号语言。它的特点是易读、易写、易记。
  它与机器语言指令是一一对应的。汇编语言不像高级语言(如BASIC)那样通用性强,而是性某种计算机所独有,与计算机的内部硬件结构密切相关。用汇编语言缩写的程序叫汇编语言程序。
  把汇编语言源程序翻译成目标程序的过程称为汇编过程,简称汇编。完成这个任务有两种方法:
  ①手工汇编。所谓手工汇编是程序设计人员根据机器语言指令与汇编语言指令对照表,把编好的汇编语言程序翻译成目标程序。
  汇编语言程序      机器语言程序
  MOV AL,0AH      B0H 0AH
  ADD AL,14H      04H 14H
  ②机器汇编。所谓机器汇编就是由汇编程序自动将用户编写的汇编语言源程序翻译成目标程序。
  这里,汇编程序是由厂家为计算机配置的担任把汇编源程序成目标程序的一种系统软件。
  以上两种程序语言都是低级语言。尽管汇编语言具有执行速度快和易于实现对硬件的控制等优点,但它仍存在着机器语言的某些缺点:与CPU的硬件结构紧密相关,不同的CPU其汇编语言是不同的,这使得汇编语言程序不能移植,使用不便;其次,要用汇编语言进行程序设计,必须了解所使用的CPU硬件的结构与性能,对程序设计人员有较高的要求,为此又出现了所谓的高级语言。
  3.高级语言
  高级语言是脱离具体机器(即独立于机器)的通用语言,不依赖于特定计算机的结构与指令系统。用同一种高级语言缩写的源程序,一般可以在不同计算机上运行而获得同一结果。
  使用高级语言编程与计算机的硬件结构没有多大关系。目前常用的高级语言有BASIC、FORTRAN、COBOL、PASCAL、PL/M、C等。一般来说,高级语言是独立于机器的,在编程时不需要对机器结构及其指令系统有深入的了解,而且用高级语言缩写的程序通用性好、便于移植。
  综上所述,比较3种语言,各有优缺点。应用时,需根据具体应用场合加以选用。一般,在科学计算方面采用高级语言比较合适;而在实时控制中,特别是在对程序的空间和时间要求很高的场合,以及需要直接控制设备的应用场合,通常要用汇编语言。
各种机器的汇编语言,其语法规则不尽相同,但基本语法结构形式类似。现以汇编语言为例加以具体讨论。
  4.2.1 汇编语言的数据与表达式
  1.汇编语言的数据
  数据是汇编语言中操作数的基本组成成分,汇编语言能识别的数据有常数、变量和标号。
  (1)常数
  常数是指那些在汇编过程中已经有确定数值的量,它主要用作指令语句中的立即操作数、变址寻址和基址加变址中的位移量DISP或在伪指令语句中用于给变量赋初值。
  常数可以分数值常数和字符串常数两类。
  数值常数:以各种进位制数值形式表示,以后缀字符区分各种进位制,后缀字符H表示十六进制,O或Q表示八进制,B表示二进制,D表示十进制,十进制常省略后缀。
  字符串常数:用单引号括起来的一串ASⅡC码字符。如字符串"ABC"等效为41H、42H、43H一组数值常数,如"179"等效为31H、37H、39H一组数值常数。
  变量是代表存放在某些存储单元的数据,这些数据在程序运行期间可以随时修改。变量是通过变量名在程序中引用的,变量名在是存放数据存储单元的符号地址,它可以作为指令中的存储器操作数来引用。
  (2)变量
  变量一般都在数据段或附加段中使用数据定义伪指令DB、DW和DD来进行定义。定义变量就是给变量分配存储单元,且对这个存储单元赋予一个符号名――变量名,同时将这些存储单元预置初值。经过定义的变量具有以下3个属性:
  ①段属性:表示与该变量相对应的存储单元所在段的段基值;
  ②偏移量属性:表示该变量相对应的存储单元与段起始地址相距的字节数;
  ③类型属性:表示变量占用存储单元的字节数。这一属性是由数据定义伪指令DB、DW、DD来规定的。它们可以是单字节变量(或称字节变量)、双字节变量(或称字变量)、4字节变量(或称双字变量)。
  (3)标号
  标号是某条指令所在存储单元的符号地址,它指示指令在汇编语言程序中的位置,通常,标号用来作为汇编语言源程序中转移、调用以及循环等指令的操作数,即转移的目标地址。
  标号和变量相似,也有3个属性:段、偏移量和距离,前两个属性和变量的同名属性完全相同,而标号的第三个属性"距离"可以是  NEAR(近距离)或FAR(远距离)。
  NEAR(近距离):本标号只能被标号所在段的转移和调用指令所访问(即段内转移)。
  FAR(远距离):本标号可被其他段(不是标号所在段)的转换和调用指令所访问(即段间转移)。
  标号的基本定义方法是在指令的操作助记符前加上标识符和冒号,该标识符就是我们所要定义的标号。例如:
  START:PUSH DS
  标号还可以采用伪指令定义,如用LABEL伪指令和过程定义伪指令来定义,这将在后面叙述。
  2.表达式
  由常数、变量或标号和运算符连接而成的式子称为表达式,它是操作数的基本形式。表达式有数字表达式和地址表达式,汇编程序在汇编期间对表达进行计算,得到一个数值或一个地址。
  汇编语言中的操作运算符分为:算术运算符、逻辑运算符、关系运算符、数值返回运算符、属性修改运算符。
  (1)算术运算符
  算术运算符包括加(+)、减(-)、乘(*)、除(/)和模运算符MOD。MOD施于操作,得到的是除法的余数,例如,27MOD 4,其结果为3。
  当算术运算为地址操作数时,应保证其结果是一个有意义的存储器地址,因而通常只使用+/-运算。
  (2)逻辑运算符
  逻辑运算符包括:非(NOT)、与(AND)、或(OR)和异或(XOR)。逻辑运算符的运算对象必须是数值型的操作数,并且是按位运算。应当注意逻辑运算符与逻辑运算指令之间的区别,逻辑运算符的功能是在汇编时由汇编程序完成,而逻辑运算指令的功能由CPU完成。
  (3)关系运算符
  关系运算符包括相等(EQ)、不等(NE)、小于(LT)、不大于(LE)、大于(GT)和不小于(GE)。关系运算符用于将两个操作数进行比较,若符合比较条件(即关系式成立),所得结果为全1;否则,所得结果为全0。
  (4)数值返回运算符
  数值返回运算符包括:段基值(SEG)、偏移量(OFFSET)、类型(TYPE)、长度(LENGTH)和字节总数(SIZE)。
  数值返回运算符用来把存储器操作数(变量或标号)分解为它的组成部分(段基值、偏移量、类型、元素个数总数和数据字节总数),并且返回一个表示结果的数值。这些运算符的格式如下:&
  运算符 变量或标号
  ①段基值SEG运算符
  当运算符SEG加在一个变量名或标号的前面时,得到的运算结果是返回这个变量名或标号所在段的段基值。
  ②偏移量OFFSET运算符
  当运算符OFFSET加在一个变量名或标号前面时,得到的运算结果是返回这个变量或标号在它段内的偏移量。例如:
    MOV SI,OFFSET KX
  设KX在它段内的偏移量是15H,那么这个指令就等效于:
    MOV SI,15H
  这个运算符十分有用。例如,现有以ARRAY为首址的字节数组,为了逐个字节进行某种操作,可以使用下面的部分程序:&
  在这段程序中,首先把数组变量的首字节偏移量送给SI,把寄存器SI作为数组的地址指针。这样在数组的逐个字节处理(即在LOP循环)中,用寄存器间接寻址方式,每处理完一个字节,就很方便地对地址指针SI进行修改,使它指向下一个字节。
  ③类型TYPE运算符
  运算结果是返回反映变量或标号类型的一个数值。如果是变量,则数值为字节数,DB为1,DW为2,DD为4,DQ为8,DT为10;如果是标号,则数值为代表标号类型的数值,NEAR为-1,FAR为-2。
  ④长度LENGTH运算符
  这个运算符仅加在变量的前面,返回的值是指数给变量的元素个数。如果变量是用重复数据操作符DUP说明的,则返回外层DUP给定的值;如果没有CUP说明,则返回的值总是1。
  对于数组变量,可以用重复操作表达式表达式表示,其格式为:
  重复次数DUP(操作数…操作数)
  其中,重复次数为正整数,DUP是重复操作符,括号中的操作数是重复的内容,操作数类型可以是字节、字或双字等。
  ⑤字节总数SIZE运算符
  SIZE运算符仅用于变量的前面,运算结果是返回数组变量所占的总字节数,也就是等于LENGTH和TYPE两个运算符返回值的乘积。
如数组变量ARRAY是用20HDUP(0)定义的,且数组元素的数据类型是字,则
     MOV AL,SIZE ARRAY
等效为:  MOV AL,40H
  (5)属性运算符
  属性运算符包括:类型修改(PTR)、短转移(SHORT)、类型指定(THIS)和段超越运算符(:)。这种运算符用来对变量、标号或某存储器操作数的类型属性进行修改。
  ①类型修改PTR运算符
  PTR运算符格式如下:
  类型 PTR 地址表达式
  其中,类型可以是BYTE(字节)、WORD(字)、DWORD(双字)、NEAR(近距离)、FAR(远距离)。
  运算结果是将地址表达式所指定的变量、标号或存储器操作数的类型属性,临时性地修改或指定为PTR运算中规定的类型。这种修改是临时性的,仅在有修改运算符的语句内有效。
  ②短转移SHORT运算符
  当JMP指令的目标地址与JMP指令之间的距离在-128~+127字节的范围内,可以用SHORT操作符来告诉汇编程序,将JMP指令汇编成两个字节的代码(一个字节为操作码,后一个字节为相对位移量)。例如:
    JMP SHORT NEAR_LABLE
  其中,目标标号NEAR_LABLE与JMP指令间的相对位移量在-128~+127个字节的范围内。
  ③类型指定THIS运算符
  THIS运算符的格式如下:
    THIS 类型
  其中,类型可以是BYTE、WORD、DWORD、NEAR或FAR。该操作符用来指定或补充说明变量或标号的类型。运算符THIS与LABEL伪指令有类似的效果,THIS运算符的应用举例,将在后面叙述。
  ④段超越运算符(跨段前缀)
  段超越运算符用来临时给变量、标号或地址表达式指定一个段属性。
  段超越运算符的格式为:
   段名:地址表达式
 或 段寄存器名:地址表达式
  例如:INC ES:[BP+3]
  ES:为跨段前缀,冒号":"前的ES段寄存器指明了操作数当前所在的段为附加数据段。如果没有跨段前缀"ES:",那么,由  [BP+3]地址表达式所表示的偏移地址将被系统默认为是在堆栈中。
3、运算符优先级(后期补充)
汇编语言的伪指令&
  汇编语言中有3种基本语句:指令语句、伪指令语句和宏指令语句。
  指令语句是上一章介绍的指令,它们经过汇编之后产生可供计算机硬件执行的机器目标代码,所以这种语句又称为执行语句;伪指令语句是一种说明(指示)性语句,仅仅在汇编过程中告诉汇编程序应如何汇编,例如告诉汇编程序已写出的汇编评议程序有几个段,段的名称是什么?是否采用过程?汇编到某处是否需要留出存储空间?应留多大?是否要用到外部变量等。所以,伪指令语句是一种汇编程序在汇编时用来控制汇编过程以及向汇编程序提供汇编相关信息的批示性语句。与指令语句不同,伪指令语句其本身并不直接产生可供计算机硬件执行的机器目标代码,它仅是一种非执行语句。&
  宏指令语句用于替代源程序中一段有独立功能的程序,由汇编时产生相应的目标代码。宏指令语句是使用指令语句和伪指令语句,由用户自己定义的新指令。本教材对宏指令语句不作讨论,在这一节里只介绍几种常用的伪指令语句。
  1.数据定义伪指令
  该指令的功能是把数据项或项表的数值存入存储器连续的单元中,并把变量名与存储单元地址联系在一起。在程序中,用户可以用变量名来访问这些数据项。
  数据定义伪指令的格式如下:
  其中,变量名是任选项。
  若用DB定义变量,则变量类型为BYTE,汇编时为每个操作数分配一个存储单元;
  若用DW定义变量,则变量类型为WORD,汇编时为每个操作数分配2个存储单元,操作数的低字节在低地址,高字节在高地址;
  若用DD定义变量,则变量类型为DWORD,汇编时为每个操作数分配4个存储单元,操作数的低字节在低地址,高字节在高地址。
  2.符号定义伪指令
  在编制源程序时,程序设计人员常把某些常数、表达式等用一特定符号表示,这样,为编写程序带来许多方便。为此,就要使用符号定义语句,这种语句有以下两种:
  (1)赋值伪指令
  赋值伪指令是为表达赋予一个符号名,其后指令中凡需要用到该表达式的地方均可以用此名字来代替。缩写程序时,通过使用赋值伪指令可以使汇编语言简明易懂,便于程序的调试和修改。赋值伪指令的格式如下:&
  符号名 EQU 表达式
  符号名是必需项,赋值伪指令仅在汇编源程序时作为替代符号用,不产生任何目标代码,也不占用存储单元。因此,赋值伪指令左边的符号名没有段、偏移量和类型3个属性。同一符号名不能重复定义。表达式可以是常数表达式、地址表达式、变量名、标号名、过程名、寄存器名或指令名等。如果表达式包含有变量、标号或过程名,则应在EQU语句以前的某处定义过。
  (2)等号伪指令
  语句格式: 符号名=表达式
  这种语句的含义和表达的内容都与赋值语句相同;但是等号语句可以重新定义符号。
  3.类型定义伪指令
  类型定义伪指令的格式如下:
  变量名或标号名 LABEL 类型
  LABEL伪指令为当前存储单元重新定义一个指定类型的变量或标号,该伪指令并不为指定的变量或标号分配存储单元。
  例如:DA-BYTE LABEL BYTE
     DA-WORD DW 20H DUP(0)
  上面第二个语句是定义了20H个字单元,如要对这数组元素中某单元以字节访问它,则可以很方便地直接使用DA-BYTE变量名。DA-BYTE和DA-WORD有相同 段和偏移量属性。同样,也可以有:
  JUMP-FAR LABEL FAR
  JUMP-NEAR:MOV AL,30H
  当从段内某指令来调用这程序段时,可以用标号JUMP-NEAR,如果从另一代码段来调用时,则可用JUMP-FAR标号。
运算符THIS和LABEL伪指令有类似的效果,上面两条LABEL伪指令可分别改为:
  DA-BYTE EQU THIS BYTE
  JUMP-FAR EQU THIS FAR 
  4.段定义伪指令
  我们知道,CPU的地址空间是分段结构的。因此,我们在编制任一源程序时,亦必须按段来构造程序。一个程序通常按用途划分成几个逻辑段(至少要定义一个段),如存放数据的段、作堆栈使用的段、存放主程序的段、存放子程序的段等等。那么,如何告诉汇编程序源程序中的哪些内容属于数据段、哪些内容属于代码段呢?这自然是由汇编系统中提供的伪指令来实现。
  段定义伪指令的功能就是把源程序划分为逻辑段,便于汇编程序在相应段名下生成目标码,同时也便于连接程序组合、定位、生成可执行的目标程序。利用段定义伪指令可以定义一个逻辑段的名称和范围,并且指明段的定位类型、组合类型和类别名,其指令格式如下:
  在源程序中,每一段都是以SEGMENT伪指令开始,以ENDS伪指令结束。其中:
  (1)段名
  由用户自己选定,通常使用与本段用途相关的名字。如第一数据段DATA1,第二数据段DATA2,堆栈段STACK,代码段CODE……一个段开始与结尾用的段名应一致。
  (2)段参数
  段参数有定位类型、组合类型和类别名,各之间必须用空格分隔。同时,它们必须按给定的顺序排定,它们都是任选项,它们决定了段与段之间联系的形态。
  ①定位类型
  定位类型表示对该段起始边界的要求,可有4种选择:
  ·BYTE 起始地址=×××× ×××× ×××× ×××× ××××,即字节型,表示本段起始单元可以从任一地址开始。&
  ·WORD 起始地址=×××× ×××× ×××× ×××× ×××0,即字型,表示本段起始地址可以是任何一个字的边界(偶地址)。
  ·PARA 起始地址=×××× ×××× ×××× ×××× 0000,即节型,表示本段起始地址必须从存储器的某一个节的边界开始(1节等于16个字节)。
  ·PAGE 起始地址=×××× ×××× ×××× ,即页型,表示本段起始地址必须从存储器的某一个页的边界开始(1页等于256个字节)。
  对于上述4种定位类型,它们20位边界地址分别可以被1、2、16、256除尽,分别称为以字节、字、节、页为边界。其中,PARA为隐含值,即如果省略"定位类型",则汇编程序按PARA处理。
  ②组合类型
  组合类型指定段与段之间是怎样连接和定位的。它批示连接程序,如何将某段与其他段组合起来的关系。连接程序不但可以将不同模块的同名段进行组合,并可根据组合类型,将各段顺序地或重叠地连接在一起。其中有6种组合类型可供选择:
  ·NONE类型
表示该段与其他段在逻辑上不发生连接关系,这是隐含的组合类型,若省略"组合类型"项,即为NONE。
  ·PUBLIC类型
  表连接时,应把不同模块中属于该类型的同名同类别的段相继顺序地连成一个逻辑运算时装入同一物理段中,使用同一段基址。连接顺序与LINK时用户所提供的各模块的顺序一致,应当注意,各模块中属于PUBLIC类型的同名同类别的各段的总长度不能超64KB。
  ·STACK类型
  与PUBLIC类型同样处理,只是组合后的该段用作堆栈。当段定义中指明了STACK类型后,说明堆栈已经确定,系统自动对段寄存器SS初始化在这个连续段的首址,并初始化堆栈指针SP。用户程序中至少有一个段用STACK类型说明,否则需要用户程序自己初始化SS和SP。
  ·COMMON类型
  表明连接时,应将不同模块中属于该类型的同名同类别的各段连接成一段,它们共用一个基地址,且互相覆盖(重叠地放在一起),连接后,段的长度取决于最长的COMMON段的长度,这样可以使不同模块的变量或标号使用同一存储区域,便于模块间的通信。
  ·AT表达式类型
  表示本段可定位在表达式所指示的节边界上。如"AT 0930H",那么本段从绝对地址09300H开始。但是,它不能用来指定代码段。
  ·MEMORY类型
  表明连接时应把本段装在被连接的其他所有段的最后(高地址端),若有几个段都指出了MEMORY纵使类型,则汇编程序认为所遇的第1个为MEMORY组合类型,其他段认为是COMMON类型。
  ③类别名
  类别名必须用单引号括起来。类别名是由用户任选字符串组成,以表示该段的类别。在连接时,连接程序将各个程序模块中具有同样类别名的逻辑段集中在一起,形成一个统一的物理段。典型的类别名有"STACK"、"CODE"、"DATA1"、"DATA2"……
  一个典型程序的段结构如下:
  STACK SEGMENT PARA STACK 'STACK';堆栈段,定位类型为节起点,组
                    合为公用堆栈段,类别名为SATCK。
  STACK ENDS
  DATA SEGMENT PARA 'DATA';数据段,定位类型为节起点,不与其他段组
                合,类别名为DATA。
  DATA ENDS
  CODE SEGMENT PARA MEMORY;代码段,定位类型为节起点,本段地址位于
                高地址端。
  CODE ENDS       &
  5.设置起始地址伪指令
  ORG伪指令用来指出其后的程序段或数据块存放的起始地址的偏移量。其指令格式为: ORG 表达式
  汇编程序把语句中表达式之值作为起始地址,连续存放程序和数据,直到出现一个新的ORG指令。若省略ORG,则从本段起始地址开始连续存放。
  6.汇编结束伪指令
  标志着整个源程序的结束,它使汇编程序停止汇编操作。其指令格式为:
  END 表达式(标号)
  其中,表达式与源程序中的第一条可执行指令语句的标号相同。它提供了代码段寄存器CS与指令指示器IP的数值,作为程序执行时第一条要执行的指令的地址。
  伪指令END必须是汇编语言源程序中的最后一条语句,而且每一个源程序只能有一条END伪指令。如果出现一第以上的END伪指令,则在第一条伪指令END以后的语句是无效的。
  7.段寄存器设定伪指令
  段寄存器设定伪指令ASSUME,一般出现在代码段中,它用来告诉汇编程序由SEGMENT/ENDS伪指令定义的段和段寄存器的对应关系,即设定已定义段各自属于哪个段寄存器。其指令格式为:
  ASSUME 段寄存器名:段名[,段寄存器名:段名]
  段寄存器名是CS、DS、SS或ES,段名必须是由SEGMENT/ENDS定义过的段名。
  应当注意:使用ASSUME伪指令,仅仅告诉汇编程序,关于段寄存器与定义段之间的对应关系。但它并不意味着汇编后这些段地址已装入了相应的段寄存器中,这些段地址的真正装入,仍需要用程序来送入,且这4个段寄存器的关入略有不同。
  8.过程定义伪指令
  在程序设计中,我们常常把具有一定功能的程序段设计成一个子程序。汇编程序用"过程"(PROCEDURE)来构造子程序。过程定义伪指令格式如下:
  一个过程是以PROC伪指令开始,以ENDP伪指令结束。
  其中,过程名不能省略,且过程的开始(PROC)和结束(ENDP)应使用同一过程名。它就是这个子程序的程序名,也是过程调用指令CALL的目标操作数。它类同一个标号的作用,仍有3个属性――段、偏移量和距离类型。过程的距离类型可选择NEAR和FAR。在定义过程时,如没有选择距离类型,则隐含为NEAR。"过程"应在一个逻辑段内。
  过程和段可以相互嵌套,即过程可以完全地包含某个段,而段也可以完全地包含某个过程,但它们不能交叉覆盖,即过程可以完全地某个段,而段也可以完全地包}

我要回帖

更多关于 angularjs 指令 scope 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信