fdsiuha
高级用户
闷
积分 587
发帖 302
注册 2005-7-25
状态 离线
|
『楼 主』:
A introduction to x86 assembly(二)
以下是A introduction to x86 assembly的第二部分
--------------------------------------------------------------------------------------------------------
语法:
SUB 操作数1,操作数2
从操作数1中减去操作数2. 当然操作数1不能是立即数,但是操作数2可以是。
MUL 两个无符号的整数相乘 (总是正的)
IMUL 两个有符号的整数相乘 (可正可负)
语法:
MUL 寄存器或变量
IMUL 寄存器或变量
与寄存器AL或AX中的数字相乘,至于选择哪个,是由操作数的大小决定的。
计算结果放入AX。如果计算结果大于 16 位的话,计算结果将放入DX:AX中
(高16位在DX中,低16位在AX中)。
在386、486或Pentium中可以使用EAX寄存器,其计算结果也将放入到EDX:EAX中。
DIV 两个无符号的整数相除(总是正的)
IDIV 两个无符号的整数相除(可正可负)
语法:
DIV 寄存器或变量
IDIV 寄存器或变量
其工作方式与MUL和IMUL是一样的。AX中的数被给定的寄存器或变量除。
计算结果储存分在两个部分中。AL中的是商而余数放在AH里。如操作数是
16位的寄存器,那么被除的数放在DX:AX中的数,计算结果储存在AX中,余数在DX中。
至于存取我们所要想显示的信息的地址则有点麻烦。这程序将占据3行,并且不是那么好记住。
mov dx,OFFSET MyMessage
mov ax,SEG MyMessage
mov ds,ax
也可以用下面仅仅一行程序来取代它。而且这样会使你的程序易读易记。
但是注意,只有要读取数据在同一个段里的时候它才能正常工作。比如说小内存模式。
lea dx,MyMessage
或 mov dx,OFFSET MyMessage
使用lea不仅会执行慢些,而且增加编译后代码的长度。
LEA 的意思是读有效地址(Load Effective Address).
语法:
LEA destination(目的),source(源)
Desination 可以为16位的寄存器,内存操作数(内存中的数据位,译注:内存地址所对应的标号)。
这将把源数据的地址偏移量送到目的寄存器中。
键盘输入:
我们将使用16h号中断,用其00h号功能来读键盘操作。它将从键盘缓存中取出一个键。
如果键盘缓存是空的, 它将一直等待到有一个键被送到这里。它会返回键的扫描码(SCAN
code)到AH中,并翻译出该键的ASCII码到AL中。
MOV ah,00h ;00h号功能
INT 16h ;16h中断
好了,我们接下来所考虑的问题就是ah现在里面的ASCII码是什么。
打印(译注:显示到屏幕)一个字符:
现在问题的关键是,我们已经把一个键放在ah中了。我们如何来显示它?我们不能够使用
9h功能,因为这样的话,我们把它转化为一个以美圆符结尾的字符串。我们现在这样做:
;在调用16h中断的00h功能后
MOV dl,al ;将al内容(ascii code)放入dl中
MOV ah,02h ;21h中断的02h号功能
INT 21h ;调用21h中断
如果你想保存一个值的话,你可以先使用PUSH,以后再用POP弹出来。
流程控制:
首先,下面是最基本的命令:
JMP label
与basic语言里面的GOTO语句很像吧?
JMP ALabel
.
.
ALabel:
;你要编写的代码
如果我们想比较两个数的大小该怎么办呢?我们从用户中得到了一个键,我们将对它
进行处理。比如当它与某个值相等,那就显示另外一个信息。我们怎样达到这个目的呢?
其实非常简单。我们使用条件跳转指令,以下是列表:
条件跳转指令:
JA
如果第一个值大于第二个值,就跳转
JAE
与上面的那条相同,只是当两值相同时,也要跳转
JB
如果第一个值小于第二个值,就跳转
JBE
与上面的那条相同,只是当两值相同时,也要跳转
JNA
第一个值不大于第二个值则跳转 (同JBE)
JNAE
第一个值不大于等于第二个值则跳转 (同JB)
JNB
第一个值不小于第二个值则跳转 (同JAE)
JNBE
第一个值不小于等于第二个值则跳转 (same as JA)
JZ
两数相同就跳转
JE
与JZ相同,只是叫法不一样罢了
JNZ
两数不同就跳转
JNE
同上
[注意: 其实有更多的跳转指令,只不过这是最常用的. 如果你想得到完整的列表就去弄本
好一点的汇编语言教材]
它们很容易使用。
语法:
CMP 包含一个数的寄存器,一个值
跳转指令 目的地址
这是一个例子:
cmp al,'Y' ;把al中的值与Y相比较
je ItsYES ;如果相等就跳转到ItsYES
下面的程序是如何使用控制命令和输入输出命令的例子.
.MODEL SMALL
.STACK ;定义一个栈
.CODE
Start: ;就在这里开始吧
lea dx,StartUpMessage ;显示一个信息
mov ah,9 ;使用09h号功能
int 21h ;21h中断
lea dx,Instructions ;显示一个信息
mov ah,9 ;使用09h号功能
int 21h ;21h中断
mov ah,00h ;00h号功能
int 16h ;16h中断将从用户那里得到一个字符
mov bl,al ;保存bl
mov dl,al ;将按键的ASCII码放入到dl中
mov ah,02h ;02h号功能
int 21h ;21h中断显示一个字符到屏幕
cmp bl,'Y' ;al=Y?
je Thanks ;确实相等就跳到Thanks继续执行
cmp bl,'y' ;al=y?
je Thanks ;确实相等就跳到Thanks继续执行
jmp TheEnd
Thanks:
lea dx,ThanksMsg ;显示信息
mov ah,9 ;使用9号功能
int 21h ;21h中断
TheEnd:
lea dx,GoodBye ;显示一个结束信息
mov ah,9 ;使用9号功能
int 21h ;21h中断
mov AX,4C00h ;结束程序并返回DOS
INT 21h ;21h号中断的4CH功能
.DATA
;0Dh,0Ah 是在字符串开始的地方加入的一个入口信息
StartUpMessage DB "A Simple Input Program$"
Instructions DB 0Dh,0Ah,"Just press a Y to continue...$?
ThanksMsg DB 0Dh,0Ah,"Thanks for pressing Y!$"
GoodBye DB 0Dh,0Ah,"Have a nice day!$"
END
过程:
汇编语言、C和Pascal都可以使用过程。对于一系列需要重复执行的指令来说是非常重要的。
以下是过程的定义方法:
PROC AProcedure
.
. ;完成一些工作的代码
.
RET ;这东西不出现在过程的结尾的话,就会当机
ENDP AProcedure
你可以指定如何来调用一个过程,只需要在过程名字后面加上一个FAR或者一个NEAR就行了
否则它将使用你正在使用的内存模式。目前在你不是很有经验的情况下还是不要随便这样做。
我经常使用NEAR,因为编译器不能够区别近的调用和远的调用。这里的意思是说,如果是
远的跳转的话,那么编译器将会发出警告,这样的话你就可以替换掉它。
这是一个使用过程的程序。
;一个简单的使用过程的程序,在屏幕上显示一条信息
;(使用tlink的/t选项)。它会在屏幕上显示Hello There!。
.MODEL TINY
.CODE
ORG 100h
MAIN PROC
JMP Start ;跳过字符串定义
HI DB "Hello There!$" ;define a message
Start: ;在这里开始
Call Display_Hi ;调用过程
MOV AX,4C00h ;终止程序并返回DOS
INT 21h ;21h号中断的4CH功能
Display_Hi PROC ;定义一个过程的开始
lea dx,HI ;把信息的地址放入到DX里
mov ah,9 ;9号功能
int 21h ;21h中断
RET ;不要忘了这个东东!
Display_Hi ENDP ;定义过程的结尾
Main ENDP
END MAIN
[ Last edited by fdsiuha on 2005-10-29 at 21:38 ]
|
欢迎造访DOS的小屋!
http://risky.ik8.com |
|