zoukankan      html  css  js  c++  java
  • 《汇编语言》课程设计2

    assume cs:code,ss:stack
    stack segment
        db 128 dup (0)
    stack ends
    code segment
    start:
        mov ax,stack
        mov ss,ax
        mov sp,128
        
        call copy_boot
        
        ;设置CS:IP为0:7e00h
        mov ax,0
        push ax
        mov ax,7e00h
        push ax
        retf
        
        mov ax,4c00h
        int 21h
    ;org 7e00h
    ;引导程序
    boot:
        jmp boot_begin
        func0    db 'Hk_Mayfly----XIUXIUXIU~',0
        func1    db '1) reset pc',0
        func2    db '2) start system',0
        func3    db '3) clock',0
        func4    db '4) set clock',0
        ;相减得到的是标号的相对位置,+7e00h得到的绝对位置
        func_pos    dw offset func0-offset boot+7e00h
                    dw offset func1-offset boot+7e00h
                    dw offset func2-offset boot+7e00h
                    dw offset func3-offset boot+7e00h
                    dw offset func4-offset boot+7e00h
        time    db 'YY/MM/DD hh:mm:ss',0
        cmos    db 9,8,7,4,2,0
        clock1    db 'F1----change the color        ESC----return menu',0
        clock2    db 'Please input Date and Time,(YY MM DD hh mm ss):',0
        change    db 12 dup (0),0
    
    boot_begin:
        call init_boot
        call cls_screen
        call show_menu 
        jmp choose
        mov ax,4c00h
        int 21h
    
    choose:
        call clear_kb_buffer
        ;获取我们输入的操作,跳转到对于函数
        mov ah,0
        int 16h
        cmp al,'1'
        je choose_func1
        cmp al,'2'
        je choose_func2
        cmp al,'3'
        je choose_func3
        cmp al,'4'
        je choose_func4
        
        jmp choose
    
    ;在题中提到了,开机后进入到ffff:0处执行指令
    ;那我们也可以把重启理解为,跳转到ffff:0执行指令
    ;所以我们利用jmp dword跳转到ffff:0地址,模拟重启
    choose_func1:
        mov bx,0ffffh
        push bx
        mov bx,0
        push bx
        retf
        
        jmp choose
    
    ;题中对引导现有的操作系统的描述是调用int 19,这里为了方便就直接写成函数了
    choose_func2:
        mov bx,0
        mov es,bx
        mov bx,7c00h
        
        mov al,1;扇区数
        mov ch,0
        mov cl,1;扇区
        mov dl,80h
        mov dh,0
        mov ah,2;读取
        int 13h
        
        mov bx,0
        push bx
        mov bx,7c00h
        push bx
        retf
        
        jmp choose
    
    ;获取时间
    choose_func3:
        call show_time
        
        jmp choose
    
    show_time:
        call init_boot
        call cls_screen
        ;显示按键信息
        mov si,offset clock1-offset boot+7e00h
        mov di,160*14+10*2;在14行10列显示
        call show_line
    show_time_start:
        ;获取时间信息,并显示(将time中的未知字符替换为当前时间)
        call get_time_info
        mov di,160*10+30*2;屏幕显示的偏移地址
        mov si,offset time-offset boot+7e00h;time标号的偏移地址
        call show_line
        
        ;获取键盘缓存区的数据
        mov ah,1
        int 16h
        ;没有数据就跳回show_time_start
        jz show_time_start
        ;判断是否按下F1
        cmp ah,3bh
        je change_color
        ;判断是否按下ESC
        cmp ah,1
        je Return_Main
        ;有数据,但是是无用的键盘中断,清除
        cmp al,0
        jne clear_kb_buffer2
        ;返回开始,重复之前的操作,达到刷新时间的效果。
        jmp show_time_start
    
    change_color:
        call change_color_show
    clear_kb_buffer2:
        call clear_kb_buffer
        jmp show_time_start
    Return_Main:
        ;返回到开始,重新打印菜单
        jmp boot_begin
        ret
    
    choose_func4:
        call set_time
        jmp boot_begin
        
    set_time:
        call init_boot
        call cls_screen
        call clear_stack
        
        ;设置提示信息显示位置
        mov di,160*10+13*2
        mov si,offset clock2-offset boot+7e00h
        call show_line
        ;显示修改后change中的内容
        mov di,160*12+26*2
        mov si,offset change-offset boot+7e00h
        call show_line
        
        call get_string
    
    get_string:
        mov si,offset change - offset boot + 07e00H
        mov bx,0
    getstring:
        ;获取键盘输入的时间信息
        mov ah,0
        int 16h
        
        ;输入的时间为数字0~9
        cmp al,'0'
        jb error_input
        cmp al,'9'
        ja error_input
        ;将我们输入的时间字符入栈
        call char_push
        ;不能超过输入的数量
        cmp bx,12
        ja press_ENTER
        mov di,160*12+26*2
        call show_line
        jmp getstring
    error_input:
        ;判断是不是按下退格或回车键
        cmp ah,0eh
        je press_BS
        cmp ah,1ch
        je press_ENTER
    
        jmp getstring
    ;按下回车
    press_BS:
        call char_pop
        mov di,160*12+26*2
        call show_line
        jmp getstring
    ;按下enter就退出
    press_ENTER:
        ret
    
    char_push:
        ;只能最多输入12个梳子
        cmp bx,12
        ja char_push_end
        ;将数值移动到对应位置
        mov ds:[si+bx],al
        inc bx;表示我们输入了多少个字符
    char_push_end:
        ret
    
    char_pop:
        ;判断是否输入了设置时间的数值,没有就相当于删完了
        cmp bx,0
        je char_pop_end
        ;否则用星号替换,相当于删除
        dec bx
        mov byte ptr ds:[si+bx],'*'
    char_pop_end:
        ret
    
    clear_stack:
        push bx
        push cx
        
        mov bx,offset change-offset boot+7e00h
        mov cx,12
    cls_stack:
        ;替换change段中内容
        mov byte ptr ds:[bx],'*'
        inc bx
        loop cls_stack
        
        pop cx
        pop bx
        ret
        
    
    ;获取时间
    get_time_info:
        ;从cmos ram获取年月日,时分秒6个数据
        mov cx,6
        ;获取存放单元地址
        mov bx,offset cmos - offset boot + 7e00H
        ;通过替换来显示
        mov si,offset time - offset boot + 7e00H
    next_point:   
        push cx
        ;获取单元号
        mov al,ds:[bx]
        ;向70h端口写入要访问的单元地址,并从71h端口读取数据
        out 70H,al
        in al,71H
        ;右移4位获取十位
        mov ah,al
        mov cl,4
        shr al,cl
        and ah,00001111b
        ;将BCD码转换为ASCII码
        add ax,3030H
         ;写入time中
        mov word ptr ds:[si],ax
        ;下一单元号
        inc bx
        ;每个数据之间距离都是3
        add si,3
        pop cx
        loop next_point
        ret
    
    ;改变颜色
    change_color_show:
        push bx
        push cx
     
        mov cx,2000
        mov bx,1
    next:
        ;属性值+1,改变颜色
        add byte ptr es:[bx],1
        ;当超出字体颜色的数值(0~111h)时,将数值重置
        cmp byte ptr es:[bx],00001000b
        jne change_end
        ;因为背景是黑色,所以文字颜色就不设置成黑色了
        mov byte ptr es:[bx],1
    change_end:
        add bx,2
        loop next
     
        pop cx
        pop bx
        ret
    
    clear_kb_buffer:
        ;1号程序,用来检测键盘缓冲区是否有数据
        ;如果有的话ZF!=0,没有,ZF=0
        mov ah,1
        int 16h
        ;通过ZF判断减缓缓冲区是否有数据,没有就跳出
        jz clear_kb_bf_end
        mov ah,0
        int 16h
        jmp clear_kb_buffer
    clear_kb_bf_end:
        ret
    
    init_boot:
        ;基本设置,注意:程序的直接定址表默认段地址是CS
        ;当程序转移到7c00h时,代码中CS值未发生改变,
        ;所以需要我们指明段地址
        mov bx,0b800h
        mov es,bx
        mov bx,0
        mov ds,bx
        ret
        
    ;清屏
    cls_screen:
        mov bx,0
        mov cx,2000
        mov dl,' '
        mov dh,2;字体为绿色,不设置的话,在我们显示菜单时,字体和背景颜色相同
    s:    mov es:[bx],dx
        add bx,2
        loop s
    sret:
        ret
    
    ;展示界面
    show_menu:
        ;在10行,30列显示菜单
        mov di,160*10+30*2
        ;保存在直接定址表的绝对位置
        mov bx,offset func_pos-offset boot+7e00h
        ;菜单有5行
        mov cx,5
    s1:
        ;这里相当于外循环,每次一行
        ;获取func_pos中每行的保存位置的偏移地址
        mov si,ds:[bx]
        ;调用内循环函数,输出一行的每个字符
        call show_line
        ;下一行偏移地址
        add bx,2
        ;下一行显示
        add di,160
        loop s1
        ret
        
    show_line:
        push ax
        push di
        push si
    show_line_start:
        ;获取这一行的第si+1个字符
        mov al,ds:[si]
        ;判断是否到末尾
        cmp al,0
        je show_line_end
        ;保存字符到显示缓冲区
        mov es:[di],al
        add di,2
        inc si
        jmp show_line_start
    show_line_end:
        pop si
        pop di
        pop ax
        ret
    
    boot_end:nop
    
    ;转存引导程序
    copy_boot:
        ;将引导程序储存到指定位置
        mov ax,0
        mov es,ax
        mov di,7e00h
        
        mov ax,cs
        mov ds,ax
        mov si,offset boot
        mov cx,offset boot_end-offset boot
        cld
        rep movsb
        
        ret
    
    code ends
    end start

    具体的在注释中都说明了。

    jz指令:https://zhidao.baidu.com/question/564008138.html

    int 16的1号程序:https://zhidao.baidu.com/question/511189643.html

    总结

      汇编的难度并不大,我认为在有编程的基础上,学习汇编要做到细致,细致的理解计算机编程的编译过程,对于我理解其他编程语言也有很大的帮助。欢迎大家关注,一起交流。

  • 相关阅读:
    移动app测试
    centos7中tomcat安装步骤
    linux下搭建数据库
    Linux 学习笔记
    vi编辑器 使用表
    python-Xml 实战
    python-Excel 实战
    手写HashMap
    volatile关键字解析
    两个栈实现队列——优化版
  • 原文地址:https://www.cnblogs.com/Mayfly-nymph/p/11270619.html
Copyright © 2011-2022 走看看