汇编语言-王爽 随书源码、检测点、实验答案

所有的程序都在笔者的机器上跑通了,大家不用担心程序有问题。

环境:Windows 10,DOSBOX,masm

本人是一名Javaer,汇编程序看起来实在是头疼,虽然无法使用面向对象的思想,但将汇编程序进行结构化可读性还是能提高不少的,所以我将书中的程序都进行了‘结构化’。

有的同学可能会觉得和书上的程序不是一模一样,觉得不爽,这部分同学可以自己改一下,如果自己不愿意动手,肯定是学不好的。

1、基础知识

检测点1.1

在这里插入图片描述
(1) 13

解析:8 = 2 ^ 3,1KB = 1024字节 = 2 ^ 10字节,8KB = [ (2 ^ 3 ) * (2 ^ 10) ] B = (2 ^13)B,寻址单位为字节,所以地址总线宽度最少为13位,才能具备寻址8KB的能力

(2) 1024,0 ~ 1023

解析:1KB = 1024B ,编号从0开始

(3) 8192,1024

解析:1 KB = 1024 Byte ,1 Byte = 8 bit

(4)102410241024, 1024*1024, 1024

解析:1 KB=1024 B,1 MB = 1024 KB ,1 GB = 1024 MB,所以1 GB = (102410241024)B …

(5)64KB , 1MB,16MB , 4GB
解析:2 ^ 16 = 64K , 2 ^ 20 = 1M , 2 ^ 24 = 16 M , 2 ^ 32 = 4 G

(6)1, 1, 2, 2, 4
解析:1根数据线一次传输1bit数据,8根1次就可以传输8bit(1B),16根1次就可以传输16bit(2B),32根1次就可以传输32bit(4B)

(7)512 , 256

解析:
8086数据总线为16位,每次读取2字节,1024 / 2 = 512
80386数据总线为32位,每次读取4个字节,1024 / 4 = 256

(8)二进制0/1

2、寄存器

检测点2.1

在这里插入图片描述

assume cs:codesg        	; 将寄存器cs和段-codesg联系起来		
		
codesg segment
	; 指令           十进制        二进制          十六进制
	mov ax,62627   ;ax=62627,‭ 1111 0100 1010 0011‬,  F4A3H
	mov ah,31h     ;ax=‭12707‬, 0011 0001 1010 0011‬,  31A3H
	mov al,23h     ;ax=‭12579‬‬, 0011 0001 0010 0011‬,‭  3123‬H
	add ax,ax      ;ax=25158, ‭0110 0010 0100 0110‬,  6246‬H
													
	mov bx,826ch   ;bx=‭33388‬,‭ 1000 0010 0110 1100‬,‭  826C‬H
	mov cx,ax      ;cx=25158, ‭0110 0010 0100 0110‬,  6246‬H
	mov ax,bx      ;ax=33388‬,‭ 1000 0010 0110 1100‬,‭  826C‬H
	add ax,bx      ;ax=‭‭1240‬ ‬, 0000 0100 1101 1000,‭  04D8‬H
													
	mov al,bh      ;ax=‭1154‬ , 0000 0100 1000 0010,‭  0482‬H
	mov ah,bl      ;ax=‭27778‬, 0110 1100 1000 0010,‭  6C82‬H
	add ah,ah      ;ax=55426‬, ‭1101 1000‬ 1000 0010,  D882‬H
	add al,6       ;ax=55432‬, ‭1101 1000‬ 1000 1000,‭  D888‬H
													
	add al,al      ;ax=55312, 1101 1000 0001 0000,‭  D810‬H
	mov ax,cx      ;ax=25158, ‭0110 0010 0100 0110‬,‭  6246‬H
				
codesg ends
end

⚠️ 注意:高8位和低8位寄存器是独立的寄存器,因此al,bl,cl,dl 进行运算时产生的进位,并不会改变高位的值,而是直接丢失。

检测点2.2

在这里插入图片描述
(1)00010H ~1000FH
解析:0001:0000 ~ 0001:FFFF,段地址左移4位+偏移地址:

   00010H 
  + 0000H
 = 00010H 

   00010H 
 +  FFFFH 
 = 1000FH

(2)1001 ~2000

⚠️ 注意:20000H - FFFFH = 10001,以这个值得出段地址1000,是访问不到20000H的,所以段地址为1001

检测点 2.3

在这里插入图片描述
解析:修改了4次,最后 IP 值为0
执行指令的过程是:1、将指令读入指令缓冲区;2、修改IP,指向下条指令;3、执行指令;

mov ax,bx		;入指令缓冲区,修改 IP,执行指令,结果:ax=bx
sub ax,ax		;入指令缓冲区,修改 IP,执行指令,结果:ax=0
jmp ax			;入指令缓冲区,修改 IP,执行指令,结果:jmp指令修改IP的值为ax的值,即IP=0

实验







第3章 寄存器(内存访问)

本章共30页

问题3.1

在这里插入图片描述

检测点 3.1

在这里插入图片描述

在这里插入图片描述

答案(1)

MOV     AX,0001     
MOV     DS,AX       
MOV     AX,[0000]   ;AX=2662H
MOV     BX,[0001]   ; BX=E626H
MOV     AX,BX       ;AX=E626H
MOV     AX,[0000]   ;AX=2662H
MOV     BX,[0002]   ; BX=D6E6H
ADD     AX,BX       ;AX=FD48H
ADD     AX,[0004]   ;AX=2C14H
MOV     AX,0000     ;AX=0000H
MOV     AL,[0002]   ;AX=00E6H
MOV     BX,0000     ; BX=0000H
MOV     BL,[000C]   ; BX=0026H
ADD     AL,BL       ;AX=000CH (E6H+26H=10CH ,10CH的最高位溢出,所以AX=000CH)

上机验证过程如下:

(1)、使用下面的命令修改内存中的内容(window xp系统下可以直接复制粘贴)

e 0:0	70
e 0:1	80
e 0:2	f0
e 0:3	30
e 0:4	ef
e 0:5	60
e 0:6	30
e 0:7	e2
e 0:8	0
e 0:9	80
e 0:a	80
e 0:b	12
e 0:c	66
e 0:d	20
e 0:e	22
e 0:f	60
e 0:10	62
e 0:11	26
e 0:12	e6
e 0:13	d6
e 0:14	cc
e 0:15	2e
e 0:16	3c
e 0:17	3b
e 0:18	ab
e 0:19	ba
e 0:1a	0
e 0:1b	0
e 0:1c	26
e 0:1d	6
e 0:1e	66
e 0:1f	88

修改后查看,结果如下

-d 0:0 1f
0000:0000  70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60   p..0.`0.....f "`
0000:0010  62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88   b&....<;....&.f.

(2)、debug下使用a命令,复制粘贴下列命令(window xp系统下可以直接复制粘贴)

mov ax,1
mov ds,ax
mov ax,[0]
mov bx,[1]
mov ax,bx
mov ax,[0]
mov bx,[2]
add ax,bx
add ax,[4]
mov ax,0
mov al,[2]
mov bx,0
mov bl,[c]
add al,bl

