王爽《汇编语言》第四版 课程设计2

本文介绍了在DOSBox中使用VMware和WindowsXP系统进行汇编语言编程,创建一个简单的mini_OS。作者分享了如何处理字符串输出、显示时间、修改日期等操作,并探讨了loader和boot程序在软盘启动过程中的作用。文章还讨论了在DOSBox和实际硬件上实验的不同挑战,如修改中断向量表的问题和软盘处理的复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用工具

VMware虚拟机

个人使用上 VMware 比 VirtualBox 要稳定很多,特别是 VMware 15。

Windows xp系统

xp 系统使用内置的 ie 不能连接网络,我这里选择火狐浏览器 xp 版本。

对于汇编文本的编辑这里选用 sublime text3 加 x86-assembly-textmate-bundle-master 高亮插件。

win xp以及所用工具资源

适用于本系统的密钥实测有效:YBVJB-YV2JW-7FHPT-6D8XG-RT83G 。

x86高亮插件的安装

点击箭头指示,将资源中 x86-assembly-textmate-bundle-master 解压然后放至该目录下,再点击上图右下角切换为如上图显示的 x86 and x86_64 assembly 主题。

实验流程

在dosbox中的实验

这里推荐先完成 mini_OS 程序的子功能1,4,5,因为这三个功能基本可以在 dosbox 下完成。在每个功能开头都可以清一次屏幕,具体原理参看《汇编语言》实验十六。

列出功能项

由于要列出的字符串比较多,故使用了直接定址表方便直接调用。

strs dw l0,l1,l2,l3,l4,l5,l6,l7,date,date_form
		l0	db 'Please choose the following options',0
		l1	db '(1) RESET PC',0
		l2	db '(2) BOOT OS',0
		l3	db '(3) SHOW CLOCK',0
		l4  db '(4) SET CLOCK',0
		l5 	db '---Copyright@kctaig---',0
		l6 	db 'Enter ESC to return or S to change colors',0
		l7 	db 'Please enter the date in the following format',0		
		date db "--/--/-- --:--:--",0
		date_form db 9,8,7,4,2,0	
		table dw str_push,str_pop,str_show
		top   dw 0

这一功能实现比较简单,主要使用了《汇编语言》中的字符串输出。这里为了后续输出方便,将字符串输出写为一个子程序。代码如下:

;----------------- 字符串输出 ---------------------
;es:di表示屏幕显示位置
;bx表示字符串在strs表中的位置
;cx表示要连续输出的字符串个数
	str_output:
			push si
			
			push di
			add bx,bx               ;strs中是dw大小,这里计算偏移量
	s0:		mov si,strs[bx]			;将定址表strs中第(bx)个的偏移地址传递到si中
	s1:		cmp	byte ptr cs:[si],0			
			je 	next_raw			;若等于零则结束该字符串
			mov al,cs:[si]
			mov es:[di],al
			inc si
			add di,2
			jmp short s1
	next_raw:	
			add bx,2				;下一个字符串,注意是加2
			pop di
			add di,160				;屏幕下一行
			push di
			loop s0	
			
			pop di					;将屏幕显示的位置pop掉
			
			pop si
			ret

所以具体功能的实现代码如下:

;------------ (1)列出功能项 ------------ 
	list_show:
			call clear			;清屏
			push ax
			push bx
			push cx
			push es
			push di
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,0
			mov cx,1
			call str_output        ;显示l0
			
			mov di,160*11+60
			mov bx,1
			mov cx,4
			call str_output        ;显示l1~l4
			
			mov di,160*15+50
			mov bx,5
			mov cx,1
			call str_output        ;显示l5
			
			pop di
			pop es
			pop cx
			pop bx
			pop ax
			ret	

动态显示当前时间

对于时间的显示参考实验《汇编语言》实验十四,这里我自己因为粗心循环调用的时候用的是 call而不是 jmp 导致程序出错浪费了一个下午,希望读者能引以为戒。

当按下 s 键后,改变现实颜色;按下 ESC 键后,返回到主选单。这里改为s键是因为我的 F1 键不管在加不加 Fn 键都无法使用,后来就放弃了,每次操作软键盘太麻烦;而且没有修改 int 9h 也不会对其他功能造成影响。

