X86汇编语言从实模式到保护模式(一)

不对请指正,欢迎交流

基础知识

单位换算:
        1 Byte = 8 bit
        1 KB = 1024 Byte
        1 MB = 1024 KB
        1 GB = 1024 MB

寄存器

8位寄存器可以容纳8比特(bit), 或者说1个字节   1byte=8bit,8个二进制数 1111 1111 -> 0xFF
16位寄存器可以存放2个字节,也就是1个字,  从右到左数,其中0~7是低字节,8~15是高字节
32位寄存器可以放4个字节也就是一个双字,其中0~15是低字,16~31是高位

8086内部有8个16位的通用寄存器,分别被命名为AX,BX,CX,DX,SI,DI,BP,SP
其中前四个AX,BX,CX,DX可以各自拆分成两个8位寄存器,总共是8个8为寄存器,分别是AH,AL,BH,BL,CH,CL,DH,DL
高低位关系是
15      8 7       0
---------------------
|   AH   |    AL    |   AX, AH位于高字节,AL位于底字节
---------------------

8086内部有4个段寄存器,CS是段代码寄存器,DS是数据段寄存器,ES是附加寄存器,SS是栈段寄存器

IP是指令指针寄存器他只和CS一起使用,而且只有处理器才能直接改变他的内容。当一段代码开始执行时,CS指向代码段的起始地址,IP则指向段内偏移,这样CS和IP共同形成的逻辑地址是处理器要执行指令的地址,并且由总线接口部件转换成物理地址来取得指令,然后处理器自动根据当前指令的长度来改变IP的值,使它指向下一条指令的地址偏移。

内存分段机制

我们把内存地址在逻辑上进行分段,便于操作,然后在使用"段地址:偏移地址"的方式还对内存进行访问,为了在硬件上提供对"段地址:编译地址"内存访问模式的支持,处理器提供了两个段寄存器,分别是代码段寄存器CS,数据段寄存器DS
    CS中存储的段地址是处理器开始执行指令的段地址
    DS中存储的段地址是处理器在访问内存是默认的地址,例如,处理器要访问内存地址0001H中的内容,会默认把0001H当作DS段中的偏移地址,如果DS是00FFH,那处理器要访问的物理地址是00FFH:0001H

由于8086处理器提供里20根地址总线,地址范围是 00000H ~ FFFFFH, 但是16位段地址加上16位的偏移地址只能表示16位的物理地址
为了解决这个问题,8086在形成物理地址前,把段地址左移四位,形成20位的段地址,然后加上16位的偏移地址,这样能够表示20位的物理地址了

因为偏移地址是16位的,所以在保证段与段值之间不重叠的情况下,可以分成16个段(因为地址范围最大F FFFFH,一个段偏移最大FFFFH,FFFFFH/FFFFH=FH 也就是10进制的16)
物理地址是82255H,如果段的划分是在段之间不重叠的情况下的,他的内存地址可以表示为是 8000H:2255H

NASM汇编指令

0x1234      ----> 立即数,字面值
[0x1234]    ----> 这个是获取地址 "段地址:0x1234"的内容

mov 传送指令

mov 目的操作数, 源操作数

注意事项:
    目的操作数与源操作数的的位数要一致
    在目的操作数是段寄存器时,源操作数不能是立即数,但是可以先把立即数放到通用寄存器中,然把通用寄存器的值传送给段寄存器
        mov ax, 0x1234
        mov ds, ax

    如果不想使用默认的ds寄存器,可以指定寄存器进行偏移,例子中指定的是ex段寄存器,这种用法叫做段超越前缀
        mov [es:0x00], 0x4c

    但是如果只是这么运行还是会出错,因为处理器不知道要操作空间的大小,因为两边都没有指定这条指令是8位的还是16位的,所以要加一个前缀byte
        mov byte [es:0x00], 0x4c

    关键词byte是用来修饰操作数的,指出本次传送是以字节的方式进行的,还有关键词word,指定是以字进行传输的

