实验3 转移指令跳转原理及其简单应用编程
实验任务 1
<task3_1.asm>
1 assume cs:code, ds:data 2 3 data segment 4 x db 1, 9, 3 5 len1 equ $ - x 6 7 y dw 1, 9, 3 8 len2 equ $ - y 9 data ends 10 11 code segment 12 start: 13 mov ax, data 14 mov ds, ax 15 16 mov si, offset x 17 mov cx, len1 18 mov ah, 2 19 s1:mov dl, [si] 20 or dl, 30h 21 int 21h 22 23 mov dl, ' ' 24 int 21h 25 26 inc si 27 loop s1 28 29 mov ah, 2 30 mov dl, 0ah 31 int 21h 32 33 mov si, offset y 34 mov cx, len2/2 35 mov ah, 2 36 s2:mov dx, [si] 37 or dl, 30h 38 int 21h 39 40 mov dl, ' ' 41 int 21h 42 43 add si, 2 44 loop s2 45 46 mov ah, 4ch 47 int 21h 48 code ends 49 end start
运行结果:
运算符offset:返回数据标号的偏移地址,按字节计算
伪指令equ:给字符串,寄存器,指令等取别名
预定义符号$:当前指令的偏移地址
反汇编结果:
由反汇编得到的机器码可知,位移量为F2(补码),即-14(十进制原码)。
从CPU角度,
(1)先读取E2 F2进入指令缓冲器
(2)IP指向下一条指令的偏移地址,即001B
(3)执行指令缓冲器中的指令E2 F2,IP(001B)加上位移量 F2,27-14=13,000DH
(4)执行之后(IP)=000DH
反汇编结果:
方法同问题1完全相同,位移量为F0(补码),即-16(十进制原码)
从CPU角度,
(1)先读取E2 F0进入指令缓冲器
(2)IP指向下一条指令的偏移地址,即0039H
(3)执行指令缓冲器中的指令E2 F0,IP(0039H)加上位移量 F0,0029H
(4)执行之后(IP)=0029H
实验任务2
<task3_2.asm>
1 assume cs:code, ds:data 2 3 data segment 4 dw 200h, 0h, 230h, 0h 5 data ends 6 7 stack segment 8 db 16 dup(0) 9 stack ends 10 11 code segment 12 start: 13 mov ax, data 14 mov ds, ax 15 16 mov word ptr ds:[0], offset s1 17 mov word ptr ds:[2], offset s2 18 mov ds:[4], cs 19 20 mov ax, stack 21 mov ss, ax 22 mov sp, 16 23 24 call word ptr ds:[0] 25 s1: pop ax 26 27 call dword ptr ds:[2] 28 s2: pop bx 29 pop cx 30 31 mov ah, 4ch 32 int 21h 33 code ends 34 end start
分析过程:
s1的偏移地址送到ds:[0],s2的偏移地址送到ds:[2];
call word ptr指令执行的时候,首先把下一条指令的偏移地址入栈,相当于push IP
call dword ptr,下一条指令的段地址和偏移地址都要入栈,相当于push CS,push IP
line 24 执行时,IP指向s1,把s1的偏移地址0021入栈,然后jmp到ds:[0]执行pop ax,此时ax=0021;
line 27 执行时,IP指向s2,依次把s2的段地址076C和偏移地址0026入栈,然后jmp到ds:[2]执行pop bx,此时bx=0026;再执行pop cx,此时cx=076C;
综上所述,ax=0021,bx=0026,cx=076C
调试过程:
单步执行,ds:[0]存储0021,pop ax 后ax为0021
执行到mov ah,4c,各寄存器内容如图,符合预期:
实验任务3
编写8086汇编源程序task3.asm,在屏幕上以十进制形式输出data段中这一组连续的数据,数据和数据 之间以空格间隔。
<task3_3.asm>
1 assume cs:code,ds:data 2 3 data segment 4 x db 99,72,85,63,89,97,55 5 len equ $-x 6 data ends 7 8 code segment 9 start: 10 mov ax,data 11 mov ds,ax 12 mov bx,0 13 mov cx,len 14 s: call printNumber 15 call printSpace 16 add bx,1 17 loop s 18 mov ax,4c00h 19 int 21h 20 printNumber: 21 push ax 22 push bx 23 push cx 24 push dx 25 26 mov si,0 ;记录位数 27 ;经过调试发现,若直接mov ax,ds:[bx],ax高位存放的是代表十六进制数的H,最后除法的时候会有问题 28 mov ah,0 29 mov al,ds:[bx] ;低位放真正的被除数 30 ;mov ax,ds:[bx] 31 convert: 32 mov dx,0 ;被除数高位置0,防止干扰 33 mov cx,10 ;除数 34 div cx 35 36 add dx,30h ;余数转为字符 37 push dx ;分离出的字符暂时保存到栈;堆栈操作是以字为单位 38 39 inc si 40 mov cx,ax ;商为0结束 41 jcxz back 42 jmp convert 43 back: 44 mov cx,si 45 s1: 46 pop bx 47 mov ah,2 48 mov dl,bl 49 int 21h 50 loop s1 51 52 pop dx 53 pop cx 54 pop bx 55 pop ax 56 ret 57 printSpace: 58 mov ah,2 59 mov dl,32 60 int 21h 61 ret 62 code ends 63 end start
数值在debug中以十六进制展示(实际上以二进制存储)
在把数据放入被除数ax寄存器时,因为是字节数据,若直接mov ax,ds:[bx],会把代表十六进制的H也赋给AH,导致后续除法结果错误,如下图数值99(十六进制为63H)存放为4863:
修改方法:让ah为0,al存放被除数,运行结果:
本题因为使用栈暂时存放数据,所以使用16位除数,若不使用栈,也可以用8位寄存器作除数。
实验任务4
<task3_4.asm>
1 assume cs:code,ds:data 2 data segment 3 str db 'try' 4 len equ $ - str 5 data ends 6 code segment 7 start: 8 mov ax,data 9 mov ds,ax 10 11 mov bh,0 ;指定行 12 mov bl,2 ;字符串颜色 13 mov cx,len ;字符串长度 14 mov si,0 15 call printStr 16 17 mov bh,24 ;指定行 18 mov bl,4 ;字符串颜色 19 mov cx,len ;字符串长度 20 mov si,0 21 call printStr 22 mov ax,4c00h 23 int 21h 24 printStr: 25 push bx 26 push cx 27 push si 28 29 mov ax,0b800h 30 mov es,ax 31 mov ax,160 32 mul bh ;ax=显示区域的偏移地址 33 34 mov di,ax 35 mov cx,len 36 s0: 37 mov ax,ds:[si] 38 mov es:[di],ax 39 mov es:[di+1],bl 40 inc si 41 add di,2 42 loop s0 43 44 pop si 45 pop cx 46 pop bx 47 ret 48 code ends 49 end start
运行结果:
实验任务5
在80×25彩色字符模式下,在屏幕最后一行正中间显示学号。要求输出窗口蓝底,学号和两侧折线,以 白色前景色显示。
<task3_5.asm>
1 assume cs:code,ds:data 2 data segment 3 stu_no db '201983300444' 4 len = $ - stu_no 5 data ends 6 code segment 7 start: 8 mov ax,data 9 mov ds,ax 10 mov ax,0b800h 11 mov es,ax 12 13 mov si,0 ;偶地址存放字符 14 mov bx,1 ;奇地址存放颜色 15 mov cx,2000 16 s: 17 mov al,17h 18 mov es:[bx],al 19 add bx,2 20 loop s 21 mov bx,0f00h 22 call printLine 23 call printNum 24 mov bx,0f5ch 25 call printLine 26 mov ax,4c00h 27 int 21h 28 29 printLine: 30 mov cx,34 31 s1: 32 mov byte ptr es:[bx],'-' 33 add bx,2 34 loop s1 35 ret 36 37 printNum: 38 mov cx,len 39 s2: 40 mov al,[si] 41 mov es:[bx],al 42 add bx,2 43 inc si 44 loop s2 45 ret 46 code ends 47 end start
设置背景颜色和前景文字颜色:直接设置属性即可,不用管字符。注意属性只占一个字节
测试结果:
总结与分析:
1、注意操纵的是字节还是字
2、注意循环的跳出时机,跳出条件,跳出后续操作
3、灵活使用堆栈