我参考的是这位仁兄的方法,我原先使用的是修改 int 9h 键盘中断,但是在调试的时候出现问题。这个问题在《汇编语言》实验十五中也出现了,就是在修改中断向量表时 debug 卡死,但是程序本身又能运行。如果非要用 int 9h 可以参考这位的实验。我偶然看到这个博客,但本人未验证过,有兴趣的朋友可以试试。

;-------------- (4)动态显示当前时间 --------------	
	show_clock:
			call clear
			call clear_input	;清理键盘缓存区
			
			push ax
			push bx
			push cx
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,6
			mov cx,1
			call str_output		;显示l6字符串
			
			mov ax,1;			;设置颜色
			push ax
			
	rep_show:
			mov ax,cs
			mov ds,ax
			mov bx,0
			
			mov ax,0b800h
			mov es,ax
			mov di,160*12+60	;es:di输出屏幕位置
			
			mov cx,6
	s:		push cx
			mov al,date_form[bx]
			push ax
			out 70h,al
			in al,71h			;此时从端口中获取到数据
			
			mov ah,al
			mov cl,4
			shr ah,cl
			and al,00001111b
			
			add ah,30h
			add al,30h			;此时ah为十位,al为个位
			
			mov es:[di],ah
			mov es:[di+2],al	;输出
			add di,4
			
			pop ax
			cmp al,9
			jne ok1
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok1:	cmp al,8
			jne ok2
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok2:	cmp al,7
			jne ok3
			add di,2
			jmp ok
			
	ok3:	cmp al,4
			jne ok4
			mov byte ptr es:[di],':'
			add di,2
			jmp ok
			
	ok4:	cmp al,2
			jne ok
			mov byte ptr es:[di],':'
			add di,2			;输出间隔符号
			
	ok:		inc bx
			pop cx	
			loop s
			
			mov ah,1
			int 16h				;利用16h中断的1号功能,非阻塞读取
			jz rep_call			;如果无缓存字符,继续循环
			mov ah,0
			int 16h				;将一号功能读取的字符从缓存中清除
			cmp al,1bh			;判段是否是esc
			je cret
			cmp ah,1fh			;用s键来改变颜色
			je change_color
			
			jmp rep_call		;若是其他键则继续显示
			
	change_color:
			pop ax
			inc ax
			cmp ax,7			;设置颜色数量上线
			je reset_col
			jmp next_col
	reset_col:					;重置颜色
			mov ax,1
	next_col:
			push ax				;将下一个颜色入栈	
			call set_color
			
	rep_call:
			jmp rep_show		;循环显示时间
			
	cret:	mov ax,7			;将前景色设置为白色
			call set_color
			
			pop ax				;将颜色出栈
			pop es
			pop ds
			pop di
			pop cx
			pop bx
			pop ax
			ret	

修改日期

先建立一个字符串 date ,利用《汇编语言》实验第十七章的 17.3 的程序(会根据需求略作修改)将输入的日期写入 date 。

再将 date 中的年月日等提取出来改写为 BCD CMOS 中。

注:在 dosbox 中修改的日期会无法显示,具体效果只能在裸机上看到。(这个坑真的浪费我好长时间,很无语)

get_str 调用十七章的 17.3,由于太长且书上有就不贴这部分代码了,所有代码会贴在最后面。

;--------------- (5)修改日期 ----------------
;先将日期写入date字符串中,保证只输入日期和格式正确
;再将字符串中的数字写入cmos中
	set_clock:
			call clear				;清除屏幕
		
			push ax
			push dx
			push si
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,7
			mov cx,1
			call str_output			;显示l7字符串
			
			mov di,160*12+64
			mov bx,8
			mov cx,1
			call str_output			;显示日期格式
			
			mov dh,12
			mov dl,32				;设置字符串在屏幕上的行列位置
			mov ax,cs
			mov ds,ax
			mov si,offset date		;ds:si指向字符栈空间
			call get_str			;输入新日期
			
			call set_cmos			;将新日期写入cmos中
			
			pop es
			pop ds
			pop di
			pop si
			pop dx
			pop ax
			
			ret
;--------------- 修改cmos中71端口的内容 ---------------
	set_cmos:
			push ax
			push bx
			push cx
			push si
			
			mov bx,0
			mov cx,6	
	cmos_s:	call get_date_cut		;此时al中为日期
			mov ah,al

			mov al,date_form[bx]
			out 70h,al			;将日期对应地址写入70h端口
			mov al,ah
			out 71h,al				;修改cmos中的内容
			
			inc bx
			loop cmos_s
			
			pop cx
			pop bx
			pop ax
			ret			
