王爽 汇编语言第二版 课程设计2

王爽汇编语言 课程设计2

  • 掌握一门编程语言最重要的就是实践,王爽老的师的课程设计二,如果完整写出来要400行至800行代码,独立完成这个课程设计,会使你熟练16位汇编,掌握8086汇编精髓。这个课程设计不方便调试,独立完成的话要费一点时间,也许你会认为汇编已经过时了,没有必要浪费时间认真学,但是学习汇编语言,是解理计算机原理的捷径,汇编语言是万国语言的基础。如果只懂高级语言,那么只能做人家设计好东西,只能用API已经给功能,学会汇编再学编程你会感觉“道”在心中,长剑在手,有种世间任你挥撒豪情与欲望,不会再因为某种语言有没有什么功能限制你的想像与构思。学了汇编,你会知道C语言一个hello world有多么可爱,完成王爽教授的课程设计2,对于面向对象概念你会不学而解,因为敲那几百行乱七八糟的代码过程中,你会感觉你迫切迫切需要的一个东西,那就是“面向对象”。完成了课程设计2,对于指针的概念,你再也不会糊里糊涂,反面会感觉倍亲切,因为你和指针他爸是已经好朋友,和指针他妈亲密接触过,广告到此为止,再一次感谢王爽老师!

一、设计要求:

  • 编写一个可以自行启动的计算机,不需要在现有操作系统环境中运行的程序
    开机后, 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 ”后,执行动态现实当前日期,时间的程序。
现实格式如下:年 / 月 / 日 时:分:秒

1、显示主菜单

王爽 汇编语言第二版 课程设计2

2、显示并秒刷新时钟

王爽 汇编语言第二版 课程设计2

3、设置时钟,左右箭头调整光标,esc退出,enter保存

王爽 汇编语言第二版 课程设计2

二、实现步骤

1、写入引导区

	将引导程序512字节写入A盘第1扇区,大多数电脑已经没软驱动,用vmware虚拟机来实现。

第511字节要以0aa55h结尾,这是引导标志。

写A盘引导区代码
        mov ax,data
		mov es,ax
		mov bx,offset bootdata;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,3;写扇区
		int 13h      
;====================================================================

引导扇区是磁盘0道0面第1扇区是512字,并且是以0aa55h结尾,上面的这些不可正好510字节,所以先写一面大小为512字并且以0aa55h结尾内存区。

data segment
bootdata db 510 dup(0)
         dw 0aa55h
data ends
;===========================
;……
;===========================
mov ax,cs
		mov ds,ax    ;ds:si是安装源
		mov si,offset boot

		mov ax,data
		mov es,ax
		mov di,offset bootdata


		mov cx,offset endboot-offset boot
		cld
		rep movsb

2、开机加载的引导

开机之后CPU会将导盘的0面0道第1扇区自动加载到内存的7c00h处,加载完成后,cs:ip会指向0:7c00h。

要写入0面0道第1扇区的代码

这段代码也是开机自动被加载到内存7c00h处的代。

;=========================下面是A盘1扇区内容=============================
boot:                 
		mov ax,0      
		mov es,ax     
		mov bx,7e00h;es:bx内存数据。        

		mov dl,0;0软区
		mov dh,0;0面(磁头)                  
		mov ch,0;磁道号                     
		mov cl,2;扇区号                     
		mov al,3;扇区数量                   
		mov ah,2;读扇区                     
		int 13h       

		mov bx,offset s7e00-offset boot+7c00h                     
		jmp dword ptr cs:[bx]               
		s7e00 dw 7e00h,0                    
endboot:nop           
;====================================================================
	

3、引导代码boot实现的两个功能
1、把磁盘上剩余的引导代码加载到内存。

mov ax,0      
		mov es,ax     
		mov bx,7e00h;es:bx内存数据。        

		mov dl,0;0软区
		mov dh,0;0面(磁头)                  
		mov ch,0;磁道号                     
		mov cl,2;扇区号                     
		mov al,3;扇区数量                   
		mov ah,2;读扇区                     
		int 13h       

2、把控制权交给剩余的引导代码

mov bx,offset s7e00-offset boot+7c00h                     
		jmp dword ptr cs:[bx]               
		s7e00 dw 7e00h,0      

三、核心代码的实现

1、设计一个循环内核

