代码截图:
对其进行汇编链接之后,使用debug进行调试
使用u指令反汇编:
可见loop要跳转到的地址为13,s1偏移量的计算方法是s1上一条指令的偏移地址加上上一条指令的长度,上一条指令的便宜地址为11,而上一条指令B402的长度为2字节,所以计算出s1的偏移量为:11+2=13
然后我们运行至第一个loop之后,再进行一次反汇编:
这次s2的计算方法和上面的一致,上一条指令的偏移地址为39,而B402的长度为2字节,所以s2的偏移地址为39+2=41
源代码截图:
理论分析:
首先,第16~18行,将s1的偏移地址存入到了data段偏移量为0,1的两个字节中,将s2的偏移地址存入到了偏移量为2,3的两个字节中,将code段地址存到了偏移量为4,5的两个字节中
然后在第24行使用了call指令,call指令后的指令偏移量,也就是s1的偏移量入栈,并且跳转执行s1的代码,将栈顶字节出栈并赋给ax,ax也就是s1标记地址的偏移量
然后在第27行第二次使用call指令,call指令之后的参数为一个双字节指针,所以当前代码段的段地址cs和下一条指令的偏移地址也就是s2的偏移地址相继入栈,然后跳转执行s2的代码,先后将栈顶出栈并赋给了bx和cx,所以bx为s2标记地址的偏移量,cx为当前代码段也就是cs的段地址
运行并求证:
汇编链接代码,并使用debug调试:
运行到第一次call之后,可见确实执行了s1后的代码,ax的值也确实是s1所标记地址的偏移量
我们继续向下执行:
执行到第二次call,并运行两次pop之后,可见第二次call之后确实执行了s2之后的代码,并且bx的值确实是s2标记地址的偏移量,cx确实是当前代码段地址也就是cs寄存器中的内容
实验代码:
运行结果:
代码:
1 assume cs:code, ds:data 2 3 data segment 4 str db 'try' 5 len equ $ - str 6 data ends 7 8 code segment 9 start: 10 mov ax, data 11 mov ds, ax 12 mov si, 0 13 mov cx, 3 14 mov bl, 02h 15 mov bh, 10 16 call printStr 17 mov ah, 4ch 18 int 21h 19 printStr: 20 mov ax, 0b800h 21 mov es, ax 22 mov ax, 0a0h 23 mul bh 24 mov bp, ax 25 mov dx, 0 26 s: 27 mov al, ds:[si] 28 mov ah, bl 29 mov es:[bp], ax 30 inc si 31 add bp, 2 32 loop s 33 ret 34 code ends 35 end start
运行结果:
源代码:
assume cs:code, ds:data data segment stu_no db '201983290396' len = $ - stu_no data ends code segment start: mov ax, data mov ds, ax mov bp, 0 mov ax, 0b800h mov es, ax mov bx, 1 mov cx, 2080 s1: mov ah, 17h mov es:[bx], ah add bx, 2 loop s1 mov bx, 3840 call printMinus mov cx, 12 call printStr call printMinus mov ax, 4c00h int 21h printMinus: mov cx, 34 s2: mov al, '-' mov es:[bx], al add bx, 2 loop s2 ret printStr: mov bp, 0 s3: mov al, ds:[bp] mov es:[bx], al add bx, 2 add bp, 1 loop s3 ret code ends end start
运行结果:
这次的实验主要锻炼了我运行call和ret指令进行代码块封装和重用的能力,运用标记和ret可以封装一部分代码,并且可以通过call指令调用这段代码,这样就可以避免代码的重复冗余,节省了代码编写的时间。此外,我还学习到了很多在dos命令行上显示字符的知识,能够在命令行的任意位置以任何颜色显示想要显示的字符。