;-------------- 截取date中年、月、日等 ----------------
;参数bx表示截取的位置
;al存储截取出的日期
	get_date_cut:
			push bx
			push cx
			push di
			push es
		
			mov al,3
			mul bl
			mov bl,al		;计算偏移量
			
			mov ax,cs
			mov es,ax
			mov di,offset date
			add di,bx		;es:di指向date字符串中日期的位置
			
			mov ax,es:[di]
			sub al,30h
			sub ah,30h
			mov cl,4
			shl al,cl		;输入的时候先输入的十位在al中
			and al,11110000b;保留al中的前四位
			add al,ah		;al中为两位数日期的的BCD码
			
			pop es
			pop di
			pop cx
			pop bx
			ret	

以上基本完成了在 dosbox 中完成的 mini_OS 中的操作,在 dobox 中还会涉及 loader 程序(即安装boot 和 mini_OS 到软盘中)。

在裸机中的实验

机器开机后执行流程

1、开机后 CPU 进入到 FFFF:0000单元处执行,此处为一条跳转指令

2、CPU 执行该跳转指令后,转去执行 BIOS 中的硬件系统检测和初始化程序

3、之后调用 int 19h 中断进行操作系统引导,这里从软盘A启动操作系统

4、int 19h 从软盘启动操作系统将完成以下工作

  1. 控制0号软驱,去读软盘的0道0面1扇区的内容(本实验中即 boot )到 0:7c00h
  2. 将 cs:ip 指向 0:7c00h

5、本实验中 loader 将 boot 写入 0:7c00h,故此处执行 boot

6、再执行 mini_OS ,一个能自行启动计算机,无需在现有操作系统中运行的程序

本实验三个子程序

loader 程序:在 dosbox 中执行,功能如下

  1. 将 boot 写入0道0面1扇区
  2. 将 mini_OS 写入之后的其他扇区

boot 程序:由 int19h 中断将之加载进入内存 0:7c00h,功能如下:

  1. 将 mini_OS 从软盘A中加载进内存 0:7e00h
  2. 将 cs:ip 指向 0:7e00h

mini_OS 程序:小型循环操作系统,功能如下:

  1. 列出功能项
  2. 用户输入1后重新启动计算机
  3. 用户输入2后引导现有操作系统( c 盘中 win xp)
  4. 用户输入3后动态显示当前时间
  5. 用户输入4后修改日期 

注:用 loader 与 boot 的目的是能够扩展引导启动程序。因为 int 19h 只能读取一个扇区,而boot 可以占据多个之后的扇区。

软盘处理

这里参考了在VMWare中软盘的安装

个人觉得这里是实验最难顶的部分,因为软盘启动黑屏无法调试,但是软盘中确实写入了 boot 和 init_OS程序。而VMware 中不好调试裸机;VirtualBox 中虽然有内置调试工具,但我用了发现无法调试引导程序,也就是 Boot 程序;Bochs 需要用 NASM ,并且对于以及写好的软盘在 Windows 写 bochsrc配置文件我没有发现中文文档有具体的方法(看了一圈都是讲bin写进 img 中生成镜像,而软盘本身就可以自己当作启动盘,如果改程序变为 NASM 格式也很麻烦。),当我打算看 Bochs 原文档时,修改了一下返回指令成功了,原因后面会详细说的。总之,这里对于调试非常不友好,在没有人带的情况下很难顶。

安装程序

即 loader,目的是将 boot(系统引导程序)放入软盘0道0面1扇区,并且将 mini_OS 放入之后的3个扇区。具体代码如下:

;------------- loader程序 --------------
	loader:	mov ax,cs
			mov es,ax
			mov bx,offset boot
			
			mov al,1
			mov ch,0
			mov cl,1
			mov dl,0
			mov dh,0
			mov ah,3	
			int 13h			;将boot写入软盘0道0面1扇区
			
			mov bx,offset mini_OS
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,3
			int 13h			;将mini_OS写入软盘之后三个扇区中
			ret

boot 程序

将 mini_OS 从软盘的0道0面2扇区及之后的2个扇区中导入内存中的 0:7E00h 中,并将 cs:ip 指向 0:7E00h。

我原本是用的 iret,但是在 dosbox 中可以用 iret 完成功能1,却无法实现引导,修改为使用 retf 后可以成功,同时还要修改栈,具体参考《汇编语言》第十章。

