课程设计2
1. 课程设计 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,0
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
- 实操版
assume cs:setupsg
;安装程序
;将引导所需的程序写入到软盘
setupsg segment
assume cs:setupsg
setup:
;主引导程序安装到第一扇区
mov ax,initsg
mov es,ax
mov bx,0
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
;子程序安装到从第2扇区开始的扇区
mov ax,syssg
mov es,ax
mov al,15
mov cl,2
mov ah,3
int 13h
;安装结束,返回
mov ax,4c00h
int 21h
setupsg ends
;主引导程序
;包含所有子程序的直接定址表,扇区加载程序,菜单
initsg segment
assume cs:initsg
init:
call loadsys
mov ax,2000h
push ax
mov ax,0
push ax
retf
loadsys:
mov ax,2000h ;软盘数据读取到2000:0
mov es,ax
mov bx,0
mov al,15 ;读取的扇区数
mov ch,0 ;0磁道
mov cl,2 ;2扇区
mov dl,0 ;0号驱动器
mov dh,0 ;0面
mov ah,2
int 13h
ret
initsg ends
;子程序
;包含所有菜单需要调用的子过程
syssg segment
assume cs:syssg
;菜单显示功能
menu:
jmp near ptr menushow
menudata dw offset md0,offset md1,offset md2,offset md3,offset md4,offset md5
md0 db "------ Hello ZhangXiao, Please select: ------",0
md1 db "1) reset pc",0
md2 db "2) start system",0
md3 db "3) clock",0
md4 db "4) set clock",0
md5 db "~Made By Zhang Kai~",0
systable dw sys_restart,sys_disksys,sys_showclock,sys_setclock
menushow:
mov dh,5
mov dl,30
mov bp,0
mov ax,cs
mov ds,ax
mov cx,5
menushow_s:
push cx
mov si,menudata[bp]
mov cl,02h
call sys_showstr
add bp,2
add dh,2
pop cx
loop menushow_s
mov si,offset md5
mov dh,23
mov dl,28
mov cl,02h
call sys_showstr
;处理用户输入
sys_input:
mov ah,0
int 16h
mov bx,0
mov bl,al
mov al,30h
sub bl,al ;ascii转换为序列号
sub bl,1 ;1-4转换为0-3
cmp bx,0
jb cycle
cmp bx,3
ja cycle
add bx,bx
call word ptr systable[bx] ;调用菜单功能
cycle:
jmp short sys_input
;重启计算机
sys_restart:
mov ax,0ffffh
push ax
mov ax,0h
push ax
retf
;从硬盘引导
sys_disksys:
call cls
mov ax,0h ;硬盘数据读取到0:7c00
mov es,ax
mov bx,7c00h
mov al,1 ;读取的扇区数
mov ch,0 ;0磁道
mov cl,1 ;1扇区
mov dl,80h ;c盘
mov dh,0 ;0面
mov ah,2
int 13h
mov ax,0h
push ax
mov ax,7c00h
push ax
retf
;显示时钟
sys_showclock:
call cls
jmp short clockread
clockdata:
clockstr dw offset cl1,offset cl2,offset cl3
clockcolor db 02h
cl1 db '00/00/00 00:00:00',0
cl2 db 'press ESC return menu!',0
cl3 db 'press F1 change color!',0
cltable db 9,8,7,4,2,0
clockread:
mov si,0 ;si指向'yy/mm/dd hh:mm:ss'的首地址
mov di,0 ;di指向9,8,7,4,2,0的首地址
mov cx,6 ;循环次数
clockread_s:
push cx
mov al,cltable[di] ;从CMOS中读出年份的BCD码
out 70h,al
in al,71h
mov ah,al ;al中位读出的数据
mov cl,4
shr ah,cl ;ah中为年份的十位数
and al,00001111b ;al中为年份的个位数
add ah,30h ;把数值转换为对应的ASCII码
add al,30h ;同上
mov byte ptr cl1[si],ah ;把读出的时间写入
mov byte ptr cl1[si+1],al
add si,3
inc di
pop cx
loop clockread_s
clockprint:
mov dh,6
mov dl,30
mov bp,0
mov ax,cs
mov ds,ax
mov cx,3
clockprint_s:
push cx
mov si,clockstr[bp]
mov cl,clockcolor[0] ;将颜色值赋值给cl
call sys_showstr
add bp,2
add dh,2
pop cx
loop clockprint_s
mov ah,1 ;调用16h中断的1号功能(非阻塞)
int 16h
cmp al,1bh ;判断是否为ESC
je clockreturn ;若是ESC,回到菜单
cmp ah,3bh ;判断是否为F1
je changecolor
jmp short clockread
clockreturn:
call cls
mov ah,0 ;16h中断的1号功能不会清除键盘缓冲区,下次读取还会读出
int 16h ;调用0号功能清除一次
jmp near ptr menu
changecolor:
inc clockcolor
mov ah,0 ;16h中断的1号功能不会清除键盘缓冲区,下次读取还会读出
int 16h ;调用0号功能清除一次
jmp near ptr clockread
;设置时钟
sys_setclock:
jmp short setclock
setclockdata db 'Please input time like "yy/mm/dd hh:mm:ss"',0
setsuccess db 'Set clock successful! Press any key return...',0
setclock:
call cls
mov dh,6
mov dl,20
mov cl,02h
mov ax,cs
mov ds,ax
mov si,offset setclockdata
call sys_showstr
call getstr
call settime
mov dh,10
mov dl,20
mov cl,02h
mov ax,cs
mov ds,ax
mov si,offset setsuccess
call sys_showstr
mov ah,0
int 16h
call cls
jmp near ptr menu
;ds:si指向时间字符串
settime:
jmp short seting
settable db 9,8,7,4,2,0
seting:
mov bx,0
mov cx,6
settime_s:
mov dh,ds:[si]
inc si
mov dl,ds:[si]
add si,2
mov al,30h
sub dl,al
sub dh,al
shl dh,1
shl dh,1
shl dh,1
shl dh,1
or dl,dh
mov al,settable[bx]
out 70h,al
mov al,dl
out 71h,al
inc bx
loop settime_s
ret
;子程序:接收字符串
getstr:
push ax
getstrs:
mov ah,0
int 16h
cmp al,20h
jb nochar
mov ah,0
call charstack
mov ah,2
mov dh,8
mov dl,25
call charstack
jmp getstrs
nochar:
cmp ah,0eh
je backspace
cmp ah,1ch
je enter
jmp getstrs
backspace:
mov ah,1
call charstack
mov ah,2
call charstack
jmp getstrs
enter:
mov al,0
mov ah,0
call charstack
mov ah,2
call charstack
pop ax
ret
;子程序:字符串入栈,出栈和显示
;参数:(ah)=功能号,0入栈,1出栈,2显示
; ds:si指向字符栈空间,对于0号功能,(al)表示入栈字符
; 1号功能,(al)返回的字符,对于2号功能,(dh)(dl)字符串在屏幕显示的行列位置
charstack:
jmp short charstart
table dw charpush,charpop,charshow
top dw 0
charstart:
push bx
push dx
push di
push es
cmp ah,2
ja sret
mov bl,ah
mov bh,0
add bx,bx
jmp word ptr table[bx]
charpush:
mov bx,top
mov [si][bx],al
inc top
jmp sret
charpop:
cmp top,0
je sret
dec top
mov bx,top
mov al,[si][bx]
jmp sret
charshow:
mov bx,0b800h
mov es,bx
mov al,160
mov ah,0
mul dh
mov di,ax
add dl,dl
mov dh,0
add di,dx
mov bx,0
charshows:
cmp bx,top
jne noempty
mov byte ptr es:[di],' '
mov byte ptr es:[di+1],02h
jmp sret
noempty:
mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],' '
mov byte ptr es:[di+1],02h
inc bx
add di,2
jmp charshows
sret:
pop es
pop di
pop dx
pop bx
ret
;显示0结尾的字符串
;参数:dh=行号,dl=列号,cl=颜色,ds:si指向字符串首地址
sys_showstr:
push ax
push cx
push dx
push si
push bp
push es
mov ax,0b800h
mov es,ax
mov al,80*2 ;80*2*行号
mul dh
mov dh,0
add dx,dx ;列号*2
add ax,dx
mov bp,ax
showstr_s:
mov ch,ds:[si]
cmp ch,0
je showstr_return
mov es:[bp],ch
inc bp
mov es:[bp],cl
inc bp
inc si
jmp short showstr_s
showstr_return:
pop es
pop bp
pop si
pop dx
pop cx
pop ax
ret
;清屏
cls:
mov ax,0b800h
mov ds,ax
mov bx,0
mov cx,24*80*2
cls_s:
mov byte ptr ds:[bx],0
add bx,2
loop cls_s
mov bx,1
resetcol:
mov byte ptr ds:[bx],07h
add bx,2
loop resetcol
ret
syssg ends
;安装过程的第一行指令
end setup