因为按设计要求要有3个界面
1)显示菜单
2)显示并刷新时钟界面
3)时钟设置界面
共有5个功能:显示菜单、重新启动、重新从C盘引导、显示时钟、设置时钟
为了实这5个功能,设计中一个单字节数字sign来标记
1)显示菜单 sign=1 esc的扫描码是1,所以用int 9h捕获到描扫码为1时,设置sign=1
2)重新启动 sign=2 键盘横排数字键1的扫描码是2,所以用int 9h捕获到描扫码为2时,设置sign=2
3)重新从C盘引导 sign=3 同上
4)显示时钟 sign=4
5)设置钟 sign=5
所以循环内核,只检测sign的值,sign=1就一直刷新显示菜单,sign=2时重启,sign=3启新引导,sign=4一直刷新显示时钟,sign=5就设置时钟。
循环代码

color        db 4
functionlist dw offset endloopcore-offset do0+7e00h
			 dw offset showmenu   -offset do0+7e00h
			 dw offset reset      -offset do0+7e00h
			 dw offset startsys   -offset do0+7e00h
			 dw offset showclock  -offset do0+7e00h
			 dw offset setclock	  -offset do0+7e00h
sign         db 5      ;设为0时退出,1,显示菜单,4、时显示时钟 5、设置时钟
;这几行程序一直在循环,相当于微内核================================
;sign=1时显示菜单,    
;sign=2时调用reset     
;sign=3时调用硬盘第一扇区                    
;sign=4时显示时钟      
;sign=5时设置时钟      
;这几行程序一直在循环,相当于微内核================================
loopcore:              
        mov bx,offset sign        -offset sysboot+7e00h            
		mov bl,cs:[bx];  取出菜单选择的数字值
		mov bh,0       
		               
		cmp bl,dl ; 检测选择键是否改变       
		je loopcore1   
		call clear     
		               
		loopcore1:     
		mov dl,bl; 暂存用于下次比较          
		mov di,offset functionlist-offset sysboot+7e00h            
		add bl,bl ;functionlist 每个函数的地址是 2 字节。          
		add di,bx      
		call word ptr cs:[di]                
		jmp loopcore   
ret                    
;=======================这几行程序一直在循环,相当于微内核=================

2、键盘捕获

要分三种状态捕获
1)sgin=1时,是菜单状态,捕获数字1、2、3、4键。
比如按键1时,扫描码为2,即捕获到扫描码为2时,就把设置sgin=2,键盘捕获模块只设置sign的值,不调用其他函数,int 9如果调用复杂的函数会现意外的错误。sign的值改变之后,loopcore就会调用对应的函数。
2)sgin=4时,是时钟显示状态,只捕获esc键,并设置sgin=1
3)sgin=5时,1、要捕获数0至9数字键设置时钟。2、左右箭头键调整光标位置。3、ese键退出。4、Enter保存时钟。
键盘捕获代码

color        db 4
sign         db 5      ;设为0时退出,1,显示菜单,4、时显示时钟 5、设置时钟
keycheck:
		push ax
		push bx
		push dx
		push di
		
		
		mov bx, offset sign-offset do0+7e00h
		mov ah,cs:[bx]
		
		cmp al,3bh; F1 改变颜色
		jne keyF2
			mov bx, offset color-offset do0+7e00h
			inc byte ptr cs:[bx]
			jmp quitkeycheck
			
			
		
		keyF2: 
		cmp al,3ch ; 接F2时设置sign=0,退出循环。
		jne keymenu
			mov byte ptr cs:[bx],0;  endloopcore
			jmp quitkeycheck
		
			
		;================================================================	
		;1、只在三种状态下接收扫描码 1、sign=1 菜单状态 2、sign =4 显示clock 3、 sign=5 时置clock
		;
		;在菜单状态下,如果按不是1、2、3、4跳转到结束。		
		keymenu:
		mov ah,cs:[bx]
		cmp ah,1
		jne keyshowclock
			cmp al,2
			jb quitkeycheck
			cmp al,5
			ja quitkeycheck
			
			mov cs:[bx],al
			
		jmp	quitkeycheck 
		;===============================================================
		;在显示时钟状态下只检测esc
		keyshowclock: 
		cmp ah,4
		jne keysetclock
		cmp al,1
		jne quitkeycheck
			
			
			mov cs:[bx],al
			
		jmp	quitkeycheck
		;===============================================================
		
		
		;===============================================================
		;设置时钟时:
		;1、检测四个按键,左箭头,右箭头 ess,enter
		;2、检测1,2,3,4,5,6,7,8,9,0
		; 4个菜单键检测完成,跳转到结束。
		keysetclock:
		cmp ah,5
		jne quitkeycheck
			cmp al,2
			jb keyleft
			cmp al,0bh
			ja keyleft
			
			call settime
		
			keyleft:
			cmp al,4bh
			jne keyright
			
				call setfocus
			
			keyright:
			cmp al,4dh
			jne keyesc			
			
				call setfocus
			
			keyesc:
			cmp al,1
			jne keyenter
			
				mov cs:[bx],al
			
			keyenter:
			cmp al,1ch
			jne quitkeycheck
			
				call saveclock
		
		;=================================================================
		quitkeycheck:
		pop di
		pop dx
		pop bx
		pop ax