;-------------- boot程序 ----------------
	boot:	mov ax,0    	;修改栈顶为0:7c00
    		mov ss,ax   
    		mov sp,7c00h 

			mov ax,0
			mov es,ax
			mov bx,7e00h	;es:bx为要读入的内存地址
			
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,2
			int 13h			;将mini_OS从软盘加载进内存0:7e00h
			
			mov ax,0   		;cs
		    push ax
		    mov ax,7e00h	;ip
		    push ax
			retf            ;通过retf设置cs:ip

系统重启

最简单的功能,直接将 cs:ip 设置为 FFFF:0000

;----------- (2)重新启动计算机 ------------
	rep_system:
			mov ax,0ffffh
			push ax
			mov ax,0
			push ax
			retf

引导现有操作系统

将 C 盘中的 xp 系统的引导写入 0:7C00h,然后跳转此处执行。

这里也要用 retf 。

;----------- (3)引导现有操作系统 ------------
	start_system: 	
		
			mov ax,0	
			mov es,ax
			mov bx,7c00h		;es:bx指向要写入的位置
			
			mov al,1			;读取一个扇区
			mov ch,0			;磁道号0
			mov cl,1			;扇区号1
			mov dh,0
			mov dl,80h			;硬盘c
			mov ah,2
			int 13h

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

完结

综上,本次实验就完成基本完成了,在这个框架上可以完善很多功能,希望大家可以做得更好。

同时感谢我引用的链接的博主们,他们使我少走了不少弯路。

实验代码

本程序要在 cmd 中执行才能写入软盘中,在 dosbox 中无效。

mini_OS 中开头的栈顶设置必须是 0:7c00h,如果设置 0:7e00 会影响引导现有os时的512B内容从而使功能2失效,7c00h + 512b = 7e00h。

 全部代码如下:

assume cs:code	

stack segment
	db 128 dup (0)
stack ends

code segment	

	start: 	call loader		;在cmd中运行安装程序,将任务程序写到软盘上
	
			mov ax,4c00h
			int 21h

;------------- loader程序 --------------
	loader:	mov ax,cs
			mov es,ax
			mov bx,offset boot
			
			mov al,1
			mov ch,0
			mov cl,1
			mov dl,0
			mov dh,0
			mov ah,3	
			int 13h			;将boot写入软盘0道0面1扇区
			
			mov bx,offset mini_OS
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,3
			int 13h			;将mini_OS写入软盘之后3个扇区中
			ret
;-------------- boot程序 ----------------
	boot:	mov ax,0    	
    		mov ss,ax   
    		mov sp,7c00h 

			mov ax,0
			mov es,ax
			mov bx,7e00h	;es:bx为要读入的内存地址
			
			mov al,3
			mov ch,0
			mov cl,2
			mov dl,0
			mov dh,0
			mov ah,2
			int 13h			;将mini_OS从软盘加载进内存0:7e00h
			
			mov ax,0   		
		    push ax
		    mov ax,7e00h	
		    push ax
			retf            
;-------------------------------------------------

 org 7e00h	        ;这一步定位mini_OS

;---------------- mini_OS操作系统 ----------------
	mini_OS:
			mov ax,0    	
    		mov ss,ax   	
    		mov sp,7c00h             ;这里必须是7c00

			jmp mini_OS_start
		
		strs dw l0,l1,l2,l3,l4,l5,l6,l7,date,date_form
		l0	db 'Please choose the following options',0
		l1	db '(1) RESET PC',0
		l2	db '(2) BOOT OS',0
		l3	db '(3) SHOW CLOCK',0
		l4  db '(4) SET CLOCK',0
		l5 	db '---Copyright@kctaig---',0
		l6 	db 'Enter ESC to return or S to change colors',0
		l7 	db 'Please enter the date in the following format',0		
		date db "--/--/-- --:--:--",0
		date_form db 9,8,7,4,2,0	
		table dw str_push,str_pop,str_show
		top   dw 0

	mini_OS_start:

			call list_show			;列出功能选项
			call check_option		;检测键盘输入
			jmp mini_OS_start
			ret
			
