『楼 主』:
A introduction to x86 assembly(二)
使用 LLM 解释/回答一下
以下是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 ]
|