汇编语言程序设计---三次实验内容

实验一

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

结果如下

汇编语言程序设计---三次实验内容_第1张图片

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,个位减1048显示
	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

结果如下

汇编语言程序设计---三次实验内容_第2张图片


实验二

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

效果如下

汇编语言程序设计---三次实验内容_第3张图片

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

效果如下

汇编语言程序设计---三次实验内容_第4张图片

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

效果如下

汇编语言程序设计---三次实验内容_第5张图片


实验三

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等价于在低位加160,因此操作就会变得非常简单
        ;使用被除数高位除以除数得到的余数×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,所以我们需要将被除数的高位补160,也就是将dx置0
                mov ax, dx
                mov dx, 0
                div cx
                ;ax存放商,dx存放余数
                ;根据公式,使用被除数高位除以除数得到的商×65536
                ;*65536等价于在低位加160,因此操作就会变得非常简单
                ;使用被除数高位除以除数得到的余数×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从120列开始设置窗口
	MOV CL,0
	MOV DH,14			;DX设置窗口大小为1450列
	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

你可能感兴趣的:(课程学习资料,汇编语言,实验)