;---------------- 输入检查 ----------------
	check_option:
			call clear_input	;清除键盘缓存		
			push ax
			mov ah,0
			int 16h
			cmp al,'1'
			je fun1
			cmp al,'2'
			je fun2
			cmp al,'3'
			je fun3
			cmp al,'4'
			je fun4
			call option_error
			
			call chret
	
	fun1:	call rep_system		;调用重启程序
			jmp chret
	fun2:	call start_system
			jmp chret
	fun3:	call show_clock		;显示时钟
			jmp chret
	fun4:	call set_clock
			jmp chret
	
	chret:	pop ax
			ret

;------------ (1)列出功能项 ------------ 
	list_show:
			call clear			;清屏
			push ax
			push bx
			push cx
			push es
			push di
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,0
			mov cx,1
			call str_output
			
			mov di,160*11+60
			mov bx,1
			mov cx,4
			call str_output
			
			mov di,160*15+50
			mov bx,5
			mov cx,1
			call str_output
			
			pop di
			pop es
			pop cx
			pop bx
			pop ax
			ret	

;----------- (2)重新启动计算机 ------------
	rep_system:
			mov ax,0ffffh
			push ax
			mov ax,0
			push ax
			retf

;----------- (3)引导现有操作系统 ------------
	start_system: 	
		
			mov ax,0	
			mov es,ax
			mov bx,7c00h		;es:bx指向要写入的位置
			
			mov al,1			;读取一个扇区
			mov ch,0			;磁道号0
			mov cl,1			;扇区号1
			mov dh,0
			mov dl,80h			;硬盘c
			mov ah,2
			int 13h

			mov ax,0
			push ax
			mov ax,7c00h
			push ax
			retf
			
;-------------- (4)动态显示当前时间 --------------	
	show_clock:
			call clear
			call clear_input	;清理键盘缓存区
			
			push ax
			push bx
			push cx
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,6
			mov cx,1
			call str_output		;显示l6字符串
			
			mov ax,1;			;设置颜色
			push ax
			
	rep_show:
			mov ax,cs
			mov ds,ax
			mov bx,0
			
			mov ax,0b800h
			mov es,ax
			mov di,160*12+60	;es:di输出屏幕行
			
			mov cx,6
	s:		push cx
			mov al,date_form[bx]
			push ax
			out 70h,al
			in al,71h			;此时从端口中获取到数据
			
			mov ah,al
			mov cl,4
			shr ah,cl
			and al,00001111b
			
			add ah,30h
			add al,30h			;此时ah为十位,al为个位
			
			mov es:[di],ah
			mov es:[di+2],al	;输出
			add di,4
			
			pop ax
			cmp al,9
			jne ok1
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok1:	cmp al,8
			jne ok2
			mov byte ptr es:[di],'/'
			add di,2
			jmp ok
			
	ok2:	cmp al,7
			jne ok3
			add di,2
			jmp ok
			
	ok3:	cmp al,4
			jne ok4
			mov byte ptr es:[di],':'
			add di,2
			jmp ok
			
	ok4:	cmp al,2
			jne ok
			mov byte ptr es:[di],':'
			add di,2			;输出间隔符号
			
	ok:		inc bx
			pop cx	
			loop s
			
			mov ah,1
			int 16h				;利用16h中断的1号功能,非阻塞读取
			jz rep_call			;如果无缓存字符,继续循环
			mov ah,0
			int 16h				;将一号功能读取的字符从缓存中清除
			cmp al,1bh			;判段是否是esc
			je cret
			cmp ah,1fh			;用s键来改变颜色
			je change_color
			
			jmp rep_call		;若是其他键则继续显示
			
	change_color:
			pop ax
			inc ax
			cmp ax,7			;设置颜色数量上线
			je reset_col
			jmp next_col
	reset_col:					;重置颜色
			mov ax,1
	next_col:
			push ax				;将下一个颜色入栈	
			call set_color
			
	rep_call:
			jmp rep_show		;循环显示时间
			
	cret:	mov ax,7			;将前景色设置为白色
			call set_color
			
			pop ax				;将颜色出栈
			pop es
			pop ds
			pop di
			pop cx
			pop bx
			pop ax
			ret	

