汇编语言 课程设计2具体实现

一、材料

  • 开机后,CPU自动进入到FFFF:0单元处执行,此处有一条跳转指令。CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。
  • 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。
  • 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。

如果设为从软盘启动操作系统,则int 19h将主要完成以下工作。

  1. 控制0号软驱,读取软盘0道0面1扇区的内容到0:7c00
  2. CS:IP指向0:7c00

软盘的0道0面1扇区中装有操作系统引导程序。int 19h将其装到0:7c00处后,设置CPU从0:7c00开始执行此处的引导程序,操作系统被激活,控制计算机。
如果在0号软驱中没有软盘,或发生软盘I/O错误,则int 19h将主要完成以下工作。

  1. 读取硬盘C的0道0面1扇区的内容到0:7c00
  2. CS:IP指向0:7c00

二、任务

编写一个可以自行启动计算机,不需要在现有操作系统环境中运行的程序。
该程序的功能如下:
( 1 )列出功能选项,让用户通过键盘进行选择,界面如下:
1 ) reset pc ; 重新启动计算机
2 ) Start system ; 引导现有的操作系统
3 ) Clock ; 进入时钟程序
4 ) Srt clock ; 设置时间
( 2 )用户输入“ 1 ”后重新启动计算机。(提示:考虑 FFFF:0 )
( 3 )用户输入“ 2 ” 后引导现有的操作系统。(提示:考虑硬盘 C 的 0 道 0 面 1 扇区)
( 4 )用户输入“ 3 ”后,执行动态现实当前日期,时间的程序。
现实格式如下:年 / 月 / 日 时:分:秒

三、代码实现

assume cs:code 

code segment 

;******** 
;安装程序 
;******** 
	setup: 
		mov ax,cs 
		mov es,ax 
		mov bx,offset main

		mov al,1
		mov ch,0 
		mov cl,1 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov bx,offset mainmenu

		mov al,5
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov ax,4c00h
		int 21h
		
	main: 
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处----- 
		mov ax,2000h 
		mov es,ax 
		mov bx,7c00h 

		mov al,5 
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,2 
		int 13h 
		
		mov ax,2000h
		mov bx,7c00h
		push ax
		push bx
		retf

;******** 
;任务程序 
;******** 

;-----主程序----- 
	mainmenu:
		call cls
;-----显示功能菜单----- 
	showmenu:
		jmp near ptr display 
		menu1 db '1) Reset PC',0 
		menu2 db '2) Start System',0 
		menu3 db '3) Clock',0 
		menu4 db '4) Set Clock',0 
		table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h 

	display: 
		mov ax,0b800h
		mov es,ax
		mov bx,offset table-offset mainmenu+7c00h 
		mov si,0 
		mov cx,4 
	s0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	s1: 
		mov al,cs:[di] 
		cmp al,0 
		je ok 
		mov es:[si],al 
		inc di 
		add si,2 
		jmp near ptr s1 
	ok: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop s0 

;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序----- 
	choose: 
		mov ah,0 
		int 16h 
		cmp al,'1' 
		je reset 
		cmp al,'2' 
		je startsystem
		cmp al,'3' 
		je clock 
		cmp al,'4' 
		je setclockok
		jmp choose    ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能 
	setclockok: 
		jmp near ptr setclock    ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况 

;---键盘输入为1时重新启动计算机--- 
	reset: 
		mov ax,0ffffh 
		mov bx,0 
		push ax 
		push bx 
		retf 

;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
	startsystem: 
		mov ax,0 
		mov es,ax 
		mov bx,7c00h 

		mov al,1 
		mov ch,0 
		mov cl,1 
		mov dl,80h 
		mov dh,0 
		mov ah,2 
		int 13h 

		mov ax,0 
		mov bx,7c00h 
		push ax 
		push bx 
		retf 

	startsystemend: 
		nop 