ret

3、时钟设置

timestr 	 db '00/00/00 00:00:00',0
timedata     db 6 dup (0)
timelimitb   db  0, 1, 1, 0, 0,0
timelimita   db 99h,12h,31h,23h,60h,60h

timedata是6字节内存,用来保存时间数据,设置时钟的数据不直接放入timestr中,而是用时间数据最大允许值timelimita,和最小允许值timelimitb,检测一下是否是时间设置的允许值,符合要求的时间数字放入timedata中,会看到该数字被显示,不符合要求的数字不放入timedata中,就不会被显示,也不会被保存。
时间设置代码

timedata     db 6 dup (0)
timeaddr     db 9,8,7,4,2,0
timestr 	 db '00/00/00 00:00:00',0
timelimitb   db  0, 1, 1, 0, 0,0
timelimita   db 99h,12h,31h,23h,60h,60h
focusplaces	 db 0,1,3,4,6,7,9,10,12,13,15,16
focus        db 3
settime:
		push si
		push di
		push ax
		push bx
		push cx
		push dx
		push bp
		
		call scantodigit
			
		mov bp,offset timedata  -offset do0 +7e00h
		
		
		mov bx,offset focus     - offset do0+7e00h
		;[focusplaces]= 0,1,3,4,6,7,9,10,12,13,15,16
		mov bx,cs:[bx]    ;取出光标索引值	
		
		mov dx,bx
		and dx,1b ;如果focus单数时,取出timedata[focus/2],用键入值替换timedata[focus/2+1]
		
		shr bx,1
		
		add bp,bx
		
		cmp dl,0
		jne getts1			
			;如果focus双数时,键入值替换timedata[focus/2],取出timedata[focus/2+1]
			mov dh,cs:[bp]
			shl al,1
			shl al,1
			shl al,1
			shl al,1
			and dh,1111b
			add al,dh
			jmp gett2
			
		getts1:
			;如果focus单数时,取timedata[focus/2],用键入值替换timedata[focus/2+1]
			mov dh,cs:[bp]
			and dh,11110000b
			add al,dh			
		gett2:
			
			mov di,offset timelimitb-offset do0 +7e00h
			mov si,offset timelimita-offset do0 +7e00h
						
			mov dl,cs:[di+bx]
			mov dh,cs:[si+bx]
			
			cmp al,dl
			jb quitgettime
			cmp al,dh
			ja quitgettime
			;call bcdtochar
			mov cs:[bp],al			
			
		quitgettime:
		pop bp
		pop dx
		pop cx
		pop bx
		pop ax
		pop di
		pop si
ret

四、运行环境

操作系统:windows10
调试环境:DOSBox 0.74-3
最后的运行环境:VMware Workstation Pro15
完整的课程设计2向软盘引导区写数据,不需要任何工具的,先在Dos下运行一次即可写入软盘,再开机软驱引导即可。
在VMWare DOS直接运行即可看到演示效果。开机重启可以看到真实的引导界面。

运行步骤:

1、在VMWare的DOS环境中,编译连接运行。
2、设置软驱引导。
3、重启即可看引导界面
王爽 汇编语言第二版 课程设计2

VMWare 设置

1、关机状态下编辑虚拟机

王爽 汇编语言第二版 课程设计2

2、添加软驱

王爽 汇编语言第二版 课程设计2

3、设置软驱引导

王爽 汇编语言第二版 课程设计2
王爽 汇编语言第二版 课程设计2

五、课程设计2的调试

1、单个模块的调试

   由于课程设二是开机引导代码,在磁盘与内间把代码数据导来导去,还有一键盘中断代码,很不方便调试。

