. : : Assembly Language : : .  |  首页  |  我提出的问题  |  我参与的问题  |  我的收藏  |  消息中心   |  游客  登录  | 
刷新 | 提问 | 未解决 | 已解决 | 精华区 | 搜索 |
  《汇编语言》论坛 ->使用BIOS进行键盘输入和磁盘读写
  管理员: assembly   [回复本贴] [收藏本贴] [管理本贴] [关闭窗口]
主题 : :  课程2 实现代码 (和别人完全不同的架构)  [待解决] 回复[ 4次 ]   点击[ 679次 ]  
ruder
[帖 主]   [ 发表时间:2013-05-21 14:11 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2011-01-02 12:37
没啥好说的,我这个代码是完全可以改进的。但是完美无尽头,先这样吧!

别人的架构都是轮询架构(不停的读键盘端口 不停的读cmos等等),这个太浪费资源了,这么点任务简直都要把cpu累死了。而且我发现大部分的实现都有问题,经不起测试。
我完全采用中断架构,不浪费任何cpu资源,cpu占用接近0。

注释详细,自己看吧。有啥错误猛拍,别客气。
ruder
[第1楼]   [ 回复时间:2013-05-21 14:14 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2011-01-02 12:37
assume cs:code

;--------------------------------------------------------
;  宏定义
;--------------------------------------------------------

; 定义时钟显示的位置

clock_line  equ 12
clock_row   equ 31

code segment

;--------------------------------------------------------
;  引导代码开始
;--------------------------------------------------------

boot_start:

    ; 此段代码将被写入软盘的0道0面1扇区上
    ; 此段代码将被bios加载到0:07c00h处 也就是07c0h:0h处
    ; 此段代码的功能是加载其它扇区的代码到内存07c0:0200h处
    ; 此段代码执行后就没什么用了 也就是说是可以被c盘的引导代码覆盖的

    ; 保存bios设置的dx的值
    ; 方便找到自己的驱动号

    mov     bp, dx

    ; 使cs = 07c0h ip = load_code
    ; 这样做是为了寻址方便

    mov     ax, 07c0h
    mov     ds, ax
    jmp     dword ptr ds:cs_ip_value

    cs_ip_value dw load_code, 07c0h
    msg_2       db 'read failure.', 0

load_code:

    ; 初始化堆栈

    mov     ss, ax
    mov     sp, 0

    ; 准备加载其它扇区的代码
    ; 使es:bx指向07c0h:0200h处

    mov     es, ax
    mov     bx, 512

    ; 计算要加载几个扇区的代码
    ; 结果放在al里

    mov     dx, 0
    mov     ax, (offset boot_end - offset global_data)
    add     ax, (512 - 1)
    mov     cx, 512
    div     cx

    ; 把软盘上的代码加载到内存07c0:0200h处

    mov     ah, 2
    mov     ch, 0
    mov     cl, 2
    mov     dx, bp
    int     013h
    jc      boot_start_error

    ; 如果加载成功则跳到boot_init执行初始化

    jmp     boot_init

    ; 如果加载失败 则显示提示信息

boot_start_error:
    call    clear_screen
    mov     ax, cs
    mov     ds, ax
    mov     si, offset msg_2
    mov     dh, 0
    mov     dl, 0
    mov     bl, 00001100b
    call    show_string

    ; 停在这里

boot_error_loop:
    hlt
    jmp     boot_error_loop

    ; 填充 使boot_start这段代码为512字节

boot_align:
    db      (510 - (offset boot_align - offset boot_start)) dup (0)
    dw      0aa55h

;--------------------------------------------------------
;  全局数据
;--------------------------------------------------------

global_data:
    current_step        db 0
    clock_color         db 00001100b
    old_int9_offset     dw 0
    old_int9_seg        dw 0
    time_string         db '00/00/00 00:00:00', 0
    time_crutch         dw 9, 0, 8, 3, 7, 6, 4, 9, 2, 12, 0, 15

;--------------------------------------------------------
;  引导代码初始化
;--------------------------------------------------------

boot_init:

    ; 设置新的键盘中断 捕获键盘操作

    call    hook_keyboard

    ; 显示菜单

    call    show_menu

    ; 空闲循环

boot_idle_loop:
    hlt
    jmp     boot_idle_loop

;--------------------------------------------------------
;  设置新的键盘中断 捕获键盘操作
;--------------------------------------------------------

hook_keyboard:
    push    ds
    push    ax
    
    ; 保存旧的中断地址

    mov     ax, 0
    mov     ds, ax
    mov     ax, ds:[9 * 4].0
    mov     old_int9_offset, ax
    mov     ax, ds:[9 * 4].2
    mov     old_int9_seg, ax

    ; 设置新的中断地址

    cli
    mov     word ptr ds:[9 * 4].0, offset keyboard_handler
    mov     word ptr ds:[9 * 4].2, cs
    sti

    pop     ax
    pop     ds
    ret

;--------------------------------------------------------
;  恢复键盘中断
;--------------------------------------------------------

unhook_keyboard:
    push    ds
    push    ax

    mov     ax, 0
    mov     ds, ax
    cli
    mov     ax, old_int9_offset
    mov     ds:[9 * 4].0, ax
    mov     ax, old_int9_seg
    mov     ds:[9 * 4].2, ax
    sti

    pop     ax
    pop     ds
    ret

;--------------------------------------------------------
;  显示菜单
;--------------------------------------------------------

show_menu:
    jmp     show_menu_start

    menu_str1       db '1) reset pc', 0
    menu_str2       db '2) start system', 0
    menu_str3       db '3) clock', 0
    menu_str4       db '4) set clock', 0
    menu_str_array  dw menu_str1, menu_str2, menu_str3, menu_str4

show_menu_start:
    push    ds
    push    bx
    push    dx
    push    si
    push    bp
    
    ; 清屏

    call    clear_screen

    ; 显示菜单

    mov     bx, cs
    mov     ds, bx
    mov     dh, 2
    mov     dl, 4
    mov     bl, 00001100b
    mov     bp, 0
show_menu_again:
    mov     si, menu_str_array[bp]
    call    show_string
    inc     dh
    add     bp, 2
    cmp     bp, 8
    jb      show_menu_again

    ; 设置光标

    add     dh, 2
    call    set_cursor

    pop     bp
    pop     si
    pop     dx
    pop     bx
    pop     ds
    ret

;--------------------------------------------------------
;  键盘中断处理函数
;--------------------------------------------------------

keyboard_handler:
    jmp     keyboard_handler_start

    key_handler dw handle_menu_key, 0, 0, handle_clock_key, handle_set_clock_key

keyboard_handler_start:
    push    ax
    push    bx

    ; 调用原中断

    pushf
    call    dword ptr old_int9_offset

    ; 如果键盘缓冲区的按键已经准备好则读出来

    mov     ah, 1
    int     016h
    jz      keyboard_handler_exit
    mov     ah, 0
    int     016h

    ; 界面转换过程中 不予处理

    cmp     current_step, 0ffh
    je      keyboard_handler_exit

    ; 根据不同的界面对按键做不同的处理

    mov     bl, current_step
    mov     bh, 0
    add     bx, bx
    call    key_handler[bx]

keyboard_handler_exit:
    pop     bx
    pop     ax
    iret

;--------------------------------------------------------
;  处理菜单界面的按键
;--------------------------------------------------------

handle_menu_key:
    jmp     handle_menu_key_start

    ; 处理菜单选项的函数列表

    menu_handler    dw reset_pc, start_system, clock, set_clock

handle_menu_key_start:
    push    ax
    push    bx

    ; ah = 扫描码 al = ascii码

    cmp     al, '1'
    jb      handle_menu_key_exit
    cmp     al, '4'
    ja      handle_menu_key_exit

    ; 清屏

    call    clear_screen

    ; 根据按键调用子函数

    sub     al, '1'
    mov     bh, 0
    mov     bl, al
    add     bx, bx
    call    menu_handler[bx]

handle_menu_key_exit:
    pop     bx
    pop     ax
    ret

;--------------------------------------------------------
;  重新启动计算机
;--------------------------------------------------------

reset_pc:
    mov     ax, 0ffffh
    push    ax
    mov     ax, 0
    push    ax
    retf

;--------------------------------------------------------
;  引导现有操作系统
;--------------------------------------------------------

start_system:
    push    es
    push    ax
    push    bx
    push    cx
    push    dx

    ; 加载C盘0道0面1扇区的内容到0h:07c00h处

    mov     ax, 0
    mov     es, ax
    mov     bx, 07c00h
    mov     ah, 2
    mov     al, 1
    mov     ch, 0
    mov     cl, 1
    mov     dh, 0
    mov     dl, 080h
    int     013h
    jc      start_system_error

    ; 恢复键盘中断

    call    unhook_keyboard

    ; 开中断 虽然unhook_keyboard中已经开中断了
    ; 但是不能依赖unhook_keyboard的内部实现

    sti

    ; 设置cs:ip指向0h:07c00h

    push    es
    push    bx
    retf

start_system_error:
    call    show_menu

    pop     dx
    pop     cx
    pop     bx
    pop     ax
    pop     es
    ret

;--------------------------------------------------------
;  时钟程序
;--------------------------------------------------------

clock:
    push    cx
    push    dx

    ; 标记当前进行到哪一步了
    ; 标记完进行到哪步后再接受按键的处理

    mov     current_step, 3
    sti

    ; 设置光标

    mov     dh, 0
    mov     dl, 0
    call    set_cursor

    ; 循环显示时间

    mov     dh, clock_line
    mov     dl, clock_row
show_clock_again:
    call    show_clock
    mov     cx, 6
clock_hlt_again:
    cmp     current_step, 3
    jne     clock_exit
    hlt
    dec     cx
    jnz     clock_hlt_again
    jmp     show_clock_again

clock_exit:
    call    show_menu
    mov     current_step, 0

    pop     dx
    pop     cx
    ret

;--------------------------------------------------------
;  功能 - 显示当前时间
;  参数 - (dh) = 显示时钟的行 (dl) = 显示时钟的列
;  返回 - 无
;--------------------------------------------------------

show_clock:
    push    ds
    push    ax
    push    bx
    push    cx
    push    si

    ; 获取时间

    mov     bx, 0
get_cmos_clock:
    mov     ax, time_crutch[bx].0
    out     070h, al
    in      al, 071h
    mov     ah, al
    mov     cl, 4
    shr     al, cl
    and     ah, 00001111b
    add     ax, 03030h
    mov     si, time_crutch[bx].2
    mov     word ptr time_string[si], ax
    add     bx, 4
    cmp     bx, 24
    jb      get_cmos_clock

    ; 显示时间

    mov     ax, cs
    mov     ds, ax
    mov     si, offset time_string
    mov     bl, clock_color
    call    show_string

    pop     si
    pop     cx
    pop     bx
    pop     ax
    pop     ds
    ret

;--------------------------------------------------------
;  处理时钟界面的按键
;--------------------------------------------------------

handle_clock_key:

    ; ah = 扫描码 al = ascii码

    cmp     ah, 01h
    jz      handle_clock_esc_key
    cmp     ah, 03bh
    jz      handle_clock_f1_key
    jmp     handle_clock_key_exit

    ; 返回到菜单

handle_clock_esc_key:
    mov     current_step, 0ffh
    jmp     handle_clock_key_exit

    ; 改变时钟颜色

handle_clock_f1_key:
    inc     clock_color
    and     clock_color, 00001111b
    jmp     handle_clock_key_exit

handle_clock_key_exit:
    ret

;--------------------------------------------------------
;  设置时间
;--------------------------------------------------------

set_clock:
    push    dx

    ; 标记当前进行到哪一步了

    mov     current_step, 4

    ; 显示当前时间

    mov     dh, clock_line
    mov     dl, clock_row
    call    show_clock

    ; 设置光标

    call    set_cursor

    ; 界面都显示完了再开中断

    sti

    pop     dx
    ret

;--------------------------------------------------------
;  处理设置时钟界面的按键
;--------------------------------------------------------

handle_set_clock_key:
    push    bx
    push    dx

    ; ah = 扫描码 al = ascii码
    ; 如果输入的不是数字键 则可能是控制键

    cmp     al, '0'
    jb      handle_set_clock_control_key
    cmp     al, '9'
    ja      handle_set_clock_control_key

    ; 获取当前的光标在字符串中的位置保存到bx里

    call    get_cursor
    sub     dl, clock_row
    mov     bl, dl
    mov     bh, 0

    ; 如果当前光标不在字数位置 则不允许输入

    cmp     time_string[bx], '0'
    jb      handle_set_clock_key_exit
    cmp     time_string[bx], '9'
    ja      handle_set_clock_key_exit

    ; 把输入的数字保存到time_string里
    ; 并显示输入的数字

    mov     time_string[bx], al
    mov     bl, clock_color
    call    show_char

    ; 光标移动到下一个数字位置

    call    reset_clock_cursor
    jmp     handle_set_clock_key_exit

    ; 处理控制键

handle_set_clock_control_key:
    cmp     ah, 04bh
    jz      handle_set_clock_left_key
    cmp     ah, 04dh
    jz      handle_set_clock_right_key
    cmp     ah, 039h
    jz      handle_set_clock_space_key
    cmp     ah, 01h
    jz      handle_set_clock_esc_key
    cmp     ah, 01ch
    jz      handle_set_clock_enter_key
    jmp     handle_set_clock_key_exit

    ; 如果按的是左方向键则左移光标

handle_set_clock_left_key:
    call    get_cursor
    cmp     dl, clock_row
    je      handle_set_clock_key_exit
    call    left_cursor
    jmp     handle_set_clock_key_exit

    ; 如果按的是右方向键则右移光标

handle_set_clock_right_key:
    call    get_cursor
    cmp     dl, (clock_row + 16)
    je      handle_set_clock_key_exit
    call    right_cursor
    jmp     handle_set_clock_key_exit

    ; 如果按的是空格键则把光标移动到下一个数字位置

handle_set_clock_space_key:
    call    reset_clock_cursor
    jmp     handle_set_clock_key_exit

    ; 如果按的esc键则取消更改回到菜单

handle_set_clock_esc_key:
    call    show_menu
    mov     current_step, 0
    jmp     handle_set_clock_key_exit

    ; 如果按enter键则保存更改

handle_set_clock_enter_key:
    call    set_cmos_clock
    call    show_menu
    mov     current_step, 0
    jmp     handle_set_clock_key_exit

handle_set_clock_key_exit:
    pop     dx
    pop     bx
    ret

;--------------------------------------------------------
;  调整设置时钟界面的光标位置
;--------------------------------------------------------

reset_clock_cursor:
    jmp     reset_clock_cursor_start

    cursor_value db 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, -16

reset_clock_cursor_start:
    push    bx
    push    dx

    call    get_cursor
    mov     bl, dl
    sub     bl, clock_row
    mov     bh, 0
    add     dl, cursor_value[bx]
    call    set_cursor

    pop     dx
    pop     bx
    ret

;--------------------------------------------------------
;  设置cmos时间
;--------------------------------------------------------

set_cmos_clock:
    push    ax
    push    bx
    push    cx
    push    bp

    mov     bx, 0
set_cmos_clock_again:
    mov     ax, time_crutch[bx].0
    out     070h, al
    mov     bp, time_crutch[bx].2
    mov     ax, word ptr time_string[bp]
    sub     ax, 03030h
    mov     cl, 4
    shl     al, cl
    or      al, ah
    out     071h, al
    add     bx, 4
    cmp     bx, 24
    jb      set_cmos_clock_again

    pop     bp
    pop     cx
    pop     bx
    pop     ax
    ret

;--------------------------------------------------------
;  功能 - 设置光标位置
;  参数 - (dh) = 行 (dl) = 列
;  返回 - 无
;--------------------------------------------------------

set_cursor:
    push    ax
    push    bx

    mov     ah, 02h
    mov     bh, 0h
    int     010h

    pop     bx
    pop     ax
    ret

;--------------------------------------------------------
;  功能 - 获取光标位置
;  返回 - (dh) = 行 (dl) = 列
;--------------------------------------------------------

get_cursor:
    push    ax
    push    bx
    push    cx

    mov     ah, 03h
    mov     bh, 0h
    int     010h

    pop     cx
    pop     bx
    pop     ax
    ret

;--------------------------------------------------------
;  功能 - 光标左移
;  返回 - 无
;--------------------------------------------------------

left_cursor:
    push    dx

    call    get_cursor
    dec     dl
    call    set_cursor

    pop     dx
    ret

;--------------------------------------------------------
;  功能 - 光标右移
;  返回 - 无
;--------------------------------------------------------

right_cursor:
    push    dx

    call    get_cursor
    inc     dl
    call    set_cursor

    pop     dx
    ret

;--------------------------------------------------------
;  功能 - 在当前光标处显示字符
;  参数 - (al) = 字符 (bl) = 颜色
;  返回 - 无
;--------------------------------------------------------

show_char:
    push    ax
    push    bx
    push    cx

    mov     ah, 09h
    mov     bh, 0
    mov     cx, 1
    int     010h

    pop     cx
    pop     bx
    pop     ax
    ret

;--------------------------------------------------------
;  功能 - 显示一个以0结束的字符串
;  参数 - (dh) = 行号 (dl) = 列号 (bl) = 颜色
;         (ds:si) = 字符串首地址
;--------------------------------------------------------

show_string:
    push    es
    push    ax
    push    si
    push    di

    ; 其实这个函数也可以通过show_char实现
    ; 但是会出现光标闪烁的情况

    mov     ax, 0b800h
    mov     es, ax
    mov     al, (80 * 2)
    mul     dh
    mov     di, ax
    mov     al, 02h
    mul     dl
    add     di, ax
    mov     ah, bl
show_string_copy:
    mov     al, ds:[si]
    test    al, al
    jz      show_string_exit
    mov     es:[di], ax
    inc     si
    add     di, 2
    jmp     show_string_copy

show_string_exit:
    pop     di
    pop     si
    pop     ax
    pop     es
    ret

;--------------------------------------------------------
;  清屏
;--------------------------------------------------------

clear_screen:
    push    es
    push    di

    mov     di, 0b800h
    mov     es, di
    mov     di, 0
clear_screen_again:
    mov     word ptr es:[di], 0700h
    add     di, 2
    cmp     di, (80 * 2 * 25)
    jb      clear_screen_again

    pop     di
    pop     es
    ret
boot_end:

;--------------------------------------------------------
;  程序入口
;--------------------------------------------------------

start:

    ; 此段代码的功能是把引导代码boot_start到boot_end连续写入软盘

    jmp     write_floppy

    msg_0   db 'write to failure.', 0
    msg_1   db 'write to successful.', 0

write_floppy:

    ; 使es:bx指向引导代码

    mov     ax, cs
    mov     es, ax
    mov     bx, offset boot_start

    ; 计算引导代码需要几个扇区
    ; 结果放在al里

    mov     dx, 0
    mov     ax, (offset boot_end - offset boot_start)
    add     ax, (512 - 1)
    mov     cx, 512
    div     cx

    ; 把引导代码写入软盘

    mov     ah, 3
    mov     ch, 0
    mov     cl, 1
    mov     dh, 0
    mov     dl, 0
    int     013h
    mov     si, offset msg_0
    jc      start_error
    mov     si, offset msg_1

start_error:
    mov     ax, cs
    mov     ds, ax
    call    get_cursor
    mov     ah, 8
    mov     bh, 0
    int     010h
    mov     bl, ah
    call    show_string

    ; 程序退出

    mov     ax, 04c00h
    int     021h
code ends

end start
zjkl19
[第2楼]   [ 回复时间:2014-03-03 19:02 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:6
注册日期:2009-07-15 11:17
楼主的思路确实是与众不同!当当从思路这一点在下就很佩服了!
lz应该是有windows的编程经验吧,看着程序很多都借鉴了windows的编程思想。
feekee1
[第3楼]   [ 回复时间:2017-09-20 17:37 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2017-09-20 17:35
您的代码主要是用了hlt指令吧,书上没讲到 也谈不上完全不同的架构啊
需要登录后才能回帖 -->> 请单击此处登录
    Copyright © 2006-2024   ASMEDU.NET  All Rights Reserved