实验任务1
任务1-1
ds = 076A; ss = 076B; cs = 076C;
X-2; X-1;
任务1-2
ds = 076A; ss = 076B; cs = 076C;
X-2; X-1;
任务1-3
ds = 076A; ss = 076C; cs = 076E;
X -4; X-2;
任务1-4
ds = 076C; ss = 076E; cs = 076A;
X + 2; X + 4;
任务1-5
问题一
\[\lceil N / 16\rceil * 16 个字节 \]问题二
我的猜想是第四组代码(task1_4.asm)可以正确执行。在将上述4组代码最后的end start
改为end
之后,如果按照编译、链接、执行的顺序依次下来,结果仍然是什么都没有(因为没有对显存进行操作)。在这一过程中,三个步骤除了链接时会警告没有栈段之外(这个警告即使不修改代码也会出现),均不会出现警告。而在自行根据代码执行到上述实验所要求的那一行后,得到的ds、ss的值也相同,即都可以正确执行。唯一不同的在于cs,但是cs:ip
最终指向的内存地址也相同。
其中,有区别的在于,由于没有指定代码段的起始地址,前三个都需要自行计算代码段,然后根据偏移地址用g
命令执行程序。
这可能是因为程序是自顶向下执行,虽然没有从指定的代码段处开始,但是由于数据段和栈段是空的,所以不会影响后面的代码段。在这样的情境下,程序都会执行到终止的指令为止。虽然第四组代码的执行过程可能与修改前一致,但是由于栈段和数据段并没有会修改程序执行的指令,所以这四组代码最终都可以正确执行。
实验任务2
分析
向b800:0f00~b800:0f9f
这连续160字节依次重复填充十六进制数据03 04
。
由于是小端存储,所以十六进制数据0304
代表的十六进制数是0403h
。这一十六进制数占2个字节,如果用循环语句来实现的话,就是要循环80次。
代码
assume cs:code
code segment
start: mov ax, 0b800h
mov ds, ax
mov bx, 0f00h
mov ax, 0403h
mov cx, 80
s: mov [bx], ax
add bx, 2
loop s
mov ax, 4c00h
int 21h
code ends
end start
截图
实验任务3
分析
由于这一程序是一个整体,那么三个数据段data1 data2 data3
和代码段code
应该是连续存储。前两个代码段长度不到16字节,那么按16字节存储。这样一来三个数据段的地址都明确了。可以让ds指向第一个数据段data1
进行处理。而debug都是将程序从076A:0
开始装入,那么可以认为第一个数据段哦才能够这里开始。
代码
assume cs:code
data1 segment
db 50, 48, 50, 50, 0, 48, 49, 0, 48, 49 ; ten numbers
data1 ends
data2 segment
db 0, 0, 0, 0, 47, 0, 0, 47, 0, 0 ; ten numbers
data2 ends
data3 segment
db 16 dup(0)
data3 ends
code segment
start: mov ax, 076ah
mov ds, ax
mov bx, 0
mov cx, 10
s: mov ax, 0
add ax, [bx]
add ax, [bx+10h]
mov [bx+20h], ax
add bx, 1
loop s
mov ah, 4ch
int 21h
code ends
end start
截图
装入debug并调试
运行前逻辑段
运行后逻辑段
实验任务4
代码
assume cs:code
data1 segment
dw 2, 0, 4, 9, 2, 0, 1, 9
data1 ends
data2 segment
dw 8 dup(?)
data2 ends
code segment
start: mov ax, data2
mov ss, ax
mov sp, 16
mov ax, data1
mov ds, ax
mov bx, 0
mov cx, 8
s: mov ax, [bx]
push ax
add bx, 2
loop s
mov ah, 4ch
int 21h
code ends
end start
截图
装入debug并调试
运行前逻辑段
运行后逻辑段
实验任务5
代码
assume cs:code, ds:data
data segment
db 'Nuist'
db 2, 3, 4, 5, 6
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 0b800H
mov es, ax
mov cx, 5
mov si, 0
mov di, 0f00h
s: mov al, [si]
and al, 0dfh
mov es:[di], al
mov al, [5+si]
mov es:[di+1], al
inc si
add di, 2
loop s
mov ah, 4ch
int 21h
code ends
end start
运行结果
装入debug并运行
line19的作用
通过位与运算,将(从左到右)第三位转换为0,从而实现小写字母转换成大写字母
line4的作用
改成全2
改成全5
这里数值的作用应该是控制显存中内容的颜色
实验任务6
代码
assume cs:code, ds:data
data segment
db 'Pink Floyd '
db 'JOAN Baez '
db 'NEIL Young '
db 'Joan Lennon '
data ends
code segment
start: mov ax, data
mov ds, ax
mov bx, 0
mov cx, 4
s: mov ax, [bx]
or ax, 2020h
mov [bx], ax
mov ax, [bx+2]
or ax, 2020h
mov [bx+2], ax
add bx, 10h
loop s
mov ah, 4ch
int 21h
code ends
end start
装入debug并调试
运行前逻辑段
运行后逻辑段
\[neil \space young, yyds! \]实验任务7
分析
data
段第一行5个数字,按字符存储,每个字符占1个字节,故一个数字占4个字节,第一行共占用20个字节,每一个数据要分两次移动。第二、第三行按字存储,每个数据固定占2个字节,每一个数据一次即可移动。table
段初始化了5行16字节的空格。本题中用到的除法,除数占1个字即2个字节,共16位,那么被除数就需要占32位,即4个字节,高位放在dx
中,低位放在ax
中,由于被除数也是按字存储,所以dx
初始化为0即可。
对于整个程序的控制是按照逻辑段table
中格式进行。年分和收入需要4个字节,就用bx
控制,循环末自加4。雇员数和人均收入需要2个字节,就用di
控制,循环末自加2。
代码
assume cs:code, ds:data, es:table
data segment
db '1975', '1976', '1977', '1978', '1979'
dw 16, 22, 382, 1356, 2390
dw 3, 7, 9, 13, 28
data ends
table segment
db 5 dup( 16 dup(' ') ) ;
table ends
code segment
start: mov ax, data
mov ds, ax
mov bx, 0
mov ax, table
mov es, ax
mov ax, 0
mov si, ax
mov di, ax
mov cx, 5
s: mov ax, [bx]
mov es:[si], ax
mov ax, [bx]+2
mov es:[si]+2, ax
mov dx, 0
mov es:[si]+5, dx
mov ax, [bx]+20
mov es:[si]+7, ax
div word ptr ds:[di]+30
mov es:[si]+13, ax
mov ax, [di]+30
mov es:[si]+10, ax
add di, 2
add bx, 4
add si, 16
loop s
mov ah, 4ch
int 21h
code ends
end start
运行前的逻辑段
运行后的逻辑段
这里实际上由于题设的代码中收入是按字存储,所以实际上并没有必要在表格中给收入分配4个字节的存储空间,所以我把高两个字节初始化为了0,其他部分可以通过计算证明正确。