强化--16位汇编课程设计


                      编写一个可以自行启动的计算机,不需要在现有操作系统环境中运行的程序
-----王爽汇编语言  课程设计2
一、相关资料






二、设计思路
(1)首先思考引导程序是干什么的?
答:引导计算机找到用户代码(引导程序就是把用户代码读到相应内存中,因为软(硬)盘的第一个扇区只有512个字节,有些稍大的程序不可能一次性就能放到第一扇区里面的,更多的是放在其它扇区,然后由引导程序(第一扇区的程序)负责把其它扇区的代码读到内存中,所以我们写的引导程序任务就是利用int13中断把我们的主程序读到内存中,然后跳转并执行)
(2)怎么把程序写入软盘?
答:首先我们利用int 13把引导程序写入软盘第一扇区,然后再把主程序写入其它扇区(引导程序应该知道在哪些扇区,好读取代码)
(3)怎么完成代码编写?
答:知道我们可以先编写功能3,4,(4的改时间功能在虚拟8086模式下不能真正改时间),然后在写功能1,2,当完成一个
小任务(或函数)时就及时测试,减少后面的负担,最后在真正真机测试,修改,测试。。。成功!

三、测试环境
虚拟机+win7 32位+一个u盘+dos系统
说明:因为虚拟机里装了一个win7所以就直接用它来测试了,首先我从网上下载了一个U盘dos系统制作工具,将下载的msdos7.1写入了u盘,然后制作一个软盘镜像,可以网上下载工具进行制作,我用的是自带的软盘镜像,准备工作做好后就可以开始了(当然还有写好的程序),设置虚拟机从u盘启动,然后进入dos系统,在纯dos系统下,运行编写好的程序(exe文件),将程序写入软盘扇区,最后设置虚拟机从软盘启动,如果一切顺利,那么就会进入的写的程序的主界面,当然也许运气不好,没法一次性成功,那么就只有检查程序,有可能把软盘写“坏”了,没关系,格式化一下就好,重复以上工作就好了。
(当然也可以不用win7系统,直接在虚拟硬盘里面装dos,这样就不需要u盘了,在dos下写扇区,这样引导的也就是dos系统了而不是win7系统)

注:不能在windos系统下直接运行程序写扇区,因为此时运行在虚拟8086模式,在windos系统保护下,没法写软盘。
下图节选自Windows环境下32位汇编语言程序设计.罗云彬.第三版


我的程序运行效果如下:
(1)写软盘(从u盘启动)


(2)从软盘启动后的主界面


(3)功能3(显示时间)
(4)功能3--q功能(改变颜色)


(5)功能4(设置时间)


修改后的效果



功能4中的数据合法性检测


1号功能就是重启,再次出现主程序界面(应该太快,没有截图)
2号功能就会引导win7系统,进入win7系统

四:程序源码参考
.model small
.data
	error  db 'fail'   ;写入软盘是否成功
	ok     db 'success'
.code
	start: 	
		  mov ax,cs  
		  mov es,ax ;es:bx指向要写的数据开始位置
		  ;==================================把引导程序写入磁盘的程序
		  lea bx,boot  ;引导程序的偏移地址
		  mov al,1;写入的扇区数
		  mov ch,0;磁道号
		  mov cl,1;扇区号
		  mov dl,0;驱动器号(软驱从0开始,0:软驱A,1:软驱B,硬盘从80h开始,80h:硬盘C,81h:硬盘D)
		  mov dh,0;磁头号(面)
		  mov ah,3;int 13h的功能号,3表示写入扇区
		
		  
		  int 13h
		  cmp ah,0
		  jne showerror 
		  
		  ;============================把主程序写入磁盘的程序===============
		  
		  ;算出需要写入的写入的扇区数
		  lea bx,Main  
		  lea ax,MainEnd
		  sub ax,bx
		  xor dx,dx
		  mov bx,512
		  div bx
		  add ax,1   
		  		  
		  lea bx,Main ;引导程序的偏移地址
		  mov ch,0;磁道号
		  mov cl,2;扇区号
		  mov dl,0;驱动器号(软驱从0开始,0:软驱A,1:软驱B,硬盘从80h开始,80h:硬盘C,81h:硬盘D)
		  mov dh,0;磁头号(面)
		  mov ah,3;int 13h的功能号,3表示写入扇区
		  int 13h
		  cmp ah,0
		  jne showerror
		  
	;================写入软盘成功
		   mov ax,@data
		   mov ds,ax
		   mov si,offset ok
		   mov di,160*7+26*2
		   mov cx,7
		   call print	
		   	  
	over:  mov ax,4c00h
		   int 21h
	;===============写入软盘失败		   