声明并输出数据 db, dw, dd, dq (伪指令)

    汇编地址 tag db 0, 1, 2, 3
    在当前汇编地址声明了4个字节的空间,并且进行初始化,其中四个字节里的内容依次是0,1,2,3 ,并且在其他地方使用的时候可是使用tag这个标识代替当前的汇编地址
    例如把第二个字节中的内容从1变成2
        mov byte [tag+1], 0x02
    首先指定本次传送的数的大小byte,指定传送的位置tag+1,然后需要传送的数0x02

db的意思是声明字节,所以他后面的操作数都会占一个字节的长度,如果要声明多个,各个操作数之间必须以逗号隔开  
dw用于声明字数据,dd用于声明双字数据(两个字),dq用于声明4字数据

div除法指令

16位的二进制数除以8位的二进制数
    被除数必须事前传送到寄存器AX中,指令执行后,商在寄存器AL中,余数在寄存器AH中
    mov ax, 0x0023
    div byte [0x1234]

32位的二进制数除以16进制的二进制数
    被除数的高16位放在DX中,低16位放到AX中,指令执行后,商在AX中,余数在DX中

xr 异或

            1 1 ---> 0
            1 0 ---> 1
            0 0 ---> 0

xr 目的操作数, 源操作数

目的操作数可以是通用寄存器和内存单元,一把来说两个操作数应当具有相同的数据宽度,两个操作数不能同时为内存单元

jmp 跳转

两种形式,第一个后面跟绝对地址
    jmp 0x5000:0xf0c0 ;段地址加偏移地址
    直接跳到0x5500:0xf0c0继续执行

    jmp near tag    ; 相对位置
    跳到tag标识处,
两条指令都是跳转,但编译完成的机器码是不一样的,第一条是绝对地址跳转,第二条是相对位置跳转,指令的机器码也不一样

time 重复指令

time 100 db 0
重复db 0 指令100次

段之间的批量数据传送 movsb movsw

movsb 传送是以字节为单位的
movsw 传送是以字为单位的

源地址:  段地址+偏移地址   DS是段地址,SI是偏移地址   DS:SI

目的地址:段地址+偏移地址   ES是段地址,DI是偏移地址   ES:DI

传送的颗粒:由使用的movsb(字节)还是movsw(字)决定

传送的次数:CX寄存器中存储传送的次数,每传送一次自动减1

传送方向: 由标志寄存器FLAGS中的DF位来决定,DF为0时,正向传送;DF为1时,反向传送
        正向传送:从内存地址的低地址开始传送,低地址端到高地址端,每次传送SI和DI加1或加2(由传送的颗粒决定)
        反向传送:从内存的地址高地址开始传送,高地址端到低地址段,每次传送SI和DI减1或减2
设置DF位:  cld可以将DF位清零---> 正向传送
            std可以将DF位置1----> 反向传送

提示: movsb/movsw执行之前就要把DS,SI,ES,DI,CX,DF位等寄存器和位中的内容设置好,然后直接执行movsb或者movsw,但是单纯的movsb/movsw只是传送一次,如果想需要希望反复执行,请加rep指令前缀
    rep指令前缀:如果CX不为0,则重复执行

执行命令    rep movsb/ rep  movsw

loop 循环

    loop 目的地址

重复执行一段相同代码
    将寄存器CX中的内容减一
    如果CX的内容不为0,转移到指定的位置处执行,否则顺序执行后面的指令

老样子:CX里的内容提前设置好

inc 自增 dec 自减

inc 目的操作数  ; 将目的操作数加一
dec 目的操作数  ; 将目的操作数减一

负数操作相关指令 neg cbw cwd sub idiv

用0减去指令中指定的操作数,如果目的操作数不能明确操作的大小可以使用byte或word指定
    neg [byte] 目的操作数

cbw:将寄存器AL中的有符号数扩展到整个AX, AL中的数提前设置 8位-->16位
        11110001 ----> 11111111 11110001
cwd :将寄存器AX中的有符号数扩展到DX:AX         16位--->32位

sub:减法指令,用法参考add

idiv: 有符号除法,用法参考div

你可能感兴趣的:(X86汇编语言从实模式到保护模式(一))