;---键盘输入为3时动态显示日期和时间--- 
	clock: 
		jmp near ptr clocks 
		tableclock db 9,8,7,4,2,0 

	clocks: 
		mov ax,0b800h 
		mov es,ax 

	clocksok: 
		mov bx,offset tableclock-offset mainmenu+7c00h 
		mov cx,6 
		mov si,0 
	s2: 
		push cx 
		mov al,cs:[bx] 
		out 70h,al 
		in al,71h 

		mov ah,al 
		mov cl,4 
		shr ah,cl 				;年份的十位
		and al,00001111b 		;年份的个位

		add ah,30h 
		add al,30h 

		mov byte ptr es:[160*12+si],ah 
		add si,2 
		mov byte ptr es:[160*12+si],al 
		add si,2 

		pop cx 

		cmp cx,1    ;注意je等转移指令的用法,调用的顺序 
		je go3 
		cmp cx,4 
		jb go2 
		cmp cx,4 
		je go3 
		cmp cx,4 
		ja go1 

	go1: 
		mov byte ptr es:[160*12+si],'/' 
		jmp goend 
	go2: 
		mov byte ptr es:[160*12+si],':' 
		jmp goend 
	go3: 
		mov byte ptr es:[160*12+si],0 
		jmp goend 

	goend: 
		add si,2 
		inc bx 

		loop s2 

		in al,60h   	
		cmp al,3bh    	;当按下F1键改变显示颜色 
		jne goesc 
		mov ah,0
		int 16h
		
		mov cx,17 
		mov di,1 
	color: 
		inc byte ptr es:[160*12+di]
		add di,2 
		loop color 

	goesc:  
		cmp al,1    	;当按下ESC键返回主菜单 
		jne nogoesc 
		jmp mainmenu  

	nogoesc: 
		jmp clocksok 
   

;---键盘输入为4时更改当前日期和时间,更改后返回主选单--- 
	setclock: 
		jmp near ptr setclocks 
		string1 db 'Input Time:yy/mm/dd hh:mm:ss',0 
		string2 db 'Change:\>',0 
		table0  dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h

;以下代码显示提示字符 
	setclocks: 
		mov bx,offset table0-offset mainmenu+7c00h 
		mov si,0 
		mov cx,2 
	t0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	t1: 
		mov al,cs:[di] 
		mov es:[160*22+si],al 
		cmp al,0 
		je ko 
		inc di 
		add si,2 
		jmp near ptr t1 
	ko: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop t0 

;以下代码接收键盘输入 
		mov dh,23    ;设置起始行数 
		mov dl,9    ;设置起始列数 
		jmp getstrs
		
	;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter_string3:
		mov cx,6
		mov si,offset string3-offset mainmenu+7c00h
		mov di,offset tableclock-offset mainmenu+7c00h
	resetclock:
		push cx
		mov ah,cs:[si]
		sub ah,30h
		mov cl,4
		shl ah,cl
		mov al,cs:[si+1]
		sub al,30h
		add ah,al
		mov al,cs:[di]
		out 70h,al
		mov al,ah
		out 71h,al
		add si,3
		inc di
		pop cx
		loop resetclock
	;以下代码将字符区清空,并重置字符显示位置,即top处数据置零 
		mov cx,20 
		mov bp,offset string3-offset mainmenu+7c00h 
	emptystring3: 
		mov byte ptr cs:[bp],0 
		inc bp 
		loop emptystring3 

		mov bp,offset top-offset mainmenu+7c00h 
		mov word ptr cs:[bp],0 

		jmp near ptr mainmenu 
		
	getstrs: 
		mov ah,0  
		int 16h 
		cmp al,20h 
		jb nochar    		;ascii码小于20h,说明不是字符   
		mov ah,0    		;ah=0,表示字符入栈,要入栈的字符在al中  
		call charstack    	;字符入栈  
		mov ah,2  
		call charstack    	;显示栈中的字符  
		jmp near ptr getstrs 

	nochar: 
		cmp ah,0eh    		;ah放扫描码,al放ascii码,退格键的扫描码0eh  
		je backspace  
		cmp ah,1ch    		;enter键的扫描码1ch  
		je enter  
		jmp near ptr getstrs  

	backspace:  
		mov ah,1  
		call charstack    	;字符出栈  
		mov ah,2  
		call charstack      ;显示栈中的字符  
		jmp near ptr getstrs  

