《汇编语言(第3版)》王爽 课程设计2

时间:2021-07-09 01:13:53

本次课程设计的任务是编写一个可以自行启动的计算机,不需要在现有的操作系统环境中运行的程序。

该程序功能如下:

(1 )、列出功能选项,让用户通过键盘进行选择,界面如下:

1)、reset  pc ;重新启动计算机

   2)、start  system;引导现有的操作系统

3)、clock ;进入时钟程序

4)、set  clock;设置时间

(2 )、用户输入1后,重新启动计算机

(3 )、用户输入2后,引导现有的操作系统

(3 )、用户输入3后,执行动态显示当前日期、时间的程序,格式:年/月/日 时/分/秒

进入此选项后,一直动态显示当前时间,按下F1键后,改变显示颜色;按下ESC键,返回到主菜单

(4 )、用户输入4后,可更改当前的时间、日期,更改后按ESC键返回主菜单


程序如下:

;本程序主要分为3部分:
;1、把程序复制到软盘
;2、把软盘从第二个扇区开始读入内存
;3、系统程序,实现本课程设计的4个要求
code segment
assume cs:code
        start:	;把install1复制到软盘的第一个扇区
                mov ax,install1
                mov es,ax
                mov bx,0
                mov ah,3
                mov al,1
                mov ch,0
                mov cl,1
                mov dh,0
                mov dl,0
                int 13h
                ;从第二个扇区开始,把install2全部复制到软盘
                mov ax,install2
                mov es,ax
                mov bx,0
                mov ah,3
                mov al,15
                mov cl,2
                int 13h
                
                mov ax,4c00h
                int 21h
code ends

install1 segment  ;负责把主程序从软盘的第二个扇区开始全部读入内存
assume cs:install1
                mov ax,2000h
                mov es,ax
                mov ah,2
                mov al,15
                mov ch,0
                mov cl,2
                mov dh,0
                mov dl,0
                mov bx,0
                int 13h

                mov ax,2000h
                push ax
                mov ax,0
                push ax
                retf

                mov ax,4c00h
                int 21h
