王爽《汇编语言》课程设计二(只包含CMOS RAM操作不包含软盘操作)

来源:互联网 发布:java web模块化开发 编辑:程序博客网 时间:2024/06/05 07:54

遇到的瓶颈问题:如何能做到随时获取键盘中断,而不是一直等着按F1或ESC键。(int 16H的0号功能与1号功能的配合):
我们知道BIOS键盘缓冲区 15字节数据,高8位保存扫描码,低8位保存ASCII码。缓冲区为一个环形队列。int 16例程的0号功能可以从键盘缓冲区中阻塞式读取数据,获取到的数据会存放到AX中,高八位存放扫描码,低八位存放对应的ASCII码。
int 9H则可以用来向缓冲区中写数据,每写一次,缓冲区队尾指针就后移一次。int 16H读取一次(0号功能读)则队头指针就后移一次(即读取某字节后,该字节就被跳过,下次不会再读)。当队列为空时(队头队尾指针重合时),int 16H的0号功能再去读,读取不到则阻塞等待。
但是我们有时候需要非阻塞式读取,因为不能说读取不到键盘输入程序就不执行了(有时候键盘输入只是影响当前状态,并不能停止当前状态),那么我们就需要用int 16H的1号功能非阻塞读取缓冲区,但是非阻塞读取时,即使数据已经读取走,缓冲区也不会发生变化(只负责读取不负责修改队列,下次读的还是上一次的字节位置),所以需要每次在处理非阻塞读取到的内容的子程序中阻塞读取一次,将非阻塞读取但没有删除(即没有移动队头指针)的数据清除,也就是将队头指针后移一次。

对于阻塞读取非阻塞的残留数据,使用的自实现方法为:clear_keyboard_buffer
几个核心点:
①阻塞与非阻塞读取键盘缓冲区:clear_keyboard_buffer
②显示信息的组织与存储(不采用data段,而直接放在code段中)
③CMOS RAM的读取与修改
④在时间显示时,按键只F1、ESC键有效,其它键无效,也不影响键盘缓冲区与下次读取(程序中解决的基本问题,但没有解决个别键的影响问题)