(3)、单步执行命令,结果如下:

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0105   NV UP EI PL NZ NA PO NC
0ADD:0105 A10000        MOV     AX,[0000]                          DS:0000=2662
-t

AX=2662  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0108   NV UP EI PL NZ NA PO NC
0ADD:0108 8B1E0100      MOV     BX,[0001]                          DS:0001=E626
-t

AX=2662  BX=E626  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=010C   NV UP EI PL NZ NA PO NC
0ADD:010C 89D8          MOV     AX,BX
-t

AX=E626  BX=E626  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=010E   NV UP EI PL NZ NA PO NC
0ADD:010E A10000        MOV     AX,[0000]                          DS:0000=2662
-t

AX=2662  BX=E626  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0111   NV UP EI PL NZ NA PO NC
0ADD:0111 8B1E0200      MOV     BX,[0002]                          DS:0002=D6E6
-t

AX=2662  BX=D6E6  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0115   NV UP EI PL NZ NA PO NC
0ADD:0115 01D8          ADD     AX,BX
-t

AX=FD48  BX=D6E6  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0117   NV UP EI NG NZ NA PE NC
0ADD:0117 03060400      ADD     AX,[0004]                          DS:0004=2ECC
-t

AX=2C14  BX=D6E6  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=011B   NV UP EI PL NZ AC PE CY
0ADD:011B B80000        MOV     AX,0000
-t

AX=0000  BX=D6E6  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=011E   NV UP EI PL NZ AC PE CY
0ADD:011E A00200        MOV     AL,[0002]                          DS:0002=E6
-t

AX=00E6  BX=D6E6  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0121   NV UP EI PL NZ AC PE CY
0ADD:0121 BB0000        MOV     BX,0000
-t

AX=00E6  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0124   NV UP EI PL NZ AC PE CY
0ADD:0124 8A1E0C00      MOV     BL,[000C]                          DS:000C=26
-t

AX=00E6  BX=0026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=0128   NV UP EI PL NZ AC PE CY
0ADD:0128 00D8          ADD     AL,BL
-t

AX=000C  BX=0026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0001  ES=0ADD  SS=0ADD  CS=0ADD  IP=012A   NV UP EI PL NZ NA PE CY
0ADD:012A B80100        MOV     AX,0001

(2)

上机验证过程:
(1)、使用debug的a命令,在指定的内存处输入指令

-a 2000:0
2000:0000 mov ax,6622
2000:0003 jmp 0ff0:100
2000:0008 mov bx,ax
2000:000A
-a 1000:0
1000:0000 mov ax,2000
1000:0003 mov ds,ax
1000:0005 mov ax,[8]
1000:0008 mov ax,[2]

(2)、使用debug的r命令,修改cs、ip的值

-r cs
CS 0ADD
:2000
-r ip
IP 0100
:0

(3)、使用debug的t命令,单步执行指令

-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0ADD  ES=0ADD  SS=0ADD  CS=2000  IP=0000   NV UP EI PL NZ NA PO NC
2000:0000 B82266        MOV     AX,6622
-t

AX=6622  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0ADD  ES=0ADD  SS=0ADD  CS=2000  IP=0003   NV UP EI PL NZ NA PO NC
2000:0003 EA0001F00F    JMP     0FF0:0100
-t

AX=6622  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0ADD  ES=0ADD  SS=0ADD  CS=0FF0  IP=0100   NV UP EI PL NZ NA PO NC
0FF0:0100 B80020        MOV     AX,2000
-t

AX=2000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0ADD  ES=0ADD  SS=0ADD  CS=0FF0  IP=0103   NV UP EI PL NZ NA PO NC
0FF0:0103 8ED8          MOV     DS,AX
-t

AX=2000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=2000  ES=0ADD  SS=0ADD  CS=0FF0  IP=0105   NV UP EI PL NZ NA PO NC
0FF0:0105 A10800        MOV     AX,[0008]                          DS:0008=C389
-t

AX=C389  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=2000  ES=0ADD  SS=0ADD  CS=0FF0  IP=0108   NV UP EI PL NZ NA PO NC
0FF0:0108 A10200        MOV     AX,[0002]                          DS:0002=EA66
-t

AX=EA66  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=2000  ES=0ADD  SS=0ADD  CS=0FF0  IP=010B   NV UP EI PL NZ NA PO NC
0FF0:010B 0000          ADD     [BX+SI],AL                         DS:0000=B8






第4章 第一个程序

本章共20页

问题3.3

mov ax,1000
mov ds,ax
mov ax,[0]
mov bx,[2]
mov cx,[1]
add bx,[1]
add cx,[2]

程序4.1


第6章 包含多个段的程序

本章共16页



检测点6.1

(1)参考答案

在这里插入图片描述


;------------------------------检测单 6.1 ----------------------
;------- 依次用0:0 ~ 0:15 单元中的内容替换程序中dw定义的数据  -------
;--------------------------------------------------------------

assume cs:codesg
		
codesg segment	

	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
	
	start:	mov ax,0		
			mov ds,ax			
			
			mov bx,0
			mov cx,8		
		s:	mov ax,[bx]    	
			mov cs:[bx],ax	
			add bx,2		
			loop s		
							
			mov ax,4c00h	
			int 21h			
			
codesg ends             	
						 
end start

(2)参考答案


;------------------------------检测单 6.1-2 ------------------------------------
;- 依次用0:0 ~ 0:15 单元中的内容替换程序中的数据 ,使用栈来传送数据 ------------
;-------------------------------------------------------------------------------

assume cs:codesg
		
codesg segment

	;--定义的数据放的初始地址是 (cs:0) --
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
	dw 0,0,0,0,0,0,0,0,0,0								
	
	start:	mov ax,cs									
			mov ss,ax									
			mov sp,24h
			
			mov ax,0
			mov ds,ax
			mov bx,0
			mov cx,8									
		l:	push ds:[bx]    							
			pop cs:[bx]							
			add bx,2								
			loop l		
												
			mov ax,4c00h								
			int 21h									
			
codesg ends             								
						 
end start

第7章 更灵活的定位内存地址的方法

7.2、ASCII

编码方案,就是一套规则,规则约定用什么样的信息来表示现实中的对象。比如ASCII编码方案中,用61H来表示现实生活中的字母 ‘a’

使用文本编辑器时,我们按下字母 a,此时会在文本编辑器中显示a,这个过程是怎样的?

1、a对应的编码送入内存
按下键盘上的字母a,键盘会触发一个外部中断 ,BIOS提供的中断处理程序会处理这个中断,该程序将a对应的ASCII编码(61H) 送入内存中的BIOS键盘缓冲区

2、文本编辑器取出a编码
文本编辑器从内存中的键盘缓冲区中取出编码,然后送入显卡上的显存中,工作在文本模式下的显卡根据ASCII码规则驱动显示器显示a在屏幕上

7.3 以字符形式给出的数据

mov ax,'a'
db 'ab'

上面的指令等价于下面的指令

mov ax,61h
db 61h,62h

7.4 大小写转换的问题

;| ------------------------------- 7.4 字符串输入  ------------
;|  功能:|	'BaSiC' 转为大写,'iNfOrMaTiOn' 转为小写
;|--------------------------------------------------------------

assume cs:code 

datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'

code segment

	main:;{
		mov ax,datasg
		mov ds,ax
		
		mov bx,0		
		mov cx,5
		for_to_uppercase:;{
			mov al,[bx]
			and al,11011111b
			mov [bx],al
			inc bx
			loop for_to_uppercase				
		;}
		
		mov cx,11
		for_to_lowercase:;{
			mov al,[bx]
			or al,00100000b
			mov [bx],al
			inc bx
			loop for_to_lowercase				
		;}
		
		mov ax,4c00h
		int 21h
	;}
	
code ends 
            						
end main           

解析:程序利用大写字母ASCII编码比小写字母ASCII编码少32(20h),且他们的ASCII码恰巧可以使用and 和or运算实现加减运算(不是所有的数都可以用一个and或or来实现加减运算的)。



7.5、[bx+idata]

问题7.1


assume cs:code 
code segment
	main:;{
		call init_data
		
		mov ax,2000h
		mov ds,ax
		mov bx,1000h
		mov ax,[bx]
		mov cx,[bx+1]
		add cx,[bx+2]	
	;}
	
	;// 初始化数据
	init_data:;{
		mov ax,2000h
		mov ds,ax
		
		mov byte ptr ds:[1000h],0beh
		mov byte ptr ds:[1001h],00h
		mov byte ptr ds:[1002h],06h
		mov byte ptr ds:[1003h],00h
		mov byte ptr ds:[1004h],00h
		mov byte ptr ds:[1005h],00h
		
		ret
	;}
code ends 
            						
end main           

问题 7.2

在这里插入图片描述

;----------------------------------------------------------------------------------
;------------------------------问题7.2 -------------------------------------------
;- 用si和di将字符串'welcom to masm!'复制到它后面的数据区中--
;----------------------------------------------------------------------------------

assume cs:codesg ,ds:datasg

datasg segment
			db 'welcome to masm!'
			db '................'			
datasg ends

codesg segment
			
	start:	mov ax,datasg	
			mov ds,ax
			
			mov si,0
			mov di,16
			
			mov cx,8
		l:	mov ax,[si]
			mov [di],ax
			add si,2
			add di,2
			loop l
			
codesg ends 

end start

问题 7.3

;----------------------------------------------------------------------------------
;------------------------------问题7.3 -------------------------------------------
;- 用si和di将字符串'welcom to masm!'复制到它后面的数据区中--
;----------------------------------------------------------------------------------

assume cs:codesg ,ds:datasg

datasg segment
			db 'welcome to masm!'
			db '................'			
datasg ends

codesg segment				
			
	start:	mov ax,datasg	
			mov ds,ax
			
			mov si,0			
			mov cx,8
		l:	mov ax,[si]
			mov  [si+16],ax	
			add si,2
			loop l
			
codesg ends

end start

问题 7.4

assume cs:code 
code segment
	main:;{
		call init_data
		
		mov ax,2000h
		mov ds,ax
		mov bx,1000h
		
		mov si,0
		mov ax,[bx+si]
		
		inc si
		mov cx,[bx+si]
		
		inc si
		mov di,si
		add cx,[bx+di]
		
	;}
	
	;// 初始化数据
	init_data:;{
		mov ax,2000h
		mov ds,ax
		
		mov byte ptr ds:[1000h],0beh
		mov byte ptr ds:[1001h],00h
		mov byte ptr ds:[1002h],06h
		mov byte ptr ds:[1003h],00h
		mov byte ptr ds:[1004h],00h
		mov byte ptr ds:[1005h],00h
		
		ret
	;}
code ends 
            						
end main           

问题 7.5

在这里插入图片描述

assume cs:code 
code segment
	main:;{
		call init_data
		
		mov ax,2000h
		mov ds,ax
		mov bx,1000h
		
		mov si,0
		mov ax,[bx+2+si]
		
		inc si
		mov cx,[bx+2+si]
		
		inc si
		mov di,si
		mov bx,[bx+2+di]
		
		nop
	;}
	
	;// 初始化数据
	init_data:;{
		mov ax,2000h
		mov ds,ax
		
		mov byte ptr ds:[1000h],0beh
		mov byte ptr ds:[1001h],00h
		mov byte ptr ds:[1002h],06h
		mov byte ptr ds:[1003h],00h
		mov byte ptr ds:[1004h],6ah
		mov byte ptr ds:[1005h],22h
		
		ret
	;}
code ends 
            						
end main           

11 标志寄存器

检测点11.2

复习标志位的意义:

位置英文缩写标志名标志为0标志为1
11OF - Overflow溢出(是/否)NV - No OverflowOV - Overflow
10DF - Direction方向(减量/增量)UP - UpDN - Down
9IF - Interrupt中断(允许/关闭)DI - Disable Interrupt 不许EI - Enable Interrupt 允许
7SF - Symbol符号(负/正)PL - Plus 非负NG - Negative 负
6ZF - Zero零(是/否)NZ - No ZeroZR -Zero
4AF - Auxiliary辅助进位(是/否)NAAC
2PF - Parity奇偶(偶/奇)PO - Odd 奇数个1PE - Parity Even 偶数个1
0CF - Carry进位(是/否)NC - No CarryCY - Carry Yes

⭐️ OF 进行有符号数运算,运算的结果超出了表示范围,此时OF为1
⭐️ CY 进行无符号数运算,运算的结果向比最高有效位的“更高位”(假想位)进位,此时CY为1

解析:
mov 对标志寄存器不产生影响,所以2,4,6,8的标志寄存器值分别和1,3,4,7一样, 所以我们分析一下1,3,4,7指令执行后标志寄存器的值。

序号指令 \ 标志位CF进位(NC,CY )溢出OF(NV,OV)符号SF(PL,NG)ZF零(NZ,ZR)奇偶PF(PO,PE)
1sub al,al00011
2mov al,10H00011

在这里插入图片描述

序号指令 \ 标志位CF进位(NC,CY )溢出OF(NV,OV)符号SF(PL,NG)ZF零(NZ,ZR)奇偶PF(PO,PE)
1sub al,al00011
2mov al,10H00011
3add al,90H00101
4mov al,80H00101

在这里插入图片描述

序号指令 \ 标志位CF进位(NC,CY )溢出OF(NV,OV)符号SF(PL,NG)ZF零(NZ,ZR)奇偶PF(PO,PE)
1sub al,al00011
2mov al,10H00011
3add al,90H00101
4mov al,80H00101
5add al,80H11011
6mov al,0FCH11011

在这里插入图片描述

序号指令 \ 标志位CF进位(NC,CY )溢出OF(NV,OV)符号SF(PL,NG)ZF零(NZ,ZR)奇偶PF(PO,PE)
1sub al,al00011
2mov al,10H00011
3add al,90H00101
4mov al,80H00101
5add al,80H11011
6mov al,0FCH11011
7add al,05H10000
8mov al,7DH10000

在这里插入图片描述

序号指令 \ 标志位CF进位(NC,CY )溢出OF(NV,OV)符号SF(PL,NG)ZF零(NZ,ZR)奇偶PF(PO,PE)
1sub al,al00011
2mov al,10H00011
3add al,90H00101
4mov al,80H00101
5add al,80H11011
6mov al,0FCH11011
7add al,05H10000
8mov al,7DH10000
9add al,0BH01101

