汇编基础总结(一)

来源:互联网 发布:linux删除文件前缀 编辑:程序博客网 时间:2024/05/16 14:45

汇编学了也有一个多月了,今天把学过的基础知识点先总结一下先。留待日后忘了查阅。

 

--------------------------------------------------------------------------------------------

 

-----------------1,2,3-----------------------------
段寄存器存放 基地址

AX  通用寄存器

CS 存放要被cpu执行的代码的基地址 code segment
IP 别名为指令指针寄存器,存放段地址的偏移地址
CS*16+IP  就是cpu要执行的指令


debug是dos、windows都提供的实模式程序调试工具,可以查看cpu各种寄存器中的内容和机器码级跟踪程序的运行

r命令用来查看和改变各个寄存器内容,
d命令查看内存中的内容,
u命令将内存机器码转为汇编指令,
a命令以汇编指令格式在内存写入指令
t命令但不跟踪


数据段:全局变量
代码段:代码
堆栈段:局部变量

CPU根据DS(Data Segment)这个寄存器和任意一个通用寄存器的值或其他数值组成数据段的物理地址如:
DS:[0] DS:[BX]  (内存寻址)  (内存访问)

mov ds:[13ABH],1234H         内存地址的内容进行赋值  内存地址=1234
mov [13ABH],1234H         CPU默认指向ds


---------------------4-----------------------------

CPU如何知道一段内存空间被当作栈使用?执行入栈出栈时,如何知道哪个单元式栈顶单元?
cpu通过ss这个寄存器和sp通用寄存器来感知堆栈段的存在。
ss存放基地址,sp存放栈顶的偏移地址,任何时候ss:sp都指向栈顶元素

---------------------5,6-----------------------------

汇编语言中变量如何定义
如何屏幕显示
如何进行调试

assume关键字
如何让汇编语言“知道”,我们编写的应用程序有多少个段组成。
assume表示用来假设某一段寄存器和程序中的某一个用segment...ends定义的段相关联

db指令
define byte
label db initializer,initializer,initializer
label 表示任选标号,相当于c语言变量名
msg db "hello world"

vga B800F
 字体属性格式
7  6 5 4 3 2 1 0
BL R G B I R G B

闪烁 背景色 高亮 前景色
红底绿字: 01000010B

vga显存地址空间
在80*25列彩色模式下显示器可以显示25行80列
每个字符可以有256种属性(背景色前景色闪烁等)
一个字符在显存中占两个字节,分别存放ascii码值和属性
显示缓冲总共分为8页,每页4kb,显卡可以显示任意页内容一般情况下显示第0页内容即B8000H~B8F9FH

es,扩展段寄存器

不能直接给段寄存器赋值,应该先给通用寄存器赋值,然后再传给段寄存器

loop关键字
语法:
标号:  指令
        指令2
       look 标号
cx内部规定为loop的循环因子

内存访问
helloworld属于数据段内容,那么其中每个字符都可以通过数据段地址存在ds寄存器中,那么要获得数据段第一个字节内容就要如下表示
ds:[0] ds:[si]
si寄存器相当于通用寄存器

代码段地址自动获取 偏移地址不知道


;;;;;;;;;;;;;;;;;;;;;hello.asm;;;;;;;;;;;;;;;;;;;;;;;;;;;;

assume cs:code,ds:data

data segment
 db "hello world" 
data ends

code segment
 start:
 mov ax,data
 mov ds,ax
 mov bx,0b800h
 mov es,bx
 mov cx,11 
 mov si,0
 mov bx,0
 mov ah,01000010b
  s:mov al,ds:[si]
    mov es:[bx],ax
    mov es:[bx+1],ah
    inc si
    add bx,2
    loop s
   
    mov ax,4c00h
    int 21h
   
code ends
end start

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


-------------------7-----------------------------

什么是中断?
任何一个通用CPU,都具有一种能力,可以在执行完成当前正在执行的指令之后,检测到从CPU外部或内部产生的一种特殊信息,
并立即对接受的信息进程处理。这种信息称之为中断信息。
中断的意思是指,CPU不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。

关于中断的疑问
中断发生时CPU如何找到中断处理程序?
中断处理程序有很多种,那么每个中断处理程序放在哪里?
中断处理程序完成后CPU如何继续运行之前被中断的程序?

中断向量表
在内存中保存,其中存放着256个中断源所对应的中断处理程序入口
中断向量表一般都保存在内存0000:0000到0000:03FE

