第 6 章教学重点在 16位 8086指令系统基础上,我们扩展到 32位 80x86
指令系统 。 重点掌握:
32位编程环境
32位寻址方式
32位指令编程方法
Windows应用程序编程第 6 章 IA-32结构的指令系统
整数指令集
16位整数指令集
32位整数指令集
浮点指令集
MMX指令集
SSE指令集
SSE2指令集
SSE3指令集主要指令集在
16
指令基础上形成的
32
位整数指令集第 6 章 6.1 32位指令的运行环境实地址方式保护方式虚拟 8086方式
16位逻辑段,段地址和偏移地址都是 16位
—— 段地址左移 4位加偏移地址形成 20位物理地址
32位逻辑段,段地址和偏移地址都是 32位
—— 段地址加偏移地址形成 32位线性地址第 6 章 6.1.1 寄存器组
8个 32位通用寄存器:
EAX EBX ECX EDX
ESI EDI EBP ESP
6个 16位段寄存器:
CS SS DS ES FS GS
32位指令指针寄存器,EIP
32位标志寄存器,EFLAGS
其他的 32位系统用寄存器在原有
16
位寄存器基础上扩展成为
32
位第 6 章 6.1.2 寻址方式
32位有效地址=
基址寄存器 + ( 变址寄存器 × 比例 ) + 位移量
基址寄存器 —— 任何 8个 32位通用寄存器之一
变址寄存器 —— 除 ESP之外的任何 32
位通用寄存器之一
比例 —— 可以是 1/ 2 / 4 / 8
位移量 —— 可以是 8/ 32位值第 6 章 32位寻址方式
⑴ mov eax,44332211h
⑵ mov eax,ebx
⑶ mov eax,[1234h]
⑷ mov eax,[ebx]
⑸ mov eax,[ebx+80h]
⑹ mov eax,[ebx+esi]
⑺ mov eax,[ebx+esi+80h]
⑻ mov eax,[esi*2]
⑼ mov eax,[ebx+esi*4]
⑽ mov eax,[ebx+esi*8+80h]
第 6 章 6.1.3 机器代码格式第 6 章 6.2 32位扩展指令
16位指令系统从两个方面向 32位扩展
⑴ 支持 32位操作数
⑵ 支持 32位寻址方式
mov ax,bx ;16位操作数
mov eax,ebx ;32位操作数
mov ax,[ebx] ;16位操作数,32位寻址方式
mov eax,[ebx] ;32位操作数,32位寻址方式
有些指令扩大了工作范围,或指令功能实现了向 32位的自然增强第 6 章 将 立即数压入堆栈
PUSHi8/i16/i32;把 16位或 32位立即数 i16/i32压入堆栈 。 若是 8
位立即数 i8,经符号扩展成 16位后再压入堆栈
push 1234h ;压入 16位立即数
push 87654321h ;压入 16位立即数
call helloabc
add esp,6 ;平衡堆栈第 6 章 通用寄存器全部进出栈
PUSHA;顺序将 AX/CX/DX/BX/SP/BP/SI/DI压入堆栈
POPA;顺序从堆栈弹出 DI/SI/BP/SP/BX/DX/CX/AX
( 与 PUSHA相反 );其中应进入 SP的值被舍弃,并不进入 SP,SP
通过增加 16来恢复第 6 章 符号扩展和零位扩展
MOVSXr16,r8/m8;把 r8/m8符号扩展并传送至 r16
MOVSXr32,r8/m8/r16/m16;把 r8/m8/r16/m16符号扩展并传送至 r32
MOVZX r16,r8/m8;把 r8/m8零位扩展并传送至 r16
MOVZX r32,r8/m8/r16/m16;把 r8/m8/r16/m16零位扩展并传送至 r32
mov bl,92h
movsx ax,bl ;ax=ff92h
movsx esi,bl ;esi=ffffff92h
movzx edi,ax ;edi=0000ff92h
第 6 章 串输入 /输出
INS ; I/O串输入;存储单元 ES:[(E)DI] ← I/O端口 [DX]; (E)DI←(E)DI ± 1/2/4
OUTS ; I/O串输出; I/O端口 [DX] ← 存储单元 DS:[(E)SI]; (E)SI←( E)SI± 1/2/4
串指令能以双字为传送单位 ( ± 4)
在 16位段,采用 SI,DI,CX
在 32位段,采用 ESI,EDI,ECX
第 6 章 条件转移
Jcc label;cc为真,转移到 label指定的段内偏移地址处
JECXZ label;ECX=0,转移到 label指定的段内偏移地址处
LOOP/LOOPZ/LOOPNZ label;循环指令,32位段采用 ECX作为计数器转移范围可达到 32位全偏移转移范围仍为短转移;将 AX的每一位依次重复一次;所得的 32位结果保存于 EAX中
mov ecx,16
mov bx,ax
next,shr ax,1
rcr edx,1
shr bx,1
rcr edx,1
loop next
mov eax,edx
例 6.1;在 16位段;把 CX字节长度的数据块从 DS∶ SI源存储区;搬到 ES∶ DI目的存储区
ror ecx,2 ;cx的低 2位移入了 ecx的高 2位
rep movsd ;每次传送双字
rol ecx,1
rep movsw ;传送可能余下的字
rol ecx,1
rep movsb ;传送可能余下的字节例 6.2
第 6 章 6.3 32位指令的程序设计
指定汇编程序识别新指令
处理 16位段和 32位段
注意有些指令在 16位逻辑段和 32位逻辑段的差别
DOS环境 ( 实地址方式和虚拟 8086方式 ),
只能使用 16位段
Windows 32位保护方式,可以使用 32位段例 6.5- 1/2
.model small
.386 ;采用 32位指令
.stack
.data
qvar dq 1234567887654321h ;数据定义
.code
.startup
mov eax,dword ptr qvar
mov edx,dword ptr qvar[4]
例 6.5- 2/2
mov ecx,8
start1,shl eax,1
rcl edx,1
loop start1
mov dword ptr qvar,eax
mov dword ptr qvar[4],edx
.exit 0
end
例 6.6- 1/10
.model small
.386 ;采用 32位指令
.stack
.data
count equ 10
darray dd 20,4500h,3f40h,-1,7f000080h
dd 81000000h,0fffffff1h
dd -45000011,12345678
dd 87654321
.code
.startup
例 6.6- 2/10
xor esi,esi
mov si,offset darray ;原序显示
mov ecx,count
start1,mov eax,[esi]
call EAXdisp
add esi,4
dec ecx
jz start2
mov dl,’,’ ;用逗号分隔数据
mov ah,2
int 21h
jmp start1
例 6.6- 3/10
start2,mov dl,0dh ;回车和换行
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
xor esi,esi
mov si,offset darray ;排序
mov ecx,count
call sorting
例 6.6- 4/10
xor esi,esi
mov si,offset darray ;降序显示
mov ecx,count
start3,mov eax,[esi]
call EAXdisp
add esi,4
dec ecx
jz start4
mov dl,’,’
mov ah,2
int 21h
jmp start3
start4,.exit 0
例 6.6- 5/10;32位有符号数据的排序子程序 ( 降序 );入口参数,DS:ESI=缓冲区首地址,ECX=个数
sorting proc
push eax
push edx
dec ecx
outlp,mov edx,0
inlp,cmp edx,ecx;内循环,使最高地址存储单元具有最小数据
jae short botm
mov eax,[esi+edx*4+4]
cmp [esi+edx*4],eax;比较前后两个数据的大小例 6.6- 6/10
jge short nswap
xchg [esi+edx*4],eax
mov [esi+edx*4+4],eax
nswap,inc edx
jmp inlp
botm,loop outlp
pop edx
pop eax
ret
sorting endp
例 6.6- 7/10;以十进制形式显示 32位有符号数据的子程序;入口参数,EAX=有符号数据
EAXdisp proc
push ebx
push edx
test eax,eax ;判断是零,正或负
jnz eaxdisp0
mov dl,’0’ ;为零,显示,0”
mov ah,2
int 21h
jmp eaxdisp4
例 6.6- 8/10
eaxdisp0,jns eaxdisp1
neg eax ;为负数,求绝对值
mov ebx,eax
mov dl,’-’ ;则显示一个符号,-”
mov ah,2
int 21h
mov eax,ebx
eaxdisp1,mov ebx,10
push bx ;压入 10作为结束标志
eaxdisp2,cmp eax,0
jz eaxdisp3;EAX=0( 数据为 0),则退出例 6.6- 9/10
sub edx,edx ;edx=0
div ebx ;EDX.EAX÷ EBX( 10)
add dl,30h ;余数转换为 ASCII码
push dx;将除 10得到的各位数依次压入堆栈
jmp eaxdisp2
eaxdisp3,pop dx ;将各位数依次出栈
cmp dl,10
je eaxdisp4;是结束标志 ( 10),则退出例 6.6- 10/10
mov ah,2 ;显示
int 21h
jmp eaxdisp3
eaxdisp4,pop edx
pop ebx
ret
EAXdisp endp
end
第 6 章 6.4.1 80386新增指令
80386的执行单元可以实现快速 移位操作,
80386新增的指令主要就是有关位操作的
80386还增加了条件设置指令,以及对控制,
调试和测试寄存器的传送指令
MASM5.0开始支持 80386指令系统第 6 章 位测试指令
BT dest,src ;把目的操作数 dest中由源操作数 src指定的位送 CF标志
BTCdest,src ;把 dest中由 src指定的位送
CF标志,然后对那一位求反
BTRdest,src ;把 dest中由 src指定的位送
CF标志,然后对那一位复位
BTSdest,src ;把 dest中由 src指定的位送
CF标志,然后对那一位置位
mov eax,12345678h ;EAX=12345678h
bt eax,5 ;EAX=12345678h,CF← 1=EAX的 D5位
btceax,10 ;EAX=12345278h,CF← 1=EAX的 D10位
btreax,20 ;EAX=12245278h,CF← 1=EAX的 D20位
btseax,34 ;EAX=1224527Ch,CF← 0=EAX的 D2位第 6 章 条件设置指令
SETccr8/m8;若条件 cc成立,则 r8/m8为 1;否则,为 0
cmp eax,32h
jge L00
mov ebx,0C1h
jmp L01
L00,mov ebx,0C2h
L01,...
xor ebx,ebx
cmp eax,32h
setge bl
dec ebx
and ebx,(0C1h-0C2h)
add ebx,0C2h
第 6 章 6.4.2 80486新增指令
80486的指令系统是在 80386指令集的基础上增加了 6条新指令,新增的指令主要用于对多处理器系统和片上高速缓冲存储器的支持
为了让汇编程序汇编这些指令,源程序中必须具有,486或,486P伪指令
MASM6.0开始支持 80486指令系统第 6 章 字节交换指令
BSWAP r32 ;将 32位通用寄存器值的第 1
和 4字节,第 2和 3字节互换
mov eax,00112233h ;EAX=00112233h
bswap eax ;EAX=33221100h
第 6 章 交换加指令
XADDreg/mem,reg;reg/mem←→ reg,reg/mem← reg+reg/mem
mov bl,12h
mov dl,02h
xadd bl,dl ;BL=14h,DL=12h
第 6 章 比较交换指令
CMPXCHG reg/mem,reg;AL/AX/EAX- reg/mem;相等,ZF=1,reg/mem←reg;不等,ZF=0,AL/AX/EAX←reg/mem
mov al,12h
mov bl,12h
mov dl,02h
cmpxchg bl,dl ;AL=12h,BL← DL=02h,ZF=1
cmpxchg bl,dl ;AL← BL=02h,DL=02h,ZF=0
第 6 章 6.4.3 Pentium新增指令
Pentium新增了几条非常实用的特权指令
源程序中必须具有,586或,586P伪指令
MASM6.11开始支持 Pentium指令系统
较低版本的汇编程序可以定义宏实现:
CPU_ID MACRO ;;CPUID指令的机器代码
DB 0FH,0A2H ;;0F A2
ENDM
也可以直接在该指令处用:
DB 0FH,0A2H
第 6 章 比较交换指令
CMPXCHG reg/mem,reg;AL/AX/EAX- reg/mem;相等,ZF=1,reg/mem←reg;不等,ZF=0,AL/AX/EAX←reg/mem
mov al,12h
mov bl,12h
mov dl,02h
cmpxchg bl,dl ;AL=12h,BL← DL=02h,ZF=1
cmpxchg bl,dl ;AL← BL=02h,DL=02h,ZF=0
第 6 章 6.4.4 Pentium Pro新增指令
PentiumPro指令系统新增 3条实用的指令
程序中必须具有,686或,686P伪指令
MASM6.12开始支持 PentiumPro指令第 6 章 条件传送指令
CMOVcc r16,r16/m16 ;若条件 cc成立,
则 r16←r 16/m16;否则,不传送
CMOVcc r32,r32/m32 ;若条件 cc成立,
则 r32←r 32/m32;否则,不传送
test ecx,ecx;判断 ECX是否等于 0
jne IH
mov eax,ebx;ECX=0,则 EAX←EBX
IH,..,;ECX≠0
test ecx,ecx;CMOVeq 有 条 件 地 将
EBX传送到 EAX
cmoveq eax,ebx;可以代替 JNE与 MOV指令,从而消除分支第 6 章 6.5 用汇编语言编写 32位 Windows应用程序
采用汇编语言可以编写 32位 Windows应用程序
调用 Windows的应用程序接口 API
借助 MASM32开发环境
可以利用 Windows的高级特性,生成的可执行文件相对较小,性能更高
从更深层次理解 Windows运行机制及程序设计思想
介绍汇编语言编写 Win32程序的基本思想,
框架结构和开发环境第 6 章 16位 DOS与 32位 Windows- 1
16位 DOS操作系统工作于实地址方式
DOS是单任务操作系统,一个正在运行的程序独占了所有系统资源
DOS系统只有一个特权级别,任何程序和操作系统都是同级的
32位 Windows操作系统运行于保护工作方式
Windows是多任务操作系统,系统资源由多个程序共享
Windows系统存在两个特权级别,操作系统运行在最高级别 0级,应用程序都运行于最低级别 3级第 6 章 16位 DOS与 32位 Windows- 2
DOS平台下
只有 1MB物理存储空间
必须分成不大于 64KB的逻辑段
Windows平台下
直接使用 32位地址寻址一个不分段的,达 4GB
的主存空间
Windows应用程序只有代码段和数据段
无须和段寄存器打交道第 6 章 16位 DOS与 32位 Windows- 3
对程序员来说,操作系统由其提供的系统功能调用定义
DOS操作系统为程序员提供中断服务程序
以中断调用的方法进行系统功能调用
DOS中断调用采用寄存器传递参数
Windows操作系统提供了动态链接库 DDL
利用应用程序接口 API调用动态链接库中的函数
Windows的 API也曾被称为软件开发包 SDK,16位和
32位 Windows的 API分别被称为 Win16和 Win32
Windows应用程序利用堆栈传递参数第 6 章 16位 DOS与 32位 Windows- 4
DOS下的程序以字符方式显示给用户
程序需要用户输入时,就停下来;用户不输入就不再执行;而且,需要输入一个数据,用户不能输入另一个数据 。
Windows程序采用图形用户界面
它时刻等待用户的操作 。 用户的每个操作都会形成消息 ( Message) 传递给程序,程序则给予响应第 6 章 6.5.2 32位 Windows控制台程序
Windows应用程序开始运行
创建控制台 ( Console) 窗口
或创建图形界面窗口
32位 Windows控制台程序
像增强版的 MS-DOS程序
使用 标准控制台,输入设备 ( 键盘 ) 和 输出设备 ( 显示器 )
32位控制台程序运行在保护方式
通过 API使用 Windows的动态链接库函数
.386
.model flat,stdcall
option casemap:none
Includelib kernel32.lib
ExitProcess proto,:DWORD
GetStdHandle proto,:DWORD
WriteConsoleA
proto,:DWORD,:DWORD,:DWORD,:DWO
RD,:DWORD
WriteConsole equ <WriteConsoleA>
ReadConsoleA
proto,:DWORD,:DWORD,:DWORD,:DWO
RD,:DWORD
ReadConsole equ <ReadConsoleA>
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
例 6.11- 1/4
源程序格式动态链接库
.data
outhandle dd?
outbuffer db 'Welcome to the Win32
Console !',0dh,0ah
db 'Please enter your
name:',0dh,0ah
outbufsize = $-outbuffer
outsize dd?
inhandle dd?
inbufsize = 80
inbuffer db inbufsize dup(?),0,0
insize dd?
例 6.11- 2/4
.code
start:;获得输出句柄
invoke
GetStdHandle,STD_OUTPUT_HANDLE
mov outhandle,eax;显示信息
invoke
WriteConsole,outhandle,
addr outbuffer,outbufsize,
addr outsize,0
例 6.11- 3/4
控制台句柄控制台输出;获得输入句柄
invoke
GetStdHandle,STD_INPUT_HANDLE
mov inhandle,eax;等待用户输入
invoke
ReadConsole,inhandle,
addr inbuffer,inbufsize,addr insize,0;退出
invoke ExitProcess,0
end start
例 6.11- 4/4
程序退出控制台输入第 6 章 汇编和连接
MASM汇编的命令:
ML /c /coff /Fl /Zi lt611.asm
参数,/coff”表示生成 COFF格式的 OBJ模块文件
需要 32位增量式链接器文件,更名为 LINK32.EXE,
连接命令:
LINK32 /subsystem:console /debug lt611.obj
参数,/subsystem:console”生成控制台程序
本程序使用了 kernel32.lib库文件,该文件也应该复制到 MASM目录第 6 章 6.5.3 MASM32开发环境
Steve Hutchesson的 免费软件包
( http://www.movsd.com/)
编辑器 geditor.exe
MASM 6.14汇编程序和链接程序
相当完整的 Win32的包含文件,库文件以及教程和示例等
一个最简单的 Win32汇编语言程序
显示标准 Windows消息窗口的程序
6.5.4 创建消息窗口例 6.12- 1/2.386
.model flat,stdcall
option casemap:none
Include include\windows.inc
Include include\kernel32.inc
Include include\user32.inc
Includelib lib\kernel32.lib
Includelib lib\user32.lib
第 6 章
.data
szCaption db 'Win32示例 ',0
szText db '欢迎进入 32位 Windows世界 ! ',0
.code
start,invoke MessageBox,NULL,\
addr szText,addr szCaption,MB_OK
invoke ExitProcess,NULL
end start
例 6.12- 2/2
显示消息框 API调用 返回操作系统 API调用对比 C++程序第 6 章 6.5.5 创建窗口应用程序
用汇编语言创建 32位 Windows应用程序
与用 C++采用 API开发没有太大区别
程序框架,用到的函数基本上一样
例 6.13程序
创建一个标准的 Windows窗口程序
包括标题栏及客户区
能够进行标准的窗口操作
例 6.14程序
点击鼠标左键
弹出消息框
.code
start,;调用主过程
invoke GetModuleHandle,NULL
mov hInstance,eax;获得实例句柄,保存
invoke GetCommandLine
mov CommandLine,eax;获得命令行参数地址指针,保存
invoke W i n M a i n,
h I n s t a n c e,N U L L,C o m m a n d L i n e,
SW_SHOWDEFAULT
invoke ExitProcess,eax
例 6.13- 1/6
主过程 WinMain;WinMain主过程
WinMain proc
hInst:DWORD,hPrevInst:DWORD,CmdLine:D
WORD,CmdShow:DWORD
LOCAL wc:WNDCLASSEX;定义窗口属性的结构变量
LOCAL msg:MSG;定义消息变量
LOCAL hwnd:DWORD;定义窗口句柄变量例 6.13- 2/6
WinMain主要任务;初始化窗口类变量
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style,CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc,OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx,addr wc ;注册窗口类例 6.13- 3/6
窗口类
INVOKE CreateWindowEx,
NULL,ADDR ClassName,ADDR AppName,
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,NULL,NULL,hInst,NULL
mov hwnd,eax ;创建窗口
invoke ShowWindow,
hwnd,SW_SHOWNORMAL ;显示窗口
invoke UpdateWindow,hwnd ;更新窗口例 6.13- 4/6
CreateWindowEx函数
.WHILE TRUE ;消息循环
invoke GetMessage,ADDR msg,NULL,0,0;获得消息
.BREAK,IF (!eax);.WHILE TRUE形成无条件循环;此处当 EAX等于 0则跳出循环
invoke TranslateMessage,ADDR msg;翻译消息
invoke DispatchMessage,ADDR msg;分派消息
.ENDW
mov eax,msg.wParam
ret
WinMain endp
例 6.13- 5/6
消息循环
WndProc proc hWnd:DWORD,
uMsg:DWORD,wParam:DWORD,lParam:DWORD
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL;处理关闭程序的消息
.ELSE;不处理的消息由系统默认操作
invoke DefWindowProc,
hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
例 6.13- 6/6
窗口过程;弹出消息的窗口应用程序;数据段增加一个字符串:
szText db '欢迎进入 32位 Windows世界 ! ',0;窗口过程增加两条语句:
invoke PostQuitMessage,NULL;处理关闭程序的消息
.ELSEIF uMsg==WM_LBUTTONDOWN;处理单击鼠标左键的消息
invoke MessageBox,NULL,
addr szText,addr AppName,MB_OK
.ELSE;不处理的消息由系统默认操作例 6.14
第 6章 教学要求
1,熟悉 32位 80x86CPU的 3种工作方式和 32位寄存器组,掌握 32位寻址方式
2,理解 16位指令如何实现 32位扩展,熟悉常用 16
位指令的 32位扩展功能及应用,了解常用的 32
位新增指令
3,掌握在 DOS环境 32位指令编程方法
4,熟悉开发 32位 Windows控制台和窗口应用程序的方法