;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter: 
		mov al,0
		mov ah,0
		call charstack		;0入栈
		mov ah,2
		call charstack		;显示栈中的字符
		jmp enter_string3

	charstack: 
		jmp near ptr charstart  

		table1     dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h  
		string3    db 20 dup (0) 
		top        dw 0 

	charstart: 
		push bx 
		push dx 
		push di 
		push es 

		mov si,offset table1-offset mainmenu+7c00h 
		mov bp,offset string3-offset mainmenu+7c00h 
		mov di,offset top-offset mainmenu+7c00h 

		cmp ah,2    				;判断子程序的功能号 
		ja sret    					;大于2返回 
		mov bl,ah    				;通过bx计算功能号在table1中的偏移地址 
		mov bh,0 
		add bx,bx    				;一个偏移地址占2个字节,所以bx*2 
		jmp word ptr cs:[si+bx]   	;跳转到子程序的相应功能:入栈,出栈,显示 

	charpush: 
		mov si,cs:[di]    		  	;di指向栈顶 
		mov cs:[bp+si],al   	  	;将al内的字符入栈 
		inc word ptr cs:[di]      	;栈顶上移一个字节 
		jmp near ptr sret    	  	;返回 

	charpop: 
		cmp word ptr cs:[di],0   	;出栈前判断字符栈是否为空 
		je sret    					;字符栈为空,不用出栈,直接返回 
		dec word ptr cs:[di]    	;字符栈不空,栈指针top下移指向栈顶元素 
		jmp near ptr sret    		;返回 

	charshow: 
		mov bx,0b800h 
		mov es,bx    				;es指向显存缓冲区 
		mov al,160 
		mov ah,0 
		mul dh    					;计算行数160*dh,结果在ax中 
		mov bx,ax    				;bx存放显示的位置 
		add dl,dl    				;计算列数 dl*2 
		mov dh,0 
		add bx,dx    				;计算出字符要显示的位置:160*dh+dl*2 

		mov si,0 

	charshows: 
		cmp si,cs:[di] 
		jne noempty 
		mov byte ptr es:[bx],' ' 
		jmp near ptr sret 
	noempty: 
		mov al,cs:[bp+si] 
		mov es:[bx],al 
		mov byte ptr es:[bx+2],' ' 
		inc si 
		add bx,2 
		jmp near ptr charshows 

	sret: 
		pop es 
		pop di 
		pop dx 
		pop bx 
		ret 
		
	cls:
		push ax
		push bx
		push cx
		push es
		
		mov ax,0b800h 
		mov es,ax 
		mov bx,0 
		mov cx,2000 
	s: 
		mov byte ptr es:[bx],' '
		mov byte ptr es:[bx+1],7
		add bx,2 
		loop s 
		
		pop es
		pop cx
		pop bx
		pop ax
		ret
code ends 
end setup
程序主要分为三部分:
安装程序:负责将main段写入1扇区,mainmenu段写入1扇区以后的扇区
setup: 
		mov ax,cs 
		mov es,ax 
		mov bx,offset main

		mov al,1
		mov ch,0 
		mov cl,1 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov bx,offset mainmenu

		mov al,5
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov ax,4c00h
		int 21h
引导程序:将mainmenu段写入2000:7c00处,然后跳转去执行
main: 
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处----- 
		mov ax,2000h 
		mov es,ax 
		mov bx,7c00h 

		mov al,5 
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,2 
		int 13h 
		
		mov ax,2000h
		mov bx,7c00h
		push ax
		push bx
		retf
主程序:实现程序功能的所有程序
;-----主程序----- 
	mainmenu:
		call cls
