荣誉值: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 | | |