第 13 章 int 指令

13.1 int指令

int n ,触发1个中断类型码为n的中断

;------------------------------ 13.1 int指令  -------------------
;功能:屏幕中间显示有个“!”,然后触发一个除法错误
assume cs:code 
	
code segment	                         
												
	start:;{
		mov ax,0b800h
		mov es,ax
		mov byte ptr es:[12*160+40*2],'!'
		int 0
		
		mov ax,4c00h                  	 
		int 21h
	;}
code ends 
            						
end start                                    	

在DOSBOX下不显示Divide overflow,但在Windows xp下会显示(我使用的是虚拟机安装的xp)
在这里插入图片描述

13.2 编写功能应用程序调用的中断例程

问题1

本程序需要复习的指令:

cld , 标志寄存器df位置0
rep 指令 , 循环执行接在rep后面的串传送指令 (cx) 次
movsb
1、将ds:si指向内存单元中的1个字节,送入es:di指向的内存单元
2、根据标志寄存器中df位,将si和di增1(df=0)或减1(df=1)

;|----------------------------- 13.2 问题1  -----------------------------------------------------
;|  功能:|	求一个word型数据的平方     
;|-----------------------------------------------------------------------------------------------
;|  参数:|	(ax) = 一个word型数据   
;|-----------------------------------------------------------------------------------------------
;|返回值:|  dx,ax中存放结果的高16位,低16位        
;|-----------------------------------------------------------------------------------------------
;|  备注:|	2*3456^2 = ‭23887872‬ = 16C8000H                                                              
;|-----------------------------------------------------------------------------------------------

assume cs:code 
	
code segment	                         
											
	main:;{	
		call copy_program
		call set_int_table
		
		mov ax,3456
		int 7ch
		;// (ax的平方) * 2
		add ax,ax      
		adc dx,dx

		mov ax,4c00h
		int 21h
	;}	
		
	;// 将`代码`复制到内存0:200处
	copy_program:;{
		;//设置`程序`的源地址 ds:si()
		set_source_addr:;{
			mov ax,cs							
			mov ds,ax		                    
			mov si,offset sqr					
		;}
		
		;//设置`程序`的目的地址 es:di(0:200)
		set_dst_addr:;{
			mov ax,0
			mov es,ax
			mov di,200h
		;}
		
		;// 循环传输`程序`代码到目的地址
		rep_copy:;{
			mov cx,offset sqr_end - offset sqr
			;// 设置DF标志位为0,表示传输方向为正向
			cld
			rep movsb							;		循环传输						
		;}
		
		ret
	;}
	
	;// 设置中断向量表。在中断向量表的0:0位置,设置`程序`的入口地址
	set_int_table:;{
		mov ax,0                            ;	
		mov es,ax                           ;
		mov word ptr es:[7ch*4],200h        ;	低位为偏移地址200h
		mov word ptr es:[7ch*4+2],0         ;	高位为段地址0
		ret
	;}
			
	;// 计算ax中数据的平方		
	sqr:;{
			mul ax
			iret
		sqr_end: 
			nop		                        
	;}
;------------------------------------------------------------------------------------------------

code ends             						
end main                                                                          	                                	                            	                              	                  	                                 	

问题2

如何将小写字母转为大写?我们 点击链接:参看一下ASCII表,小写字母比大写字母的值大32H。那么用小写字母的值减去32H就是大写之母的值了,转换指令就是:

指令
小写字母转大写and 小写,11011111B
大写字母转小写

安歇

;|----------------------------- 13.2 问题2  -----------------------------------------------------
;|  功能:|	将一个全是字母,以0结尾的字符串,转换为大写   
;|-----------------------------------------------------------------------------------------------
;|  参数:|	ds:si 指向字符串首地址
;|-----------------------------------------------------------------------------------------------

assume cs:code 
	
data segment
		db 'conversation',0
data ends
	
code segment

	main:;{	
		mov bx,data
		
		call copy_program
		call set_int_table
		
		int 7ch

		mov ax,4c00h
		int 21h
	;}	
		
	;// 将`代码`复制到内存0:200处
	copy_program:;{
		;//设置`程序`的源地址 ds:si()
		set_source_addr:;{
			mov ax,cs							
			mov ds,ax		                    
			mov si,offset capital					
		;}
		
		;//设置`程序`的目的地址 es:di(0:200)
		set_dst_addr:;{
			mov ax,0
			mov es,ax
			mov di,200h
		;}
		
		;// 循环传输`程序`代码到目的地址
		rep_copy:;{
			mov cx,offset capital_end - offset capital
			;// 设置DF标志位为0,表示传输方向为正向
			cld
			rep movsb							;		循环传输						
		;}
		
		ret
	;}
	
	;// 设置中断向量表。在中断向量表的0:0位置,设置`程序`的入口地址
	set_int_table:;{
		mov ax,0                            ;	
		mov es,ax                           ;
		mov word ptr es:[7ch*4],200h        ;	低位为偏移地址200h
		mov word ptr es:[7ch*4+2],0         ;	高位为段地址0
		ret
	;}
			
	;// 计算ax中数据的平方		
	capital:;{
		;// 备份程序中用到的寄存器
		back_reg:;{
			push ax
			push ds
			push si
			push cx
		;}
		
		mov ax,data
		mov ds,ax
		mov si,0			
		for_uppercase:;{	
			mov cl,[si]
			mov ch,0
			jcxz for_change_end
			and byte ptr ds:[si],11011111b
			inc si
			jmp short for_uppercase
		;}
		
		;// 程序结束
		for_change_end:;{	
			pop cx
			pop si
			pop ds
			pop ax			
			iret  
		;}
		
		capital_end: 		 
			nop		                        
	;}
;------------------------------------------------------------------------------------------------

code ends             						
end main                                                      	

13.3 int、iret和栈的深入理解

在这里插入图片描述


;| ------------------------------- 13.3 7ch中断处理程序完成loop指令的功能 ----------------------- 
;|  功能:|	在屏幕中显示80个“!”   
;|-----------------------------------------------------------------------------------------------
;|  参数:|	
;|-----------------------------------------------------------------------------------------------
;|返回值:|      
;|-----------------------------------------------------------------------------------------------
;|  备注:|                                                          
;|-----------------------------------------------------------------------------------------------

assume cs:code 

	data segment
		db 'conversation',0
	data ends
	
	code segment	                         
												
		start:					
				call copy_program
				call set_int_table
				
				mov ax,0b800h
				mov es,ax
				mov di,160*12
				
				mov bx,offset s - offset s_end              ;
				mov cx,80				
		s:	
				mov byte ptr es:[di],'!'
				add di,2         	   			
				int 7ch
		s_end:
				nop
											
				mov ax,4c00h                
				int 21h                     
								
