x86 16位实模式05——简单数据处理

镇楼图

x86 16位实模式05——简单数据处理

Pixiv:Csyady



〇、数据处理基本问题

任何的数据都存在两个基本问题(这应该是在数据结构里学的)

①如何存储数据

②数据有多大


一、数据定义

我们之前使用dw来定义数据,dw其实就是define word定义一个字

还有其他的定义方式,这是基于不同尺寸来定义的

db(define byte)用来定义字节

db 30h,31h,32h

dd(define double word)用来定义双字

dd 12345678h

df用来定义三字

dq用来定义四字

dt用来定义五字

依据这些我们可以较方便地在数据段、栈段来定义数据

但是还存在一个问题,如果我要初始化1000个字节,总不可能写1000次0吧,因此存在另外一个语法能快速帮助我们定义数据

dup语法专门用来重复定义

定义用的伪指令 次数 dup (数据)

db 3 dup (0)
/*db定义3次0*/
db 4 dup (0,1,2)
/*db定义4次0,1,2也就是定义12个字节*/
dt 50 dup (0,0)
/*同理是定义500字*/

依据如此丰富的定义方法和dup可以很高效地定义数据

不过需要注意这是直接定义在内存上的


二、数据存储问题

就汇编里有三种存储方式

(1)立即数idata

(2)寄存器reg

(3)内存SA:EA

立即数对标C语言的常数

寄存器和内存你可以认为都是变量

在C语言里有个关键词是register作用是将变量定义为寄存器变量

现在我们学了汇编,知道寄存器的基本作用是存储数据,但同时我们也知道寄存器如同多才多艺的人,具有很多功能。

寄存器首先在速度上会比内存要快,寄存器可以直接访问CPU,而内存需要先通过地址总线来寻找位置

但由于寄存器数量太少了,因此我们还是多使用内存来存储数据


三、ASCII码与and、or指令

汇编里也存在ASCII码,因此我们可以用来定义字符(串)

一个字符便占1字节

比如

db ‘a‘
db ‘B‘
db ‘Assembly‘
/*不推荐使用其他类型来定义
  很可能会出bug*/

实现大小写转换

实现这个正常我们会使用if语句来判断字符类型然后在转换,但目前为止我们并没有学习汇编的IF语句,汇编的IF要复杂很多,在之后会遇到,但现在我们要怎么进行大小写转换呢?

我们先对ASCII码进行转换,转成16进制、2进制来寻求答案

字符 ASCII码 16进制 2进制
A 65 41 0100 0001
B 66 42 0100 0010
C 67 43 0100 0011
... ... ... ...
a 97 61 0110 0001
b 98 62 0110 0010
c 99 63 0110 0011
... ... ... ...

当我们列出所有大小写字母不同码值后,我们便能发现一个最为明显的规律

在二进制下,大写字母在第5位为0,小写字母在第5位为1

因此如果我们只要能修改二进制码中的第5位即可

而且在汇编里确实存在这种操作(在C里也有)

那就是逻辑运算and和or

首先在C语言里我们可以这样操作

a = a && 11011111b;
//a转为大写
b = b || 00100000b;
//b转为小写

这里a操作具有这样一个功能

and:0的位置设为0

or :1的位置设为1

*采用if转大小写(bushi)

在汇编里为下述代码

and al,11011111b
/*转大写*/
or  dl,00100000b
/*转小写*/

四、bx、bp、si、di寄存器

这四个寄存器尤为特殊,都具有和bx寄存器一样的功能,可以作为偏移地址的参数,比如

mov ax,ds:[bx]
mov bx,ds:[bp]
mov cx,ds:[si]
mov dx,ds:[di]

同理我们可以进行加减

mov cx,ds:[0+bx]
mov ax,ds:[2+bx]
mov dx,ds:[4+si+bp]

仅仅依靠这种灵活定位偏移地址的方式,我们可以玩出很多花

但语法并非那么宽松,可以说是刻意指定好的

bx、bp为一块,si、di为一块

寻址方式 含义 语法糖
直接寻址 用idata、bx、bp、si或di来
相对寻址 用bx、bp、si或di与idata相加得到
表示为[bx+idata]、[si+idata]等
[bx].idata
idata[bx]
或[bx] [idata]
变基址寻址 用bx、bp与si、di相加组合而成
表示为[bx+di]、[bp+si]等
但注意不可以表示为[bx+bp]或[si+di]
[bx] [si]
相对变基址寻址 在变基址寻址的基础加上idata
表示为[bx+di+idata]等
idata[bx] [si]
或[bx].idata[si]