;--------------- (5)修改日期 ----------------
;先将日期写入date字符串中,保证只输入日期和格式正确
;再将字符串中的数字写入cmos中
	set_clock:
			call clear				;清除屏幕
		
			push ax
			push dx
			push si
			push di
			push ds
			push es
			
			mov ax,0b800h
			mov es,ax
			mov di,160*10+40
			mov bx,7
			mov cx,1
			call str_output			;显示l7字符串
			
			mov di,160*12+64
			mov bx,8
			mov cx,1
			call str_output			;显示日期格式
			
			mov dh,12
			mov dl,32				;设置字符串在屏幕上的行列位置
			mov ax,cs
			mov ds,ax
			mov si,offset date		;ds:si指向字符栈空间
			call get_str			;输入新日期
			
			call set_cmos			;将新日期写入cmos中
			
			pop es
			pop ds
			pop di
			pop si
			pop dx
			pop ax
			
			ret
;--------------- 修改cmos中71端口的内容 ---------------
	set_cmos:
			push ax
			push bx
			push cx
			push si
			
			mov bx,0
			mov cx,6	
	cmos_s:	call get_date_cut		;此时al中为日期
			mov ah,al

			mov al,date_form[bx]
			out 70h,al			;将日期对应地址写入70h端口
			mov al,ah
			out 71h,al				;修改cmos中的内容
			
			inc bx
			loop cmos_s
			
			pop cx
			pop bx
			pop ax
			ret	
		
;-------------- 截取date中年、月、日等 ----------------
;参数bx表示截取的位置
;al存储截取出的日期
	get_date_cut:
			push bx
			push cx
			push di
			push es
		
			mov al,3
			mul bl
			mov bl,al		;计算偏移量
			
			mov ax,cs
			mov es,ax
			mov di,offset date
			add di,bx		;es:di指向date字符串中日期的位置
			
			mov ax,es:[di]
			sub al,30h
			sub ah,30h
			mov cl,4
			shl al,cl		;输入的时候先输入的十位在al中
			and al,11110000b;保留al中的前四位
			add al,ah		;al中为两位数日期的的BCD码
			
			pop es
			pop di 			;指向date字符串中日期的位置
			pop cx
			pop bx
			ret	

;--------------- 接受字符串的子程序 ---------------
	get_str:call clear_input
			push ax
	get_char:
			mov ah,0
			int 16h				;从缓存区中取字符
			cmp al,20h
			jb not_char			;ASCII码小于20h不是字符
			mov ah,0	
			call str_stack		;添加字符
			mov ah,2			
			call str_stack		;显示栈中字符
			jmp get_char
			
	not_char:
			cmp ah,0eh
			je backspace		;退格键的扫描码
			cmp ah,1ch
			je enter			;回车键的扫描码
			
			jmp get_char
			
	backspace:	
			mov ah,1
			call str_stack		;删除一个字符
			mov ah,2
			call str_stack		;显示栈中字符
			jmp get_char
	enter:		
			mov al,0
			mov ah,0
			call str_stack		;字符串末尾加0
			mov ah,2
			call str_stack
			
			pop ax				;字符串输入完成
			ret		
	
;--------------- 字符栈的入栈、出栈和显示 -----------------
	str_stack:		
			push bx
			push dx
			push di
			push es
			
			cmp ah,2
			ja sret
			mov bl,ah
			mov bh,0
			add bx,bx
			jmp word ptr table[bx]
	str_push:
			mov bx,top
			mov [si][bx],al
			inc top
			jmp sret
	str_pop:	
			cmp top,0
			je sret
			dec top
			mov bx,top
			mov al,[si][bx]
			jmp sret
			
	str_show:	
			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				;es:di表示在屏幕中要输入的位置
			
			mov bx,0
			
	char_show:
			cmp bx,top
			jne char_not_empty
			jmp sret
	char_not_empty:	
			mov al,[si][bx]
			mov es:[di],al
			inc bx
			add di,2
			jmp char_show

	sret:	pop es
			pop di
			pop dx
			pop bx
			ret

;----------------- 选项输入错误 -------------------
	option_error:
			call check_option
			ret
;----------------- 字符串输出 ---------------------
;es:di表示屏幕显示位置
;bx表示字符串在strs表中的位置
;cx表示要连续输出的字符串个数
	str_output:
			push si
			
			push di
			add bx,bx
	s0:		mov si,strs[bx]			;将定址表strs中第(bx)个的偏移地址传递到si中
	s1:		cmp	byte ptr cs:[si],0			
			je 	next_raw			;若等于零则结束该字符串
			mov al,cs:[si]
			mov es:[di],al
			inc si
			add di,2
			jmp short s1
	next_raw:	
			add bx,2				;下一个字符串,注意是加2
			pop di
			add di,160				;屏幕下一行
			push di
			loop s0	
			
			pop di					;将屏幕显示的位置pop掉
			
			pop si
			ret