在第55行可以调试单个模块的功能。
王爽 汇编语言第二版 课程设计2

2、演示效果调试

  • 为了实现演示效,在调试时的,可以先把导代码写入A盘0面0道1扇区,剩余的引导代码写到2、3、4扇区。然再写代码模拟int 19h,把引导代码从引导扇区读到内存7c00h,然后把cs:ip指向0:7c00h如后面的代码能正常运,则可以正常开机引导。

    演示代码

     copyToDiskAsector:
		mov ax,cs
		mov ds,ax    ;ds:si是安装源
		mov si,offset boot

		mov ax,data
		mov es,ax
		mov di,offset bootdata


		mov cx,offset endboot-offset boot
		cld
		rep movsb
		

		mov ax,data
		mov es,ax
		mov bx,offset bootdata;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,3;写扇区
		int 13h

		mov ax,cs
		mov es,ax
		mov bx,offset sysboot;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,2;扇区号
		mov al,3;扇区数量
		mov ah,3;写扇区
		int 13h
		
		mov ax,0
		mov es,ax
		mov bx,7c00h;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,2;读扇区
		int 13h

		mov bx,offset s7c00h
		jmp dword ptr ds:[bx]
		s7c00h dw 7c00h,0
		
		mov ah,4ch
		int 21h
copyToDiskAsectorEnd:nop

六、关于偏移地址

1、代码段的所有offset label都是基于当前的CS,即段地是当时的CS,code segment的偏移值为0。
2、jmp label与jmp word ptr [offset label]的区别:假设label代表的地址为A,前者直接跳到label处执代码,执行结果IP=A,后者取出A处存放双字节地址B,然后跳转到B,执行效果是IP=B。call亦是如此。

mov bx,offset address -offset boot
jmp dword ptr cs:[bx]
call dword ptr cs:[bx]
address:
dw 2000h,0
;cs:[offset address -offset boot]可以准确定位到address,不怕来回复制。
;jmp 或 call可以跳到0:2000h

七、完整的代码

assume cs:code,ds:data
data segment
bootdata db 510 dup(0)
         dw 0aa55h
data ends
code segment




;=======================下面是A盘2至4扇区内容===========================
             
    ;offset sysbootend-offset sysboot之间的内容是拷贝到A盘2-4扇区的内容。
                 
;=======================下面是A盘2至4扇区内容===========================

sysboot:
jmp  near ptr dostart
oldint9addr  dw 0,0
;如果不加offset偏移地址会出错
menudata     dw  offset md0-offset sysboot+7e00h
			 dw  offset md1-offset sysboot+7e00h
   			 dw  offset md2-offset sysboot+7e00h
			 dw  offset md3-offset sysboot+7e00h
			 dw  offset md4-offset sysboot+7e00h
			 dw  offset md5-offset sysboot+7e00h
md0 		 db '------ welcome ------',0
md1 		 db '1) reset pc',0
md2 		 db '2) start system',0
md3 		 db '3) clock',0
md4 		 db '4) set clock',0
md5 		 db 'copyright @ 2020 LiuJianKun,Inc.All rights reserved.',0
timedata     db 20h,2h,18h,17h,51h,0h
timeaddr     db 9,8,7,4,2,0
timestr 	 db '00/00/00 00:00:00',0
timelimitb   db  0, 1, 1, 0, 0,0
timelimita   db 99h,12h,31h,23h,60h,60h
focusplaces	 db 0,1,3,4,6,7,9,10,12,13,15,16
focus        db 0
color        db 4
functionlist dw offset endloopcore-offset sysboot+7e00h
			 dw offset showmenu   -offset sysboot+7e00h
			 dw offset reset      -offset sysboot+7e00h
			 dw offset startsys   -offset sysboot+7e00h
			 dw offset showclock  -offset sysboot+7e00h
			 dw offset setclock	  -offset sysboot+7e00h
sign         db 5      ;设为0时退出,1,显示菜单,4、时显示时钟 5、设置时钟
stack		 db 128 dup (0)
		
dostart:
mov ax,cs;cs=0
mov ds,ax
mov ss,ax
add ax,offset dostart-offset sysboot+7e00h
mov sp,ax

call setint9    ;捕获键盘,设置9号中断,
jmp loopcore    ;循环显示菜单。
;call saveclock ;调试保存时钟
;call showclock ;调试显示时钟

