汇编学习--第十天

10.5 转移地址在寄存器中的call指令

call 16位reg

相当于执行

push IP

jmp 标号

 

检测点 10.4

AX=000BH

这个程序稍微修改一下,就很清晰了。

assume cs:codesg
codesg segment
        mov ax,6
        ;首先sp=sp-2=fffeh,将IP=5压入栈中
        ;跳转到ax处,jmp 6
        call ax
        inc ax
        ;在bp的段地址是ss,下面两句相当于
        ;pop bp
        ;add ax,bp
        mov bp,sp
        add ax,ss:[bp]
        
        mov ax,4c00h
        int 21h
codesg ends
end

 

10.6 转移地址在内存中的call指令

call word ptr 内存单元地址

相当于进行

push IP

jmp word ptr 内存单元地址

 

 

call dword ptr 内存单元地址

相当于

push CS

push IP

jmp dword ptr 内存单元地址

 

检测点 10.5

(1)

ax=3

在call word ptr ds:[0eh]时,ax=0,将下一条指令IP压入栈中,然后指令跳到ds:[0eh](实际上就是栈顶),所以实际上就是跳到了inc ax执行,最后得到ax = 3

 

(2)

assume cs:code
data segment
    dw 8 dup (0)
data ends
code segment
start:  mov ax,data
        mov ss,ax
        mov sp,16
        mov word ptr ss:[0],offset s;将标号s的地址存入ss:[0]
        mov ss:[2],cs
        call word ptr ss:[0]
        ;这里首先将CS=076bh和IP=19h压入栈中
        ;再跳转到ss:[0]中的地址处,也就是标号s处
        nop
s:      mov ax,offset s
        ;将标号s的地址放到ax=001ah
        sub ax,ss:[0ch]
        ;ss:[0ch]也就是IP=0019h
        ;ax=1ah-19h=1
        mov bx,cs
        ;bx=076bh
        sub bx,ss:[0eh]
        ;ss:[0eh]也就是CS=076bh
        ;bx=076bh-076bh=0
        
        mov ax,4c00h
        int 21h
code ends
end start

 

10.7 call和ret的配合使用

问题 10.1

assume cs:code
code segment
start:  mov ax,1
        mov cx,3
        call s;将下一指令偏移地址压入栈中,然后跳转到标号s
        mov bx,ax
        mov ax,4c00h
        int 21h
s:      add ax,ax
        loop s
        ret;pop IP,跳转到call s指令的下一指令
code ends
end start

 

利用call和ret可以实现子程序机制

 

10.8 mul指令

 (1)计算10*100

assume cs:code
code segment
start:  mov al,100
        mov bl,10
        mul bl
        
        mov ax,4c00h
        int 21h
code ends
end start

 

 

(2)计算100*10000

assume cs:codesg
codesg segment
    mov ax,100
    mov bx,10000
    mul bx
    
    mov ax,4c00h
    int 21h
codesg ends
end

 

10.9 模块化程序设计

 

10.10 参数和结果传递问题

assume cs:code
data segment
    dw 1,2,3,4,5,6,7,8
    dd 8 dup (0)
data ends
code segment
start:    mov ax,data
        mov ds,ax
        mov si,0
        mov di,16
        
        mov cx,8
s:        mov bx,[si]
        call cube
        mov [di],ax
        mov [di].2,dx
        add si,2
        add di,4
        loop s
        
        mov ax,4c00h
        int 21h
        
cube:    mov ax,bx
        mul bx
        mul bx
        ret
        
code ends
end start

 

10.11 批量数据的传递

将data段的字符串转换为大写

assume cs:codesg
datasg segment
    db 'heyyoubiubiubiu'
datasg ends
codesg segment
start:    mov ax,datasg
        mov ds,ax
        mov si,0
        mov cx,15
        call capital
        
        mov ax,4c00h
        int 21h
        
capital:    and byte ptr [si],11011111b
        inc si
        loop capital
        ret
codesg ends
end start

 

附注4 用栈传递参数

将需要的数据压入栈中,再由子程序出栈

计算(a-b)^3,a=3,b=1

assume cs:codesg
stacksg segment
    db 16 dup (0)
stacksg ends
codesg segment
start:    mov ax,stacksg
        mov ss,ax
        mov sp,16
        
        mov ax,1
        push ax
        mov ax,3
        push ax
        ;将,1,3分别入栈,ss:[sp]=3, ss:[sp+2]=1
        call difcube
        ;将IP入栈,ss:[sp]=IP, ss:[sp+2]=3, ss:[sp+4]=1
        
        mov ax,4c00h
        int 21h
        
difcube:    push bp
        ;将bp入栈,ss:[sp]=bp,ss:[sp+2]=IP, ss:[sp+4]=3, ss:[sp+6]=1
        mov bp,sp
        mov ax,[bp+4];3
        sub ax,[bp+6];1
        mov bx,ax
        mul bx
        mul bx
        pop bp
        ;bp出栈,ss:[sp]=IP, ss:[sp+2]=3, ss:[sp+4]=1
        ret 4
        ;IP出栈,ss:[sp]=3, ss:[sp+2]=1,并sp=sp-4=10h
codesg ends
end start

 

10.12 寄存器冲突问题

问题 10.2

