• 汇编语言学习记录02之程序设计技巧


    位运算技巧在汇编程序中的应用

    • 寄存器初始化

      举例使用:

      xor ax, ax ;将ax寄存器置零
      
    • 判断寄存器是否为0

      假如我们刚刚进行完除法运算,需要判断余数(默认存放在 DX 中)是否为0,我们可以对 DX 自身按位与,触发标志位影响,举例使用如下:

      and dx, dx
      jz next
      
    • 求反的位同 1 异或,维持不变的位同 0 异或

    • 检查某些位是否同时1

      举例:检查 CX 中的第1,6和11位中是否同时为1

      not cx ;先取反,如果所求位是1的话,此时转为0
      test cx, 0842h ;对其他位置0,对所求位同1按位与
      jz yes ;如果结果是0,说明所求位都转变成0了,即所求位原来同时为1
      not cx ;记得取补恢复
      

    16进制数转换为ASCII码

    DOS功能调用的2号功能的输出,需要传入一个ASCII码的字符。然而我们现在只有一个16进制数,如何得到对应的ASCII码呢?

    方法一:查表法

    零、适用范围:

    适用于只有一位的16进制数(即4位二进制),不适用于两位或两位以上的16进制数。

    一、要点:

    • 换码指令—— XLAT:将BX指定的缓冲区中、AL 指定的位移处的一个字节数据取出赋给 AL ,实际相当于 (AL) = (DS:(BX+AL)) 。注意,不是单纯地赋予 AL+BX ,而是对应地址的值。

      换码指令执行前,一般在主存建立一个字节量表格(如下代码4-6行),内含要转换成的目的代码表格首地址存放于 BXAL 存放相对表格首地址的位移量

      换码指令执行后,将 AL 寄存器内容转换为目的代码

    二、代码:

    assume cs:codesg, ds:datasg
    
    datasg segment
            ASCII   db 30h,31h,32h,33h,34h,35h
                    db 36h,37h,38h,39h	   ;0~9的ASCII码
                    db 41h,42h,43h,44h,45h,46h ;A~F的ASCII码
            hex     db 09h                     ;任意设定了一个待转换的一位16进制数,这里以'0f'为例
    datasg ends
    
    codesg segment
    start:
            mov ax, datasg
            mov ds, ax
    
            mov bx, offset ASCII ;bx存储标号ASCII的偏移地址(将其作为基准值)
            mov al, hex          ;将待转换的16进制数放到低位寄存器
            and al, 0fh          ;按位与,对8位的前4位清0(因为实验只要求输出后4位)
            xlat                 ;换码:al<- DS:[BX+AL] 基准值BX(ASCII的offset)+位移量AL(待转换)
            mov dl, al           ;入口参数:dl<-al
            mov ah, 2            ;02号DOS功能调用
            int 21h              ;显示一个ASCII码字符
    
            mov ah, 4ch
            int 21h
    codesg ends
    
    end start
    
    

    三、内存中存储的数据:

    四、运行结果:

    hex赋值 0fh 09h
    运行结果

    方法二:位运算法

    零、适用范围:

    适用于多位16进制数,下面演示的是两位16进制数(即8位二进制,1个字节大小,可以存放到 8位寄存器 如 AL等)

    一、分析:

    我们观察一下 ASCII 码表,发现(0-9)的ASCII码(二进制8位)下,前8位具有共同前缀 0011 ,而末8位恰好就是(0-9)的二进制表示!那 16 进制下的(A-F)呢?我们只需要特判即可,如果该数字的ASCII码不超过 39H (即数字9),那么直接输出即可;若超过(说明数字是A-F),那么直接对其**加上 (7) **即可

    由于两位16进制数即是8位二进制数,故我们需要先对高4位二进制数进行转换,再对低4位二进制数进行转换。

    二、代码:

    datasg segment
    datasg ends
        
    stacks segment
        db 256 dup(?)
    stacks ends
    
    codesg segment
        assume cs:codesg, ds:datasg, ss:stacks
    start:
        mov ax, datasg
        mov ds, ax
        mov al, 0fah; //将你需要转换的两位16进制放到al寄存器中
    
        push ax     ;暂存ax
        mov dl, al 
        mov cl, 4   ;用于shr指令,代表移位4位
        shr dl, cl  ;dl先取出al的高4位
        call transASCII
        pop dx      ;将之前存的ax给pop出来
        and dl, 0fh ;dl取出取出al的低4位
        call transASCII
    
        mov ah, 4ch
        int 21h
    
    ;子程序实现转换
    transASCII proc far
        or dl, 30h ;对该串的前4位按位或上"0011"(ASCII码中数字的前缀串)
        cmp dl, 39h ;39H是数字9的ASCII码16进制
        jbe output ;小于等于39(即数字没超过9),直接输出
        add dl, 7  ;大于等于39 (即数字为A-F),需要对ASCII码加上7
    output:
        mov ah, 2
        int 21h
        ret
    transASCII endp
    
    codesg ends
        end start
    
    
    

    三、运行结果:

    AL赋值 fah 9ch
    运行结果

    多分支结构——地址表法

    一、要点:

    • 当程序分支较多时,可使用地址表(跳转表)来实现程序分支。我们在数据段事先安排一个地址表,里面连续存放的是一系列跳转地址,即各分支程序的入口地址

    • 由于使用 DOS 功能调用的1号功能,输入只能是一个字节大小的字符,同时是以二进制的ASCII码存入 AL 中。故需将 AL 存下 ASCII 码转换为对应的数字。(在下面的第30行代码)

    • ⭐ 因为一个段最大存储空间是 (64) KB,偏移地址故使用 16 位表示。因此需要用 2 个字节存储单元 (1个字)去储存偏移地址。对于这个地址表而言,要两个字节、两个字节…如此去寻找表中的每个元素。(在下面的第32行代码)

    二、代码:

    datasg segment
        str0 db 0dh,0ah,'input number (1-3):',0dH,0ah,'$'
        str1 db 0dh,0ah,'chapter1: introduction',0dH,0ah,'$'
        str2 db 0dh,0ah,'chapter2: designing method',0dH,0ah,'$'
        str3 db 0dh,0ah,'chapter3: experiment',0dH,0ah,'$'
        table dw disp1, disp2, disp3  ;在数据段中定义各个标号的偏移地址,相当于offset disp1,此时相应内存单元即存了(16位)偏移量
    datasg ends
    
    stacks segment
        ;
    stacks ends
    
    codesg segment
        assume cs:codesg, ds:datasg, ss:stacks
    start:
        mov ax, datasg
        mov ds, ax
    input:
        mov ah, 09h ;输出字符串
        mov dx, offset str0
        int 21h
    
        mov ah, 01h
        int 21h
        cmp al, '1'
        jb input ;如果小于(below)1,则重新输入
        cmp al, '3'
        ja input ;如果大于(above)3,则重新输入
        
        and ax, 000fh; //16位ax只保留最后4位,从而实现ASCII转数字(具体见上)
        dec ax ; 因为地址表table里面的元素从0开始计数
        shl ax, 1; 左移一位,因为16位偏移地址占用2个字节单元
        mov bx, ax
        jmp table[bx]; //寄存器相对寻址,(ip)=table+bx
    
    
    disp1:
        mov dx, offset str1
        jmp output
    disp2:
        mov dx, offset str2
        jmp output
    disp3:
        mov dx, offset str3
        jmp output 
    output:
        mov ah, 09h
        int 21h
        mov ah, 4ch
        int 21h
    codesg ends
        end start
    

    逻辑尺

    逻辑尺,其实就是将有限个算式中的加减运算符用 01 进行区分,由此得到有限长度的 01 串。

    一、题目

    给定数组 (x (x_1,x_2,...,x_{10}))(y (y_1,y_2,...,y_{10})) ,要求计算下面的 (z (z_1,z_2,...,z_{10})),具体算式如下:

    [egin{cases} z_1 &= x_1 + y_1 \ z_2 &= x_2 + y_2 \ z_3 &= x_3 - y_3 \ z_4 &= x_4 - y_4 \ z_5 &= x_5 - y_5 \ z_6 &= x_6 + y_6 \ z_7 &= x_7 - y_7 \ z_8 &= x_8 - y_8 \ z_9 &= x_9 + y_9 \ z_{10}&= x_{10} + y_{10} \ end{cases} ]

    二、分析

    注意只有(10)项,每一项的(z_i(1leq ileq 10))都是由 (x_i)(y_i) 进行加或减的运算,我们用 1 表示减法、0 表示加法,因此,从第一条式(在低位)到第10条式(在高位)的加减符号便能够得到一条 01 串:0000,0000,1101,1100b(16进制为 00dch),接着我们只需要循环依次遍历 (z_i) 便能够得出结果了。

    三、代码

    datasg segment
        x dw x1,x2,x3,x4,x5,x6,x7,x8,x9,x10 ;注意,存放的应该是x数组每个元素的值,这里写成x1,x2,...只是占个位
        y dw y1,y2,y3,y4,y5,y6,y7,y8,y9,y10 ;同上理y1,y2,...只是占个位,实际情况是要填上数字的
        z dw z1,z2,z3,z4,z5,z6,z7,z8,z9,z10 ;同上理z1,z2,...只是占个位,实际情况是要填上数字的
        logic_rule dw 00dch ; 0000,0000,1101,1100,其中1表示减法,0表示加法
    datasg ends
    
    stacks segment
        ;
    stacks ends
    
    codesg segment
        assume cs:codesg, ds:datasg, ss:stacks
    start:
        mov ax, datasg
        mov ds, ax
    
        mov bx, 0
        mov cx, 10 ;10个元素
        mov dx, logic_rule
    next:
        mov ax, x[bx] 
        shr dx, 1    ;右移,使得CF标记逻辑尺的最低位
        jc subtract  ;当前最低位为1,进行减法运算
        add ax, y[bx];当前最低位为0,进行加法运算
        jmp restore 
    subtract:
        sub ax, y[bx]
    restore:
        mov z[bx], ax;将结果存到z数组中
        add bx, 2 ;找到数组下一个字单元
        loop next
    
        mov ah, 4ch
        int 21h
    codesg ends
        end start
    

    其他算法

    • 阶乘运算(递归实现)
      《汇编语言程序设计教程》(卜艳萍,清华出版社)P175

    • 辗转相除法
      《汇编语言程序设计教程》(卜艳萍,清华出版社)P165

    • 冒泡排序
      《汇编语言程序设计教程》(卜艳萍,清华出版社)P157

  • 相关阅读:
    C#多线程学习(四) 多线程的自动管理(线程池)
    CHR6dm 校准软件下载
    WinAPI打开串口失败
    AHRS(航姿参考系统)和IMU(惯性测量单元)的区别【转】
    课程1:开始Direct3D
    CHR6dm datasheet 中文翻译
    从D3D将画面渲染至桌面
    Hello GPU
    linux配置java环境变量(详细)
    java中的多线程
  • 原文地址:https://www.cnblogs.com/J-StrawHat/p/14040100.html
Copyright © 2020-2023  润新知