伪代码是给处理器的指令,它实际上是原始十六进制代码的可读版。因此,汇编是最低级的编程语言。汇编中的所有东西被直接翻译为十六进制码。换句话说,你没有把高级语言翻译为低级语言的编译器上的烦恼,汇编器仅仅把汇编代码转化为原始数据。
下面将讨论一些用来运算,位操作等的伪代码。还有跳转指令,比较等伪代码。
1.一些基本的计算伪代码(又称指令)
(1)MOV
这条指令用来把一个地方移往(事实上是复制到)另一个地方。这个地方可以是寄存器,内存地址或是直接数值(当然只能作为源值)。Mov指令的语法是:
mov 目标,源
你可把一个寄存器移往另一个(注意指令是在复制那个值到目标中,尽管”mov”这个名字是移的意思)
mov edx, ecx
上面的这条指令把ecx的内容复制到了ecx中,源和目标的大小应该一致。例如这个指令是非法的:
mov al, ecx;非法
这条伪代码试图把一个DWORD(32位)值装入一个字节(8位)的寄存器中。这不能个由mov指令来完成(有其他的指令干这事)。但这些指令是允许的因为源和目标在大小上并没有什么不同:
mov al, blmov cl, dlmov cx, dxmov ecx, ebx
内存地址由offset指示(在win32中,前一章中有更多信息)你也能从地址的某一个地方获得一个值并把它放入一个寄存器中。下面有一个例子:
每一个块代表一个字节
offset的值这里是用字节的形式表示的,但它事实上是32位的值,比如3A(这不是一个常见的offset的值,但如果不这样简写表格装不下),这也是一个32位的值:0000003Ah。只是为了节省空间,使用了一些不常见的低位offset。所有的值均为16进制。
看上表的offset 3A。那个offset的数据是25, 7A, 5E, 72, EF等。例如,要把这个位于3A的值用mov放入寄存器中:
mov eax, dword ptr[0000003Ah]
(h后缀表明这是一个十六进制值)
mov eax, dword ptr[0000003Ah]这条指令的意思是:把位于内存地址3A的DWORD大小的值放入eax寄存器。执行了这条指令后,eax包含了值725E7A25h。可能你注意到了这是在内存中时的反转结果:25 7A 5E 72。这是因为存储在内存中的值使用了little endian格式。这意味着越靠右的字节位数越高:字节顺序被反转了。我想一些例子可以使你把这个搞清楚。
十六进制dword(32位)值放在内存中时是这样:40, 30, 20, 10(每个值占一个字节(8位))
十六进制word(16位)值放在内存中时是这样:50, 40
回到前面的例子。你也可以对其他大小的值这么做:
mov cl, byte ptr [34h] ; cl得到值0Dh(参考上表)
mov dx, word ptr [3Eh] ; dx将得到值 7DEFh (看上表,记住反序)
大小有时不是必须的。
Mov eax,[00403045h]
因为eax是32位寄存器,编译器假定(也只能这么做)它应该从地址403045(十六进制)取个32位的值。
可以直接使用数值:
mov edx, 5006
这只是使得edx寄存器装有值5006,综括号[和]用来从括号间的内存地址处取值,没有括号就只是这个值。寄存器和内存地址也可以(他应该是32位程序中的32位寄存器):
mov eax,403045h;使eax装有值403045h(十六进制)
mov cx,[eax];把位于内存地址eax的word大小的值(403045)移入cx寄存器。
在mov cx, [eax]中,处理器会先查看eax装有什么值(=内存地址),然后在那个内存地址中有什么值,并把这个word(16位,因为目标-cx-是个16位寄存器)移入cx。
(2)ADD, SUB, MUL, DIV
许多伪代码做计算工作。你可以猜出它们中的大多数的名字:add(加),sub(减),mul(乘),div(除)等。
Add伪代码有如下语法:
Add 目标,源
执行的运算是 目标=目标+源。下面的格式是允许的。
这条指令非常简单。它只是把源值加到目标值中并把结果保存在目标中。其他的数学指令有:
sub 目标,源(目标=目标-源)mul 目标,源(目标=目标×源)div 源(eax=eax/源,edx=余数)
减法和加法一样做,乘法是目标=目标×源。除法有一点不同,因为寄存器是整数值(注意,绕回数不是浮点数)除法的结果被分为商和余数。例如:
28/6->商=4,余数=430/9->商=3,余数=397/10->商=9,余数=718/6->商=3,余数=0
现在,取决于源的大小,商(一部分)被存在eax中,余数(一部分)在edx:
*:例如,如果dx=2030h,而ax=0040h,dx:ax=20300040h。dx:ax是一个双字值。其中高字代表dx,低字代表ax,Edx:eax是个四字值(64位)其高字是edx低字是eax。
Div伪代码的源可以是
· 8位寄存器 (al, ah, cl,…)
· 16位寄存器 (ax, dx, …)
· 32位寄存器 (eax, edx, ecx…)
· 8位内存数 (byte ptr [xxxx])
· 16位内存数(word ptr [xxxx])
· 32位内存数(dword ptr [xxxx])
源不可以是直接数值,因为处理器不能决定源参数的大小。
(3)位操作
这些指令都由源和目标,除了”NOT”指令。目标中的每位与源中的每位作比较,并看是那个指令,决定是0还是1放入目标位中。
如果源和目标均为1,AND把输出位设为1。
如果源和目标中有一个为1,OR把输出位设为1。
如果源和目标位不一样,XOR把输出位设为1。
NOT反转源位
一个例子:
mov ax, 3406mov dx, 13EAhxor ax,dx
ax=3406(十六进制)是二进制的0000110101001110
dx=13EA(十六进制)是二进制的0001001111101010
对这些位进行xor操作:
新dx是0001111010100100 (十进制的7845, 十六进制的1EA4)
另一个例子:
mov ecx, FFFF0000hnot ecx
FFFF0000在二进制中是11111111111111110000000000000000(16个1,16个0)如果反转每位会得到
00000000000000001111111111111111(16个0,16个1)在十六进制中是0000FFFF。因而执行NOT操作后,ecx是0000FFFFh。
(4)步增INC/步减DEC
有两个很简单的指令,DEC和INC。这些指令使内存地址和寄存器步增或步减,就是这样:
inc reg -> reg = reg + 1
dec reg -> reg = reg – 1
inc dword ptr [103405] -> 位于103405的值步增
dec dword ptr [103405] -> 位于103405的值步减
(5)NOP
这条指令什么都不干。它仅仅占用空间和时间。它用作填充或给代码打补丁的目的。
(6)移位(Bit Rotation 和 shifiting)
注意:下面的大部分例子使用8位数,但这只是为了使目的清楚。
(7)Shifting函数
SHL 目标,计数(count)
SHR 目标,计数(count)
SHL和SHR在寄存器,内存地址中像左或向右移动一定数目(count)的位。
例如:
;这儿al=01011011(二进制)
shr al, 3
它的意思是:把al寄存器中的所有位向右移三个位置。因而al会变成为00001011。左边的字节用0填充,而右边的字节被移出。最后一个被移出的位保存在carry-flag中。Carry-flag是处理器标志寄存器的一位,它不是像eax或ecx一样的,你可以访问的寄存器(虽然有伪代码干这活),但它的值决定于该指令的结构。它(carry-flag)会在后面解释,你要记住的唯一一件事是carry是标志寄存器的一位且它可以被打开或者关闭。这个位等于最后一个移出的位。
shl和shr一样,只不过是向左移。
;这儿bl=11100101(二进制)
shl bl, 2
执行了指令后bl是10010100(二进制)。最后的两个位是由0填充的,carry-flag是1,因为最后移出的位是1。
还有两个伪代码:
SAL 目标, 计数(算术左移)
SAR 目标, 计数(算术右移)
SAL和SHL一样,但SAR不完全和SHR一样。SAR不是用0来填充移出的位而是复制MSB(最高位)例如:
al = 10100110
sar al, 3
al = 11110100
sar al, 2
al = 11101001
bl = 00100110
sar bl, 3
bl = 00000100
(8)Rotation(循环移动) 函数
Rol 目标,计数;循环左移
Ror 目标,计数;循环右移
Rcl 目标,计数;通过carry循环左移
Rcr 目标,计数;通过carry循环右移
循环移动(Rotation)看上去就像移(Shifting),只是移出的位又到了另一边。
例如:ror(循环右移)
如你在上图所见,位循环了。注意,每个被推出的位又移到了另一边。和Shifting一样,carry位装有最后被移出的位。Rcl和Rcr实际上和Rol,Rcr一样。它们的名字暗示了它们用carry位来表明最后移出的位,但和Rol和Ror干同样的事情。它们没有什么不同。
(9)交换
XCHG指令也非常简单。它同在两个寄存器和内存地址之间交换:
eax = 237h
ecx = 978h
xchg eax, ecx
eax = 978h
ecx = 237h
技术和通信的概念
2.条件跳转
例如下面这段代码:
mov eax, edx
sub eax, ecx
cmp eax, 2
jz loc1
xor eax, eax
jmp loc2
loc1:
xor eax, eax
inc eax
loc2:
(xor eax, eax意为:eax=0,eax清零)
让我们来看看这些代码:
mov eax, edx ;把edx放入eax中
sub eax, ecx ;eax-ecx
cmp eax, 2
这有一条新指令:cmp。Cmp意为compare(比较)。它能比较两个值(或寄存器、或内存、或直接数值)并设置Z-flag(零标志)。零标志很像carry,也是内部标志寄存器的一位。
Jz loc1
这也是条新的。它是条件跳转指令。Jz=jump if zero(如果设置了零标志就跳转)。Loc1是一个标记指令”xor eax,eax|inc eax”内存开始处offset的标签。因而jz loc1=如果设置了零标志,跳往位于loc1的指令。
Cmp eax, 2;如果eax=2设置零标志
Jz loc1;如果设置了零标志就跳转
= 如果eax等于2,跳往位于loc1的指令
然后有jmp loc2.这也好似一个跳转,但是是一个无条件跳转:它总是执行。上面的代码,用c语言描述就是:
if ((edx-ecx)==2)
{
eax = 1;
}
Else
{
eax = 0;
}
或者,用Basic语言描述:
IF (edx-ecx)=2 THEN
EAX = 1
ELSE
EAX = 0
END IF
程序员职业-编写程序代码在笔记本电脑上
3.标志寄存器
标志寄存器有一套标志。它们设不设置取决于计算或其他时间。我不会讨论它们的全部。只拣几个重要的说:
还有更多的标志(Parity, Auxiliary, Trap, Interrupt, Direction, IOPL, Nested Task, Resume, & Virtual Mode)但因为我们不用它们,所以我不解释。
Unix 词云
4.跳转系列
有一整套的条件跳转,而且它们跳转与否均取决于标志的状态。但由于大部分跳转指令有明白的名字,你甚至无需知道哪个标志要设置,例如:”如果大于等于就跳转”(jge)和”符号标志=溢出标志”一样,而”如果零就跳转”和”如果零标志=1就跳转”一样。死背住下面的表即可
在下表中,”意思”指的是什么样的计算结果该跳转。”如果大于就跳转”意为:
cmp x, y
jump 如果 x 比 y大 就跳
所有的跳转指令后面要跟一个参数:要跳往的偏移地址(距离)offset。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。