第七章-X86汇编语言从实模式到保护模式

第七章 比高斯更快的计算

7.3 显示字符串

  • '1+2+3+…+100='字符串编译阶段,编译器将把它们拆开,形成一个个单独的字节
  • jump near start跳过没有指令的数据区
  • 11~15行初始化数据段寄存器DS和附加段寄存器ES
  • 18~28行用于显示字符串,
    首先索引寄存器SI指向DS段内待显示字符串的首地址,即标号"message"所代表的汇编地址
    用另一个索引寄存器DI指向ES段内的偏移地址0处,ES是指向0xB800段的
  • 字符串的显示依赖循环。如使用loop,需要CX寄存器
    20行循环次数为字符串长度=start-message
    22行,首先从数据段中,逻辑地址为DS:SI取得第一个字符
    将其传送到逻辑地址ES:DI即显示缓冲区
    24行,DI的内容加一,以指向该字符在显示缓冲区内的属性字节,0x07,黑底白字
    26,27行,寄存器SIDI加一指向原位置和目标位置的下一个单元
    28行,执行循环。先将CX的内容减一,然后根据CX是否为零决定是否循环,CX为零时,显示完毕。

7.4 计算1到100的累加和

AX清零,1传到寄存器CX,开始累加。CX同100比较
AX中得到累加和。

7.5 累加和各个数位的分解与显示

栈和栈段的初始化

得到累加和,要把各数位分解出来,并准备在屏幕上显示。
保存在栈中,栈段,由段寄存器SS指向。
需要栈指针寄存器SP来指示下一个数据应当压入栈内的什么位置,或者数据从哪里出栈

40~42行初始化SSSP的内容

在这里插入图片描述

分解各个数位并压栈

44行 除数传送到BX
每次除法结束后,都做一次判断,如果商为0的话,分解过程可以提前结束,必须记住实际有多少个数位。45行CX寄存器清零,用来累计

47到53行也是循环体,每次执行,分解一个数位。每次分解CX加一,表明多一个数位

48,49行,将DX清零,和AX一起形成32的被除数。

50行,or不是真正的加法。这是因为每次是除以10,所以DL中得到的余数,高四位必定是0x30低四位是0,高4位是3。所以or之后是相当于加

51行,push指令将DX内容压入栈中。push指令在8086中只能压入一个字,但其后的32位64位处理器允许压入字,双字,四字。因此必须有关键字。

执行push指令,首先将SP的内容减去操作数的字长,然后把要压入栈的数据放到逻辑地址SS:SP所在位置。

SP-2即0x0000-0x0002=0xFFFE,错位被忽略。Intel使用低端字节序,低字节在低地址,高字节在高地址。压栈操作是从高地址端向低地址短推进。

在这里插入图片描述

出栈并显示各个数位

57行,pop弹出到DX中,然后将SP的内容加上操作数的字长
执行完最后一次出栈,SP的内容重新为0

进一步认识栈

引入栈和push、pop只是为了方便程序开发。临时保存一个数值到栈中使用push指令是最简洁最省事的。否则push ax 可为

sub sp,2
mov bx,sp
mov [ss:bx],ax

pop ax类似

mov bx,sp
mox ax,[ss:bx]
add sp,2

如果你想把临时数据都保存在数据段,那么你必须在数据段中开辟一些空间,并亲自维护一个指针来跟综这些数据的存入和取出。

  • 保持栈的平衡,SP在做事之前的值和之后的值要相同,即push和pop数相等。

  • 必须充分估计需要的栈空间,如果栈段和代码段属于同一内存段,栈定义得过小,程序编写不当,栈可能破坏有用数据。

  • 请举例说明为什么在压栈操作中,栈指针需要先减去一个偏移量,然后再将数据存储到栈中在出栈操作中,在出栈操作中,栈指针需要先访问栈中的数据,然后再将栈指针向上移动一个偏移量。

    假设我们有一个栈,初始时栈指针 SP 指向栈底的内存地址,栈中已经存储了两个元素。此时我们要进行一次压栈操作和一次出栈操作,来说明为什么在这两个操作中需要先进行偏移量处理。

    假设要压入的数据为 0x1234,大小为 2 字节,压栈操作流程如下:

    1. 将栈指针 SP 减去 2,这是偏移量处理的过程,让 SP 指向栈顶空闲位置;
    2. 将数据 0x1234 存储到栈指针 SP 指向的内存位置中;
    3. 将栈指针 SP 指向 0x1234 存储的内存位置,此时 SP 指向栈顶元素的位置。

    此时栈的情况如下:

    栈顶 0x1234
    元素2 0x5678
    元素1 0x9abc
    栈底 SP指向

    接下来,我们进行一次出栈操作,将栈顶的元素弹出,出栈操作流程如下:

    1. 访问栈指针 SP 指向的内存位置,即访问栈顶元素 0x1234;
    2. 将栈指针 SP 加上 2,这是偏移量处理的过程,让 SP 指向下一个栈元素的位置;
    3. 返回栈顶元素 0x1234。

    此时栈的情况如下:

    栈顶 0x5678
    元素2 0x5678
    元素1 0x9abc
    栈底 SP指向

    可以看到,在进行压栈和出栈操作时,都需要先进行偏移量处理,这样才能保证栈指针的正确性和栈中数据的一致性。