;-----显示功能菜单----- 
	showmenu:
		jmp near ptr display 
		menu1 db '1) Reset PC',0 
		menu2 db '2) Start System',0 
		menu3 db '3) Clock',0 
		menu4 db '4) Set Clock',0 
		table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h 

	display: 
		mov ax,0b800h
		mov es,ax
		mov bx,offset table-offset mainmenu+7c00h 
		mov si,0 
		mov cx,4 
	s0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	s1: 
		mov al,cs:[di] 
		cmp al,0 
		je ok 
		mov es:[si],al 
		inc di 
		add si,2 
		jmp near ptr s1 
	ok: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop s0 

;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序----- 
	choose: 
		mov ah,0 
		int 16h 
		cmp al,'1' 
		je reset 
		cmp al,'2' 
		je startsystem
		cmp al,'3' 
		je clock 
		cmp al,'4' 
		je setclockok
		jmp choose    ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能 
	setclockok: 
		jmp near ptr setclock    ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况 

;---键盘输入为1时重新启动计算机--- 
	reset: 
		mov ax,0ffffh 
		mov bx,0 
		push ax 
		push bx 
		retf 

;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
	startsystem: 
		mov ax,0 
		mov es,ax 
		mov bx,7c00h 

		mov al,1 
		mov ch,0 
		mov cl,1 
		mov dl,80h 
		mov dh,0 
		mov ah,2 
		int 13h 

		mov ax,0 
		mov bx,7c00h 
		push ax 
		push bx 
		retf 

	startsystemend: 
		nop 

;---键盘输入为3时动态显示日期和时间--- 
	clock: 
		jmp near ptr clocks 
		tableclock db 9,8,7,4,2,0 

	clocks: 
		mov ax,0b800h 
		mov es,ax 

	clocksok: 
		mov bx,offset tableclock-offset mainmenu+7c00h 
		mov cx,6 
		mov si,0 
	s2: 
		push cx 
		mov al,cs:[bx] 
		out 70h,al 
		in al,71h 

		mov ah,al 
		mov cl,4 
		shr ah,cl 				;年份的十位
		and al,00001111b 		;年份的个位

		add ah,30h 
		add al,30h 

		mov byte ptr es:[160*12+si],ah 
		add si,2 
		mov byte ptr es:[160*12+si],al 
		add si,2 

		pop cx 

		cmp cx,1    ;注意je等转移指令的用法,调用的顺序 
		je go3 
		cmp cx,4 
		jb go2 
		cmp cx,4 
		je go3 
		cmp cx,4 
		ja go1 

	go1: 
		mov byte ptr es:[160*12+si],'/' 
		jmp goend 
	go2: 
		mov byte ptr es:[160*12+si],':' 
		jmp goend 
	go3: 
		mov byte ptr es:[160*12+si],0 
		jmp goend 

	goend: 
		add si,2 
		inc bx 

		loop s2 

		in al,60h   	
		cmp al,3bh    	;当按下F1键改变显示颜色 
		jne goesc 
		mov ah,0
		int 16h
		
		mov cx,17 
		mov di,1 
	color: 
		inc byte ptr es:[160*12+di]
		add di,2 
		loop color 

	goesc:  
		cmp al,1    	;当按下ESC键返回主菜单 
		jne nogoesc 
		jmp mainmenu  

	nogoesc: 
		jmp clocksok 
   

;---键盘输入为4时更改当前日期和时间,更改后返回主选单--- 
	setclock: 
		jmp near ptr setclocks 
		string1 db 'Input Time:yy/mm/dd hh:mm:ss',0 
		string2 db 'Change:\>',0 
		table0  dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h

;以下代码显示提示字符 
	setclocks: 
		mov bx,offset table0-offset mainmenu+7c00h 
		mov si,0 
		mov cx,2 
	t0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	t1: 
		mov al,cs:[di] 
		mov es:[160*22+si],al 
		cmp al,0 
		je ko 
		inc di 
		add si,2 
		jmp near ptr t1 
	ko: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop t0 