不使用寄存器进行操作

像下面代码能成立吗?

mov ds:[bx],12

或许能成立,但mov操作将12赋值给ds:[bx],但ds:[bx]占几个字节?

寄存器al就是一字节,ax就是一字,这很明确

但对于某一内存初始地址这并不清楚这块内存多大,我们需要使用X ptr来告诉编译器我们将ds:[bx]当作了几个字节

X可以为字节byte、字word、双字dword、三字fword、四字qword、五字tbyte

不过在16位实模式上你基本没机会使用dword以上的

基本上会报错,就不要想什么tbyte ptr之类的

mov word ptr ds:[bx],12

还有一种情况,就是指令本身指定了传入数据大小

因为只有不确定参数的情况下才需要使用X ptr语法

比如push和pop,这两个指令明确表示是以字来传输的

push ds:[bx]
pop ds:[bp]

上面看似不清楚,但指令本身指定了是传输1字


五、构造数据类型

比如单纯用1个字节或1个字可以当作一个变量

但同时我们依据灵活定位偏移地址的方式,我们可以构造出数组、多维数组(至多两维)、结构体这些基本的数据类型

内存上当然看不出什么,但逻辑上我们依据构造好了,而且x86汇编语言也存在着一些语法糖辅助我们理解

①数组

这个比较简单,比如我们定义09为字符串a,1019为字符串b

逻辑上我们划分好了,然后我们在数据段定义数据

data segment
	db ‘0123456789‘,‘9876543210‘
data ends

然后我们尝试获取数据

code segment
	start:	mov ax,data
			mov ds,ax
			
			mov bx,0
			mov si,0100h
			mov di,0200h
			/*我们将两个字符串
			  分别搬到ds:0100和ds:0200处*/
			
			mov cx,10
			loop1:	
					mov al,ds:[bx]
					mov ds:[si],al
					mov al,ds:[1*10+bx]
					mov ds:[di],al
					/*注意没有这种语法
					  mov byte ptr ds:[si],ds:[bx]
					  因此我使用al暂时存储*/
					
					inc bx
					inc si
					inc di
			loop loop1
					
			mov ax,4c00h
            int 21h
code ends

x86 16位实模式05——简单数据处理

我们可以看到确实成功了

②二维数组

在刚才的程序其实我已经隐含地表达了如何构造二维数组

中间有行代码mov al,ds:[1*10+bx]

我这里表达为1*10其实就是二维数组的一种表达

如果有15个长度10的字符串我可以表示为

mov si,0
...
mov al,ds:[si*10+bx]
...
inc si

当然这在语法上是不允许,我们进行改良

mov si,0
...
mov al,ds:[si+bx]
...
add si,10

同时我们可以使用语法糖加强语意

mov si,0
...
mov al,ds:[si][bx]
...
add si,10

如果我们在逻辑上定义了多个二维数组我们可以使用语法糖idata[bx][si]

如果有多个一维数组我们使用语法糖idata[bx]

③结构体

结构体比二维数组复杂很多,我们先用C语言描述一个结构体,考虑用汇编实现结构体

struct {
	char a;
    char b[2];
    char c;
};

我们用语法糖[bx].idata

mov bx,0
...
mov al,ds:[bx].0
...
mov al,ds:[bx].1
...
mov al,ds:[bx].3
...
add bx,4

LAST、乘除

①乘法mul

mul ax,2

②整除div

除法比较复杂,首先div是整除指令

完成一个除法,需要被除数、除数,而结果为商、余数

除数分为8位和16位

(1)8位整除

被除数为16位且存储在ax寄存器内

除数为8位由寄存器或者内存存放

比如

mov ax,17
mov dl,4
div dl
/*注意不能使用idata作为div的参数*/

那么结果分别为4和1,而余数被存储于ah,商存储于al

(2)16位整除

被除数高16位存储在DX,低16位存储在AX

除数必须为16位

余数最后存储在DX,商存储在AX

mov dx,0001h
mov ax,86a1h
/*这个被除数也就是
  dx*10000h+ax=100001h*/
mov bx,0100h
div bx

我们调用可以看到最后结果AX=0186h,DX=00A1h

(注:使用除法很有可能会出错,若出错你将不得不重启系统


参考书籍

《汇编语言 第四版》——王爽

参考网站

https://fishc.com.cn/forum-39-1.html

https://b23.tv/qUfphX

x86 16位实模式05——简单数据处理

上一篇:高效的异步日志


下一篇:range语句