;201751316:18:14;Mail:mailbox_krj@163.com;Author:KangRuoJin;Version:v1.2assume cs:code,ss:stackstack segment    db 128 dup (0)stack endscode segmentstart:    mov bx,stack    mov ss,bx    mov sp,128    call copy_boot    mov bx,0    push bx    mov bx,7E00H    push bx    retf    ;跳转到0:7E00H开始执行mov ax,4C00Hint 21H;================================================================boot:    jmp boot_start;*******************************************************************************************    MENU_1  db 'Weclome to System of Mr.Kang',0    MENU_2  db '(1) Reset PC',0    MENU_3  db '(2) Start System',0    MENU_4  db '(3) ShowClock',0    MENU_5  db '(4) Set Clock',0    MENU_6  db 'Please Enter Your Choose(1~4):',0    MENU_ADDRESS    dw offset MENU_1 - offset boot + 7E00H                    dw offset MENU_2 - offset boot + 7E00H                    dw offset MENU_3 - offset boot + 7E00H                    dw offset MENU_4 - offset boot + 7E00H                    dw offset MENU_5 - offset boot + 7E00H                    dw offset MENU_6 - offset boot + 7E00H    time    db 'YY/MM/DD hh:mm:ss',0    cmos    db 9,8,7,4,2,0    clockone    db 'press F1 to change the color, press ESC to return',0    clock2  db 'Please input Date and Time,(YY MM DD hh mm ss):',0    change  db 12 dup (0),0;*******************************************************************************************boot_start:    call init_reg ;ds、es寄存器初始化    call clear_screen ;清屏与屏幕初始化(包括边框显示)    call show_menu ;显示menu菜单    jmp choose_option ;开始进入选择的循环中去mov ax,4C00Hint 21H;================================================================choose_option:    call clear_keyboard_buffer  ;清除键盘缓冲区    mov ah,0    int 16H    cmp al,'1'    je choose_option_one    cmp al,'2'    je choose_option_two    cmp al,'3'    je choose_option_three    cmp al,'4'    je choose_option_four    jmp choose_option;================================choose_option_one:               jmp choose_option                choose_option_two:               jmp choose_option                choose_option_three:                 call ShowClock               jmp boot_start          ;跳回到boot_start清屏,并重新显示                choose_option_four:        call SetClockjmp boot_start     ;=================================================================SetClock:    call init_reg           ;寄存器初始化    call clear_screen       ;清屏并重新画边框    call clear_change_stack ;清空存放修改内容的栈缓冲区    ;设置显示提示信息的位置为1015列开始    mov di,160*10+15*2    mov si,offset clock2 - offset boot + 7E00H    call show_line    ;显示修改缓冲区中的内容    mov di,160*12+30*2          mov si,offset change - offset boot + 7E00H    call show_line    call get_string    call set_CMOSRAMret;=================================================================set_CMOSRAM:    mov bx,offset cmos - offset boot + 7E00H    mov si,offset change - offset boot + 7E00H    mov cx,6    ChangeMessage:   ;GetMessage的逆过程        push cx        mov dx,ds:[si]        sub dx,3030H        mov cl,4        shl dl,cl        and dh,00001111B        or dl,dh        mov al,ds:[bx]        out 70H,al    ;将al送入地址端口70h        mov al,dl        out 71H,al ;    将数据写入CMOSRAM时钟        inc bx        add si,2        pop cx    loop ChangeMessageret;================================================================= get_string:    mov si,offset change - offset boot + 07E00H    mov bx,0getSting:    mov ah,0    int 16H    cmp al,'0'  ;判断是否是小于‘0’    jb noNumber    cmp al,'9'  ;判断是否大于'9'    ja noNumber    call char_push    cmp bx,12    ja EnterKey         ;超过输入范围就不允许再输,直接结束    mov di,160*12+30*2  ;与SetClock中,显示修改缓冲区中的内容位置相对应    call show_line    jmp getStingnoNumber:    cmp ah,0EH  ;退格通码    je BackspaceKey    cmp ah,1CH  ;回车通码    je EnterKey    jmp getStingBackspaceKey:    call char_pop    mov di,160*12+30*2  ;与SetClock中,显示修改缓冲区中的内容位置相对应        call show_line    jmp getStingEnterKey:ret;=================================================================char_push:    cmp bx,12    ja char_push_over ;大于12则不再入栈,直接返回    mov ds:[si+bx],al ;入栈    inc bx    char_push_over:retchar_pop:    cmp bx,0    je char_pop_over    dec bx    mov byte ptr ds:[si+bx],'*'    char_pop_over:ret;=================================================================      clear_change_stack:    push bx    push cx    mov bx,offset change - offset boot + 7E00H    mov cx,6                ClearChangeStack:        mov word ptr ds:[bx],'**'        add bx,2    loop ClearChangeStack    pop cx    pop bxret;=================================================================ShowClock:    call init_reg           ;寄存器初始化    call clear_screen       ;清屏并重新画边框    mov di,14*160+15*2      ;设置显示提示信息的位置为1415列开始    mov si,offset clockone - offset boot + 7E00H    call show_line  ;显示    ShowClockStart:        call GetTimeMessage     ;获取时间信息放到time中去        mov di,10*160+30*2      ;设置显示时间与日期的位置为1030列开始        mov si,offset time - offset boot + 7E00H        call show_line          ;按规则显示        ;这里的读取键盘缓冲区是自己完成,我们亦可以在        mov ah,1        ;非阻塞式读取,由于是非阻塞读取,当读取到之后,缓冲区不会清空        int 16H        jz ShowClockStart        cmp ah,3BH        je ChangeColor        cmp al,1BH      ;ESC扫描码为1,与设定的功能号有冲突,所以用ASCII码        je ReturnToMainMenu        cmp al,0        ;能到达这一步说明不为F1也不为ESC        jne ClearKeyBuffer  ;但是不为0说明一定是有键盘中断产生,就要清除无用的键盘中断    jmp ShowClockStart;================================ChangeColor:                    ;改变颜色    call ChangeShowColorClearKeyBuffer:    call clear_keyboard_buffer  ;清除非阻塞读取时未清空的缓冲区    jmp ShowClockStartReturnToMainMenu:               ;退回到choose_option时会清除缓冲区,所以这里不需要清除缓冲区ret;================================================================GetTimeMessage:    mov cx,6    mov bx,offset cmos - offset boot + 7E00H    mov si,offset time - offset boot + 7E00H    GetMessage:           push cx        mov al,ds:[bx]        out 70H,al    ;将al送入地址端口70h        in al,71H ;从数据端口71h处读出单元内容        mov ah,al        mov cl,4        shr al,cl ;右移4and ah,0FH    ;al分成两个表示BCD码值的数据        add ax,3030H  ;BCD码+30h=10进制数对应的ASCII码        mov word ptr ds:[si],ax     ;ASCII码写入time段        inc bx        add si,3        pop cx    loop GetMessageret;================================================================ChangeShowColor:    push bx    push cx    mov cx,2000    mov bx,1    loopChangeShowColor:        add byte ptr es:[bx],1        or byte ptr es:[bx],00001000B       ;高亮不闪烁设置        and byte ptr es:[bx],10001111B      ;背景一直为黑色        add bx,2    loop loopChangeShowColor    pop cx    pop bxret;================================================================clear_keyboard_buffer:    mov ah,1    int 16H         ;修改了ax,所以ZF可能会改变    jz ClearKeyboardBufferOver  ;如果非阻塞读取的数据为0则结束    mov ah,0        ;否则,则继续用阻塞的方式清除一次缓冲区字节    int 16H    jmp clear_keyboard_buffer    ClearKeyboardBufferOver:ret;================================================================init_reg:    mov bx,0B800H    mov es,bx    mov bx,0    mov ds,bxret;================================================================clear_screen:    mov bx,0    mov cx,2000    mov dx,1F00H    ;黑屏白字    ClearScreen:        mov es:[bx],dx          add bx,2    loop ClearScreen    mov bx,6*160+10*2    mov cx,60    clear_showframe_UpandDown:        mov byte ptr es:[bx],'#'        mov byte ptr es:[bx+160*9],'#'        add bx,2    loop clear_showframe_UpandDown    mov bx,7*160+10*2    mov cx,8    clear_showframe_LeftandRight:        mov byte ptr es:[bx],'#'        mov byte ptr es:[bx+59*2],'#'        mov byte ptr es:[bx+2],'#'        mov byte ptr es:[bx+59*2-2],'#'        add bx,160    loop clear_showframe_LeftandRightret;================================================================show_menu:    mov bx,offset MENU_ADDRESS - offset boot + 7E00H    mov di,160*8 + 25*2    mov cx,6    ShowMenu:           mov si,ds:[bx]  ;获取输出行信息的偏移地址        call show_line  ;给定显示的位置显示一行        add bx,2        ;获取下一条内容的地址        add di,160      ;输出到下一行    loop ShowMenuret;================================================================show_line:    push ax    push si    push diShowLineStart:    mov al,ds:[si]  ;获取一个字符    cmp al,0        ;遇到0则输出结束    je ShowLineOver    mov es:[di],al  ;遇到的不是0则输出到显示屏上    add di,2    inc si    jmp ShowLineStartShowLineOver:    pop di    pop si    pop axret;================================================================boot_end:nop;================================================================copy_boot:  ;将代码段复制到0:7E00H处(正式的写法都需要将代码放到内存中去)    mov bx,cs    mov ds,bx    mov si,offset boot    mov bx,0    mov es,bx    mov di,7E00H    mov cx,offset boot_end - offset boot    cld    rep movsbret;================================================================code endsend start

测试环境:VMWare Workstation 下的WINOWS XP(WINDOWS 8.1下的DOSBOX中去修改CMOS RAM的内容会无效,即选项4测试不成功)。
测试截图:
(1)、主界面:
这里写图片描述

(2)、时间显示:
这里写图片描述

(3)、F1键改变颜色:
上图也是按F1键改变颜色后的截图(总共有000~111的八种颜色切换)
这里写图片描述

这里写图片描述
(4)、设置日期与时间:
这里写图片描述
可以看到,我们修改的时间成功了:
这里写图片描述

0 0
原创粉丝点击