;以下代码接收键盘输入 
		mov dh,23    ;设置起始行数 
		mov dl,9    ;设置起始列数 
		jmp getstrs
		
	;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter_string3:
		mov cx,6
		mov si,offset string3-offset mainmenu+7c00h
		mov di,offset tableclock-offset mainmenu+7c00h
	resetclock:
		push cx
		mov ah,cs:[si]
		sub ah,30h
		mov cl,4
		shl ah,cl
		mov al,cs:[si+1]
		sub al,30h
		add ah,al
		mov al,cs:[di]
		out 70h,al
		mov al,ah
		out 71h,al
		add si,3
		inc di
		pop cx
		loop resetclock
	;以下代码将字符区清空,并重置字符显示位置,即top处数据置零 
		mov cx,20 
		mov bp,offset string3-offset mainmenu+7c00h 
	emptystring3: 
		mov byte ptr cs:[bp],0 
		inc bp 
		loop emptystring3 

		mov bp,offset top-offset mainmenu+7c00h 
		mov word ptr cs:[bp],0 

		jmp near ptr mainmenu 
		
	getstrs: 
		mov ah,0  
		int 16h 
		cmp al,20h 
		jb nochar    		;ascii码小于20h,说明不是字符   
		mov ah,0    		;ah=0,表示字符入栈,要入栈的字符在al中  
		call charstack    	;字符入栈  
		mov ah,2  
		call charstack    	;显示栈中的字符  
		jmp near ptr getstrs 

	nochar: 
		cmp ah,0eh    		;ah放扫描码,al放ascii码,退格键的扫描码0eh  
		je backspace  
		cmp ah,1ch    		;enter键的扫描码1ch  
		je enter  
		jmp near ptr getstrs  

	backspace:  
		mov ah,1  
		call charstack    	;字符出栈  
		mov ah,2  
		call charstack      ;显示栈中的字符  
		jmp near ptr getstrs  

;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter: 
		mov al,0
		mov ah,0
		call charstack		;0入栈
		mov ah,2
		call charstack		;显示栈中的字符
		jmp enter_string3

	charstack: 
		jmp near ptr charstart  

		table1     dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h  
		string3    db 20 dup (0) 
		top        dw 0 

	charstart: 
		push bx 
		push dx 
		push di 
		push es 

		mov si,offset table1-offset mainmenu+7c00h 
		mov bp,offset string3-offset mainmenu+7c00h 
		mov di,offset top-offset mainmenu+7c00h 

		cmp ah,2    				;判断子程序的功能号 
		ja sret    					;大于2返回 
		mov bl,ah    				;通过bx计算功能号在table1中的偏移地址 
		mov bh,0 
		add bx,bx    				;一个偏移地址占2个字节,所以bx*2 
		jmp word ptr cs:[si+bx]   	;跳转到子程序的相应功能:入栈,出栈,显示 

	charpush: 
		mov si,cs:[di]    		  	;di指向栈顶 
		mov cs:[bp+si],al   	  	;将al内的字符入栈 
		inc word ptr cs:[di]      	;栈顶上移一个字节 
		jmp near ptr sret    	  	;返回 

	charpop: 
		cmp word ptr cs:[di],0   	;出栈前判断字符栈是否为空 
		je sret    					;字符栈为空,不用出栈,直接返回 
		dec word ptr cs:[di]    	;字符栈不空,栈指针top下移指向栈顶元素 
		jmp near ptr sret    		;返回 

	charshow: 
		mov bx,0b800h 
		mov es,bx    				;es指向显存缓冲区 
		mov al,160 
		mov ah,0 
		mul dh    					;计算行数160*dh,结果在ax中 
		mov bx,ax    				;bx存放显示的位置 
		add dl,dl    				;计算列数 dl*2 
		mov dh,0 
		add bx,dx    				;计算出字符要显示的位置:160*dh+dl*2 

		mov si,0 

	charshows: 
		cmp si,cs:[di] 
		jne noempty 
		mov byte ptr es:[bx],' ' 
		jmp near ptr sret 
	noempty: 
		mov al,cs:[bp+si] 
		mov es:[bx],al 
		mov byte ptr es:[bx+2],' ' 
		inc si 
		add bx,2 
		jmp near ptr charshows 

	sret: 
		pop es 
		pop di 
		pop dx 
		pop bx 
		ret 
		
	cls:
		push ax
		push bx
		push cx
		push es
		
		mov ax,0b800h 
		mov es,ax 
		mov bx,0 
		mov cx,2000 
	s: 
		mov byte ptr es:[bx],' '
		mov byte ptr es:[bx+1],7
		add bx,2 
		loop s 
		
		pop es
		pop cx
		pop bx
		pop ax
		ret