showerror:
	       mov ax,@data
		   mov ds,ax
		   mov si,offset error
		   mov di,160*7+26*2
		   mov cx,4
		   call print
	     jmp over		   
		  	
;==================================引导程序===========================   
org 7c00h
boot: 	  
		  jmp BootStart		
    no   db 'failure'
BootStart:
		  xor ax,ax
    	  mov es,ax
    	  mov bx,7e00h
          mov al,4;读入的扇区数  (前面计算所得)
		  mov ch,0;磁道号
		  mov cl,2;扇区号
		  mov dl,0;驱动器号
		  mov dh,0;磁头号(面)
		  
		  mov ah,2;int 13h的功能号,2表示从扇区读进内存
		  int 13h
		  cmp ah,0
		  je continue
		  call fail
		  jmp $ ;无限循环
		  
continue: jmp bx
		  
;================================磁盘读写失败显示===========================	

fail 	  proc
		  push ax
          push es
          push si
          push bx
          push cx
 	      mov ax,0b800h
	      mov es,ax
	      xor si,si
	      lea bx,no
	      mov cx,7  ;字符串长度
FailStart:
	      mov al,cs:[bx]
		  mov es:[si],al
		  add si,2
		  inc bx
	      loop FailStart  
	      	      
	      pop cx
	      pop bx
	      pop si
	      pop es
	      pop ax
		  ret
fail endp
		  lea ax,boot
          db 510-($- boot) dup(0)	 
          dw 0aa55h ;以aa55结尾
             
;=========================================主程序===========================	
  ORG 7e00h
  Main: jmp  MainStart
       ;子功能直接定址表 
        keyNum dw resetPC,startSys,Clock,SetClock 
               
        ;菜单字符串 
str0 db '                  System Startup Options: Made By ztgreat 2015/02/1 ',0 
str1 db '                 ===================================================',0 
str2 db '                       1. Restart The Computer                      ',0 
str3 db '                       2. Guide The Current System                  ',0             
str4 db '                       3. Into Clock Program                        ',0 
str5 db '                       4. Set Time                                  ',0 
str6 db 'Please input the number of the menu:                                ',0 
stringtable dw offset str0,offset str1,offset str2,offset str3,offset str4,offset str5,offset str6	
	
MainStart:
	        call clearScreen  ;清屏 
            mov ax,cs 
            mov ds,ax     
         
            mov cx,68
            xor bx,bx
            mov di,160*2 ;提示信息首句,第2行第7列起 
            
    printf: mov si,cs:[stringtable+bx]      
            call print
            add bx,2
            add di,160+160;换两行
            cmp bx,14
            jne printf
             
            
           ;设置光标位置 
            mov ah,2 
        	mov bh,0   ;页号 
        	mov dh,13   ;行号 
       	 	mov dl,116  ;列号 
        	int 10h 
         
           ; --------接收键盘输入
 checkKey : mov ah,0
            int 16h    
            mov bx,0b800h
            mov es,bx
            mov es:[14*160+36*2],al  ;显示输入结果
            call delay  ;便于看到输入结果
            
            ;----------------数据合法性判断------------
            cmp al,31h 
            jb checkKey 
            cmp al,34h 
            ja checkKey
            
          
            xor bx,bx
            mov bl,al
            sub bl,30h ;将数字1~4的ASCII码转换为0~3 
            dec bl
            
            add bx,bx    ;bx=bx*2  换算出定址表地址 
            call clearScreen  
            call word ptr cs:keyNum[bx]   ;根据直接定址表跳转到相应功能区 
             
            ;若调用返回则重新生成菜单 
            
            jmp near ptr MainStart  ;从头开始         
            
            
;================字符串显示函数===========================
;入口参数说明:
;ds:si指向字符串地址
;di显存偏移地址
;cx字符长度
print  proc
		push di
        push cx
        push ax
        push es
        mov ax,0b800h
        mov es,ax
  @@ :  mov al,[si]
        mov byte ptr es:[di],al
        inc si
        inc di
        mov byte ptr es:[di],00000010b
        inc di
        loop @B
        pop es
        pop ax
        pop cx
        pop di
        ret       
  print endp                           