7.6 程序的编译和运行

Bochs中察看栈的命令是"print-stack",默认显示当前栈中的16个字

7.7 8086处理器的寻址方式

寻址方式就是如何找到要操作的数据,以及如何找到存放操作结果的地方(基于16位处理器)

  1. 寄存器寻址
mov ax,cx
add bx,0xf000
inc dx
  1. 立即(数)寻址
add bx,0xf000
mov dx,label_a

标号在编译阶段转化为立即数
立即寻址的操作数位于指令中,是指令的一部分

  1. 内存寻址
    段地址由4个段寄存器之一来提供,偏移地址要由指令来提供
    内存寻址实际上就是要寻找偏移地址,这称为有效地址
    即如何在指令中提供偏移地址,供处理器访问内存时使用
  • 直接寻址
mov ax,[0x5c0f]
add word [0x0230],0x5000
xor byte [es:label_b],0x05
  • 基址寻址
    基址寻址就是在指令的地址部分使用基址寄存器BXBP来提供偏移地址
mov [bx],dx //将DS的内容左移4位,加上基址寄存器BX的内容
add byte [bx-2],0x55  //可加减一个偏移量不改变bx内容
mov ax,[bp] //该寄存器的默认段寄存器是SS,常用于访问栈

高级语言里的函数调用,所有的参数都在栈中。为了能访问到那些被压在栈底的参数,就需要用到BP

在这里插入图片描述

  • 变址寻址
    寻址方式使用的是变址寄存器(索引寄存器)SI和DI
  • 基址变址寻址

​ 将 string db 'abcdef到z’原地反向排列

	mov bx,string 
	mov si,0
	mov di,25
order:
	mov ah,[bx+si]
	mov al,[bx+di]
	mov [bx+si],al
	mov [bx+di],ah
	
	inc si
	dec di 
	cmp si,di
	jl order

本章汇编:

                                               ;代码清单7-1
                                               ;文件名:c07_mbr.asm
                                               ;文件说明:硬盘主引导扇区代码
                                               ;创建日期:2011-4-13 18:02                             
     00000000 E90E00                           jmp near start
                                      	
     00000003 312B322B332B2E2E2E-      message db '1+2+3+...+100='
     0000000C 2B3130303D         
                                              
                                       start:
     00000011 B8C007                           mov ax,0x7c0           ;设置数据段的段基地址 
     00000014 8ED8                             mov ds,ax
                                      
     00000016 B800B8                           mov ax,0xb800          ;设置附加段基址到显示缓冲区
     00000019 8EC0                             mov es,ax
                                      
                                               ;以下显示字符串 
     0000001B BE[0300]                         mov si,message          
     0000001E BF0000                           mov di,0
     00000021 B90E00                           mov cx,start-message
                                           @g:
     00000024 8A04                             mov al,[si]
     00000026 268805                           mov [es:di],al
     00000029 47                               inc di
     0000002A 26C60507                         mov byte [es:di],0x07
     0000002E 47                               inc di
     0000002F 46                               inc si
     00000030 E2F2                             loop @g
                                      
                                               ;以下计算1到100的和 
     00000032 31C0                             xor ax,ax
     00000034 B90100                           mov cx,1
                                           @f:
     00000037 01C8                             add ax,cx
     00000039 41                               inc cx
     0000003A 83F964                           cmp cx,100
     0000003D 7EF8                             jle @f
                                      
                                               ;以下计算累加和的每个数位 
     0000003F 31C9                             xor cx,cx              ;设置堆栈段的段基地址
     00000041 8ED1                             mov ss,cx
     00000043 89CC                             mov sp,cx
                                      
     00000045 BB0A00                           mov bx,10
     00000048 31C9                             xor cx,cx
                                           @d:
     0000004A 41                               inc cx
     0000004B 31D2                             xor dx,dx
     0000004D F7F3                             div bx
     0000004F 80CA30                           or dl,0x30
     00000052 52                               push dx
     00000053 83F800                           cmp ax,0
     00000056 75F2                             jne @d
                                      
                                               ;以下显示各个数位 
                                           @a:
     00000058 5A                               pop dx
     00000059 268815                           mov [es:di],dl
     0000005C 47                               inc di
     0000005D 26C60507                         mov byte [es:di],0x07
     00000061 47                               inc di
     00000062 E2F4                             loop @a
                                             
     00000064 E9FDFF                           jmp near $ 
                                             
                                      
     00000067 00<rept>                times 510-($-$$) db 0
     000001FE 55AA                                     db 0x55,0xaa

猜你喜欢

转载自blog.csdn.net/weixin_61631200/article/details/129501043
今日推荐