实验任务1:
task1.asm代码:
assume cs:code,ds:data ;两个之间要用 , 分隔否则报错 data segment db 'Welcome to masm!' ;数据段中保存显示的字符串 data ends code segment start: mov ax,data mov ds,ax mov ax,0b800h mov es,ax ;es保存目标显存段地址 mov bx,720h ;bx保存第一行目标显存偏移地址的起始地址 mov cx,16 ;cx保存循环次数,此处一共需要输入16个字符 mov si,0 mov di,0a0h s: mov al,ds:[si] ;移入al,用以确保每次移入内存1个字节 mov es:[bx],al ;偏移地址:bx——第一行
mov es:[bx+di],al ;偏移地址:bx+di——第二行(di=0a0h=160) mov es:[bx+di+0a0h],al ;偏移地址:bx+di+0a0h——第三行 inc bx ;bx自增一次,接下来分别加入各行的属性控制 mov byte ptr es:[bx],02h ;0 000 0 010 B —— 黑(000)底绿(010)字 mov byte ptr es:[bx+di],24h ;0 010 0 100 B —— 绿(010)底红(100)字 mov byte ptr es:[bx+di+0a0h],71h ;0 111 0 001 B —— 白(111)底蓝(001)字 inc si ;si自增继续读入数据段中下一个字符 inc bx ;bx再次自增,指定下一个目标内存地址偏移 loop s mov ah, 4ch int 21h code ends end start
编译、连接,运行结果截图:
(1) 显示属性:
使用8位(1字节)进行表示
使用mov指令是时, 移入数据至内存空间,需要指明数据类型 否则报错 operand must have size
(2)屏幕*位置的计算:
一行显示80个字符,显示一个字符需要2字节(内容 属性),一行共160字节
正中间三行为Line 11、12、13;
目标显存第一个字符的偏移地址为:1760+(160/2 - 1)- 15 = 1824 = 0720H
第一行:0720H~073FH 之后两行对应位置的偏移量分别加上 160 (0a0H)和 320 (0a0H + 0a0H);
实验成功。
实验任务2:
task2.asm源代码:
assume cs:code, ds:data data segment str db 'try', 0 ;str 'another try',0 data ends code segment start: mov ax, data mov ds, ax mov si, offset str mov al, 2 ;mov al,4 call printStr mov ah, 4ch int 21h printStr: push bx push cx push si push di mov bx, 0b800H mov es, bx mov di, 0 s: mov cl, [si] mov ch, 0 jcxz over mov ch, al mov es:[di], cx inc si add di, 2 jmp s over: pop di pop si pop cx pop bx ret code ends end start
(1)原始代码编译、连接,执行结果:
(2)修改:将代码中数据段中字符串修改为 :‘another try’,0
…………………………
data segment str db 'another try', 0 data ends
…………………………
mov al,4 ;mov al,2
…………………………
编译、连接,执行结果:
问题:
1 assume cs:code, ds:data 2 data segment 3 str db 'another try', 0 4 data ends 5 6 code segment 7 start: 8 mov ax, data 9 mov ds, ax 10 11 mov si, offset str ;str为数据段起始偏移地址 ——— 设置数据段入口参数 12 mov al, 4 ;设置字符显示属性 00000 100B——红色 13 call printStr 14 15 mov ah, 4ch 16 int 21h 17 18 printStr: 19 push bx 20 push cx 21 push si 22 push di 23 24 mov bx, 0b800H 25 mov es, bx 26 mov di, 0 27 s: mov cl, [si] ;cl保存当前读入数据段中的字符; 28 mov ch, 0 ;设置ch=0,若cl读入0,即已读入字符串末尾;则cx=0 29 jcxz over ;当cx = 0 jcxz跳转条件成立,跳转至over处,即子程序执行结束 30 mov ch, al 31 mov es:[di], cx 32 inc si 33 add di, 2 34 jmp s 35 36 over: pop di 37 pop si 38 pop cx 39 pop bx 40 ret 41 42 code ends 43 end start
(3)基于运行结果,理解源代码:
组合使用转移指令call和ret实现子程序的原理与方法:
具体地,在line18-49中:
①line19-22,line36-39,这组对称使用的push、pop,这样用的目的是什么?
功能:子程序开始之前,将主程序中使用的保存着主程序参数的寄存器的值存入栈中,在子程序功能结束之后,恢复寄存器中参数;
目的:保持主干程序的参数在调用子程序前后一致;
由于寄存器数量有限,子程序在实现其功能时有可能会改变保存着主程序重要参数的寄存器的值,导致主程序执行出错;在子程序运行之前,先将寄存器值全部入栈,子程序调用完成后恢复寄存器值。无法预料子程序中会用到哪些寄存器,索性统一全部进行入栈,结束后出栈恢复的操作。
②line31的功能是什么?
每次移入一个字数据cx至目标显存,cl:目标内容,ch:目标属性;
实验任务3:
task3.asm源代码为:
assume cs:code, ds:data data segment ;数据段共占据20H个字节 x dw 1984 ;存储长度为一个字两字节, str db 16 dup(0) ;存储长度为一个字节,共16字节,两段数据共17字节,补全至32字节存储 data ends code segment start: mov ax, data mov ds, ax ;将ds段寄存器设置为数据段开始 mov ax, x ;反汇编查看实际代码为:mov ax,[0000] ds:[0000]=07C0 mov di, offset str ;设置偏移量di,为后续保存结果设置入口参数 offset str = 0002 call num2str mov ah, 4ch int 21h num2str: push ax ;进入子程序先保存参数 push bx push cx push dx mov cx, 0 mov bl, 10 ;设置除数,8位 s1: div bl ;被除数默认保存在AX中 inc cx ;记录位数,作为s2中loop指令的循环次数 mov dl, ah ;将AH中的余数移入dl push dx ;将DX入栈,(出栈入栈以字为单位 mov ah, 0 cmp al, 0 ;若此次除法结果的商为0,表示已取到所有数字;cmp al,0 若al=0,指令执行后zf=1 jne s1 ;若zf=0,回到s1再次执行 s2: pop dx ;取出一个字 or dl, 30h ;将dl中保存的数字转换为字符 mov [di], dl ;[di]为str的起始偏移地址 将结果保存到str:依次为1 9 8 4 inc di inc ax ;依旧保存循环次数 loop s2 ;循环四次 mov cx, ax ;赋给CX mov bx, 0b800H mov es, bx ;es保存显存段地址 mov al, 2 ;设置输出字符串属性值 mov di, offset str ;取需要复制的数据的起始偏移地址,段地址在ds中 mov si, 0 ;目标显存的偏移地址 s3: push cx ;保存cx,因为代码中改变了其值 mov cl, [di] ;低位保存内容 mov ch,al ;高字节保存属性
mov es:[si], cx ;内容+属性入栈 inc di ;di加1,继续下一个字符 add si, 2 ;目标地址偏移加2 pop cx ;还原cx的值 loop s3
pop dx pop cx pop bx pop ax ret code ends end start
运行结果:
(1)查看程序段内容是否改变:
已经成功转换为字符串,并保存在str开始的内存中;
(2)加入输出代码后能输出;
改变数字:结果如下,
输出成功。
实验任务4:
task4.asm源代码:
1 assume cs:code, ds:data 2 data segment 3 str db 80 dup(?) 4 data ends 5 6 code segment 7 start: 8 mov ax, data 9 mov ds, ax 10 mov si, 0 11 12 s1: 13 mov ah, 1 14 int 21h 15 mov [si], al 16 cmp al, '#' 17 je next 18 inc si 19 jmp s1 20 next: 21 mov cx, si ;确定输出时的循环次数 22 mov si, 0 23 s2: mov ah, 2 24 mov dl, [si] 25 int 21h 26 inc si 27 loop s2 28 29 mov ah, 4ch 30 int 21h 31 code ends 32 end start
运行结果:
①line12-19实现的功能:
当前输入字符不为‘#’时,持续从键盘读入字符并在屏幕上显示;
并将输入的字符保存在data段预留的内存中;
②line21-27实现的功能:
在屏幕上输出读入的字符(不包括最后的‘#’);
实验任务5:
反汇编代码:
#include <stdio.h> int sum(int,int); int main() { int a = 2, b = 7, c; c = sum(a, b); return 0; } int sum(int x, int y) { return (x + y); }
设置断点,开始调试,查看反汇编:
分析反汇编代码,从汇编的角度,观察高级语言中参数传递和返回值是通过什么实现
①高级语言中参数传递和返回值是通过什么实现的:通过寄存器和栈来实现;
此处为值传递,被调函数的形参作为被调函数的局部变量,在该函数的栈中开辟内存空间以存放主函数传递进来的实参值,对参数的操作不影响主函数实参的值。
②参数的入栈顺序:参数入栈根据参数列表中的顺序,从右往左;
③返回值的带回方式:结果保存在寄存器中带回。