;================调试时钟设置================
;mov al,0ah
;call settime
;call showtimedata
;================调试时钟设置================

mov ax,4c00h
int 21h
;ret
;这几行程序一直在循环,相当于微内核================================
;sign=1时显示菜单,    
;sign=2时调用reset     
;sign=3时调用硬盘第一扇区                    
;sign=4时显示时钟      
;sign=5时设置时钟      
;这几行程序一直在循环,相当于微内核================================
loopcore:              
        mov bx,offset sign        -offset sysboot+7e00h            
		mov bl,cs:[bx];  取出菜单选择的数字值
		mov bh,0       
		               
		cmp bl,dl ; 检测选择键是否改变       
		je loopcore1   
		call clear     
		               
		loopcore1:     
		mov dl,bl; 暂存用于下次比较          
		mov di,offset functionlist-offset sysboot+7e00h            
		add bl,bl ;functionlist 每个函数的地址是 2 字节。          
		add di,bx      
		call word ptr cs:[di]                
		jmp loopcore   
ret                    
;=======================这几行程序一直在循环,相当于微内核=================




;sign=2
reset:
		push bx
		mov bx,offset rebootdata-offset sysboot+7c00h
		jmp dword ptr cs:[bx]
		rebootdata   dw 0,0ffffh
		pop bx
ret

;sign=3
startsys:
		mov ax,0
		mov es,ax
		mov bx,7c00h;es:bx内存数据。

		mov dl,80h;硬盘C
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,2;读扇区
		int 13h

		mov bx,offset startsys7c00h-offset sysboot+7e00h
		jmp dword ptr cs:[bx]
		startsys7c00h dw 7c00h,0
ret

;sign=4
showclock:
		call gettimedata
		call showtimedata
ret

;sign=5
setclock:		
		call showtimedata
ret

gettimedata:
		push si
		push di
		push ax		
			mov si,offset timeaddr  - offset sysboot+7e00h
			mov di,offset timedata  - offset sysboot+7e00h		
			mov cx,6
			gettimedatas0:
				mov al,cs:[si]
				out 70h,al
				in  al,71h
				mov cs:[di],al
				inc si
				inc di
			loop gettimedatas0
		pop ax
		pop di
		pop si
ret

showtimestr:
		push si
		push bx
			mov si,offset timestr  - offset sysboot+7e00h
			mov bl,32;列
			mov bh,13;行		
			call showstr
		pop si
		pop bx
ret

setfocus:
		push ax
		push bx
		push dx
		push si
		push di		
			mov si,offset focus    - offset sysboot+7e00h
			mov bl,cs:[si]
			cmp al,4bh
			jne rightfocus
				cmp bl,0
				jne focusdec
					mov bl,11
					jmp showfocus
				
				focusdec:
					dec bl
					jmp showfocus
			
			
			rightfocus:
			cmp al,4dh
			jne showfocus
			cmp bl,11
				jne focusinc
					mov bl,0
					jmp showfocus
					
				focusinc:
					inc bl
					jmp showfocus
				
				
			showfocus:
			mov cs:[si],bl;保存移动后的光标索引值		
			mov di,offset focusplaces - offset sysboot+7e00h
			mov bh,0
			mov dl,cs:[di+bx];取出光标的实际位
			
			mov ah,2
			mov bh,0
			mov dh,13;行
			add dl,32;列
			
			int 10h
		;setfocusquit:
		pop di
		pop si
		pop dx
		pop bx
		pop ax
		
ret

settime:
		push si
		push di
		push ax
		push bx
		push cx
		push dx
		push bp
		
		call scantodigit
			
		mov bp,offset timedata  -offset sysboot +7e00h
		
		
		mov bx,offset focus     - offset sysboot+7e00h
		mov bl,cs:[bx]    ;取出光标索引值
		mov bh,0
		
		mov dx,bx
		and dx,1b ;如果focus单数时,取出timedata[focus/2],用键入值对应timedata[focus/2+1]
				  
		shr bx,1
		
		add bp,bx
		
		cmp dl,0
		jne getts1			
			;如果focus是双数时,键入值对应timedata[focus/2],取出timedata[focus/2+1]
			mov dh,cs:[bp]
			shl al,1
			shl al,1
			shl al,1
			shl al,1
			and dh,1111b
			or al,dh
			jmp gett2
			
		getts1:
			;如果focus是单数时,取timedata[focus/2],用键入值对应timedata[focus/2+1]
			mov dh,cs:[bp]
			and dh,11110000b
			or al,dh			
		gett2:
			
			mov di,offset timelimitb-offset sysboot +7e00h 
			mov si,offset timelimita-offset sysboot +7e00h 
						
			mov dl,cs:[di+bx];时钟的最小允许值
			mov dh,cs:[si+bx];时钟的最大允许值
							 ;用timedata[focus/2][focus/2+1] 与时钟最大允许值,和最小允计值比较
							 ;判断键入的时钟最是否可用,如果可用
							 ;则把timedata[focus/2][focus/2+1]填入timedata
			cmp al,dl
			jb quitgettime
			cmp al,dh
			ja quitgettime
			;call bcdtochar
			mov cs:[bp],al			
		quitgettime:
		pop bp
		pop dx
		pop cx
		pop bx
		pop ax
		pop di
		pop si
