1.将内存fff:0~fff:b中的数据复制到0:200~0:20b单元中
要求:分别采用堆栈和常规访问实现
assume cs:codes
codes segment
start:
;第一种方式采取传统访址
; mov bx,0
; mov cx,12
;用字节复制12次
; s:mov ax,0FFFFh
; mov ds,ax
; mov dh,[bx]
; mov ax,0000h
; mov ds,ax
; mov [bx+200h],dh
; inc bx
; Loop s
; mov ah,4ch
; int 21h
;第二种方式采取堆栈访址
mov bx,0
;堆栈以字为处理单位循环6次
mov cx,6
mov ax,0000h
;设置ss,sp
mov ss,ax
;当前sp为中转地址
mov sp,021ch
s:mov ax,0FFFFh
mov ds,ax
mov dx,[bx]
push dx
mov ax,0000h
mov ds,ax
pop [bx+200h]
add bx,2
Loop s
mov ah,4ch
int 21h
codes ends
end start
结果如下
2.从键盘输入两个1位的十进制数,相加以后以蓝底黄字显示在屏幕的第5行第16列。
assume cs:codes
datas segment
str1 db 'Please input first number:','$'
str2 db 'Please input second number:','$'
str3 db 0Ah,'$'
codes segment
start:
;调用系统清屏功能便于显示
mov ax,0003h
int 10h
;设置ds
mov ax,datas
mov ds,ax
;输出第一个提示信息
lea dx,str1
mov ah,9
int 21h
;读入第一个数字
mov ah,1
int 21h
mov cl,0
;转成数值大小存入cl
sub al,48
mov cl,al
;输出换行
lea dx,str3
mov ah,9
int 21h
;输出第二个提示信息
lea dx,str2
mov ah,9
int 21h
;读入第二个数字
mov ah,1
int 21h
;转成数值大小
sub al,48
;相加得值
add cl,al
;设置显存段地址
mov ax,0b800h
mov es,ax
;比较加跳转
;如何是9以上跳入L1执行
cmp cl,9
jg L1
;显示两位数,其中十位始终为 0,个位直接加48显示
mov bl,48
mov es:[340h],bl
mov byte ptr es:[341h],1eh
add cl,48
mov es:[342h],cl
mov byte ptr es:[343h],1eh
mov ah,4ch
int 21h
L1:
;显示两位数,其中十位始终为 1,个位减10加48显示
mov bl,49
mov es:[340h],bl
mov byte ptr es:[341h],1eh
sub cl,10
add cl,48
mov es:[342h],cl
mov byte ptr es:[343h],1eh
mov ah,4ch
int 21h
codes ends
end start
结果如下
1.编程实现书上问题7.9
assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov si,0
mov cx,4
s:
;记得循环嵌套保存cx
mov ax,cx
mov cx,4
mov si,0
s0:
and byte ptr [bx+si+3],11011111b
inc si
Loop s0
add bx,16
mov cx,ax
Loop s
mov ah,4ch
int 21h
codesg ends
end start
效果如下
2.实验七
assume cs:codesg
data segment
db '1975','1976','1977','1978','1979','1980'
db '1981','1982','1983','1984','1985','1986'
db '1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup('your summ ne ?? ')
table ends
codesg segment
start:
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
mov si,0
mov di,0
mov bx,0
mov cx,21
s:
;year
mov ax,ds:[si]
mov es:[di],ax
mov ax,ds:[si+2]
mov es:[di+2],ax
;income
mov ax,ds:[si+84]
mov es:[di+5],ax
mov dx,ds:[si+84+2]
mov es:[di+7],dx
;number
mov bp,ds:[bx+168]
mov es:[di+10],bp
;直接用内存单元中的数做除数
div word ptr ds:[bx+168]
mov es:[di+13],ax
;步长不同
add si,4
add bx,2
add di,16
Loop s
mov ah,4ch
int 21h
codesg ends
end start
效果如下
3.将任意输入的十进制数(<32768) 转换成四位十六机制进行输出显示。
要求:
1)有提示信息
2)按下ESC退出程序
3)将结果显示在屏幕中间
assume cs:codesg,ds:datasg
datasg segment
str1 db 0ah,'Please input a number:','$'
str2 db 0,0,0,0,0,0 ;前两个字节存放输入数值;后四个字节存放每四个二进制数对应的十六进制数
str3 db '0123456789ABCDEF','$'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov ss,ax
mov ax,3
int 10h
start1:
lea dx,str1
mov ah,9
int 21h
lea bx,str2
mov word ptr [bx],0
mov word ptr [bx+2],0
mov word ptr [bx+4],0
call input
lea dx,str3
call shift
call output
jmp start1
mov ah,4ch
int 21h
;读入数据模块,直接存储数值大小
input:
mov ah,1
int 21h
mov ah,0
cmp al,27
jz quit
sub al,48
cmp al,9
jg myret
cmp al,0
jl myret
mov bp,ax
mov ax,[bx]
mov cx,10
mul cx
mov [bx],ax
add [bx],bp
jmp input
myret:
ret
quit:
mov ah,4ch
int 21h
;进制转换模块
shift:
mov al,[bx+1]
and al,11110000b
mov cl,4
rol al,cl
mov ah,0
mov si,ax
add si,dx
mov cl,ss:[si]
mov [bx+2],cl
mov al,[bx+1]
and al,00001111b
mov ah,0
mov si,ax
add si,dx
mov cl,ss:[si]
mov [bx+3],cl
mov al,[bx]
and al,11110000b
mov cl,4
rol al,cl
mov ah,0
mov si,ax
add si,dx
mov cl,ss:[si]
mov [bx+4],cl
mov al,[bx]
and al,00001111b
mov ah,0
mov si,ax
add si,dx
mov cl,ss:[si]
mov [bx+5],cl
ret
;显示模块采用直接写显存的方法
output:
mov ax,0b800h
mov es,ax
mov dl,[bx+2]
mov es:[2158],dl
mov dl,[bx+3]
mov es:[2160],dl
mov dl,[bx+4]
mov es:[2162],dl
mov dl,[bx+5]
mov es:[2164],dl
ret
codesg ends
end start
效果如下
1.实验10 (3 个程序)
第一个程序
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: push ax
push bx
push cx
push dx
push es
push di
push si
;根据上节中的框架,为了不让子程序干扰主程序中寄存器的值,将所有子程序会用到的寄存器进行压栈
mov ax, 0b800h
mov es, ax
;颜色区的段地址
mov al, 160
mul dh
;每行占160个字节,乘以行数
push ax
;将行计算的结果存储到栈中
mov al, 2
mul dl
;每列占2个字节,乘以列数
pop bx
;将上次运算的结果(160×行数)的值转移到bx中
add bx, ax ;此时的ax值为(2×列数)
;将两者相加,最终结果保存到bx中
mov dl, cl
;因为下面的跳转指令jcxz需要用到cx寄存器,故需要将cl的值先保存在dl中
mov di, 0
change: mov cl, ds:[di]
mov ch, 0
jcxz ok
mov ch, dl
mov es:[bx+si], cx
add si, 2
inc di
jmp short change
ok: pop si
pop di
pop es
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start
第二个程序
assume cs:code
data segment
dw 16 dup(0)
;此数据段用来临时存放数据
data ends
code segment
start: mov ax, 4240h
mov dx, 000fh
mov cx, 0ah
mov bx, data
mov ds, bx
call divdw
mov ax, ds:[0]
mov dx, ds:[2]
mov cx, ds:[4]
mov ax, 4c00h
int 21h
divdw: push ds
push dx
push cx
push ax
mov ax, dx
mov dx, 0
div cx
;ax存放商,dx存放余数
;根据公式,使用被除数高位除以除数得到的商×65536
;*65536等价于在低位加16个0,因此操作就会变得非常简单
;使用被除数高位除以除数得到的余数×65536+被除数的低位,再将得到的结果除以除数
;两者的结果相加,即可得到32位/16位的无溢出结果
push dx
;使用栈临时保存余数
mov dx, ax
mov ax, 0
mov ds:[0], ax
mov ds:[2], dx
pop dx
;弹出余数,作为右操作数中被除数的高16位
pop ax
;得到被除数的低16位
push ax
;恢复栈顶数据,避免对主程序造成干扰
;将右操作数[]中的左操作数的低16位和被除数的低16位相加
;但是右操作数[]中的左操作数的低16位一定是全0的,因此我们可以省略这一步
;直接执行pop ax,将被除数的低16位作为右操作数的低16位
div cx
;ax存放商,dx存放余数
;由于左操作数的低16位一定是全0,所以不必与其相加,直接将
;右操作数的低16位存储到ds:[0]内存单元即可
mov ds:[0], ax
;商的低16位放到ds:[0]单元中
mov ds:[4], dx
;余数放到ds:[4]单元中
;ds:[2]中一直保存的都是商的高16位,且没有被更改过,因此无须任何操作
pop ax
pop cx
pop dx
pop ds
ret
code ends
end start
第三个程序
assume cs:code
data segment
dw 123, 12666, 1, 8, 3, 38
data ends
ascii segment
db 100 dup(0)
;ascii码值,一个字节即可存储
ascii ends
div segment
dw 16 dup(0)
;除法溢出计算需要使用该数据段来临时保存结果
div ends
code segment
start: mov bx, data
mov ds, bx
;ds段寄存器用来存放待处理的数据
mov si, 0
call dtoc
mov cx, 6
mov ax, 0
mov dh, 8
show: push cx
mov dl, 3
mov cl, 2
call show_str
pop cx
;我们需要更改行号来避免覆盖
inc dh
loop show
mov ax, 4c00h
int 21h
dtoc: ;该子程序用于将数值型的数字转换为字符串
;十进制数值转换为ASCII码值,转换关系为:ascii=10进制+30H
;要想将一个十进制的整数拆分成一个一个的数值,那我们需要让这个数
;除以10,然后将得到的结果依次入栈,除完之后再依次出栈,即可得到由高位到低位
;的所有数值,之后将这些值加上30H,即得到其对应的ASCII码值,然后将这些
;ASCII码值存放到一个数据段中,调用show_str函数,来在屏幕上显示这些数值
;为了存储转换后的ASCII码值,我们需要新开辟一个数据段
push ax
push bx
push cx
push dx
push ds
push si
push es
mov ax, ascii
mov es, ax
;使用es段寄存器来存储转换后的ascii码值
mov di, 0
;存储ASCII数据时用来指向ascii段中的每个内存单元
mov cx, 6
loop_zone: push cx
;因为内层循环会更改cx的值,所以我们需要使用栈结构来保存cx的值
mov dx, 0
;记录十进制数据的位数
mov ax, ds:[si]
;ax存放被除数
split: push dx
;下面要用到dx寄存器,因此我们先保存dx
mov cx, 0ah
;cx存放除数
mov dx, 0
;dx作为被除数高16位,置0
div cx
;32/16的除法运算,商存储在ax中,余数存储在dx中
mov cx, dx
;call divdw
;其实用不着调用divdw,这个除法溢出问题不是真正的除法溢出问题
;我们只需要将被除数凑成32位的,除数当做16位的即可
;此程序返回运算后的商和余数,分别保存在ax和cx中
;如果被除数大于2550,al是无法存放商的,会造成溢出,因此,我们需要调用本实验中第二个函数
;专门用于解决除法溢出问题的函数,虽然程序2解决的是32/16的除法运算的溢出问题,但是对于16/8位的
;除法运算也是完全适用的
;由于入栈时只能使用字型数据,所以我们压入的是ax,此时需要将
;无关数据,也就是al置0
pop dx
;取出dx更改前的值
push cx
;余数入栈
inc dx
;当循环终止的时候可以进行弹栈存储操作了,但是我们需要一个标记,来标识我们需要
;弹出多少次,我们使用dx来进行存储
mov cx, ax
add cx, dx
;ax中的值在下一次运算中一定会用到,dx中的值也有可能会用到(当被除数很大时)
;此时可以临时保存数据的只有cx了,因此我们直接将运算结果放到cx中
;一举两得
jcxz ok1
;处理过程是需要循环的,循环结束的条件是商==0
;我们只需要将执行jcxz指令即可,当cx的值位0的时候,它会自动跳转到ok1循环的
jmp short split
ok1: pop ax
add al, 30h
mov byte ptr es:[di], al
inc di
dec dx
mov cx, dx
jcxz last
jmp short ok1
last: ;最后一步,在数据的ASCII数据形式的最后加上一个0
mov ah, 0
mov byte ptr es:[di], ah
inc di
;从split到ok1到最后一步是对data段中第一个数据的处理
;这个过程需要进行循环操作
;在这个循环中,di,bx是放在循环外的
pop cx
add si, 2
loop loop_zone
pop es
pop si
pop ds
pop dx
pop bx
pop cx
pop ax
ret
divdw: push ds
push dx
push cx
push ax
mov ax, div
mov ds, ax
mov dx, 0
;由于本程序中被除数是16位,但是divdw是32/16,所以我们需要将被除数的高位补16个0,也就是将dx置0
mov ax, dx
mov dx, 0
div cx
;ax存放商,dx存放余数
;根据公式,使用被除数高位除以除数得到的商×65536
;*65536等价于在低位加16个0,因此操作就会变得非常简单
;使用被除数高位除以除数得到的余数×65536+被除数的低位,再将得到的结果除以除数
;两者的结果相加,即可得到32位/16位的无溢出结果
push dx
;使用栈临时保存余数
mov dx, ax
mov ax, 0
mov ds:[0], ax
mov ds:[2], dx
pop dx
;弹出余数,作为右操作数中被除数的高16位
pop bx
;得到被除数的低16位
push bx
;恢复栈顶数据,避免对主程序造成干扰
;add ax, bx
;将右操作数[]中的左操作数的低16位和被除数的低16位相加
;但是右操作数[]中的左操作数的低16位一定是全0的,因此我们可以省略这一步
;直接执行mov ax, bx
mov ax, bx
div cx
;ax存放商,dx存放余数
;由于左操作数的低16位一定是全0,所以不必与其相加,直接将
;右操作数的低16位存储到ds:[0]内存单元即可
mov ds:[0], ax
;商的低16位放到ds:[0]单元中
mov ds:[4], dx
;余数放到ds:[4]单元中
;ds:[2]中一直保存的都是商的高16位,且没有被更改过,因此无须任何操作
pop ax
pop cx
pop dx
mov ax, ds:[0]
;ax保存商的低16位
mov dx, ds:[2]
;dx保存商的高16位
mov cx, ds:[4]
;cx保存余数
pop ds
;之所以要在pop ds之前将数据转移,是因为子程序divdw调用前,ds已经被使用
;指向的是其他的段,如果不在pop之前转移数据,那么div段的数据就无法获取了
ret
show_str: push bx
push cx
push dx
push ds
push es
push di
push ax
push si
;根据上节中的框架,为了不让子程序干扰主程序中寄存器的值,将所有子程序会用到的寄存器进行压栈
mov di, ax
mov ax, 0b800h
mov es, ax
;颜色区的段地址
mov ax, ascii
mov ds, ax
;待输出的ASCII码值数据段
mov al, 160
mul dh
;每行占160个字节,乘以行数
push ax
;将行计算的结果存储到栈中
mov al, 2
mul dl
;每列占2个字节,乘以列数
pop bx
;将上次运算的结果(160×行数)的值转移到bx中
add bx, ax ;此时的ax值为(2×列数)
;将两者相加,最终结果保存到bx中
mov dl, cl
;因为下面的跳转指令jcxz需要用到cx寄存器,故需要将cl的值先保存在dl中
change: mov cl, ds:[di]
mov ch, 0
inc di
;我们需要记录下di的值,下一轮循环还会用到它
;这样一来,我们就需要调整入栈和出栈寄存器的位置了
;我们在pop di之前pop ax,然后使用ax来保存di的值
jcxz ok2
mov ch, dl
mov es:[bx+si], cx
add si, 2
jmp short change
ok2: pop si
pop ax
mov ax, di
pop di
pop es
pop ds
pop dx
pop cx
pop bx
ret
code ends
end start
2.用7ch中断例程完成jmpnearptrs指令的功能,用bx向中断例程传送转移位移。在屏幕的第12行显示data段中,以0结尾的字符串。
(最好能用上光标控制,开窗口等功能)
DATAS SEGMENT
DB 'conversation',0
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
;ES:DI指向安装的目的地址
;DS:SI指向源地址
MOV AX,0
MOV ES,AX
MOV DI,200H
MOV AX,OFFSET I
MOV SI,AX
MOV AX,CS
MOV DS,AX
MOV CX,OFFSET Intnop - OFFSET I
CLD
REP MOVSB
;安装完成
MOV AX,0
MOV ES,AX
MOV DI,200H
MOV word ptr ES:[7CH*4],DI
MOV AX,0
MOV word ptr ES:[7CH*4+2],AX
;中断向量表设置完成
MOV AH,2 ;置光标子程序编号
MOV BH,0 ;第0页
MOV DH,12 ;dh放行号dl放列号
MOV DL,0 ;至此光标位置设置完毕
INT 10H ;调用中断程序
MOV AH,6 ;置开窗口子程序编号
MOV AL,0
MOV BH,70H ;白底黑字
MOV CH,12 ;CX从12行0列开始设置窗口
MOV CL,0
MOV DH,14 ;DX设置窗口大小为14行50列
MOV DL,50
INT 10H ;调用中断程序
MOV AX,DATAS
MOV DS,AX
MOV SI,0
;MOV AX,0B800H
;MOV ES,AX
;MOV DI,12*160 ;直接屏幕指定位置输出
S:
CMP byte ptr [SI],0
JZ Ok ;如果是0跳出循环,程序结束
;MOV AL,[SI]
;MOV ES:[DI],AL ;设置字符
;INC SI
;ADD DI,2
MOV DL,[SI]
MOV AH,2
INT 21H ;光标置好位置后,调用功能实现单个字符输出
INC SI
MOV BX,OFFSET S - OFFSET Ok ;计算位移
INT 7CH
Ok:
MOV AH,4CH
INT 21H
I: ;中断例程
POP AX
ADD AX,BX
PUSH AX
iret
Intnop:NOP
CODES ENDS
END START