zoukankan      html  css  js  c++  java
  • 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
    

    我们可以看到确实成功了

    ②二维数组

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

    中间有行代码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

  • 相关阅读:
    [BZOJ]4810: [Ynoi2017]由乃的玉米田
    VK Cup 2017
    Educational Codeforces Round 19
    [BZOJ]4162: shlw loves matrix II
    2017-4-14校内训练
    第一次 CSP-S 的游记
    APIO2009 采油区域
    NOIP2017 逛公园
    NOIP2013 货车运输
    【9018:1458】征兵
  • 原文地址:https://www.cnblogs.com/AlienfronNova/p/14939600.html
Copyright © 2011-2022 走看看