四、处理

使用vmare14及DOS7.1进行程序的测试

在这里插入图片描述

使用软盘进入系统,需要创建软盘文件,后缀为.img.flp	

在这里插入图片描述

写入软盘之后,需要进入固件配置软盘启动

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

设置好保存并退出即可

在这里插入图片描述

五、优化

发现源程序的三号功能在摁下F1后虽然颜色变了,但是动态显示的功能实现不了了,所以做了一些优化,重写了3号功能。

assume cs:code 

code segment 

;******** 
;安装程序 
;******** 
	setup: 
		mov ax,cs 
		mov es,ax 
		mov bx,offset main

		mov al,1
		mov ch,0 
		mov cl,1 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov bx,offset mainmenu

		mov al,5
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,3 
		int 13h 
		
		mov ax,4c00h
		int 21h
		
	main: 
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处----- 
		mov ax,2000h 
		mov es,ax 
		mov bx,7c00h 

		mov al,5 
		mov ch,0 
		mov cl,2 
		mov dl,0 
		mov dh,0 
		mov ah,2 
		int 13h 
		
		mov ax,2000h
		mov bx,7c00h
		push ax
		push bx
		retf

;******** 
;任务程序 
;******** 

;-----主程序----- 
	mainmenu:
		call cls
;-----显示功能菜单----- 
	showmenu:
		jmp near ptr display 
		menu1 db '1) Reset PC',0 
		menu2 db '2) Start System',0 
		menu3 db '3) Clock',0 
		menu4 db '4) Set Clock',0 
		table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h 

	display: 
		mov ax,0b800h
		mov es,ax
		mov bx,offset table-offset mainmenu+7c00h 
		mov si,0 
		mov cx,4 
	s0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	s1: 
		mov al,cs:[di] 
		cmp al,0 
		je ok 
		mov es:[si],al 
		inc di 
		add si,2 
		jmp near ptr s1 
	ok: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop s0 

;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序----- 
	choose: 
		mov ah,0 
		int 16h 
		cmp al,'1' 
		je reset 
		cmp al,'2' 
		je startsystem
		cmp al,'3' 
		je save_old_int9
		cmp al,'4' 
		je setclockok
		jmp choose    ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能 
	setclockok: 
		jmp near ptr setclock    ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况 

;---键盘输入为1时重新启动计算机--- 
	reset: 
		mov ax,0ffffh 
		mov bx,0 
		push ax 
		push bx 
		retf 

;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
	startsystem: 
		mov ax,0 
		mov es,ax 
		mov bx,7c00h 

		mov al,1 
		mov ch,0 
		mov cl,1 
		mov dl,80h 
		mov dh,0 
		mov ah,2 
		int 13h 

		mov ax,0 
		mov bx,7c00h 
		push ax 
		push bx 
		retf 

	startsystemend: 
		nop 

