代码语言:javascript复制
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
总结
汇编的难度并不大,我认为在有编程的基础上,学习汇编要做到细致,细致的理解计算机编程的编译过程,对于我理解其他编程语言也有很大的帮助。欢迎大家关注,一起交流。