• 汇编语言实验十


    一、编写一个通用的子程序来实现显示字符串的功能

    名称:show_str

    功能:在指定的位置,用指定的颜色,显示一个用 0 结束的字符串。

    参数:(dh)= 行号;(dl)= 列号;(cl)= 颜色,ds:si 指向字符串的首地址

    返回:无

    应用举例:在屏幕的 3 行 8 列,用绿色显示 data 段中的字符串。

    assume cs:code
    data segment
       db 'Welcome to masm!',0
    data ends
    
    code segment
    start: mov dh,8          ; 行数
           mov dl,3          ; 列数
           mov cl,2          ; 绿色
           mov ax,data
           mov ds,ax
           mov si,0          ; 数据
           call show_str
    
           mov ax,4c00h
           int 21h
    
    show_str: 
    
            ............
    
    code ends
    end start

    本题由于参数的个数不是很多,所以不用将参数放进栈中存储。

    在编写子程序之前还是先回答两个问题:

    • 要处理的数据在什么地方?
    • 要处理的数据有多长?

    首先本题要处理的数据就是 data 段中的内容,在进行子程序调用之前就已经将 data 段的首地址存储进了寄存器中以备用。

    然后就是 data 段的数据有多长的问题,很显然 data 段中的数据是以 0 字符作为结尾,可以使用 jcxz 指令来检测 0 而知道数据是否处理完,所以子程序不需要字符串的长度参数。

    下面是子程序的代码:

    assume cs:code,ds:data
    
    data segment
            db 'welcome to masm!',0
    data ends
    
    code segment
    start:
        mov ax,data
        mov ds,ax
        mov si,0
    
        mov dh,8
        mov dl,3
        mov cl,2
        call show_str
        mov ax,4c00h
        int 21h
    
    
    show_str:                  ;这里是显示字符串子程序的入口
        push ax                ;因为子程序会用到相关的寄存器
        push dx                ;与 pop 指令结合进行相关寄存器的保护工作            
        push cx
        push es
        push di
        push si
        
        mov ax,0b800h          ;B800H 是显示缓冲区的首地址的段地址, 
        mov es,ax            
        
        sub ax,ax              ;将 ax 寄存器中的值置零
        mov al,160             ;显示器上的一行总共有 80 个字符, 在显示缓冲区中占有 160 个字节
        mul dh                 ;上面一行字符所占的字节数存放在 al 寄存器中, 作为一个乘数, dh 寄存器中的数据作为另外一个乘数, 结果存放在 ax 中
        sub dh,dh
        add dl,dl              ;由于一个字符占 2 个字节, 所以需要将 dl 中的数据乘以 2
        add ax,dx            
        mov di,ax              ;最后将字符串在显示缓冲区中首字符的地址存放在 di 寄存器中
    
        mov al,cl         
        sub cx,cx              ;将 cx 寄存器置零, 以备下面 jcxz 使用
      next:
        mov cl,[si]            ;在调用子程序之前就将要操作字符串的首偏移地址存放在了 si 中, 
        jcxz sret              ;判断 cx 中即 ds:[si] 所指的内存单元是否为 0 , 如果为 0, 则跳转到 sret 标号的位置
        mov es:[di],cl
        mov es:[di+1],al       ;在目的地址分别存放字符本身和字符的颜色属性
        inc si                
        add di,2
        jmp short next
       sret:    
        pop si                 ;将寄存器中的值还原, pop 指令的顺序与 push 指令相反
        pop di
        pop es
        pop cx
        pop dx
        pop ax    
    
        ret                     ;子程序返回 , 继续执行 mov ax,4c00h
    code ends
    end start

    二、解决除法溢出的问题

    名称:divdw

    功能:进行不会产生溢出的除法运算,被除数为 dword 型,除数为 word 型,结果为 dword 型

    参数:(ax)= dword 型数据的低 16 位;(dx)= dword 型数据的低 16 位;(cx)= 除数

    返回:(dx)= 结果的高 16 位;(ax)= 结果的低 16 位;(cx)= 余数

    应用举例:计算 1000000 / 10

    在解决除法溢出的问题上,有这样一个公式能够完美的规避掉溢出现象:被除数 / 除数 = (被除数的高 16 位 / 除数)的商 * 65535 + [ (被除数的高 16 位 / 除数)的余数 * 65535 + 被除数的低 16 位 ] / 除数

    这个公式将可能会产生溢出的除法运算,转变成了多个不会产生溢出的除法运算。在公式中,等号右边的所有除法运算都可以用 div 指令来完成,并且不会产生溢出现象。

    assume cs:code
    
    code segment
    start:
        mov dx,128          ;被除数的高 16 位
        mov ax,0            ;被除数的低 16 位
        mov cx,128            ;16 位除数
        call divdw
    
        mov ah,4ch
        int 21h
    
    ;返回参数:商得高16位dx;低16位ax;余数cx
    ;32 位除法
    divdw:
        jmp short divstart
        datareg dw  4 dup (0)
    divstart:
        push bx                    ;照常进行寄存器的保护工作
        push ds
        push si
    
        cmp dx,cx                ;通过这里实现兼容没有溢出的除法运算
        jb divnoflo
    
        mov bx,cs
        mov ds,bx                ;ds中存放代码段的段地址
        mov si,offset datareg    ;取得自定义数据 datareg 的偏移地址
    
        mov [si],ax              ;将被除数的低 16 位保存进 datareg 处的第一个字里
        mov ax,dx                ;
        sub dx,dx                ;对 dx 置零, 避免溢出
        div cx                   ;求被除数的高 16 位/除数, 得到商和余数,分别保存在ax和dx当中
        mov [si+2],dx            ;将余数保存进第 datareg 处的第二个字
        mov bx,512                   
       mul bx mov bx,128 mul bx ;将商*65536 , 其中512 * 128 = 65535 mov [si+4],ax ;保存int(H/N)*65536 mov [si+6],dx mov ax,[si+2] ;求得rem(H/N)*65536 mov bx,512 mul bx mov bx,128 mul bx add ax,[si] ;求得rem(H/N)*65536+L div cx ;求得[rem(H/N)*65536+L]/N ***注意这里进行的除法不能清除dx,这里不可能会溢出 mov cx,dx ;求得结果的余数 add ax,[si+4]     ;求得结果的低 16 位 mov dx,[si+6] ;求得结果的低高 16 位 jmp short dsret divnoflo: div cx mov cx,dx sub dx,dx dsret: pop si pop ds pop bx ret code ends end start

    三、实现一个子程序,该子程序能将 word 型数据转变为表示十进制数的字符串

    子程序代码如下:

    dtoc:   push ax
            push si
            push di
            push dx
            push bx
            push cx
            mov di, 0
            mov dx, 0
            mov bx, 10
    
    devide: mov cx, ax         ;将 12666 这个存进寄存器 cx, 在寄存器中它的表现形式是 0011 0001 0111 1010
            jcxz stop
            div bx             ;利用除法来求得 12666 十进制数每一位的值
            inc di
            push dx            ;将这个值存放进栈中
            mov dx, 0
            jmp devide
    stop:   mov cx, di      
    string: pop bx
            add bx, 30h        ;将每一位的值转换为 ASCII 码的表现形式
            mov [si], bl
            inc si
            loop string
    
            pop cx
            pop bx
            pop dx
            pop di
            pop si
            pop ax
    ret
  • 相关阅读:
    文本框设置只读,后台可获取
    div 在同一行的 CSS处理
    在标签中添加属性
    (转)如何使用SignalTap II觀察reg與wire值? (SOC) (Verilog) (Quartus II) (SignalTap II)
    (转)如何使用ModelSim對Megafunction或LPM作仿真? (SOC) (MegaCore) (ModelSim)
    (笔记)TSL235新型光感器件强烈推荐使用
    (转)如何增加SignalTap II能觀察的reg與wire數量? (SOC) (Quartus II) (SignalTap II)
    (转) 如何將10進位轉2進位? (C/C++) (C)
    (转)如何使用ModelSim作前仿真與後仿真? (SOC) (Quartus II) (ModelSim)
    (笔记)关于LM3S片内FLASH编程的一点建议
  • 原文地址:https://www.cnblogs.com/KKSJS/p/10009980.html
Copyright © 2020-2023  润新知