;------------------------------------------------------------------------------------------------

		copy_program:	                            		 ;{	//将`代码`复制到内存0:200处
				mov ax,cs									 ;	{	// 设置`程序`的源地址 ds:si
				mov ds,ax		                    		 ;		
				mov si,offset loop_sim						 ;	}	
															 	
				mov ax,0                            		 ;	{	// 设置`程序`的目标地址 es:di
				mov es,ax                           		 ;	
				mov di,200h                         		 ;	}
															 
				mov cx,offset loop_sim_end - offset loop_sim	 ;	{	设置循环次数,循环次数= 代码的长度 
				cld											 ;		设置DF标志位为0,0-传输方向为正向
				rep movsb									 ;		循环传输
															 ;	}
				ret                                 		 ;}
						 
		set_int_table:	                            		 ;{	// 在中断向量表的0:0位置,设置`程序`的入口地址
				mov ax,0                            		 ;	
				mov es,ax                           		 ;
				mov word ptr es:[7ch*4],200h        		 ;	低位为偏移地址200h
				mov word ptr es:[7ch*4+2],0         		 ;	高位为段地址0
				ret											 ;}
;------------------------------------------------------------------------------------------------

;------------------------------------------------------------------------------------------------						 
		loop_sim:                                            ;见上图解释
				push bp
				mov bp,sp
				dec cx
				jcxz loop_ret
				add [bp+2],bx
		loop_ret:
				pop bp
				iret
		loop_sim_end: 		 
				nop		                        			 
;------------------------------------------------------------------------------------------------

	code ends             						
end start                                       	                            	

13.6 BIOS 中断例程应用

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


;| ------------------------------- 13.6 ----------------------- 
;|  功能:|	屏幕第5行12列显示3个红底,高亮闪烁的绿色'a' 
;|-----------------------------------------------------------------------------------------------
;|  参数:|	
;|-----------------------------------------------------------------------------------------------
;|返回值:|      
;|-----------------------------------------------------------------------------------------------

;|-----------------------------------------------------------------------------------------------

assume cs:code 

	code segment	                         
												
		start:					
			mov ah,2					; ah存中断例程的“内部子程序编号”
			mov bh,0
			mov dh,5
			mov dl,12
			int 10h
			
			mov ah,9
			mov al,'a'
			mov bh,0
			mov bl,11001010b
			mov cx,3
			int 10h
			
			mov ax,4c00h
			int 21h
			
	code ends             						
end start                                       	

13.7 DOS 中断例程应用


;| ------------------------------- 13.7 ------------------------ 
;|  功能:|	屏幕第5行12列显示“Welcome to masm” 
;|--------------------------------------------------------------
;|  参数:|	
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

data segment
		db 'Welcome to masm','$'
data ends

code segment	                         
											
	start:
		mov ah,2				  ; ah存中断例程的“内部子程序编号”
		mov bh,0
		mov dh,5
		mov dl,12
		int 10h 
		
		mov ax,data
		mov ds,ax
		mov dx,0
		mov ah,9
		int 21h
		
		mov ax, 4c00H                
		int 21h
		
code ends 
            						
end start                                       	

第 14 章、端口

检测点 14.1-1


;| ------------------------------- 检测点 14.1 ------------------------ 
;|  功能:|	读取CMOS RAM的2号单元
;|--------------------------------------------------------------
;|  参数:|	
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	start:
		mov al,2
		out 70h,al
		in al,71h
		
		mov ax, 4c00H                
		int 21h
		
code ends 
            						
end start                                       	

检测点 14.1-2


;| ------------------------------- 检测点 14.2 ------------------------ 
;|  功能:|	向CMOS RAM的2号单元写入0
;|--------------------------------------------------------------
;|  参数:|	
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	start:
		mov al,2
		out 70h,al
		
		mov al,0
		out 71h,al
		
		mov ax, 4c00H                
		int 21h
		
code ends 
            						
end start                                       	

14.3 shl 和 shr 指令

在这里插入图片描述

检测点 14.2



;| ------------------------------- 检测点 14.2 ------------------------ 
;|  功能:|	用加法和移位指令计算(ax) = (ax)*10 = (ax)*2+(ax)*8
;|--------------------------------------------------------------
;|  参数:|	
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	start:
		mov ax,10					;测试值10
		
		mov bx,ax                  	; 暂存ax值到 bx
		shl ax,1                    ; (ax)=(ax) * 2
		
		mov cl,3
		shl bx,cl                   ;(bx)=(bx) * 8
		
		add ax,bx		            ; 
		
		mov ax, 4c00H                
		int 21h
code ends 
            						
end start   

14.4 CMOS RAM中存储的时间信息

在这里插入图片描述

;| ------------------------------- 检测点 14.2 ------------------------ 
;|  功能:|	
;|--------------------------------------------------------------
;|  参数:|	
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	start:
		mov al,8                        ; CMOS RAM 8的位置存的是月
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将8单元的内容-月,读入al中
		
		mov ah,al
		mov cl,4
		shr ah,cl
		
		and al,00001111b
		
		add ah,30h
		add al,30h
		
	display:                            ; 在12行,40列处,显示月份
		mov bx,0b800h
		mov es,bx
		mov bx,160*12+40*2
		mov byte ptr es:[bx],ah
		mov byte ptr es:[bx+2],al
			
		mov ax, 4c00H                
		int 21h
code ends 
            						
end start                                       	

实验14


;| ------------------------------- 检测点 14.2 ------------------------ 
;|  功能:|	以“年/月/日 时:分:秒”的格式,显示当前的日期、时间
;|--------------------------------------------------------------
;|  参数:|	dl - 存“间隔字符”
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:|
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	start:
		
	year:
		mov al,9                        ; CMOS RAM 9的位置存的是年
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将9单元的内容-月,读入al中
		call calc
		mov dl,'/'
		
		mov bx,0b800h                   ;初始化显示的参数
		mov es,bx
		mov bx,160*12+40*2
		call display
	month:
		mov al,8                        ; CMOS RAM 8的位置存的是月
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将8单元的内容-月,读入al中
		call calc
		mov dl,'/'
		call display
	day:
		mov al,7                        ; CMOS RAM 7的位置存的是日
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将7单元的内容-月,读入al中
		call calc
		mov dl,0
		call display
	hour:
		mov al,4                        ; CMOS RAM 4的位置存的是时
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将4单元的内容-时,读入al中
		call calc
		mov dl,':'
		call display
	minutes:
		mov al,2                        ; CMOS RAM 2的位置存的是分
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将8单元的内容-分,读入al中
		call calc
		mov dl,':'
		call display
	
		
	seconds:
		mov al,0                        ; CMOS RAM 0的位置存的是秒
		out 70h,al                      ; 设置要读的存储单元
		in al,71h                       ; 将0单元的内容-秒,读入al中
		call calc
		mov dl,' '
		call display
	
		mov ax, 4c00H                
		int 21h	
		
;-----------------------------------------------------------------------		
	calc:
		mov ah,al
		mov cl,4
		shr ah,cl
		
		and al,00001111b
		
		add ah,30h
		add al,30h
		ret
		
	display:                            		
		mov byte ptr es:[bx],ah
		mov byte ptr es:[bx+2],al
		mov byte ptr es:[bx+4],dl
		add bx,6
		ret

code ends 
            						
end start                                       	

第 15 章 外中断

在这里插入图片描述

1、屏幕中间依次显示a~z