;---键盘输入为3时动态显示日期和时间--- 
	save_old_int9:		;保存九号中断
		mov bx,0
		mov es,bx

		push es:[9*4]
		pop es:[200h]
		push es:[9*4+2]
		pop es:[202h]
	set_new_int9:
		mov bx,0
		mov es,bx

		cli
		mov word ptr es:[9*4],offset newint9-offset mainmenu+7c00h
		mov word ptr es:[9*4+2],2000h
		sti
		
	clock: 
		jmp near ptr clocks 
		tableclock db 9,8,7,4,2,0 

	clocks: 
		mov ax,0b800h 
		mov es,ax 

	clocksok: 
		mov bx,offset tableclock-offset mainmenu+7c00h 
		mov cx,6 
		mov si,0 
	s2: 
		push cx 
		mov al,cs:[bx] 
		out 70h,al 
		in al,71h 

		mov ah,al 
		mov cl,4 
		shr ah,cl 				;年份的十位
		and al,00001111b 		;年份的个位

		add ah,30h 
		add al,30h 

		mov byte ptr es:[160*12+si],ah 
		add si,2 
		mov byte ptr es:[160*12+si],al 
		add si,2 

		pop cx 

		cmp cx,1    ;注意je等转移指令的用法,调用的顺序 
		je go3 
		cmp cx,4 
		jb go2 
		cmp cx,4 
		je go3 
		cmp cx,4 
		ja go1 

	go1: 
		mov byte ptr es:[160*12+si],'/' 
		jmp goend 
	go2: 
		mov byte ptr es:[160*12+si],':' 
		jmp goend 
	go3: 
		mov byte ptr es:[160*12+si],0 
		jmp goend 

	goend: 
		add si,2 
		inc bx 

		loop s2
		
		in al,60h
		cmp al,1
		je goesc
		jmp near ptr clocks
	goesc:  
		mov bx,0
		mov es,bx
		
		cli
		push es:[200h]
		pop es:[9*4]
		push es:[202h]
		pop es:[9*4+2]
		sti
		
		jmp near ptr mainmenu
		
	newint9:
		push ax
		push es
		push bx
		push cx
		push di
		
		mov ax,0
		mov es,ax
		in al,60h
		
		pushf
		call dword ptr es:[200h]
       
		cmp al,3bh		;f1
		jne int9ret
		call a_green
		mov bx,0b800h
		mov es,bx
		mov cx,17
		mov di,1
		mov ah,0
		int 16h
	change_time_colors:
		inc byte ptr es:[160*12+di]
		add di,2
		loop change_time_colors 
	int9ret:
		pop di
		pop cx
		pop bx
		pop es
		pop ax
		iret
	
;---键盘输入为4时更改当前日期和时间,更改后返回主选单--- 
	setclock: 
		jmp near ptr setclocks 
		string1 db 'Input Time:yy/mm/dd hh:mm:ss',0 
		string2 db 'Change:\>',0 
		table0  dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h

;以下代码显示提示字符 
	setclocks: 
		mov bx,offset table0-offset mainmenu+7c00h 
		mov si,0 
		mov cx,2 
	t0: 
		push cx 
		push si 
		mov di,cs:[bx] 
	t1: 
		mov al,cs:[di] 
		mov es:[160*22+si],al 
		cmp al,0 
		je ko 
		inc di 
		add si,2 
		jmp near ptr t1 
	ko: 
		pop si 
		add si,160 
		add bx,2 
		pop cx 
		loop t0 

;以下代码接收键盘输入 
		mov dh,23    ;设置起始行数 
		mov dl,9    ;设置起始列数 
		jmp getstrs
		
	;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter_string3:
		mov cx,6
		mov si,offset string3-offset mainmenu+7c00h
		mov di,offset tableclock-offset mainmenu+7c00h
	resetclock:
		push cx
		mov ah,cs:[si]
		sub ah,30h
		mov cl,4
		shl ah,cl
		mov al,cs:[si+1]
		sub al,30h
		add ah,al
		mov al,cs:[di]
		out 70h,al
		mov al,ah
		out 71h,al
		add si,3
		inc di
		pop cx
		loop resetclock
	;以下代码将字符区清空,并重置字符显示位置,即top处数据置零 
		mov cx,20 
		mov bp,offset string3-offset mainmenu+7c00h 
	emptystring3: 
		mov byte ptr cs:[bp],0 
		inc bp 
		loop emptystring3 

		mov bp,offset top-offset mainmenu+7c00h 
		mov word ptr cs:[bp],0 

		jmp near ptr mainmenu 
		
	getstrs: 
		mov ah,0  
		int 16h 
		cmp al,20h 
		jb nochar    		;ascii码小于20h,说明不是字符   
		mov ah,0    		;ah=0,表示字符入栈,要入栈的字符在al中  
		call charstack    	;字符入栈  
		mov ah,2  
		call charstack    	;显示栈中的字符  
		jmp near ptr getstrs 

	nochar: 
		cmp ah,0eh    		;ah放扫描码,al放ascii码,退格键的扫描码0eh  
		je backspace  
		cmp ah,1ch    		;enter键的扫描码1ch  
		je enter  
		jmp near ptr getstrs  

	backspace:  
		mov ah,1  
		call charstack    	;字符出栈  
		mov ah,2  
		call charstack      ;显示栈中的字符  
		jmp near ptr getstrs  

