这学期学微机原理,顺便学了点汇编,遇到了很多问题,到百度上去求助几乎没人理我,到CSDN来后发现这里的大牛们特别热情,以后就在CSDN扎根了。
下面是我作的一点点笔记(有许多点子还是大牛们告诉我的),如果其中的错误的地方还希望大家指出。马上就得回家了,就不再排版了,以后再修改。
我用的汇编工具是TASM,对于其它的工具可能结果不同 。
DB:Define Byte(未完)
———————————————————————————————————
eg:
JC again;在反汇编时会以JB出现
(三)、syntax error
语法错误,如宏命令与已有的指令名,标号名冲突
(四)、关于MOV的用法:
Mov dst, src
Reg reg/mem/data/segreg
Mem reg/data/segreg
Segreg reg/mem
Ac mem(ac指ax,al,该指令较其它指令短一个字节,执行速度也快些)
Mem ac
注:
1、SEGREG只能出现在MOV,PUSH,POP中
2、Mov mem,mem;错误,如MOV byte ptr [BX],[SI]; Illegal memory reference
3、MOV segreg,data;错误,立即数不能传给Segreg
4、MOV segreg,segreg ;SEGMENT不可相互传送
(五)、ASSUME作用(猜测):
ASSUME 作用:汇编时在符号表中记录下——变量和标号的段存储位置,如CS、DS、SS。
汇编程序遇到变量、标号时动作:
1、在对变量操作指令中,汇编程序会在符号表中查找该变量的段地址存放地点、偏移地址(段地址存放地点由ASSUME 指定如果没有ASSUME 指定,会报错Can't address with currently ASSUMEd segment registers)。
2、在对标号操作指令中,汇编程序会在符号表中查找该标号的段地址存放地点、偏移地址(段地址存放地点由ASSUME 指定, 如果没有ASSUME 指定,会报错Near jump or call to different CS)。
注意后最几段(39行后)的对变量NO(在CODE中定义)和BUF(在DATA中定义)的寻址,如果没有ASSUME,汇编时就会报错。
30 0009 C6 07 01 mov byte ptr [bx],1;use ds
31 000C C6 07 01 mov byte ptr ds:[bx],1;use ?
32 000F C6 47 01 01 mov byte ptr 1[bx],1;use ds
33 0013 C6 47 01 01 mov byte ptr ds:1[bx],1;use ?
34 0017 36: C6 07 01 mov byte ptr ss:[bx],1;use ?
35 001B C6 46 00 01 mov byte ptr [bp],1;use ss
36 001F 3E: C6 46 00 01 mov byte ptr ds:[bp],1;use ?
37 0024 3E: C7 46 00 0001 mov ds:[bp],1;use ?
*Warning* /y/a.ASM(28) Argument needs type override
38 002A 3E: C6 46 00 01 mov byte ptr ds:[bp],1;use ?
39 002F C6 87 0000r 01 mov buf[bx],1;use ds
40 0034 C6 87 0001r 01 mov buf+1[bx],1;use ds
41 0039 2E: C6 87 0005r 01 mov no[bx],1;use ?
42 003F 3E: C6 86 0000r 01 mov buf[bp],1;use ss
43 0045 2E: C6 86 0005r 01 mov no[bp],1;use ?
44 004B C6 06 0000r 01 mov buf,1
45 0050 2E: C6 06 0005r 01 mov no,1
注:1、使用默认段比使用段跳转速度快一些,因为段跳转会在操作码前增加一个字节
2、使用间接寻址比使用相对寻址要快,因为相对寻址还要经相加得到地址(如NO[BX]),而且其指令较长
EG:mov byte ptr [bx],1(3字节)比mov byte ptr[bp],1(四字节)短一个字节
30 0009 C6 07 01 mov byte ptr [bx],1;use ds
35 001B C6 46 00 01 mov byte ptr [bp],1;use ss
因为mov byte ptr[bp],1在汇编时被认为是MOV TYTE PTR[BP+00],1,即相对寻址方式,而不是间接寻址方式
(六)、输入时单十进制数据是以实际数值ASCII码保存的(高于实际值30H),如果只是用于加减运算、被除、被乘运算,为了方便显示,可以不必再去减30H。
如:输入7个数(0~9),求其平均值,得到的平均值整数部分的ASCII码(高于实际值30H),所以其整数部分可以直接显示,而只有余数是实际数值
(七)、关于类型指示PTR的使用:
双操作数,如双方类型不明确,就应当使用PTR,否则程序就默认为WORD型。.
36 001F 3E: C6 46 00 01 mov byte ptr ds:[bp],1;BYTE
37 0024 3E: C7 46 00 0001 mov ds:[bp],1;WORD
*Warning* /y/a.ASM(28) Argument needs type override,
注:如下双内存操作数,都是不合法的:
MOV [BX],[SI];error:Illegal memory reference非法内存参数
MOV byte ptr [BX],[SI];error:Illegal memory reference非法内存参数
MOV byte ptr [BX], byte ptr [SI];error:Illegal memory reference非法内存参数
(八)、mov [2134],bx在很多汇编软件中会报错:Illegal immediate
该指令在实际编程中是不可能用到的(我们一般都是利用变量名来对内存进行寻址)
因为,汇编时内存的偏移与段基址都是浮动的(偏移地址(r)在连接时才被最终确定下来,段地址(s)在程序运行时才被最终确定下来)
注:如果指令MOV [2134],BX在连接时,有另外一个段要同连接到此指令所处的段相连接,那么偏移2134必须改变,否则程序运行仍然指向2134。如果使用变量名MOV NUM,BX,在连接时就能适应偏移改变的要求(因为汇编程序建立了NUM的符号表,连接时会根据符号表中NUM的位置信息来改变指令中NUM的偏移)。
(九)、标号伪指令
用于标号或者变量的命名:
Bufw label word(无=、equ)
BUFW EQU this word
Bufw = this word
--------上面的三条指令应该放在地址入口前面----
Bufw EQU word ptr bufb
Bufw =word ptr bufb
注:(ptr与this的作用是相同的)
(十)、解决8086中条件跳转不能用NEAR的麻烦:
Relative jump out of range by 0126h bytes
8086中的条件跳转只有short类型;80186的条件跳转增加了near类型。当使用了的条件跳转超出SHORT型的范围就会报此错。(参看《汇编语言程序设计》第117页,朱玉龙 任文岚 朱彤编著 清华大学出版社)
解决方法:
1、如果超过的字节数较少,可以优化代码,减少中间代码的字节数。
2、把部分中间代码移到其它位置定义成函数。
3、把条件跳转改成jmp指令和一条相反的条件跳转指令。例如:
Ll:
; ……
jz Ll
改成:
L1:
; ……
jnz L2
jmp L1 ;jmp是非条件跳转,有SHORT,NEAR,FAR型
L2:
4、几乎所有的跳转指令都成对,所以上面的‘二级跳’做法总行得通。但是极少跳转指令(JCXZ和LOOP)没有相对应的指令,可以采用如下的‘三级跳’
Jcxz skip
Jmp short skip1(提前引用如果不使用SHORT,此跳转就被汇编为NEAR)
Skip:jmp lab
Skip1:
5、可以将3、4语句写成相应的宏指令***M,如JNZ写成能支持远调用的宏指令JNZM,其中在文件头可以加入JUMPS后就完全可以实现这样的宏功能
当在.asm文件头加入JUMPS时,生成的LIST文件为:
75 03 E9 FE57 je start1
L1:
可以看到:TASM自动对上面的指令作了跳转处理,这里的指令码:75 03 E9 FE57表示的是两个指令:75 03指的是jne L1的机器码;E9 FE57是jmp start1的机器码
6、可以在需要使用条件跳转的指令前加入.386,在指令后加入.8086:
0F 84 FE58 je start1
这个已经不是8086的指令了,而是386指令(是一条指令)
注:I、有部分汇编工具如(MASM FOR WINDOWS)带有上述5所述的功能,就不会出现超出跳转范围的错误。
II、上面的例子中,我对机器码的验证是通过DEBUG得出的.
在16BIT DEBUG下
-A
-****:**** DB 75 03 E9
-****:****
-U
在32位 DEBUG下
-E 0100 0F 84 58 FE
-U
(十一)、经常遇到在CMD下输入命令后无反映
cmd没有转入 虚拟8086模式,只要运行一下8086的程序或者按Alt+Enter键进入全屏模式(或者在按Alt+Enter恢复窗口)运行就会正常
(十二)、程序加载到内存时,在程序头部有一个记录,就是记录了程序的入口地址。
还应该记录了关于段基址的传送指令的位置信息,如MOV AX,DATA,而END 语句只是告诉了汇编系统——程序入口在哪里,但是它并不生成相应的机器码。
(十三)、变量不可以提前引用(标号可以被提前引用)
Eg: 230 00F5 2E: A1 00F8r mov ax,yy
**Error** c1.ASM(141) Forward reference needs override
231 00F8 6173 yy dw 'as'
(十四)、以下显示字符的中断会改变AL的值:最终使AL=DL。
MOV AH,2
INT 21H
(十五)、汇编中的数值表示([]表示可以省略)
1、 二进制数:01组合+B,111B,1010B
2、 八进制数:0~8组合+O。
3、 十进制数:0~9组合+[D]
4、 十六进制数:0~9组合(至少一个)+[A~F组合]+H
5、 字符串:”字符”,或者’字符’
注:除了DB可以直接跟任意长度的字符串(DB ‘asdfsdafsdf’),其它情况下初始化内容必须是在DB、DW、DD等等的范围之内(DD ‘ABC’超出了范围,DB ABC也超出了范围)
(十六)、变量的引用,作为变量而言,它是不能被提前引用的,变量可以被汇编系统(TASM)视为标号,此时就可以被提前引用。
变量的引用 |
||
|
提前引用 |
非提前引用 |
在转移指令后作为标号(须有标号类型) |
可行(1.1) JMP 标号类型 变量 |
可行(2.1) JMP 标号类型 变量 |
作为普通变量 |
不行(1.2) |
可行(2.2) |
在内存分配伪指令后作为标号(只用于DW、DD) |
可行(1.3) DD/DW 变量 DD OFFSET 变量 |
可行(2.3) DD/DW 变量 DD OFFSET 变量 |
下面以对字变量VW和字节变量VB的引用为例,作一个说明:
1.1、提前引用:字变量VW与字节变量VB被当作标号来使用(在转移语句中,必须加标号类型)
229 00F5 EB 38 jmp short vw
230 00F7 EB 36 90 jmp near ptr vw
231 00FA EB 33 90 90 90 jmp far ptr vw
232 00FF EB 30 jmp short vb
233 0101 EB 2E 90 jmp near ptr vb
234 0104 EB 2B 90 90 90 jmp far ptr vb
1.2、字变量VW与字节变量VB作为普通变量,不能出现在提前引用
237 0109 2E: FF 26 012Fr jmp vw
**Error** c1.ASM(148) Forward reference needs override
238 010C 2E: FF 26 012Fr jmp word ptr vw
**Error** c1.ASM(149) Forward reference needs override
239 0110 2E: FF 2E 012Fr jmp dword ptr vw
**Error** c1.ASM(150) Forward reference needs override
240 0114 2E: FF 26 0131r jmp vb
**Error** c1.ASM(151) Argument to operation or instruction has illegal size
241 0117 2E: FF 26 0131r jmp word ptr vb
**Error** c1.ASM(152) Forward reference needs override
242 011B 2E: FF 2E 0131r jmp dword ptr vb
**Error** c1.ASM(153) Forward reference needs override
1.3、变量在内存分配伪指令后被作为标号来使用(如果是在DW后取偏移,在DD后取偏移加段地址,在DB后会报错),以下是提前引用
244 011F 012Fr dw offset vw
245 0121 0131r dw vb
246 0123 012Fr dw vw
247 0125 0000012Fsr dd vw
248 0129 00000131sr dd vb
249 012D 00 db vb
**Error** c1.ASM(160) Expecting scalar type
250 012E 00 db vw
**Error** c1.ASM(161) Expecting scalar type
-----------------------------------------------------------------------------------
252 012F 0061 VW DW 'a'
253 0131 BC VB DB 0BCH
-----------------------------------------------------------------------------------------
2.1、变量在转移指令后作为标号使用(须有标号类型)(非提前引用):
255 0132 EB FB jmp short vw
256 0134 EB F9 jmp near ptr vw
257 0136 EB F7 jmp far ptr vw
258 0138 EB F7 jmp short vb
259 013A EB F5 jmp near ptr vb
260 013C EB F3 jmp far ptr vb
-----------------------------------------------------------------------------------
2.2、变量被作为普通变量而言(用于间接转移,或者MOV等数值传递等语句),只能先定义后使用(即非提前引用)
262 013E 2E: FF 26 012Fr jmp vw
263 0143 2E: FF 26 012Fr jmp word ptr vw
264 0148 2E: FF 2E 012Fr jmp dword ptr vw
265 014D 2E: FF 26 0131r jmp vb
**Error** c1.ASM(176) Argument to operation or instruction has illegal size
266 0152 2E: FF 26 0131r jmp word ptr vb
267 0157 2E: FF 2E 0131r jmp dword ptr vb
-------------------------------------------------------------------------
2.3、变量在内存分配伪指令后被作为标号来使用(如果是在DW后取偏移,在DD后取偏移加段地址,在DB后会报错),以下是非提前引用
269 015C 012Fr dw offset vw
270 015E 0131r dw vb
271 0160 012Fr dw vw
272 0162 0000012Fsr dd vw
273 0166 00000131sr dd vb
274 016A 00 db vb
**Error** c1.ASM(185) Expecting scalar type
275 016B 00 db vw
**Error** c1.ASM(186) Expecting scalar type
一般而言,指令长度越短,速度越快(减少指令预读时间);而用寄存器对寄存器初始化,相对用立即数对寄存器初始化而言,指令长度更短,程序预读取指令也越快;还有指令越复杂,速度也越慢。
Eg: XOR BX较MOV BX,0更快;后者是用立即数对BX置0,指令长度较长
OR AX,30H较ADD AX,30H更快,虽然指令一样长,但是后者是加法运算,更为复杂一些,时间也长一些(将这两个语句放在一个比较大的循环语句中,前者的运行时间是7s,后者的运行时间是21s)。
另外,用移位的方式来实现乘法较用MUL指令实现乘法,速度也会快一些。