一个表项存放一个中断向量,也就是一个中断程序入口地址,这个地址包括段地址和偏移地址,每个表占两个字(4个字节),高地址存放段地址,低地址存放偏移地址。

遇到中断时CPU把cs和ip内存入栈暂时保存起来。等中断程序执行结束后通过出栈指令重新获得原来的cs和ip的值(这就是c语言函数调用具体实现)
中断过程这些操作,cpu自动完成

div指令
除法指令:div 寄存器
除数:有8位和16位两种,在一个寄存器或内存单元中。
被除数:默认放在ax或dx中,如果除数为8位,被除数位16位默认放在ax中。如果除数为16位被除数为32位分别放在ax和dx中,dx存放高位,ax存放地位。
结果:如果除数位8位,则al存储除法的商,ah存储除法的余数,如果除数是16位,则ax存储商,dx存储余数。

修改中断表:
如何让CPU不去执行原来的中断处理程序,而去执行我们自己编写的处理程序?
修改中断向量表的入口地址就可以实现。


-------------------8----------------------
如何修改中断向量表?
系统默认在0000:0000到0000:03FE专门存放中断向量表。并且每个表占用两个字。
那么我们就知道0号中断表项所在的内存地址是0000:0000开始的4个字节中。汇编代码就是要对这4个字节赋予我们自己编写的中断处理程序入口地址。
汇编伪代码如下:
   mov ds:[0],我们自己中断处理程序偏移地址
   mov ds:[2],我们自己中断处理程序段地址

中断随时都可以产生,那么当中断产生时必须马上执行中断处理程序,那么中断处理程序必须放在内存何处?
要保证任何时候中断处理程序存放位置不能被其他程序覆盖。
因此我们必须在内存中找出一段空间是任何程序不适用的。
在正常情况下内存地址0000:0200到0000:0300这段内存是没有其他程序使用的。

中断处理程序内存分布
当中断被触发时程序将被执行,但是程序最开始是数据定义指令,而不是代码执行指令,如何解决?
我们希望一开始执行中断处理程序时马上跳转到显示字符串的汇编代码中执行,这时我们就需要使用汇编指令:jump

跳转指令jump分为三种:
段间跳转 jump far 标号                      把cs和ip寄存器的值变为标号所在的内存地址
段内跳转指令 jump near 标号                 只修改ip寄存器值为标号的偏移地址
段内短跳转指令 jump short 标号              不修改cs和ip的值,编译器自动计算跳转的位置,不超过256


-------------------9--------------------------
如何把一段汇编代码拷贝到指定的内存位置?
可以用loop指令语句来实现,但是该语句比较繁琐,不太合适。
汇编语言提供rep和movsb指令实现相同的功能。
movsb指令:
字节传送指令:指令在存储单元之间传送字符串
使用movsb指令时ds:si指向了要拷贝字符串的首地址,es:di指向了要拷贝的目的地址。
cld指令拷贝数据的方向是从低字节往高字节拷贝,也就是说每拷贝一个字节si和di加1。
std指令和cld相反。

;;;;;;;;;;;;;;;;;;;;;;;;;;ins.asm;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
assume cs:code

code segment
 ;第一步:把中断向量表中0号表项内容进行修改,使之指向我们自己编写的中断处理程序的入口地址
   start:
 mov ax,0
 mov ds,ax
 mov word ptr ds:[0],0200h
 mov word ptr ds:[2],0
 ;第三步:把我们刚才编写好的0号中断处理程序拷贝到中断向量表中0号表项所指向的内存地址中
 ;0000:0200
 mov ax,cs
 mov ds,ax
 mov si,offset int0;ds:si 可得拷贝源地址
 mov ax,0
 mov es,ax
 mov di,200h;目的地址设置完毕es:di
 mov cx,offset int0end - offset int0;计算出程序总共占多少内存
 cld
 rep movsb;自动利用es:di,ds:si,cx
 ;第四步:利用代码自动引发0号中断处理程序
 mov ax,1000h
 mov bh,1
 div bh
 
 mov ax,4c00h
 int 21h
  
 
 ;第二步:编写自己的中断处理程序,实现在屏幕中央显示字符串的功能
  int0:jmp short int0start
     db "i am student"
 int0start:mov ax,0b800h
 mov es,ax  ;配置显存首地址
 ;要把字符串一个个拷贝到显存地址空间中
 mov ax,cs
 mov ds,ax
 
 mov si,202h
 mov di,12*160+36*2
 mov cx,12
  s:mov al,ds:[si]
 mov es:[di],al
 inc si
 add di,2
 loop s
 
 mov ax,4c00h
 int 21h
  int0end:nop
 