;=======================================重启计算机===============================           
resetPC: jmp reset
	     p dw 0,0ffffh
   reset: jmp dword ptr  p
;========================================引导现有操作系统===========================
startSys:
         mov ax,0
         mov es,ax
         mov bx,7c00h
         
         mov al,1
         mov ch,0
         mov cl,1
         mov dl,80h   ;硬盘C
         mov dh,0
         mov ah,2
         int 13h
         push es
         push bx
         
         retf  ;跳转到操作系统引导程序所在内存区:0:7c00h 
         
;=================================循环显示时钟===========================
Clock    proc
         push ax  
         xor al,al
         call setframe       
C_S:     call showClock    ;显示时钟函数 
	     in al,60h         ;读取60h端口的键盘输入 
	     cmp al,10h         ;q键的扫描码为10h  (因为电脑原因,改为q键)
	     je setcolor	 
	     cmp al,01h       ;Esc键的扫描码为01h 
	     jne C_S
	     pop ax
	     ret  
setcolor: 
	      push bx
	      push cx 
		  mov ax,0b800h
		  mov es,ax
		  mov bx,160*12+30*2+1
		  mov cx,17
	   @@:inc byte ptr es:[bx]
	      add bx,2
	      loop @B
	      pop cx
	      pop bx
		  jmp C_S   
Clock endp
   
;=================================设置显示时间边框============================
;al=0 时钟显示边框,否则为时钟设置时的边框
setframe proc 
         push cx
   	     push ds
   	     push di
   	     push bx
   	     push si
   	     push cs
   	     pop ds
   	     
   	     xor bx,bx
   	     mov cx,8
   	     
   	    mov di,160*7+26*2   ;定位边框显示位置
   	    lea bx,frametable     ;时钟显示时的边框地址偏移
   	    cmp al,0  	    
        je frame
        lea bx,frametable2   ;时钟设置时的显示边框地址偏移
        add cx,2
        mov di,160*5+26*2   ;定位边框显示位置
        jmp frame
        framea db '   [<--]   move left     ',0 
        frameb db '   [-->]   move right    ',0 
        framec db '   [Esc]   return menu   ',0 
        framed db '   [Enter] set over      ',0 
        
        frame0 db '    [q]   change color   ',0 
        frame1 db '    [Esc] return menu    ',0 
        frame2 db '                         ',0 
        frame3 db '=========================',0 
        frame4 db '==                     ==',0 
        frame5 db '==                     ==',0 
        frame6 db '==                     ==',0 
        frame7 db '=========================',0 
        frametable dw offset frame0,offset frame1,offset frame2,offset frame3,offset frame4,offset frame5,offset frame6,offset frame7  
       frametable2 dw offset framea,offset frameb,offset framec,offset framed,offset frame2,offset frame3,offset frame4,offset frame5,offset frame6,offset frame7   
   frame:push cx
   	     mov cx,25
   	     mov si,[bx]
   	     call print
   	     add bx,2
   	     add di,160
   	     pop cx
   	     loop frame
   	          
   	     
   	          
   	     pop si
   	     pop bx
   	     pop di
   	     pop ds
   	     pop cx
   	     ret
   	     
setframe endp  	          
       
showClock proc 
	      push ax
 	      push si
 	      push bx
 	      push dx
 	      push di
 	      push cx
          jmp ShowStart
          ;日期时间单元号列表 
          cmos db 9,8,7,4,2,0      ;年,月,日,时,分,秒 
          signs db '/','/',' ',':',':',' '  ;分隔符
 ShowStart: 
		   xor si,si
		   xor di,di
		   
		   mov ax,0b800h
		   mov es,ax
	       xor bx,bx
	       mov cx,6
	       
show:	   push cx
		   mov al,cmos[si]
		   out 70h,al
		   in al,71h		   
		   ;将BCD码转换为相应字符的ASCII码
		   mov ah,al
		   mov cl,4
		   shr ah,cl;右移4位让高4位变为第4位(得到0-9之间的数)
		   and al,00001111b ;获得低4位
		   add ah,30h ;获得各字相应的ASCII码
		   add al ,30h		   
		   mov byte ptr es:[160*12+30*2+bx],ah
		   mov byte ptr es:[160*12+30*2+bx+2],al		   
		   mov ah,signs[di]
		   mov byte ptr es:[160*12+30*2+bx+4],ah
		   add bx,6
		   inc di
		   inc si
		   pop cx
		   loop show
		  
 ShowRet:   
 	       pop cx         
           pop di
           pop dx
           pop bx
           pop si
           pop ax
           ret