install1 ends
install2 segment
assume cs:install2
	;主程序实现4个小功能
        ready:  jmp short main

        table   dw resetpc, startos, clock, setclock
         
         main:
                call manu
                call rdkb0
                cmp ah,02
                jb main
                cmp ah,05
                ja main
                mov al,ah
                mov ah,0
                sub al,2
                add al,al
                mov bx,ax
                call word ptr table[bx]
                jmp short main
                mov ax,4c00h
                int 21h

         rdkb0: mov ah,0  ;读取键盘一个输入,同时清除缓冲区
                int 16h   ;注意此时,键盘没有输入则会等待输入
                ret

         rdkb1: mov ah,1  ;读取键盘一个输入,不清除缓冲区 
                int 16h   ;此时,此时不会等待键盘输入
                ret

      resetpc:  call clear     ;重启计算机
                mov ax,0ffffh
                push ax
                mov ax,0
                push ax
                retf

      startos:  call clear     ;引导操作作系统
                mov ax,0
                mov es,ax
                mov bx,7c00h
                
                mov al,1
                mov ch,0
                mov cl,1
                mov dh,0
                mov dl,80h

                mov ah,2
                int 13h
             
                mov ax,0
                push ax
                mov ax,7c00h
                push ax
                retf
  
        clock:  jmp short showclock0	;显示时钟

      clkchar:  db 'xx/xx/xx xx:xx:xx',0
      clkaddr:  db 9,8,7,4,2,0

   showclock0:  
                push ds
                push bp
                push bx
                push cx
                push ax
                push es
                push di
                push dx

                call clear
                mov dl,2

   showclock1:  
                push cs
                pop ds
                mov bp,offset clkchar
                mov bx,offset clkaddr
                mov cx,3
         clks:  push cx
                mov al,[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 ds:[bp],ah
                mov ds:[bp+1],al
                inc bx
                add bp,3
                pop cx
                loop clks

                mov cx,3
        clks2:  push cx
                mov al,[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 ds:[bp],ah
                mov ds:[bp+1],al
                inc bx
                add bp,3
                pop cx
                loop clks2

                mov ax,0b800h
                mov es,ax
                mov di,12*160+20*2
                mov bx,offset clkchar
       clks4:   mov cl,[bx]
                mov ch,0
                jcxz clks3
                mov es:[di],cl
                mov es:[di+1],dl
                inc bx
                add di,2
                jmp short clks4

       clks3:   call rdkb1
                cmp al,1bh
                je clks5
                cmp ah,3bh
                je clks6
       clks7:   jmp near ptr showclock1
       clks6:   call rdkb0
                inc dl
                jmp near ptr showclock1
       clks5:   call rdkb0
                call clear
                pop dx
                pop di
                pop es
                pop ax
                pop cx
                pop bx
                pop bp
                pop ds
                ret

     setclock: jmp near ptr clkconf	;设置时钟

  instruction:  db '             Warning!             ',0
                db 'Please strictly follow the example',0
                db '      yy/mm/dd hh:mm:ss           ',0
    clocktemp:  db 20 dup (0)
      setaddr:  db 9,8,7,4,2,0
      clkconf:  push ax
                push bx
                push cx
                push dx
                push bp
                push si
                push di
                push ds
                push es
                call clear
                push cs
                pop ds
              
                mov ax,0b800h
                mov es,ax
                mov di,10*160+20*2
                mov bx,offset instruction

                mov cx,3
      strs3:    push cx
                mov bp,0
      strs2:    mov cl,[bx]
                mov ch,0
                jcxz strs1
                mov es:[di+bp],cl
                mov byte ptr es:[di+bp+1],4
                inc bx
                add bp,2
                jmp short strs2
        strs1:  pop cx
                inc bx
                add di,160
                loop strs3

                mov si,offset clocktemp
                mov dh,13
                mov dl,26
                call getstr
          
                mov bx,offset clocktemp
                mov bp,offset setaddr

                mov cx,6
        strs4:  push cx
                mov ch,0
                mov cl,4
                mov ah,[bx]
                mov al,[bx+1]
                sub ah,30h
                sub al,30h
                shl ah,cl
                add ah,al
                mov al,ds:[bp]
                out 70h,al
                mov al,ah
                out 71h,al
                pop cx
                add bx,3
                inc bp
                loop strs4
     rdagain:   call rdkb0
                cmp al,1bh
                jne rdagain
                call clear
                pop es
                pop ds
                pop di
                pop si
                pop bp
                pop dx
                pop cx
                pop bx
                pop ax
                ret

     getstr:    push ax

     getstrs:   mov ah,0
                int 16h
                cmp al,20h
                jb nochar
                mov ah,0
                call charstack
                mov ah,2
                call charstack
                jmp getstrs

     nochar:    cmp ah,0eh
                je backspace
                cmp ah,1ch
                je enter
                jmp getstrs
    backspace:  mov ah,1
                call charstack
                mov ah,2
                call charstack
                jmp getstrs
     enter:     mov al,0
                mov ah,0
                call charstack
                mov ah,2
                call charstack
                pop ax
                ret

    charstack: jmp short charstart

        table2  dw charpush,charpop,charshow
         top    dw 0

    charstart: 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 table2[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+1],2
               mov byte ptr es:[di+2],' '
               inc bx
               add di,2
               jmp charshows
        sret:  pop es
               pop di
               pop dx
               pop bx
               ret


              

         manu:  jmp short show
                db '1) reset pc',0
                db '2) start system',0
                db '3) clock',0
                db '4) set clock',0
                                                      
         show:  push ds
                push si
                push ax
                push es
                push bx
                push cx
                push di

                push cs
                pop ds
                mov si,offset manu
                add si,2
                mov ax,0b800h
                mov es,ax
                mov bx,12*160+25*2

                mov cx,4
          s3:   push cx
                mov di,0
          s1:   mov cl,[si]
                mov ch,0
                jcxz s2
                mov es:[bx+di],cl
                mov byte ptr es:[bx+di+1],2
                inc si
                add di,2
                jmp short s1
          s2:   pop cx
                inc si
                add bx,160
                loop s3

                pop di
                pop cx
                pop bx
                pop es
                pop ax
                pop si
                pop ds
                ret                             
                                                            
        clear:  push ax
                push es
                push cx
                push si
				
                mov ax,0b800h
                mov es,ax
                mov cx,2000
                mov si,0
     clears:    mov byte ptr es:[si],' '
                add si,2
                loop clears
                mov cx,2000
                mov si,1
    clears2:    mov byte ptr es:[si],07
                add si,2
                loop clears2
				
                pop si
                pop cx
                pop es
                pop ax
                ret


       
install2 ends


end start

小结:

非常激动,第一次,自己写了一个类似开机引导的的小程序。在这里要感谢网友shiying的程序分享。在我的程序中,参考了他编写的安装程序的结构,以及子程序中的一些小细节。在整个过程中,我自己遇到的几个大问题归结如下。本程序可能还是有很多小问题,但是整体已经实现所要求的内容。
问题1、程序无法编译,或者编译出现一系列错误
原因:我把安装程序和系统程序全都放在了code段中。这样做非常紊乱,自己分不清,何况编译器,所以把要安装的子程序放到es段中。此外,bios开机只会把软盘的第一个扇区复制到内存,所以,这里有两种做法,把系统程序从第一个扇区开始安装,此外在第一个扇区一进来就把剩余的扇区读入内存。第二种做法,也就是第一个扇区只做一件事,把剩余的扇区读入到内存,最后跳转到读入内存的起始地址。我使用了第二种做法,这样的好处就是我不管第一个扇区末尾的地址在哪里,因为第二个扇区的读入地址非常重要,此时如果读错,程序将跑飞,所以使用第二种做法就不会有这种问题。
问题2、开机后可以显示table界面,但是无法引导操作系统
原因:我在进入主程序前定义了一个128B的栈段。删掉我定义的这个栈后就可以引导操作系统。我觉得可能的原因是bios默认的栈段是一个很大的空间,至少可以满足int中断例程的开销。但是当我们重新定义了一个栈段后,由于栈空间太小,导致调用bios的中断程序的时候不够用。总结:在我们自己系统程序中,不要定义栈,因为虽然我们自己写的子程序栈的大小可以满足,但是调用bios的中断程序不知道栈要开销多少,所以,不定义栈也就是使用bios的默认栈。另外,平时写的程序没有发生这种问题原因是:1、本身程序小栈空间用的少;2、程序本身是运行在操作系统上,操作系统有自己的保护机制。
问题3、程序在不清屏下,可以引导操作系统,但是加入清屏程序后,进入操作系统后屏幕全黑,光标也不见。
原因:我在清屏程序中,对字符的属性设置成了0,即背景,前景全为0。实际上清屏中对字符属性设置应该为07h,而不是0。可能原因是操作系统的背景设置是0,前景为111,这样在系统中看起来就是屏幕是黑色,字符是白色。如果设置成了全0,这样字符就看不见了,这就导致屏幕全黑,光标和字符全都看不见。
问题4、可以显示时间,刷新时间有问题,只能改变颜色的时候刷新一次。
原因:我在读取键盘的时候给ah的参数为0,也就是读取键盘缓冲区之后,清除缓冲区该数值,注意,如果缓冲区为空,则程序一直在循环读取键盘缓冲区,直到缓冲区有数据。这就导致了按下改变颜色能刷新一次,但是不按时间一直显示不变。解决方法就是,配合使用缓冲区的另一种读法,给ah=0,即读取缓冲区时,不清除缓冲区。
问题5、键盘无输入读出的扫描码和ESC键扫描码相同
解决方法:扫描码不行,那就读字符码,这两个的字符码不同,从al读。
问题6、主程序进入子程序的时候没有问题,但是子程序回到主程序中,程序跑飞。
原因:我在主程序中进入子程序是用jmp word ptr table[bx]。使用这句进入子程序非常危险,因为它没有保存当前的ip值,也就是没有push ip。当子程序ret时候,相当于pop ip 此时整个程序的栈空间就崩溃了,所以程序跑飞。
总结:凡是涉及到栈空间的使用都要非常小心,除了明显可以看出来的push和pop,跳转指令jmp、返回指令ret、retf、调用指令call都会对栈有影响。栈空间如果没有平衡,程序就会跑飞,所以当程序跑飞的时候,可以考虑对栈空间的使用进行检查。