fdsiuha
高级用户
闷
积分 587
发帖 302
注册 2005-7-25
状态 离线
|
『楼 主』:
A introduction to x86 assembly(一)
闲来无事,看到一篇英文ASM教学,非常的好,值得一读,所以翻译在下面,希望我没有在重复别人的工作,总不能第一次发帖就CRUSH把^^
以下是第一部分,以后的再慢慢加上,另外我手上没ASM教材,翻译出错误请多指正,需要原文的我可以发给你们。
--------------------------------------------------------------------------------------------------------
Written by Gavin Estey email: gavin@senator.demon.co.uk
This document is version: 0.9 Beta 2/19/95
这希望是该书的最后一个版本,如果没有人找出错误的话。我化了620分钟创建的这个文档(不包括写和测试代码或先前的文档),所以我希望你能够觉得它对你有帮助。
这个版本仍然是测试版,意味着我不能保证所有的代码将会编译和工作正常。我尽了最大的努力来确保在这篇文档里所包含的所有的信息都是正确的。如果发现有任何错误的话请给我发EMAIL。
如果有人有不带VGA显示卡/显示器的电脑,请编译P29上的代码,它将检测这个计算机所能够处理的图形,如果它返回的DX值为0的话请告诉我。
这个文档由 Gavin Estey 所编写。没有我的书面许可不允许以任何商业产品形式改变和出售(给我发EMAIL获得更多的信息)。我不对这篇文档所造成的任何损失(译注:我也是^_^)负责。
如果你喜欢这篇文档...(译注:此段要求捐献广告略...呵呵)
首先你需要一个合适的汇编程序来编译你的程序。所有的例子都在两个编译器上测试过的:A86和Turbo Assembler ver 4。A86是一个非常好的共享软件,能够生产出80286的代码。这个软件能够在你的本地simtel镜像中找到(ftp.demon.co.uk 或者 ftp.cdrom.com) 在simtel/msdos/asmutil目录下一个叫做A86V322.ZIP的文件。
首先我将要讲一下SEGMENT和OFFSET。这可能是汇编语言中最难懂的地方。
段和偏移:
8088最初的设计者觉得没有人会使用超过1MB的内存。所以他们的设计芯片不能存取超过这么多的内存。这个问题的出现是由于存取整个1MB内存需要20位(每位由0和1组成)。寄存器只能够有16位,并且他们不想使用一对,因为这将达到32位,他们觉得对所有人来说这太多了。所以他们决定用两个寄存器来寻址,而不是32位,觉得困惑吗,责备8088的设计者吧。
OFFSET = SEGMENT * 16
SEGMENT = OFFSET / 16 (低4位自动丢失了)
SEGMENT * 16 |0010010000010000 - ---| 范围 (0 to 65535) * 16
OFFSET |-- - -0100100000100010| 范围 (0 to 65535)
20位地址 |00101000100100100010| 范围 0 to 1048575 (1 MEG)
===== DS =====
====== SI =====
(注意DS和SI是重叠的)。这就是DS:SI如何组成一个20位的地址。段是在DS中,而偏移量在SI中。
段寄存器有:BX, DI, SI, BP, SP, IP。在386+的保护模式下,任何一个一般的(非段寄存器)都能够被用做偏移量寄存器。(除了IP,你不能使用它)
寄存器:
AX, BX, CX 和 DX 是一般用途的寄存器。在一个386以上,它们是32位的; EAX,
EBX, ECX 和 EDX。它们能够被分成高低两部分,每个部分8位。
EAX 32 bits
AX 16 bits
AH 8 bits
AL 8 bits
这意味这你能使用AH和AL来储存不同的数字,并将它们作为不同的寄存器使用。
BX (BH/BL): 与 AX 一样
CX (CH/CL): 与 AX 一样(用于循环)
DX (DH/DL): 与 AX 一样(用于乘除法)
DI 和 SI 是索引寄存器,并能够被用做偏移寄存器。
SP: 。栈指针。只用做该目的。指向栈中的当前位置。除非你已经知道你在做什么,请不要改变它,否则会当机。
栈:
这是一内存中的一小块区域,就像一堆盘子。最后放上去的一个也将最先拿出来(LOFO,后入先出)。如果另一个数据放到了栈上,(之前最上面的数据)它将向下一格。
表 1:指出了一个栈是怎样组织的
PUSH 和 POP:
Push 将一个数值放入栈中 pop 将他取回。这里有一些代码. (现在你还不能将其编译)
push cx ;cx入栈
push ax ;ax入栈
.
.
. ;将要把他们弹出栈
. ;但是下面的操作顺序反了
pop cx ;从栈中取出一个值给cx
pop ax ;从栈中取出一个值给ax
这将导致cx与ax原值一致,而ax的内容则变成cx最初值.
一些指令:
MOV: 将数据从一个地方传送到另一个地方.
语法:
MOV 目的, 数据源
例子:
MOV ax,10 ;将一个立即数放入ax
MOV bx,cx ;将cx中的值传送到bx
INT:调用一个DOS和BIOS功能,主要是做一些你不用操心去写的功能,例如改变显示模式,打开一个文件等等。
语法:
INT 中断号
注意: 在一个数字后有个h意味着这个数字是16进制的。
INT 21h ;调用DOS标准的21h号中断
INT 10h ;调用显卡BIOS中断
所有的中断都需要一个指定的值来说明到底要使用那个子例程。这通常由AH中的值指定。
为了在屏幕上打印一段话,所有你要做的只是:
MOV ah,9 ;9号子功能
INT 21h ;调用中断
但是首先你应该指定要输出那些内容。这个功能需要用到DSX作为一个远
指针来指向字符串的位置。这个字符串必须以美圆符结束($).
这个例子向你展示它是怎样工作的:
MyMessage db "This is a message!$"
.
.
.
mov dx,OFFSET MyMessage
mov ax,SEG MyMessage
mov ds,ax
mov ah,9
int 21h
DB 声明的是一段数据。这与BASIC中是一样的:
LET MyMessage$ = "This is a message!"
DB 声明的是字节, DW 声明的是字而 DD 声明的是双字.
这是你第一个汇编程序。去掉这些说明然后编译它。如果你使用
A86,输入:
A86 文件名
如果你正在使用Turbo Assembler请输入:
tasm 文件名
tlink 不要扩展名的文件名
使用tlink的参数/t你可以生成一个.COM文件。
;这是一个简单的程序,能够在屏幕上显示一段信息。
;会在屏幕上显示This is a message!并且无错误值返回
;被TLINK做成一个.COM文件后回出错。
.MODEL SMALL ;忽略前面这两行
;之后我会解释
.STACK ;分配一个栈
.CODE ;直接开始代码段
START: ;一般来说这是标识入口的好名字
;
JMP BEGIN ;跳转到BEGIN执行: (就像goto一样)
MyMessage db "This is a message!$"
BEGIN: ;代码从这里开始
mov dx,OFFSET MyMessage ;将输出信息的偏移地址
;放到DX里
mov ax,SEG MyMessage ;将输出信息的段地址
;放到AX里
mov ds,ax ;将AX拷贝到DS里
mov ah,9 ;将数字9送到ah里以调用
int 21h ;21h中断的9号子功能
;显示一段消息到
;屏幕
MOV ax,4c00h ;4C00的16进制数据送到AX里
INT 21h ;21h中断, 4C子功能
;把控制权返回给DOS,否则就有崩溃的麻烦
;末尾的00是返回给DOS的errorlevel
;对于批处理文件来说很有用(译注:大部分DOS外部命令都有errorlevel用以后续检查)
END START ;终止
这里有一些指令你必须掌握:
下面是一个基本汇编指令列表,他们非常的重要,使用非常频繁。
ADD 把一个数的内容与另外一个相加
语法:
ADD 操作数1,操作数2
这将把操作数2与操作数1相加.计算结果储存在操作数1里。
操作数1不能是立即数(译注:就是数字拉),只有操作数2可以是立即数
SUB 从一个数中减取去一个数
[ Last edited by fdsiuha on 2005-11-3 at 18:12 ]
|
欢迎造访DOS的小屋!
http://risky.ik8.com |
|