showClock endp

;===================================时间设置================================	  	    
SetClock  proc near
          push ax
          push si
          push es
          push dx
          push di
          
          mov al,1
		  call setframe
          call showclock 
          jmp set 
      bit db 0,2,6,8,12,14,18,20,24,26,30,32
    set:  mov si,0
          mov ax,0b800h
  	      mov es,ax 
  	      xor dh,dh 
    
settime:
          mov di,160*12+30*2
          mov dl,bit[si]    
          add di,dx                           ;es:dx指向屏幕要修改的日期和时间的单元地址 
  	      mov byte ptr es:[di+1],97h          ;控制移动当前要修改的日期和时间的单元地址,背景色为蓝色闪烁 
  	      mov ah,0 
          int 16h                              ;读取键盘缓冲区第一个字单元中的键盘输入 
          cmp ah,01h                            ;Esc键的扫描码为01h 
          jne next
          jmp clockret
next: 
         cmp ah,1ch                            ;enter键的扫描码为1ch 
         jne next_left
         jmp befor_enter 
next_left: 
        cmp ah,4bh                            ;←键的扫描码为4bh 
        jne next_right
        jmp left 
next_right: 
        cmp ah,4dh                              ;→键的扫描码为4dh 
        jne next_1 
        jmp right 
next_1: 
        cmp al,30h                           ;数字0的ASCⅡ码为30h 
        jb  settime
        cmp al,39h                           ;数字9的ASCⅡ码为39h 
        ja settime 
next_2: 
        mov byte ptr es:[di],al                 ;修改当前的日期和时间的单元内容 
        mov al,0                              ;清空al值,防止right重复检测al 
        jmp right                           ;修改完成自动跳向下一个修改字单元 
        jmp settime 
clockret:
		pop di
   	    pop dx
   	    pop es
   	    pop si
   	    pop ax
        ret    

;------------------------方向键向左移动-------------------------------- 

left: 
        cmp al,34h                          ;判断是否为数字键盘4,数字键盘ah=4bh,al=34h 
        jne lefts 
        jmp next_2 
lefts: 
        mov byte ptr es:[di+1],02h         ;恢复当前字节的背景色 
        cmp si,0 
        je lefte 
        dec si 
        jmp settime
lefte: 
        mov si,11                                ;移动超界跳到最右边 
        jmp settime

;------------------------方向键向右移动-------------------------------- 

right: 
        cmp al,36h                          ;判断是否为数字键盘6,数字键盘ah=4dh,al=36h 
        jne rights 
        jmp next_2
rights:         
        mov byte ptr es:[di+1],02h          ;恢复当前字节的背景色 
        cmp si,11 
        je righte 
        inc si 
        jmp settime
righte: 
        mov si,0                                  ;移动超界跳到最左边 
        jmp settime 

befor_enter:
	    mov si,0
	    mov cx,6
	    mov ax,0b800h
        mov es,ax
        xor dh,dh
        xor bx,bx
enters:  
	    push cx 
        mov di,160*12+30*2
        mov dl,bit[si]    
        add di,dx 
        mov ah,byte ptr es:[di]        
        sub ah,30h                                    ;十位显示字符转化为数字 
        mov al,byte ptr es:[di+2] 
        sub al,30h                                    ;个位显示字符转化为数字 
        mov ch,cmos[bx]          ;ch指向CMOS RAM中要修改的单元地址
        mov cl,4 
        shl ah,cl
        add ah,al                ;ah指向CMOS RAM中要修改的单元数据
          
        call checktime          ;时间设置错误检测       
        cmp cl,1
        je  setagain            ;如果数据不合法则重新输入
        mov al,ch
        out 70h,al                ;向地址端口70h写入要访问的单元地址 
        mov al,ah
        out 71h,al              ;向数据端口71h写入指定单元的数据 
        inc bx
        add si,2
        pop cx
        loop enters
        jmp clockret
setagain:pop cx                 ;把刚刚push的cx出栈,否则没有平衡栈,将会出错
		 jmp set     
        
        
SetClock endp	    

;------------------------范围检测函数---------------------------------- 

;功能:(1) (bx)=9,代表年份,ah代表数据,不需要检测 