;----------- 清屏,将显存中当前屏幕中的字符设为空格符 -----------
	clear:	push bx
			push cx
			push es
			
			mov bx,0b800h
			mov es,bx
			mov bx,0
			mov cx,2000
	clear_char:	
			mov byte ptr es:[bx],' '
			add bx,2
			loop clear_char
			
			pop es
			pop cx
			pop bx
			ret	
		
;---------------- 设置前景色 ----------------
	set_color:	
			push bx
			push cx
			push es
			
			mov bx,0b800h
			mov es,bx
			mov bx,1
			mov cx,2000
	sc:		and byte ptr es:[bx],11111000b
			or es:[bx],al			;改为al中的颜色
			add bx,2
			loop sc
	
			pop es
			pop cx
			pop bx
			ret

;--------------- clear_input_buffer --------------
	clear_input:
			push ax
	clear_word:
			mov ah,1
			int 16h
			jz clear_input_ret		;无读取则退出循环
			
			mov ah,0
			int 16h
			call clear_word			;将非阻塞读取的字符从buffer中删除
	clear_input_ret:
			pop ax
			ret

code ends
end start

阅读下面的材料 : 开机后, CPU 自动进入到 FFF0:0 单元处执行,此处有一条跳转指令。 CPU 执行该指令后,转去执行 BIOS 中的硬件系统检测和初始化程序。 初始化程序将建立 BIOS 所支持的中断向量,即将 BIOS 提供的中断历程的入口地址登记在中断向量表中。 硬件系统检测和初始化完成后,调用 INT 19H 进行操作系统的引导。 如果设为从软盘启动操作系统,则 INT 19H 将主要完成一下工作: ( 1 )控制 0 号软驱,读取软盘 0 道 0 面 1 扇区的内容到 0 : 7C 00 。 ( 2 )将 CS:IP 指向 0 : 7C 00 。 软盘的 0 道 0 面 1 扇区中装有操作系统引导程序。 INT 19H 将其装到 0 : 7C 00 处后,设置 CPU 从 0 : 7C 00 开始执行此处的引导程序,操作系统被激活,控制计算机。 如果在 0 号软驱中没有软盘,或发生软盘 I/O 错误,则 INT 19H 将主要完成以下工作 ; (1) 读取硬盘 C 的 0 道 0 面 1 扇区的内容到 0 : 7C 00 ; (2) 将 CS:IP 指向 0 : 7C 00 。 这次课程设计的任务是编写一个可以自行启动计算机,不需要在现有操作系统环境中运行的程序。 改程序的功能如下: ( 1 )列出功能选项,让用户通过键盘进行选择,界面如下: 1 ) reset pc ; 重新启动计算机 2 ) Start system ; 引导现有的操作系统 3 ) Clock ; 进入时钟程序 4 ) Srt clock ; 设置时间 ( 2 )用户输入“ 1 ”后重新启动计算机。(提示:考虑 FFFF:0 ) ( 3 )用户输入“ 2 ” 后引导现有的操作系统。(提示:考虑硬盘 C 的 0 道 0 面 1 扇区) ( 4 )用户输入“ 3 ”后,执行动态现实当前日期,时间的程序。 现实格式如下:年 / 月 / 日 时:分:秒 进入此项功能后,一直动态现实当前的时间,在屏幕上将出现时间按秒变化的效果。(提示:循环读取 CMOS ) 当按下 F1 键后,改变现实颜色;按下 ESC 键后,返回到主选单。(提示:利用键盘中断) ( 5 )用户输入“ 4 ”后可更改当前的日期,时间,更改后返回到主选单。(提示:输入字符串) 下面给出的几点建议: ( 1 )在 DOS 下编写安装程序,在按转程序中包含任务程序; ( 2 )运行安装程序,将任务程序写到软盘上; ( 3 )若要任务程序可以在开机后自行执行,要将它写到软盘的 0 道 0 面 1 扇区上。如果程序长度大于 512B ,则需要用多个扇区存放,这种情况下,处于软盘 0 道 0 面 1 扇区中的程序就必须负责将其他扇区中的内容读入内存。 这个程序较为复杂,它用到了我们所学到的所有技术,需要进行仔细地分析和耐心地调试。这个程序对于我们的整个学习过程是具有总结性的,希望读者能够尽力完成。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值