ret

showtimedata:
		push si
		push di
		push ax
		push bx
		push cx		
			mov si,offset timedata  - offset sysboot+7e00h
			mov di,offset timestr   - offset sysboot+7e00h		
			mov cx,6
			showclocks0:		   
			   mov al,cs:[si]
				call bcdtochar
				
				mov cs:[di+0],ah
				mov cs:[di+1],al
				add di,3
				inc si
			loop showclocks0
			mov si,offset timestr  - offset sysboot+7e00h
			mov bl,32;列
			mov bh,13;行
			call showstr
			showclockquit:
		pop cx
		pop bx
		pop ax
		pop di
		pop si
ret

newint9:
		push ax
		push bx
		push es		
			in al,60h
			
			mov bx,0
			mov es,bx
			pushf
			mov bx,offset oldint9addr-offset sysboot+7e00h
			call dword ptr es:[bx]
			call keycapture		
		pop es
		pop bx
		pop ax		
iret

setint9:
		push di
		push ax
		push es
		push ds		
			mov ax,0
			mov es,ax
			
			mov bx,offset oldint9addr-offset sysboot+7e00h
			push es:[9*4]
			pop es:[bx]
			push es:[9*4+2]
			pop  es:[bx+2]
			
			cli
			mov word ptr es:[9*4],offset newint9-offset sysboot+7e00h
			mov word ptr es:[9*4+2],0
			sti
		
		pop ds
		pop es
		pop ax
		pop di

ret
;在bh行,bl列,显si开始处的中以0结尾的字符
showstr:;9E
		push es
		push ax
		push di
		push cx
		push bx
		push bp		
			mov ax,0b800h
			mov es,ax
			
			mov al,160;每行显示80个字符,显示一个字要两字节内存1每个字符1字节,颜色1字
			mul bh   ;dh是行数
			
			mov cx,bx;保存bx
			mov bh,0 ;总列数保存在ax
			
			add ax,bx;计算列数,如果用add al,bl会溢位出错
			add ax,bx;计算列数,字符1字节,颜色1字节,所以加两次
			
			mov di,ax;目标地址
			mov bx,cx;恢复bx
			mov bp,offset color-offset sysboot+7e00h
			mov ah,cs:[bp];高字节存放颜色
			mov ch,0
			showstr1:
			  mov al,cs:[si];低字节是数据
			  mov es:[di],ax;高字节是颜色
			  
			  inc si   
			  add di,2
			  mov cl,al
			  jcxz showstr2
			jmp showstr1
showstr2:  ;D7
    pop bp
    pop bx
    pop cx
	pop di
	pop ax
	pop es
ret



clear:
		push bx
	    push dx
	    push cx
		push es		 
			mov bx,0b800h
			mov es,bx
			mov bx,0
			mov ds,bx
			
			mov bx,0
			mov dx,0700h   ;清屏中对字符属性设置应该为07h,而不是0
			mov cx,2000
				clear1:
				mov es:[bx],dx
				add bx,2
				loop clear1
		pop es
		pop cx
		pop dx
		pop bx
		ret

endloopcore:
		mov bx,offset sysboot-offset oldint9addr
		push es:[bx]
		pop es:[9*4]
		push  es:[bx+2]
		pop es:[9*4+2]
		mov ah,4ch
		int 21h
ret