;     (2) (bx)=8,代表月份,ah取值范围(01-12) 

;     (3) (bx)=7,代表天数,ah取值范围(01-31) 

;     (4) (bx)=4,代表小时,ah取值范围(00-23) 

;     (5) (bx)=2,代表分钟,ah取值范围(00-59) 

;     (6) (bx)=0,代表秒钟,ah取值范围(00-59) 
bcd_12        equ 00010010b 
bcd_23        equ 00100011b 
bcd_31        equ 00110001b 
bcd_59        equ 01011001b 
checktime proc
	        
	         push bx
	         xor bh,bh
	         mov bl,ch
	         mov cl,0  ;0表示没有问题,1表示出现问题
	         cmp bx,8
	         je checkmouth   ;检测月
	         
	          cmp bx,7
	         je checkday     ;检测日
	         	         
	          cmp bx,4
	         je checkhour    ;检测时
	         	         
	          cmp bx,2
	         je checkminute   ;检测分
	         	         
	          cmp bx,0
	         je checksecond  ;检测秒
	         
	         jmp  checkret  
	         
	     checkmouth:            ;检查月份
	                  cmp ah,1
	                  jb moutherror
	                  cmp ah,bcd_12
	                  ja moutherror
	                  jmp checkret
	     checkday:                  ;检查日合法性
	                  cmp ah,1
	                  jb dayerror
	                  cmp ah,bcd_31
	                  ja dayerror
	                  jmp checkret
	     checkhour:         		;检查时合法性
	                  cmp ah,0
	                  jb hourerror
	                  cmp ah,bcd_23
	                  ja hourerror
	                  jmp checkret
	     checkminute:					;检查分合法性
	                  cmp ah,1
	                  jb minerror
	                  cmp ah,bcd_59
	                  ja minerror
	                  jmp checkret
	     checksecond:                    ;检查秒合法性
	                  cmp ah,1
	                  jb secerror
	                  cmp ah,bcd_59
	                  ja secerror
	                  jmp checkret 
	                  
	      moutherror: mov bx,0     ;月份出现问题,bx对应月份错误代码号
	                  mov cl,1     ;标志置1
	      			  call error2  ;显示错误
	                  jmp checkret
	                  	                  
	       dayerror:  mov bx,2
	       			  mov cl,1
	       			  call error2
	                  jmp checkret
	                  	                  
	       hourerror: mov bx,4
	       			  mov cl,1
	       			  call error2
	                  jmp checkret
	                  
	       minerror:  mov bx,6
	       			  mov cl,1
	       			  call error2
	                  jmp checkret
	                   
	       secerror:  mov bx,8
	       		      mov cl,1
	       			  call error2
	                                 
	      checkret: pop bx
	      			ret   
	        
checktime endp

;==========================时间错误提示==============================

	error2 proc
	    jmp errorstart
		errort db 'mouth error,please change again!  ',0 
		errord db 'day error,please change again!    ',0 
		errorh db 'hour error,please change again!   ',0 
		errorm db 'minute error,please change again! ',0 
		errors db 'second error,please change again! ',0 
		errorkey dw offset errort,offset errord,offset errorh,offset errorm,offset errors 
	errorstart:   push ax
	              push ds
	              push si
	              push cx
	              push di
				  push cs
				  pop ds
		          mov si,cs:[errorkey+bx]
		          mov cx,34
		          mov di,160*17+23*2
		         
	              call print
	              pop di
	              pop cx
	              pop si
	              pop ds
	              pop ax
	              ret
    error2 endp  


;=======================================清屏函数===========================
clearScreen proc
			push ax
	        push es
	        push di
	        push cx	
            mov ax,0b800h
	        mov es,ax
	        mov di,0
	        mov cx,2000
	 clear: mov byte ptr es:[di],' '
	        add di,2
	        loop clear    
	        pop cx
	        pop di
	        pop es
	        pop ax
	        ret
clearScreen endp    
	        
;===============================延时程序==============================	        
delay proc   
          push ax
 	      push dx
 	      mov dx,10h
 	      mov ax,0
 	  s1: sub ax,1
 	  	  sbb dx,0
 	  	  cmp ax,0
 	  	  jne s1
 	  	  cmp dx,0
 	  	  jne s1
 	  	  pop dx
 	  	  pop ax
 	  	  ret
delay endp  
    
MainEnd:nop
	end start


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值