;| ------------------------------- 15.4  ------------------------ 
;|  功能:|	屏幕中间依次显示a~z
;|--------------------------------------------------------------

assume cs:code 

code segment	                         
											
	main:;{
		mov ax,0b800h
		mov es,ax
		mov ah,'a'
		
		for_:;{
			mov es:[160*12+40*2],ah
			call delay
			inc ah
			cmp ah,'z'
			jna for_
		;}
		
		mov ax, 4c00H                
		int 21h	
	;}
	
	delay:;{
		;// 备份后续用到的两个寄存器
		push ax
		push dx
		
		;// dx存放高16位,ax存放低16位
		mov dx,5h                                         
		mov ax,0
		for_delay:;{
			sub ax,1
			sbb dx,0
			cmp ax,0
			jne for_delay
			cmp dx,0
			jne for_delay
		;}
		
		;// 恢复备份的两个寄存器的内容
		pop dx
		pop ax
		ret
	;}
		
code ends 
            						
end main       

2、依次显示a~z,Esc 改变现实的颜色


;| ------------------------------- 15.4  ------------------------ 
;|  功能:|	屏幕中间依次显示a~z
;|--------------------------------------------------------------
;|  参数:|	dl - 存“间隔字符”
;|--------------------------------------------------------------
;|返回值:|      
;|--------------------------------------------------------------
;|  备注:
;|	1、备份BIOS               `int 9`的 “中断处理程序的入口地址”
;|  2、编写自己               `int 9`的 “中断处理程序”,
;|  3、在中断向量表中设置自己 `int 9`的 “中断处理程序的入口地址”。
;|     此时发生`int 9`的中断,就可以使用自己的程序进行处理了
;|  4、依次显示a~z ,在此期间按下esc键,触发自定义的int 9 处理程序,从而改变字母的颜色属性
;|  5、恢复BIOS               `int 9`的 “中断处理程序的入口地址”


assume cs:code 

;// 开辟一块栈空间,给程序中的`push`指令使用	
stack segment
		db 128 dup (0)
stack ends

BIOS_int9_backup segment
		dw 0,0
BIOS_int9_backup ends