saveclock:
	push ax
	push si
	push di
	mov di,offset timedata  -offset sysboot +7e00h
	mov si,offset timeaddr  -offset sysboot +7e00h
	
	mov cx,6
	saveclocks:
		mov al,cs:[si]
		out 70h,al
		mov al,cs:[di]
		out 71h,al
		inc di
		inc si
	loop saveclocks
	
	pop di
	pop si
	pop ax
	
ret
scantodigit:
		mov ah,al
		cmp al,0bh
		jne gettnum1 
			mov al,0
			jmp quitscantodigit
		gettnum1:
			cmp al,2
			jb quitscantodigit            			
			cmp al,0bh
			ja quitscantodigit
				dec al
		quitscantodigit:
ret



resetfocus:
		push ax
		push bx
		push dx
			mov ah,2
			mov bh,0
			mov dh,25
			mov dl,0
			int 10h
		pop dx
		pop bx
		pop ax
ret

;al 是bcd码,返回ah高字符,al低字符
bcdtochar:
		push dx		
			mov dl,al			
			shr al,1
			shr al,1
			shr al,1
			shr al,1
			add al,30h
			mov ah,al
			mov al,dl
			and al,1111b
			add al,30h		
		pop dx
ret

setcolor:
		push ax
		push bx
			mov bx,offset color-offset sysboot+7e00h
			mov al,cs:[bx]
			inc al
			mov cs:[bx],al
		pop bx
		pop ax
ret
sysbootend:nop
;=======================上面是A盘2至4扇区内容===========================
                    
    ;offset sysbootend-offset sysboot之间的内容是拷贝到A盘2-4扇区的内容。           
               
;=======================上面是A盘2至4扇区内容===========================



;==========================下面是A盘1扇区内容===========================
                   
             ;boot:磁盘第一扇区被加载到7c00h                    
             ;引导扇区的功能是实现把2,3,4,5扇区的               
             ;加载到内7e00h,并把转跳到7e00h.                    
                   
;=========================下面是A盘1扇区内容=============================
boot:                 
		mov ax,0      
		mov es,ax     
		mov bx,7e00h;es:bx内存数据。        

		mov dl,0;0软区
		mov dh,0;0面(磁头)                  
		mov ch,0;磁道号                     
		mov cl,2;扇区号                     
		mov al,3;扇区数量                   
		mov ah,2;读扇区                     
		int 13h       

		mov bx,offset s7e00-offset boot+7c00h                     
		jmp dword ptr cs:[bx]               
		s7e00 dw 7e00h,0                    
endboot:nop           
;====================================================================

testRun7e00h:

		mov ax,cs
		mov ds,ax
		
		mov si,offset sysboot
		mov ax,0h
		mov es,ax
		mov di,7e00h
		mov cx,offset sysbootend-offset sysboot
		cld
		rep movsb


		mov bx,offset s7e00h-offset sysboot
		jmp dword ptr cs:[bx]
		s7e00h dw 7e00h,0
		testRun7e00hEnd:nop
		
	ret


copyToDiskAsector:
		mov ax,cs
		mov ds,ax    ;ds:si是安装源
		mov si,offset boot

		mov ax,data
		mov es,ax
		mov di,offset bootdata


		mov cx,offset endboot-offset boot
		cld
		rep movsb
		

		mov ax,data
		mov es,ax
		mov bx,offset bootdata;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,3;写扇区
		int 13h

		mov ax,cs
		mov es,ax
		mov bx,offset sysboot;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,2;扇区号
		mov al,3;扇区数量
		mov ah,3;写扇区
		int 13h
		
		mov ax,0
		mov es,ax
		mov bx,7c00h;es:bx内存数据。

		mov dl,0;0软区
		mov dh,0;0面(磁头)
		mov ch,0;磁道号
		mov cl,1;扇区号
		mov al,1;扇区数量
		mov ah,2;读扇区
		int 13h

		mov bx,offset s7c00h
		jmp dword ptr ds:[bx]
		s7c00h dw 7c00h,0
		
		mov ah,4ch
		int 21h
copyToDiskAsectorEnd:nop


start:
;call copyToDiskAsector
call testRun7e00h
code ends

end start
王爽 汇编语言第二版 课程设计2王爽 汇编语言第二版 课程设计2 hne71 发布了7 篇原创文章 · 获赞 0 · 访问量 2007 私信 关注
上一篇:使用linux shell读取yaml文件


下一篇:汇编语言实现通过8255A和4个开关控制实现8个LED灯和8个7位数码管显示指定数字全亮、全灭、从左至右、从右至左跑马灯式点亮