;回车键时将输入的日期和时间写入cmos,完成后返回主选单 
	enter: 
		mov al,0
		mov ah,0
		call charstack		;0入栈
		mov ah,2
		call charstack		;显示栈中的字符
		jmp enter_string3

	charstack: 
		jmp near ptr charstart  

		table1     dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h  
		string3    db 20 dup (0) 
		top        dw 0 

	charstart: 
		push bx 
		push dx 
		push di 
		push es 

		mov si,offset table1-offset mainmenu+7c00h 
		mov bp,offset string3-offset mainmenu+7c00h 
		mov di,offset top-offset mainmenu+7c00h 

		cmp ah,2    				;判断子程序的功能号 
		ja sret    					;大于2返回 
		mov bl,ah    				;通过bx计算功能号在table1中的偏移地址 
		mov bh,0 
		add bx,bx    				;一个偏移地址占2个字节,所以bx*2 
		jmp word ptr cs:[si+bx]   	;跳转到子程序的相应功能:入栈,出栈,显示 

	charpush: 
		mov si,cs:[di]    		  	;di指向栈顶 
		mov cs:[bp+si],al   	  	;将al内的字符入栈 
		inc word ptr cs:[di]      	;栈顶上移一个字节 
		jmp near ptr sret    	  	;返回 

	charpop: 
		cmp word ptr cs:[di],0   	;出栈前判断字符栈是否为空 
		je sret    					;字符栈为空,不用出栈,直接返回 
		dec word ptr cs:[di]    	;字符栈不空,栈指针top下移指向栈顶元素 
		jmp near ptr sret    		;返回 

	charshow: 
		mov bx,0b800h 
		mov es,bx    				;es指向显存缓冲区 
		mov al,160 
		mov ah,0 
		mul dh    					;计算行数160*dh,结果在ax中 
		mov bx,ax    				;bx存放显示的位置 
		add dl,dl    				;计算列数 dl*2 
		mov dh,0 
		add bx,dx    				;计算出字符要显示的位置:160*dh+dl*2 

		mov si,0 

	charshows: 
		cmp si,cs:[di] 
		jne noempty 
		mov byte ptr es:[bx],' ' 
		jmp near ptr sret 
	noempty: 
		mov al,cs:[bp+si] 
		mov es:[bx],al 
		mov byte ptr es:[bx+2],' ' 
		inc si 
		add bx,2 
		jmp near ptr charshows 

	sret: 
		pop es 
		pop di 
		pop dx 
		pop bx 
		ret 
		
	cls:
		push ax
		push bx
		push cx
		push es
		
		mov ax,0b800h 
		mov es,ax 
		mov bx,0 
		mov cx,2000 
	s: 
		mov byte ptr es:[bx],' '
		mov byte ptr es:[bx+1],7
		add bx,2 
		loop s 
		
		pop es
		pop cx
		pop bx
		pop ax
		ret
	
	a_green:
		push bx
		push es
		
		mov bx,0b800h
		mov es,bx
		
		mov bl,'A'
		mov bh,2
		mov es:[160*12+40],bx
		
		pop es
		pop bx
		ret
code ends 
end setup
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值