子程序和主程序循环使用了同一个cx判断,可以将cx入栈

assume cs:codesg
datasg segment
    db 'word',0
    db 'unix',0
    db 'wind',0
    db 'good',0
datasg ends
codesg segment
start:    mov ax,datasg
        mov ds,ax
        mov bx,0
        
        mov cx,4
s:        push cx
        mov si,bx
        call capital
        pop cx
        add bx,5
        loop s
    
        mov ax,4c00h
        int 21h
        
capital:    mov cl,[si]
        mov ch,0
        jcxz ok
        and byte ptr [si],11011111b
        inc si
        jmp short capital
ok:        ret
codesg ends
end start

 

如果我们期望:

  • 1.调用子程序时,不必关心子程序的程序使用了哪些寄存器。
  • 2.编写子程序时,不必关心调用者使用了哪些寄存器。
  • 3.不会发生寄存器冲突

 

下面这种会更合适

assume cs:codesg
datasg segment
    db 'word',0
    db 'unix',0
    db 'wind',0
    db 'good',0
datasg ends
codesg segment
start:    mov ax,datasg
        mov ds,ax
        mov si,0
        
        mov cx,4
s:        call capital
        add si,5
        loop s
    
        mov ax,4c00h
        int 21h
        
capital:    push cx
        push si

change:    mov cl,[si]
        mov ch,0
        jcxz ok
        and byte ptr [si],11011111b
        inc si
        jmp short change
        
ok:        pop si
        pop cx
        
        ret
codesg ends
end start

 

实验 10 编写子程序

1.显示字符串

步骤:

  1. 将行列值转换为正确的偏移地址,保存到寄存器(起始偏移地址:(行数 - 1) * 160, 列数偏移地址:(列数 - 1) * 2)
  2. 读取字符和属性值,判断是否为0,不是,则存入显示缓冲区

 

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:    ;计算行的偏移地址,利用上次得到的结论,起始偏移地址:行数 * 160
            mov al,dh
            mov ah,0
            ;dec ax
            mov bx,160
            push dx;因为下面乘法是16位的,因此dx会被用作高位,原来保存颜色的值会消除
            mul bx
            pop dx
            mov di,ax
            
            ;结算列的偏移地址,地址0开始
            mov bl,dl
            mov bh,0
            dec bx
            add bx,bx
            
            ;显示缓冲区地址
            mov ax,0b800h
            mov es,ax
            
            ;颜色参数值(不变)和字符
            mov ah,cl
str:            mov al,ds:[si]
            
            ;检查是否遇到尾部的0
            mov cl,al
            mov ch,0
            jcxz ok
            
            ;将字符和属性存入显示缓冲区
            mov es:[bx+di],al
            mov es:[bx+di+1],ah
            
            inc si;字符偏移地址
            add bx,2;列的位置
            loop str

ok:            ret
code ends
end start

 

2.解决除法溢出

(1)除数:有8位和16位两种,在一个reg或者内存单元中

(2)被除数:默认放在AX或者DX和AX中,如果除数为8位,被除数为16位,默认放在AX中;如果除数为16位,被除数为32位,在DX和AX中存放,DX存高六位,AX存低六位。

(3)结果:如果除数为8位。则AL储存除法操作的商,AH储存除法操作的余数;如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数

 

1000/1溢出,al无法存下1000

assume cs:code
code segment
start:    mov bh,1
        mov ax,1000
        div bh
        
        mov ax,4c00h
        int 21h
code ends
end start

 

11000H/1,ax存放不下商

assume cs:code
code segment
start:    mov ax,1000h
        mov dx,1
        mov bx,1
        div bx
        
        mov ax,4c00h
        int 21h
code ends
end start

 

解决方法:

实际上,我们解决的方法是将除法拆分成了两个16位除数,32位被除数的除法

例如:1000000/10(F4240H/0AH)

我们将这个除法拆分成:高16位:000FH/0AH  低16位:4240H/0AH  两个除法式子

然后将第一个的结果放到dx,第二个的结果放到ax,余数放到cx

 

这里唯一不同的就是,我们高16位计算的时候,我们相当于把尾数的0省略了,所以真正结果需要乘上  16^尾数0的个数,上面例子就是16^4=65536。

通过理解 12341000/10 =  int(1234/10) * 10^4 + (rem(1234/10) * 10^4 + 1000) / 10  给的公式也就很好理解了

这里还需要知道,溢出的基本概念:超过寄存器表示范围的值被舍弃。

assume cs:code
stack segment
    dw 8 dup (0)
stack ends
code segment
start:    mov ax,stack
        mov ss,ax
        mov sp,16
        mov ax,4240h
        mov dx,00Fh
        mov cx,0ah
        call divdw
        
        mov ax,4c00h
        int 21h
        
divdw:    push ax
        ;高16位计算
          mov ax, dx
          mov dx, 0 
          div cx
          mov bx, ax
          ;低16位计算
          pop ax
      ;高16位计算的余数作为了低16位计算的高位,对应公式的rem(H/N)*65536 div cx ;低16位 mov cx, dx ;余数 mov dx, bx ret code ends end start

 

posted @ 2019-07-13 23:50  Hk_Mayfly  阅读(394)  评论(0编辑  收藏  举报