x86汇编学习历程5----更紧凑科学的显示文字和数字

Posted 4nc414g0n

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了x86汇编学习历程5----更紧凑科学的显示文字和数字相关的知识,希望对你有一定的参考价值。

新知识点

定义存放字符串的数据区

当传送 文本和显示他们的指令过多时 便会显得臃肿
所以定义一个存放字符串的数据区,
当我们要使用他们的时候再用指令显示出来
这样负责显示的指令和显示的内容就无关了.

注意:换行可以使用 续行符‘\\’,
但我们通常在下一行加上一个 db 而不是使用‘\\’
如下所示
mytext	db 'L',0x07,'a',0x07,'b',0x07,'e',\\
		0x07,'l',0x07,' ',0x07,'o',0x07,
		db 'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07

设置数据段基地址

mov ax,0x7c0
mov ds,ax

在这里插入图片描述
数据串传送指令movsb,movsw和标志寄存器FLAGS
注:8086以上处理器可以按双字及更多进行操作

在8086处理器中
movsb:按字节(byte)只能执行或传送一次
movsw:按字(word)只能执行或传送一次
要重复操作要加上rep前缀 如 rep movsw
rep重复的次数由寄存器cx指定
-------------------------------------------------------------
16位标志寄存器FLAGS:
按位:
|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
|  |  |  |  |  |DF|  |  |SF|ZF|  |  |  |  |  |  |
DF: 方向标志
SF: 符号位
ZF: 零标志
(判断最近一次运算的结果是零或非零,如果是非零这一位为零反之为1)

传送前的准备工作:
DS:SI     原始数据串的段地址:偏移地址
ES:DI     目标位置的段地址:偏移地址
正向传送时,每进行一次movsb或movsw SI和DI的值自动加上传送的字节数
		   自动指向下一个传送的位置
反向传送时,每进行一次movsb或movsw SI和DI的值自动减去传送的字节数
		   自动指向下一个传送的位置
-------------------------------------------------------------
cld: 方向标志DF清零指令,将flags寄存器DF标志清零以指示传送为正方向
std: 与cld相反

在这里插入图片描述

$ 和 $$
这两个标记为NASM编译器特有的

$: 当前行的汇编地址
$$: 当前程序段的汇编地址

loop指令

用法:
	loop 标号
如:
	loop digit
loop指令编译之后操作码为E2 后面是8位相对偏移量
所以标号的位置不能太远
执行过程:
	将cx减一,cx不为0,转到标号位置执行,
	如为0,则顺序执行后面的指令

inc和dec指令
每个寄存器的用途参见计算机寄存器分类简介

在8086处理器中要使用寄存器提供偏移地址只能使用
BX SI DI BP 其他的都是非法的

inc:加一操作
	用法:inc r/m
    如:inc al 或 inc byte [0x2002]
dec:减一操作
	用法:dec r/m
    如:dec di 或 dec byte [0x2002]

代码

		jmp start
mytext	db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,
		db 'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07

start:
		mov ax,0x7c00   ;//设置数据段基地址,此处ax只是一个介质
					   ;//(主引导扇区加载的逻辑段地址为0x7c0)
		mov ds,ax

		mov ax,0xb800  ;//设置附加段基地址
		mov es,ax

		cld  
		;//方向标志清零指令,将flags寄存器DF标志清零以指示传送为正方向(与其相反的std)
		
		mov si,mytext  ;//由于设置了数据段基地址 mytext汇编地址为偏移地址
		mov di,0    ;//文本的传送是从显存的起始位置开始的 即es附加段,di为偏移地址即为0
		mov cx,(start-mytext)/2
		;//设置cx,cx的值为rep的次数实际上等于13
		rep movsw ;//按字操作

		mov ax,number  
		mov bx,ax   ;//得到标号所代表的汇编地址


		mov cx,5    ;//loop循环次数
		mov si,10   ;//除数10传送到寄存器si
digit:
		xor dx,dx   ;//dx清零
		div si    
		mov [bx],dl  ;//访问的偏移地址来自bx寄存器
		inc bx       ;//[bx]指向下一个0的地址
		loop digit
		

		;//开始显示各个数位
		mov cx,5
show:
		dec bx  
		;//在上一部分操作中最后我们把dx指向number的最后一个0处
		;//所以显示时从尾部开始显示
		mov al,[bx]
		add al,0x30   ;//转换数字为字符编码
		mov ah,04     ;//黑底红字
		mov [es:di],ax  ;//前面已将显存地址传给es且di位置为最后一个字符属性的位置
		add di,2     ;//一个字符一个属性共一个字
		loop show

		jmp $  ;//跳到本行

number  db 0,0,0,0,0 ;//用于临时存放余数
		times 510-($-$$) db 0
		db 0x55, 0xaa

使用bochs调试
我们设置断点来到指令’cld‘,指令r观察寄存器si和di内容
在这里插入图片描述
多次执行单步执行命令s后发现的确是按我们的逻辑进行
在这里插入图片描述
注:eflags为32位标志寄存器 如图小写代表为0 大写为1

基址变址寻址和条件转移指令

在8086处理器中 
只允许以下几种基址变址组合:
bx + si
bx + di
bp + si
bp + di

标志寄存器FLAGS的第7位SF符号位
如果最近一次逻辑运算结果二进制最高位为1则将SF置为1反之置为0
(不一定是符号位处理器不管你是不是符号位只管最高位)

jns指令跳转指令:
	如果标志寄存器FLAGS的第7位SF位为0就跳转为1就不跳转

我们改写loop digit后的代码

		;//开始显示各个数位
		mov bx,number
		mov si,4
show:
		mov al,[bx+si] ;//si的作用相当于索引
		;//所以显示时从尾部开始显示
		add al,0x30   ;//转换数字为字符编码
		mov ah,04     ;//黑底红字
		mov [es:di],ax  ;//前面已将显存地址传给es且di位置为最后一个字符属性的位置
		add di,2     ;//一个字符一个属性共一个字
		dec si	  ;//最后一次si为-1
		jns show  ;//基于SF位决定是否跳转

		jmp $  ;//跳到本行

number  db 0,0,0,0,0
		times 510-($-$$) db 0
		db 0x55, 0xaa
第一次执行 dec si 后 si 为3 二进制为:0000 0000 0000 0011     SF位为0

最后一次执行 dec si 后 si 为-1 二进制为:1111 1111 1111 1111     SF为1

调试bochs虚拟机
设置断点到0x7c58 键入info eflags命令 可以看到位sf为0
在这里插入图片描述
多次执行c命令 可以看到最后SF为1
在这里插入图片描述

最后我们在vbox上直接显示结果
在这里插入图片描述

以上是关于x86汇编学习历程5----更紧凑科学的显示文字和数字的主要内容,如果未能解决你的问题,请参考以下文章

x86汇编学习历程6----负数在计算机中的表示和应用方法(附FLAGS拓展和cmp及条件转移指令)

C++软件异常分析与排查的学习历程

X86汇编1.汇编语言基础

X86汇编5.高级指令详解

X86汇编7.内部中断

X86汇编6.编写汇编语言程序