x86汇编语言学习笔记
以李忠老师的《x86汇编语言:从实模式到保护模式》为学习教材!
第一章 十六进制计数法
学习二进制,十进制,十六进制,以及他们之间的相互转换(太简单了,省略不记)
第二章 处理器,内存和指令
- Intel处理器用的是小端格式 0000H(5D)0001H(00) —> 0x 005D
- 一个处理器能识别的指令的集合,称之为该处理器的指令集
一般指令组成:操作码+操作数(有些没有操作数)
- 比如:B8005D,B8是一个字节的操作码(有些是两个字节),005D是操作数
- 可以立即在指令里面取得的操作数称之为立即数
- 因为操作码和操作数都是2进制数,为了区分,所以要分开存放在代码区和数据区
Intel8086处理器:
- 有8个16bit的通用寄存器:
AX
,BX
,CX
,DX
,SI
,DI
,BP
,SP
- 前四个又可以分成8个8bit的寄存器:
AH
,AL
,BH
,BL
…
处理器是自动化的,给出一个首地址,就会自动执行下去,因此一个工作他的所有代码应该集中起来,处于内存的某一段位置,称之为代码段.
数据同理,有数据段.
- 因为程序在内存中的位置是随机的,因此我们不能使用绝对内存地址.
- 所以处理器使用了内存分段机制:通过
段:偏移地址
的方式找到真实地址 - 在开始前,CS(代码段寄存器)和DS(数据段寄存器)指向程序代码段和数据段的开始位置,然后处理器将指令里面的地址都视作偏移地址.
8086的内存分段机制
CS指向代码段起始位置,IP指向偏移地址,共同组成逻辑地址,由总线接口转成物理地址
还有6字节的指令预取队列
有四个段寄存器(CS,DS,ES附加段寄存器,SS栈寄存器)
8086的总线有20根,而寄存器却是16位的,最经典的情况,段:偏移地址
中将段左移4bit
第三章 汇编语言和汇编软件
略
其实就是编译器+编辑器+16进制文件查看器
第四章 虚拟机的安装和使用
VirtualBox…
前四章,预备知识到这里就结束了,因为太容易所以写的很简略
第5章 编写主引导扇区代码
第一个扇区叫主引导扇区,有512字节,开机后被加载到内存地址0x07c00处,然后判断是否有效(扇区最后两个字节为0x55,0xAA,则有效),然后跳到内存0x07c00处开始执行.
显卡和显存
- 有两种显示模式:图形模式,文本模式
- 显卡在加电自检后初始化到80*25的文本模式
8086有1M的地址数,00000H~FFFFFH
- 00000H~9FFFFH:给常规内存(内存条)
- A0000HBFFFF固定给显卡)
- F0000H~FFFFFH:给ROM-BIOS
传送指令MOV
mov 目的操作数,源操作数
- mov只改变目的操作数的值,不改变源操作数的值
- Intel不允许直接将立即数传送到段寄存器,于是只能
- mov 段寄存器,通用寄存器
- mov 段寄存器,内存单元
mov byte [es:0x00],'L'
- byte/word:16bit字节/8bit字,修饰目的操作数,如果目的操作数明确位数,则无需修饰符
- []:取内存地址的值
- es:代表使用ES段寄存器,而不是默认的DS
- ‘’,使用字符需要用’’
每条指令都可以有标号
flag:
mov byte [es:0x00],'L'
和C语言的Goto的使用方式一样
显示十进制数
- 需要通过除余操作来获取各位的数
- 加上0x30为ASCII上对应数字的值
声明数据命令
- 声明字节命令db
- 声明字命令dw
- 声明四字命令dq
除法命令div(两种使用方法)
16位/8位
- 被除数必须在AX中,执行后,商在AL中,余数在AH中
32位/16位
- 被除数高位在DX中,低位在AX中,执行后,商在AX中,余数在DX中
位异或命令xor
还有跳转命令jmp
全部代码:
;代码清单5-1
;文件名:c05_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-3-31 21:15
mov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,ax ;因为立即数不能字节传入段寄存器
;以下显示字符串"Label offset:"
mov byte [es:0x00],'L'
mov byte [es:0x01],0x07
mov byte [es:0x02],'a'
mov byte [es:0x03],0x07
mov byte [es:0x04],'b'
mov byte [es:0x05],0x07
mov byte [es:0x06],'e'
mov byte [es:0x07],0x07
mov byte [es:0x08],'l'
mov byte [es:0x09],0x07
mov byte [es:0x0a],' '
mov byte [es:0x0b],0x07
mov byte [es:0x0c],"o"
mov byte [es:0x0d],0x07
mov byte [es:0x0e],'f'
mov byte [es:0x0f],0x07
mov byte [es:0x10],'f'
mov byte [es:0x11],0x07
mov byte [es:0x12],'s'
mov byte [es:0x13],0x07
mov byte [es:0x14],'e'
mov byte [es:0x15],0x07
mov byte [es:0x16],'t'
mov byte [es:0x17],0x07
mov byte [es:0x18],':'
mov byte [es:0x19],0x07
;这前面的代码显示"Label offset:"
;下面的代码是以十进制显示number的偏移地址
mov ax,number ;取得标号number的偏移地址
mov bx,10 ;用来进行运算,取各位的数
;设置数据段的基地址
mov cx,cs ;将代码段的地址传入数据段
mov ds,cx
;求个位上的数字
mov dx,0 ;dx=0,表示是32位/16位的情况
div bx ;标号number的偏移地址除以10
mov [0x7c00+number+0x00],dl ;保存个位上的数字
;求十位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x01],dl ;保存十位上的数字
;求百位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x02],dl ;保存百位上的数字
;求千位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x03],dl ;保存千位上的数字
;求万位上的数字
xor dx,dx
div bx
mov [0x7c00+number+0x04],dl ;保存万位上的数字
;以下用十进制显示标号的偏移地址
mov al,[0x7c00+number+0x04]
add al,0x30
mov [es:0x1a],al
mov byte [es:0x1b],0x04
mov al,[0x7c00+number+0x03]
add al,0x30
mov [es:0x1c],al
mov byte [es:0x1d],0x04
mov al,[0x7c00+number+0x02]
add al,0x30
mov [es:0x1e],al
mov byte [es:0x1f],0x04
mov al,[0x7c00+number+0x01]
add al,0x30
mov [es:0x20],al
mov byte [es:0x21],0x04
mov al,[0x7c00+number+0x00]
add al,0x30
mov [es:0x22],al
mov byte [es:0x23],0x04
mov byte [es:0x24],'D'
mov byte [es:0x25],0x07
infi: jmp near infi ;无限循环,jum跳转命令,后面接偏移地址,直接跳转,如果有near,跳转到相对于当前命令地址的偏移地址
number db 0,0,0,0,0 ;用于占位(5个字节),位置用来保存各位的值
times 203 db 0 ;把第一扇区空闲位填0
db 0x55,0xaa ;检查的扇区的标志位
第六章 相同的功能,不同的代码
数据区数据写在前面,用跳转命令跳转到真正的执行代码处
数据声明时使用字面值
div是无符号数除法指令,有符号的应该用idiv
用idiv时需要注意不能溢出,如果需要把被除数扩展成32bit,用cwd命令