code segment	 
   
	main:;{	
		;设置栈相关的寄存器{
			mov ax,stack
			mov ss,ax
			mov sp,128
		;}
		
		call backup_BIOS_int9
		call set_new_int9_addr
		call display_a2z
		call recover_BIOS_int9_backup
		
		mov ax, 4c00H                
		int 21h 
	;}
		
	;// 1、备份BIOS`int 9`的 “中断处理程序的入口地址” 到
	backup_BIOS_int9:;{                             
		;// 设置“备份”内存单元的段地址
		mov ax,BIOS_int9_backup
		mov ds,ax
		;// BIOS的int 9中断处理程序的段地址为0
		mov ax,0
		mov es,ax
		
		;// 通过入栈、出栈,将int 9中断处理程序的入口地址进行备份
		push es:[9*4]
		pop ds:[0]		
		push es:[9*4+2]
		pop ds:[2]
		ret
	;}	
	
	;// 设置自定义的“int 9中断处理程序”的入口地址
	set_new_int9_addr:;{
		pushf
		;// 设置中断标志位为0,不允许中断以防止被别的中断打断设置入口地址指令
		cli
		mov word ptr es:[9*4],offset int9
		mov es:[9*4+2],cs
		popf
		ret
	;}
	
	;//	依次显示a~z
	display_a2z:;{
		mov ax,0b800h
		mov es,ax
		mov ah,'a'		
		for_display:;{
			mov es:[160*12+40*2],ah
			call delay
			inc ah
			cmp ah,'z'
			jna for_display
		;}
		ret
	;}
											
	;// 恢复BIOS “int 9中断处理程序”的入口地址
	recover_BIOS_int9_backup:;{
		mov ax,0
		mov es,ax
		push ds:[0]
		pop es:[9*4]
		push ds:[2]
		pop es:[9*4+2]
		ret
	;}									   
	
	delay:;{
		;// 备份后续用到的两个寄存器
		push ax
		push dx
		
		;// dx存放高16位,ax存放低16位
		mov dx,5h                                         
		mov ax,0
		for_delay:;{
			sub ax,1
			sbb dx,0
			cmp ax,0
			jne for_delay
			cmp dx,0
			jne for_delay
		;}
		
		;// 恢复备份的两个寄存器的内容
		pop dx
		pop ax
		ret
	;}
	
	;// 自定义的`int 9`中断处理程序,esc键改变显存地址“160*12+40*2”处字符的颜色属性
	int9:;{	
		push ax
		push bx
		push es

		;// 将if,tf设置为0
		;set0_if_tf{
			;// 为什么要两个pushf? 
			;// 第二个`pushf` 是为了通过将标志寄存器内容出入栈来修改其内容(if,tf置零)
			;// 第一个`pushf` 是为了支持BIOS中断处理程序中的`iret` 指令
			;pushf                                         
			;pushf
			;pop bx
			;and bh,1111100b
			;push bx
			;popf			
		;}
		;call dword ptr ds:[0]
		
		;// 进入中断处理程序后,if和tf都已经设置为0了,所以上面7条指令精简后,变成2条指令	
		pushf
		call dword ptr ds:[0] 
		
		;// 读按键的扫描码到al中
		in al,60h 
		;if_not_esc{			    
			; esc键改变显存地址“160*12
			cmp al,1
			jne int9ret
		;else{
			mov ax,0b800h                      
			mov es,ax
			;// 将字符的颜色属性加1,即改变目前的颜色属性
			inc byte ptr es:[160*12+40*2+1] 
		;}
		
		int9ret:;{
			pop es
			pop bx
			pop ax
		;}
		iret
	;}
	
code ends 
            						
end main                                                                 	

检测点 15.1-1

pushf
call dword ptr ds:[0]

⭐️ 有的同学会对程序中连续调用两次 pushf 有疑问,为什么要调用两次pushf?

第二个pushf 是为了通过将标志寄存器内容出入栈来修改其内容(if,tf置零)
第一个pushf 是为了支持BIOS中断处理程序中的iret 指令

检测点 15.1-2

mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs

改为

pushf
;// 设置中断标志位为0,不允许中断以防止被别的中断打断设置入口地址指令
cli
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
popf

16、直接定位表

检测点16.1

;--------------------------------------------------------------------
;| ------------------------------- 15.4  ----------------------
;|  功能:|	将code段中a处的8个数据累加,结果存储到b处的双字中
assume cs:code 

code segment	 
	a dw 1,2,3,4,5,6,7,8
	b dd 0
	
	start:;{ 
		
		mov si,0		
		mov cx,8		
		for_:;{
			mov ax,a[si]
			add a[si+2],ax
			adc b[0],0
			add si,2
			loop for_
		;}
		
		mov ax,4c00h
		int 21h
	;}	
		
code ends 
            						
end start     

在这里插入图片描述

16.2 其他段中使用数据标号

;| ------------------------------- 16.2 ----------------------
;|  功能:|	将data段中a处的8个数据累加,结果存储到b处的字中
;|-------------------------------------------------------------
assume cs:code ,ds:data

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0 
data ends

code segment
	main:;{
		mov ax,data	
		mov ds,ax
		
		mov si,0
		mov cx,8
		
		for_:;{
			mov b,2
			mov al,a[si]
			mov ah,0
			add b,ax
			inc si
			loop for_
		;}
		
		mov ax,4c00h
		int 21h
	;}	
		
code ends 
            						
end main

检测点 16.2


assume cs:code ,es:data

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0 
data ends

code segment
	main:;{
		mov ax,data
		; // data 段与es建立的联系
		mov es,ax
		
		mov si,0
		mov cx,8
		
		for_:;{
			mov al,a[si]
			mov ah,0
			add b,ax
			inc si
			loop for_
		;}
		
		mov ax,4c00h
		int 21h
	;}	
		
code ends 
            						
end main   

16.3 直接定址表

程序1

在这里插入图片描述

现在要取al中高4位和低4位的内容,然后在屏幕上用16进制显示出来(需要对内容进行数值转换,直接显示会显示数值对应的ASCII码字符)

我们想得到的结果如下图,然后将ah,al的内容作为偏移量到’字符表’中取对应的字符,然后发送到显存进行显示

在这里插入图片描述
在这里插入图片描述


assume cs:code

code segment
	main:;{
		jmp short show 
		table db '0123456789ABCDEF'
		
		show:;{
			push bx
			push es
			
			;// 获取al中高4位的值,放入ah中,用做table的偏移量
			mov ah,al
			mov cl,4
			shr ah,cl
			
			;// 获取al中低4位的值,用作table的偏移量
			and al,0001111b
			
			;//从talbe中取al高4位的16进制字符
			mov bl,ah
			mov bh,0
			mov ah,table[bx]
			
			;// 显示字符
			mov bx,0b800h
			mov es,bx
			mov es:[160*12+40*2],ah
			
			;//从talbe中取al低4位的16进制字符
			mov bl,al
			mov bh,0
			mov al,table[bx]
			
			mov es:[160*12+40*2+2],al
			
			pop es
			pop bx
			ret
		;}
			
		
		mov ax,4c00h
		int 21h
	;}	
		
code ends 
            						
end main                                       	                                      	

程序2

assume cs:code

code segment
	main:;{
		jmp short show 
		table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
		ag0   db '0',0
		ag30  db '0.5',0
		ag60  db '0.866',0
		ag90  db '1',0
		ag120 db '0.866',0
		ag150 db '0.5',0
		ag180 db '0',0
		
		
		show:;{
			push bx
			push es
			push si
		
			mov bx,0b800h
			mov es,bx
			
			;// (角度值/30)作为table的偏移量,取得对应字符串的偏移地址放入bx中
			mov ax,60
			mov bl,30
			div bl
			mov bl,al
			mov bh,0
			add bx,bx
			mov bx,table[bx]
			
			mov si,160*12+40*2
		for_:;{			
			mov ah,cs:[bx]
			cmp ah,0;// 取到的值是0则跳出循环
			je show_return
			mov es:[si],ah
			inc bx
			add si,2
			jmp short for_
		;}
			
		show_return:
			pop si
			pop es
			pop bx
			; // ret 不应该有,否则程序又返回for_循环中了
			;ret
		;}
			
		
		mov ax,4c00h
		int 21h
	;}	
		
code ends 
            						
end main                                       	

程序2 改进

assume cs:code

code segment

		table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
		ag0   db '0',0
		ag30  db '0.5',0
		ag60  db '0.866',0
		ag90  db '1',0
		ag120 db '0.866',0
		ag150 db '0.5',0
		ag180 db '0',0
		
	main:;{
	
		back:;{
			push bx
			push es
			push si
		;}
		
		;/* 
		; * (角度值/30)作为table的偏移量,然后取得对应字符串的偏移地址放入bx中
		; * 参数:   ax存放角度; 
		; * 返回值: bx存放返回值-sin x 结果字符串的偏移地址;
		; */
		mov ax,30
		get_value_offset:;{			
			mov bl,30
			div bl
			mov bl,al
			mov bh,0
			;// table中的偏移地址占两个字节,所以bx要乘以2
			add bx,bx
			;// 获取角度对应的`sin x` 值的字符串偏移量
			mov bx,table[bx]
		;}
		
		;// 显示sin x 的值
		display:;{
			mov ax,0b800h
			mov es,ax
			mov si,160*12+40*2
			for_:;{			
				mov ah,cs:[bx]
				cmp ah,0
				;// 取到的值是0则跳出循环,结束程序
				je main_end
				mov es:[si],ah
				inc bx
				add si,2
				jmp short for_
			;}
		;}
			
		main_end:;{
			pop si
			pop es
			pop bx
			; // ret 不应该有,否则程序又返回for_循环中了
			;ret
			mov ax,4c00h
			int 21h
		;}			
		
		
	;}	
		
code ends 
            						
end main                                       	

16.4

assume cs:code,ds:stack

stack segment
		dw 0;
stack ends

code segment
	;table dw clear_screem,set_screem_front_color,set_screem_bg_color,roll_next
	
	main:;{
		mov ah,3	
		;push bx
		;cmp ah,3
		;ja ret_		
		;mov bl,ah
		;mov bh,0
		;add bx,bx		
		;call word ptr table[bx]		
		;ret_:
		;	pop bx
		;	ret	
		
		cmp ah,0
		je do1
		cmp ah,1
		je do2
		cmp ah,2
		je do3
		cmp ah,3
		je do4
		
		end_:;{
			mov ax,4c00h
			int 21h
		;}
		
		do1:;{
			call clear_screem
			jmp short end_
	    ;}
		do2:;{
			call set_screem_front_color
			jmp short end_
	    ;}
		do3:;{
			call set_screem_bg_color
			jmp short end_
	    ;}
		do4:;{
			call roll_next
			jmp short end_
	    ;}
		
	;}	
	
	;/* 
	; * 1、清屏:将显存中当前屏幕中的字符设置为空格符
	; */
	clear_screem:;{
		call backup_reg
		
		mov bx,0b800h
		mov es,bx			
		mov bx,0
		;// 80 *25 = 2000,一屏显示的字符个数
		mov cx,2000
		
		for_clear_screem:;{
			mov byte ptr es:[bx],' '
			add bx,2
			loop for_clear_screem
		;}			
		call recover_backup_reg
		ret
	;}
	
	;/* 
	; * 2、设置前景颜色,设置显存中当前屏幕中处于奇数地址的属性字节的第0,1,2位
	; */
	set_screem_front_color:;{
		call backup_reg
		
		mov bx,0b800h
		mov es,bx
		mov bx,1
		mov cx,2000
		
		for_set_screem_front_color:;{
			and byte ptr es:[bx],11111000b
			or es:[bx],al
			add bx,2
			loop for_set_screem_front_color
		;}
		call recover_backup_reg
		ret
	;}
	
	;/* 
	; * 3、设置背景颜色:设置显存中当前屏幕中处于奇数地址的属性字节的第4,5,6位
	; */
	set_screem_bg_color:;{
		call backup_reg
		mov cl,4
		shl al,cl
		
		mov bx,0b800h
		mov es,bx
		mov bx,1
		mov cx,2000
		
		for_set_screem_bg_color:;{
			and byte ptr es:[bx],10001111b
			or es:[bx],al
			add bx,2
			loop for_set_screem_bg_color
		;}
		call recover_backup_reg
		ret
		
	;}
	
	;/* 
	; * 4、向上滚动一行: 依次将第n+1行的内容复制到第n行处,最后一行为空
	; */
	roll_next:;{
		call backup_reg
		
		mov si,0b800h
		mov es,si
		mov ds,si
		mov si,160
		mov di,0
		cld
		
		mov cx,24			
		for_roll_next:;{
			push cx
			mov cx,160
			rep movsb
			pop cx
			loop for_roll_next				
		;}
		
		;// 设置屏幕的最后一行(80列)为空格字符
		mov cx,80
		mov si,0
		for_clear_last_row:;{
			mov byte ptr [160*24+si],' '
			add si,2
			loop for_clear_last_row
		;}
		
		call recover_backup_reg
		ret		
	;}
	
	;/* 
	; * 备份几个后面要用到的寄存器
	; */
	backup_reg:;{
		;// 调用者的ip出栈,放入ax中,ret调用前再压栈,
		pop ax		
		push bx
		push cx
		push si
		push di
		push es
		push ds
		;//将调用者ip的值入栈,功能ret用
		push ax
		ret
	;}
	
	;/* 
	; * 恢复备份的寄存器数据
	; */
	recover_backup_reg:;{
		;// 调用者的ip出栈,放入ax中,ret调用前再压栈,
		pop ax
		
		pop ds
		pop es
		pop di
		pop si
		pop cx
		pop bx
		;//将调用者ip的值入栈,功能ret用
		push ax
		ret
	;}

		
code ends 
            						
end main                                   	

17、使用BIOS进行键盘输入和磁盘读写

17.2、根据输入的字符改变屏幕中字体颜色



;| ------------------------------- 15.4  ------------------------ 
;|  功能:|	键盘输入字符来改变屏幕上所有字符的颜色,r 为红色,b为蓝色,g为绿色
;|--------------------------------------------------------------



assume cs:code 


code segment	 
   
	main:;{
		;// 执行int 16h的0号功能
		mov ah,0
		int 16h
		
		;// ah保存颜色属性,1表示前景色为蓝
		mov ah,1
		cmp al,'r'
		je red
		cmp al,'g'
		je green
		cmp al,'b'
		je blue
		jmp short main_end
		
	red:
		;// ah保存颜色属性,从这里开始,会左移2位,变成4,表示红色
		shl ah,1
	green:
		;// ah保存颜色属性,从这里开始,会左移1位,变成2,表示绿色
		shl ah,1
	blue:
		mov bx,0b800h
		mov es,bx
		;// 屏幕中字符颜色属性的起始偏移地址为1
		mov bx,1
		mov cx,2000
	for_:;{
		;// 颜色属性的后三位是前景色
		and byte ptr es:[bx],11111000b
		;// 设置字符前景色
		or es:[bx],ah
		add bx,2
		loop for_
	;}
		
		jmp main
	main_end:;{
		mov ax, 4c00H                
		int 21h 
	;}
	
	;}	

	
code ends 
            						
end main           

17.3、字符串输入

复习:int 16 读键盘缓冲区,结果放在ax中,al是ASCII码,ah是扫描码


;| ------------------------------- 17.3 字符串输入  ------------------------ 
;|  功能:|	键盘输入字符来改变屏幕上所有字符的颜色,r 为红色,b为蓝色,g为绿色
;|--------------------------------------------------------------



assume cs:code 


code segment
;----------- 接收字符串输入的子程序 -----------------------------

	getstr:
		push ax
		
	;// 循环获取字符	
	for_getstrs:;{
		mov ah,0
		int 16h
		
		;if(输入的不是字符){
			;// ascii码小于20h,说明不是字符
			cmp al,20h
			jb notchar
		;}
		
		;// ah开始用来存储功能号,0 入栈,1 出栈,2显示
		;pushChar{
			mov ah,0
			call charstack
		;}
		
		;displayChar{
			mov ah,2
			call charstack
		;}
				
		jmp for_getstrs
	;}
		
	;// 
	notchar:;{
		;// 0eh为退格键的扫描码
		cmp ah,0eh
		je backspace
		
		;// 1ch为回车键的扫描码
		cmp ah,1ch
		je enter
		
		jmp for_getstrs
	;}
	
	backspace:;{
		mov ah,1
		call charstack
		mov ah,2
		call charstack
		jmp for_getstrs
	;}
	
	enter:;{
		mov al,0
		mov ah,0
		call charstack
		mov ah,2
		call charstack
	;}
		
		pop ax
		ret

;----------- 字符栈的入栈、出栈和显示 -----------------------------
		
	charstack:
		jmp short charstart
		table	dw charpush,charpop,charshow
		top		dw 0
	
	charstart:;{
		push bx
		push dx
		push di
		push es
		
		;if(功能号大于2){
			cmp ah,2
			; 返回
			ja sret
		;}
		
		;根据ah的值来选择功能执行{
			mov bl,ah
			mov bh,0
			add bx,bx
			;// 根据ah的值从table中获取偏移地址
			jmp word ptr table[bx]
		;}
		
	;}	
	
	charpush:;{
		mov bx,top
		mov [si][bx],al
		inc top
		jmp sret
	;}
	
	charpop:;{
		cmp top,0
		je sret
		dec top
		mov bx,top
		mov al,[si][bx]
		jmp sret
	;}
	
	charshow:;{
		mov bx,0b800h
		mov es,bx
		
		mov al,160
		mov ah,0
		
		mul dh
		mov di,ax
		add dl,dl
		mov dh,0
		add di,dx
		
		mov bx,0
		charshows:;{
			cmp bx,top
			jne noempty
			mov byte ptr es:[di],' '
			jmp sret
		;}
		noempty:;{
			mov al,[si][bx]
			mov es:[di],al
			mov byte ptr es:[di+2],' '
			inc bx
			add di,2
			jmp charshows
		;}
	;}
	
	
	;// 恢复备份,并ret
	sret:;{
		pop es
		pop di
		pop dx
		pop bx
		ret
	;}


	
code ends 
            						
end getstr           

附录A: 通用程序

1、拷贝代码的程序


2、延迟

delay:;{
		;// 备份后续用到的两个寄存器
		push ax
		push dx
		
		;// dx存放高16位,ax存放低16位
		mov dx,5h                                         
		mov ax,0
		for_delay:;{
			sub ax,1
			sbb dx,0
			cmp ax,0
			jne for_delay
			cmp dx,0
			jne for_delay
		;}
		
		;// 恢复备份的两个寄存器的内容
		pop dx
		pop ax
		ret
;}






附录B:颜色属性

内存空间中,0xB8000H ~ 0xBFFFFH,共32KB空间为显存空间。

将32KB的空间分布为8页,每页4KB,显示器可以显示任一页的内容,但一般情况下,显示第0也的内容。

屏幕上的每个字符对应显存中“两个连续的字节”,第一个是字符的ASCII码,后一个是字符的“显示属性”。显示属性格式如下:

在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java硕哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值