code ends
end start

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

-------------------10--------------------------
主要内容
开发环境搭建
认识引导程序
nasm汇编
编写第一个启动程序

Visual PC2004 用来启动我们编写的启动程序
VMware运行linux,利用nasm汇编器在linux上进行启动程序的编译工作

知识预备
理解计算机加电过程
BIOS对系统内存的分配
认识引导程序的概念
使用nasm进行编译
制作软盘镜像

当我们按下电源按钮后,计算机是如何从无到有将操作系统运行起来的?
按下开机按钮后,将发送电信号给BIOS。
BIOS获得电信号后将启动自检查程序,检查周边设备是否通电完毕。
检查完毕后自检程序把控制权交还给BIOS,BIOS将读取引导驱动器中的启动程序。

在系统加电时,最初的1MB内存是BIOS为我们准备好的,如下:
0x00000~0x003FF  中断向量表
0x00400~0x004FF  BIOS数据区
0x00500~0x07BFF  自由内存区
0x07C00~0x07DFF  引导程序加载区   正好512字节
ox07E00~0x9FFFF  自由内存区
0xA0000~0xBFFFF  显示内存区
0xC0000~0xFFFFF  中断处理程序

认识引导程序
什么样程序才能称为引导程序?
BIOS将所检查启动磁盘的第一个扇区512字节载入内存,放于内存0x0000:0x07c00处。
如果第一扇区最后两个字节是55AA,那么它就是一个引导程序。

引导程序特点
大小是512字节,不能多也不能少,因为BIOS只读取512B到内存中。
它结尾必须是55AA,这是引导扇区标志。
它总是放在磁盘第一个扇区上(0磁头0此道1扇区)因为BIOS只读取第一个扇区。

NASM汇编
是一个为可移植性与模块化而设计的一个80*86的汇编器。它支持相当多的目标文件格式包括linux和windows

引导程序编写
如何使用nasm编写一个引导程序?
什么是nasm,它和masm有什么区别?
如何在linux下安装nasm?
如何用nasm编译自己编写的汇编代码?

nasm和masm区别
nasm拥有一个相当简单的内存引用规则,是任何对内存中内容的存取操作必须要在地址上加方括号。但任何地址值的操作不需要。
比如mov ax,bar的指令表示把bar的地址赋给ax寄存器,这相当于masm中 mov ax,offset bar。
要获得bar变量的值则:mov ax,[bar]。
masm mov ax,es:di      nasm   mov ax,[es:di]

linux 下安装nasm,下载nasm的rpm包,安装 rpm -ivh nasm***.rpm
使用:nasm hello.asm -o hello ,反汇编:ndisasm hello


-------------------11-----------------------------

BIOS中断程序
系统BIOS为我们提供了众多的中断处理程序给我们调用,其中中断编号为10h的中断处理程序专门实现显示功能。
特别注意:编号为10h的中断并不只提供一个程序,而是提供了很多子程序供我们调用。

$和$$关键字
在nasm中$表示当前指令的偏移地址
在nasm中$$表示指令所在的开始地址
因此我们就可以推算出剩余字节数公式
剩余字节数=510-($-$$)

;;;;;;;;;;;;boot.asm;;;;;;;;;;;;;;;;

;我们的启动程序实现很简单的功能,在屏幕中央打印一行字符串即可

org 07c00h ;org指令明确告诉编译器我程序的段地址是7C00h,而不是原来的0000
;int汇编指令 “int 10h”调用bois里的中断程序:显示字符串

 mov ax,cs
 mov es,ax
 mov bp,msgstr  ;es:bp 指向的内容就是我们要显示的字符串地址了
 
 mov cx,12   ;显示的字符串长度
 mov dh,12   ;显示的行号
 mov dl,36   ;显示的列号
 mov bh,0    ;显示的页数
 mov al,1    ;显示的是串结构
 mov bl,0ch  ;显示的字符属性
 mov ah,13h  ;明确调用13h子程序
 msgstr: db "hello my os!"
 int 10h
 times 510-($-$$) db 0  ;重复n次每次填充值为0
 dw 55aah
 jmp $   ;不断跳转到当前位置,是个死循环

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

原创粉丝点击