一、材料
- 开机后,CPU自动进入到FFFF:0单元处执行,此处有一条跳转指令。CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。
- 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。
- 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。
如果设为从软盘启动操作系统,则int 19h将主要完成以下工作。
- 控制0号软驱,读取软盘0道0面1扇区的内容到0:7c00;
- 将CS:IP指向0:7c00。
软盘的0道0面1扇区中装有操作系统引导程序。int 19h将其装到0:7c00处后,设置CPU从0:7c00开始执行此处的引导程序,操作系统被激活,控制计算机。
如果在0号软驱中没有软盘,或发生软盘I/O错误,则int 19h将主要完成以下工作。
- 读取硬盘C的0道0面1扇区的内容到0:7c00;
- 将CS:IP指向0:7c00。
二、任务
编写一个可以自行启动计算机,不需要在现有操作系统环境中运行的程序。
该程序的功能如下:
( 1 )列出功能选项,让用户通过键盘进行选择,界面如下:
1 ) reset pc ; 重新启动计算机
2 ) Start system ; 引导现有的操作系统
3 ) Clock ; 进入时钟程序
4 ) Srt clock ; 设置时间
( 2 )用户输入“ 1 ”后重新启动计算机。(提示:考虑 FFFF:0 )
( 3 )用户输入“ 2 ” 后引导现有的操作系统。(提示:考虑硬盘 C 的 0 道 0 面 1 扇区)
( 4 )用户输入“ 3 ”后,执行动态现实当前日期,时间的程序。
现实格式如下:年 / 月 / 日 时:分:秒
三、代码实现
assume cs:code
code segment
;********
;安装程序
;********
setup:
mov ax,cs
mov es,ax
mov bx,offset main
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
mov bx,offset mainmenu
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,3
int 13h
mov ax,4c00h
int 21h
main:
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处-----
mov ax,2000h
mov es,ax
mov bx,7c00h
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,2
int 13h
mov ax,2000h
mov bx,7c00h
push ax
push bx
retf
;********
;任务程序
;********
;-----主程序-----
mainmenu:
call cls
;-----显示功能菜单-----
showmenu:
jmp near ptr display
menu1 db '1) Reset PC',0
menu2 db '2) Start System',0
menu3 db '3) Clock',0
menu4 db '4) Set Clock',0
table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h
display:
mov ax,0b800h
mov es,ax
mov bx,offset table-offset mainmenu+7c00h
mov si,0
mov cx,4
s0:
push cx
push si
mov di,cs:[bx]
s1:
mov al,cs:[di]
cmp al,0
je ok
mov es:[si],al
inc di
add si,2
jmp near ptr s1
ok:
pop si
add si,160
add bx,2
pop cx
loop s0
;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序-----
choose:
mov ah,0
int 16h
cmp al,'1'
je reset
cmp al,'2'
je startsystem
cmp al,'3'
je clock
cmp al,'4'
je setclockok
jmp choose ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能
setclockok:
jmp near ptr setclock ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况
;---键盘输入为1时重新启动计算机---
reset:
mov ax,0ffffh
mov bx,0
push ax
push bx
retf
;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
startsystem:
mov ax,0
mov es,ax
mov bx,7c00h
mov al,1
mov ch,0
mov cl,1
mov dl,80h
mov dh,0
mov ah,2
int 13h
mov ax,0
mov bx,7c00h
push ax
push bx
retf
startsystemend:
nop
;---键盘输入为3时动态显示日期和时间---
clock:
jmp near ptr clocks
tableclock db 9,8,7,4,2,0
clocks:
mov ax,0b800h
mov es,ax
clocksok:
mov bx,offset tableclock-offset mainmenu+7c00h
mov cx,6
mov si,0
s2:
push cx
mov al,cs:[bx]
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl ;年份的十位
and al,00001111b ;年份的个位
add ah,30h
add al,30h
mov byte ptr es:[160*12+si],ah
add si,2
mov byte ptr es:[160*12+si],al
add si,2
pop cx
cmp cx,1 ;注意je等转移指令的用法,调用的顺序
je go3
cmp cx,4
jb go2
cmp cx,4
je go3
cmp cx,4
ja go1
go1:
mov byte ptr es:[160*12+si],'/'
jmp goend
go2:
mov byte ptr es:[160*12+si],':'
jmp goend
go3:
mov byte ptr es:[160*12+si],0
jmp goend
goend:
add si,2
inc bx
loop s2
in al,60h
cmp al,3bh ;当按下F1键改变显示颜色
jne goesc
mov ah,0
int 16h
mov cx,17
mov di,1
color:
inc byte ptr es:[160*12+di]
add di,2
loop color
goesc:
cmp al,1 ;当按下ESC键返回主菜单
jne nogoesc
jmp mainmenu
nogoesc:
jmp clocksok
;---键盘输入为4时更改当前日期和时间,更改后返回主选单---
setclock:
jmp near ptr setclocks
string1 db 'Input Time:yy/mm/dd hh:mm:ss',0
string2 db 'Change:\>',0
table0 dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h
;以下代码显示提示字符
setclocks:
mov bx,offset table0-offset mainmenu+7c00h
mov si,0
mov cx,2
t0:
push cx
push si
mov di,cs:[bx]
t1:
mov al,cs:[di]
mov es:[160*22+si],al
cmp al,0
je ko
inc di
add si,2
jmp near ptr t1
ko:
pop si
add si,160
add bx,2
pop cx
loop t0
;以下代码接收键盘输入
mov dh,23 ;设置起始行数
mov dl,9 ;设置起始列数
jmp getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter_string3:
mov cx,6
mov si,offset string3-offset mainmenu+7c00h
mov di,offset tableclock-offset mainmenu+7c00h
resetclock:
push cx
mov ah,cs:[si]
sub ah,30h
mov cl,4
shl ah,cl
mov al,cs:[si+1]
sub al,30h
add ah,al
mov al,cs:[di]
out 70h,al
mov al,ah
out 71h,al
add si,3
inc di
pop cx
loop resetclock
;以下代码将字符区清空,并重置字符显示位置,即top处数据置零
mov cx,20
mov bp,offset string3-offset mainmenu+7c00h
emptystring3:
mov byte ptr cs:[bp],0
inc bp
loop emptystring3
mov bp,offset top-offset mainmenu+7c00h
mov word ptr cs:[bp],0
jmp near ptr mainmenu
getstrs:
mov ah,0
int 16h
cmp al,20h
jb nochar ;ascii码小于20h,说明不是字符
mov ah,0 ;ah=0,表示字符入栈,要入栈的字符在al中
call charstack ;字符入栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
nochar:
cmp ah,0eh ;ah放扫描码,al放ascii码,退格键的扫描码0eh
je backspace
cmp ah,1ch ;enter键的扫描码1ch
je enter
jmp near ptr getstrs
backspace:
mov ah,1
call charstack ;字符出栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter:
mov al,0
mov ah,0
call charstack ;0入栈
mov ah,2
call charstack ;显示栈中的字符
jmp enter_string3
charstack:
jmp near ptr charstart
table1 dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h
string3 db 20 dup (0)
top dw 0
charstart:
push bx
push dx
push di
push es
mov si,offset table1-offset mainmenu+7c00h
mov bp,offset string3-offset mainmenu+7c00h
mov di,offset top-offset mainmenu+7c00h
cmp ah,2 ;判断子程序的功能号
ja sret ;大于2返回
mov bl,ah ;通过bx计算功能号在table1中的偏移地址
mov bh,0
add bx,bx ;一个偏移地址占2个字节,所以bx*2
jmp word ptr cs:[si+bx] ;跳转到子程序的相应功能:入栈,出栈,显示
charpush:
mov si,cs:[di] ;di指向栈顶
mov cs:[bp+si],al ;将al内的字符入栈
inc word ptr cs:[di] ;栈顶上移一个字节
jmp near ptr sret ;返回
charpop:
cmp word ptr cs:[di],0 ;出栈前判断字符栈是否为空
je sret ;字符栈为空,不用出栈,直接返回
dec word ptr cs:[di] ;字符栈不空,栈指针top下移指向栈顶元素
jmp near ptr sret ;返回
charshow:
mov bx,0b800h
mov es,bx ;es指向显存缓冲区
mov al,160
mov ah,0
mul dh ;计算行数160*dh,结果在ax中
mov bx,ax ;bx存放显示的位置
add dl,dl ;计算列数 dl*2
mov dh,0
add bx,dx ;计算出字符要显示的位置:160*dh+dl*2
mov si,0
charshows:
cmp si,cs:[di]
jne noempty
mov byte ptr es:[bx],' '
jmp near ptr sret
noempty:
mov al,cs:[bp+si]
mov es:[bx],al
mov byte ptr es:[bx+2],' '
inc si
add bx,2
jmp near ptr charshows
sret:
pop es
pop di
pop dx
pop bx
ret
cls:
push ax
push bx
push cx
push es
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s:
mov byte ptr es:[bx],' '
mov byte ptr es:[bx+1],7
add bx,2
loop s
pop es
pop cx
pop bx
pop ax
ret
code ends
end setup
程序主要分为三部分:
安装程序:负责将main段写入1扇区,mainmenu段写入1扇区以后的扇区
setup:
mov ax,cs
mov es,ax
mov bx,offset main
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
mov bx,offset mainmenu
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,3
int 13h
mov ax,4c00h
int 21h
引导程序:将mainmenu段写入2000:7c00处,然后跳转去执行
main:
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处-----
mov ax,2000h
mov es,ax
mov bx,7c00h
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,2
int 13h
mov ax,2000h
mov bx,7c00h
push ax
push bx
retf
主程序:实现程序功能的所有程序
;-----主程序-----
mainmenu:
call cls
;-----显示功能菜单-----
showmenu:
jmp near ptr display
menu1 db '1) Reset PC',0
menu2 db '2) Start System',0
menu3 db '3) Clock',0
menu4 db '4) Set Clock',0
table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h
display:
mov ax,0b800h
mov es,ax
mov bx,offset table-offset mainmenu+7c00h
mov si,0
mov cx,4
s0:
push cx
push si
mov di,cs:[bx]
s1:
mov al,cs:[di]
cmp al,0
je ok
mov es:[si],al
inc di
add si,2
jmp near ptr s1
ok:
pop si
add si,160
add bx,2
pop cx
loop s0
;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序-----
choose:
mov ah,0
int 16h
cmp al,'1'
je reset
cmp al,'2'
je startsystem
cmp al,'3'
je clock
cmp al,'4'
je setclockok
jmp choose ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能
setclockok:
jmp near ptr setclock ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况
;---键盘输入为1时重新启动计算机---
reset:
mov ax,0ffffh
mov bx,0
push ax
push bx
retf
;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
startsystem:
mov ax,0
mov es,ax
mov bx,7c00h
mov al,1
mov ch,0
mov cl,1
mov dl,80h
mov dh,0
mov ah,2
int 13h
mov ax,0
mov bx,7c00h
push ax
push bx
retf
startsystemend:
nop
;---键盘输入为3时动态显示日期和时间---
clock:
jmp near ptr clocks
tableclock db 9,8,7,4,2,0
clocks:
mov ax,0b800h
mov es,ax
clocksok:
mov bx,offset tableclock-offset mainmenu+7c00h
mov cx,6
mov si,0
s2:
push cx
mov al,cs:[bx]
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl ;年份的十位
and al,00001111b ;年份的个位
add ah,30h
add al,30h
mov byte ptr es:[160*12+si],ah
add si,2
mov byte ptr es:[160*12+si],al
add si,2
pop cx
cmp cx,1 ;注意je等转移指令的用法,调用的顺序
je go3
cmp cx,4
jb go2
cmp cx,4
je go3
cmp cx,4
ja go1
go1:
mov byte ptr es:[160*12+si],'/'
jmp goend
go2:
mov byte ptr es:[160*12+si],':'
jmp goend
go3:
mov byte ptr es:[160*12+si],0
jmp goend
goend:
add si,2
inc bx
loop s2
in al,60h
cmp al,3bh ;当按下F1键改变显示颜色
jne goesc
mov ah,0
int 16h
mov cx,17
mov di,1
color:
inc byte ptr es:[160*12+di]
add di,2
loop color
goesc:
cmp al,1 ;当按下ESC键返回主菜单
jne nogoesc
jmp mainmenu
nogoesc:
jmp clocksok
;---键盘输入为4时更改当前日期和时间,更改后返回主选单---
setclock:
jmp near ptr setclocks
string1 db 'Input Time:yy/mm/dd hh:mm:ss',0
string2 db 'Change:\>',0
table0 dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h
;以下代码显示提示字符
setclocks:
mov bx,offset table0-offset mainmenu+7c00h
mov si,0
mov cx,2
t0:
push cx
push si
mov di,cs:[bx]
t1:
mov al,cs:[di]
mov es:[160*22+si],al
cmp al,0
je ko
inc di
add si,2
jmp near ptr t1
ko:
pop si
add si,160
add bx,2
pop cx
loop t0
;以下代码接收键盘输入
mov dh,23 ;设置起始行数
mov dl,9 ;设置起始列数
jmp getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter_string3:
mov cx,6
mov si,offset string3-offset mainmenu+7c00h
mov di,offset tableclock-offset mainmenu+7c00h
resetclock:
push cx
mov ah,cs:[si]
sub ah,30h
mov cl,4
shl ah,cl
mov al,cs:[si+1]
sub al,30h
add ah,al
mov al,cs:[di]
out 70h,al
mov al,ah
out 71h,al
add si,3
inc di
pop cx
loop resetclock
;以下代码将字符区清空,并重置字符显示位置,即top处数据置零
mov cx,20
mov bp,offset string3-offset mainmenu+7c00h
emptystring3:
mov byte ptr cs:[bp],0
inc bp
loop emptystring3
mov bp,offset top-offset mainmenu+7c00h
mov word ptr cs:[bp],0
jmp near ptr mainmenu
getstrs:
mov ah,0
int 16h
cmp al,20h
jb nochar ;ascii码小于20h,说明不是字符
mov ah,0 ;ah=0,表示字符入栈,要入栈的字符在al中
call charstack ;字符入栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
nochar:
cmp ah,0eh ;ah放扫描码,al放ascii码,退格键的扫描码0eh
je backspace
cmp ah,1ch ;enter键的扫描码1ch
je enter
jmp near ptr getstrs
backspace:
mov ah,1
call charstack ;字符出栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter:
mov al,0
mov ah,0
call charstack ;0入栈
mov ah,2
call charstack ;显示栈中的字符
jmp enter_string3
charstack:
jmp near ptr charstart
table1 dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h
string3 db 20 dup (0)
top dw 0
charstart:
push bx
push dx
push di
push es
mov si,offset table1-offset mainmenu+7c00h
mov bp,offset string3-offset mainmenu+7c00h
mov di,offset top-offset mainmenu+7c00h
cmp ah,2 ;判断子程序的功能号
ja sret ;大于2返回
mov bl,ah ;通过bx计算功能号在table1中的偏移地址
mov bh,0
add bx,bx ;一个偏移地址占2个字节,所以bx*2
jmp word ptr cs:[si+bx] ;跳转到子程序的相应功能:入栈,出栈,显示
charpush:
mov si,cs:[di] ;di指向栈顶
mov cs:[bp+si],al ;将al内的字符入栈
inc word ptr cs:[di] ;栈顶上移一个字节
jmp near ptr sret ;返回
charpop:
cmp word ptr cs:[di],0 ;出栈前判断字符栈是否为空
je sret ;字符栈为空,不用出栈,直接返回
dec word ptr cs:[di] ;字符栈不空,栈指针top下移指向栈顶元素
jmp near ptr sret ;返回
charshow:
mov bx,0b800h
mov es,bx ;es指向显存缓冲区
mov al,160
mov ah,0
mul dh ;计算行数160*dh,结果在ax中
mov bx,ax ;bx存放显示的位置
add dl,dl ;计算列数 dl*2
mov dh,0
add bx,dx ;计算出字符要显示的位置:160*dh+dl*2
mov si,0
charshows:
cmp si,cs:[di]
jne noempty
mov byte ptr es:[bx],' '
jmp near ptr sret
noempty:
mov al,cs:[bp+si]
mov es:[bx],al
mov byte ptr es:[bx+2],' '
inc si
add bx,2
jmp near ptr charshows
sret:
pop es
pop di
pop dx
pop bx
ret
cls:
push ax
push bx
push cx
push es
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s:
mov byte ptr es:[bx],' '
mov byte ptr es:[bx+1],7
add bx,2
loop s
pop es
pop cx
pop bx
pop ax
ret
四、处理
使用vmare14及DOS7.1进行程序的测试
使用软盘进入系统,需要创建软盘文件,后缀为.img.flp
写入软盘之后,需要进入固件配置软盘启动
设置好保存并退出即可
五、优化
发现源程序的三号功能在摁下F1后虽然颜色变了,但是动态显示的功能实现不了了,所以做了一些优化,重写了3号功能。
assume cs:code
code segment
;********
;安装程序
;********
setup:
mov ax,cs
mov es,ax
mov bx,offset main
mov al,1
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
mov bx,offset mainmenu
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,3
int 13h
mov ax,4c00h
int 21h
main:
;-----读取0面0道2扇区开始的5个扇区的内容到2000:7c00处-----
mov ax,2000h
mov es,ax
mov bx,7c00h
mov al,5
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,2
int 13h
mov ax,2000h
mov bx,7c00h
push ax
push bx
retf
;********
;任务程序
;********
;-----主程序-----
mainmenu:
call cls
;-----显示功能菜单-----
showmenu:
jmp near ptr display
menu1 db '1) Reset PC',0
menu2 db '2) Start System',0
menu3 db '3) Clock',0
menu4 db '4) Set Clock',0
table dw offset menu1-offset mainmenu+7c00h,offset menu2-offset mainmenu+7c00h,offset menu3-offset mainmenu+7c00h,offset menu4-offset mainmenu+7c00h
display:
mov ax,0b800h
mov es,ax
mov bx,offset table-offset mainmenu+7c00h
mov si,0
mov cx,4
s0:
push cx
push si
mov di,cs:[bx]
s1:
mov al,cs:[di]
cmp al,0
je ok
mov es:[si],al
inc di
add si,2
jmp near ptr s1
ok:
pop si
add si,160
add bx,2
pop cx
loop s0
;-----调用int 16h中断例程,读取键盘输入,确定要调用的子程序-----
choose:
mov ah,0
int 16h
cmp al,'1'
je reset
cmp al,'2'
je startsystem
cmp al,'3'
je save_old_int9
cmp al,'4'
je setclockok
jmp choose ;此处必须加这一句,否则键盘缓冲区中不是'4'时也会继续后面的代码,要注意je的功能
setclockok:
jmp near ptr setclock ;因为转移位移大,所以用jmp near ptr中转,后面有类似情况
;---键盘输入为1时重新启动计算机---
reset:
mov ax,0ffffh
mov bx,0
push ax
push bx
retf
;---键盘输入为2时引导现有操作系统,即读取硬盘C的0面0道1扇区的内容到0:7c00处,
;并将CS:IP指向0:7c00h
startsystem:
mov ax,0
mov es,ax
mov bx,7c00h
mov al,1
mov ch,0
mov cl,1
mov dl,80h
mov dh,0
mov ah,2
int 13h
mov ax,0
mov bx,7c00h
push ax
push bx
retf
startsystemend:
nop
;---键盘输入为3时动态显示日期和时间---
save_old_int9: ;保存九号中断
mov bx,0
mov es,bx
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
set_new_int9:
mov bx,0
mov es,bx
cli
mov word ptr es:[9*4],offset newint9-offset mainmenu+7c00h
mov word ptr es:[9*4+2],2000h
sti
clock:
jmp near ptr clocks
tableclock db 9,8,7,4,2,0
clocks:
mov ax,0b800h
mov es,ax
clocksok:
mov bx,offset tableclock-offset mainmenu+7c00h
mov cx,6
mov si,0
s2:
push cx
mov al,cs:[bx]
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl ;年份的十位
and al,00001111b ;年份的个位
add ah,30h
add al,30h
mov byte ptr es:[160*12+si],ah
add si,2
mov byte ptr es:[160*12+si],al
add si,2
pop cx
cmp cx,1 ;注意je等转移指令的用法,调用的顺序
je go3
cmp cx,4
jb go2
cmp cx,4
je go3
cmp cx,4
ja go1
go1:
mov byte ptr es:[160*12+si],'/'
jmp goend
go2:
mov byte ptr es:[160*12+si],':'
jmp goend
go3:
mov byte ptr es:[160*12+si],0
jmp goend
goend:
add si,2
inc bx
loop s2
in al,60h
cmp al,1
je goesc
jmp near ptr clocks
goesc:
mov bx,0
mov es,bx
cli
push es:[200h]
pop es:[9*4]
push es:[202h]
pop es:[9*4+2]
sti
jmp near ptr mainmenu
newint9:
push ax
push es
push bx
push cx
push di
mov ax,0
mov es,ax
in al,60h
pushf
call dword ptr es:[200h]
cmp al,3bh ;f1
jne int9ret
call a_green
mov bx,0b800h
mov es,bx
mov cx,17
mov di,1
mov ah,0
int 16h
change_time_colors:
inc byte ptr es:[160*12+di]
add di,2
loop change_time_colors
int9ret:
pop di
pop cx
pop bx
pop es
pop ax
iret
;---键盘输入为4时更改当前日期和时间,更改后返回主选单---
setclock:
jmp near ptr setclocks
string1 db 'Input Time:yy/mm/dd hh:mm:ss',0
string2 db 'Change:\>',0
table0 dw offset string1-offset mainmenu+7c00h,offset string2-offset mainmenu+7c00h
;以下代码显示提示字符
setclocks:
mov bx,offset table0-offset mainmenu+7c00h
mov si,0
mov cx,2
t0:
push cx
push si
mov di,cs:[bx]
t1:
mov al,cs:[di]
mov es:[160*22+si],al
cmp al,0
je ko
inc di
add si,2
jmp near ptr t1
ko:
pop si
add si,160
add bx,2
pop cx
loop t0
;以下代码接收键盘输入
mov dh,23 ;设置起始行数
mov dl,9 ;设置起始列数
jmp getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter_string3:
mov cx,6
mov si,offset string3-offset mainmenu+7c00h
mov di,offset tableclock-offset mainmenu+7c00h
resetclock:
push cx
mov ah,cs:[si]
sub ah,30h
mov cl,4
shl ah,cl
mov al,cs:[si+1]
sub al,30h
add ah,al
mov al,cs:[di]
out 70h,al
mov al,ah
out 71h,al
add si,3
inc di
pop cx
loop resetclock
;以下代码将字符区清空,并重置字符显示位置,即top处数据置零
mov cx,20
mov bp,offset string3-offset mainmenu+7c00h
emptystring3:
mov byte ptr cs:[bp],0
inc bp
loop emptystring3
mov bp,offset top-offset mainmenu+7c00h
mov word ptr cs:[bp],0
jmp near ptr mainmenu
getstrs:
mov ah,0
int 16h
cmp al,20h
jb nochar ;ascii码小于20h,说明不是字符
mov ah,0 ;ah=0,表示字符入栈,要入栈的字符在al中
call charstack ;字符入栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
nochar:
cmp ah,0eh ;ah放扫描码,al放ascii码,退格键的扫描码0eh
je backspace
cmp ah,1ch ;enter键的扫描码1ch
je enter
jmp near ptr getstrs
backspace:
mov ah,1
call charstack ;字符出栈
mov ah,2
call charstack ;显示栈中的字符
jmp near ptr getstrs
;回车键时将输入的日期和时间写入cmos,完成后返回主选单
enter:
mov al,0
mov ah,0
call charstack ;0入栈
mov ah,2
call charstack ;显示栈中的字符
jmp enter_string3
charstack:
jmp near ptr charstart
table1 dw offset charpush-offset mainmenu+7c00h,offset charpop-offset mainmenu+7c00h,offset charshow-offset mainmenu+7c00h
string3 db 20 dup (0)
top dw 0
charstart:
push bx
push dx
push di
push es
mov si,offset table1-offset mainmenu+7c00h
mov bp,offset string3-offset mainmenu+7c00h
mov di,offset top-offset mainmenu+7c00h
cmp ah,2 ;判断子程序的功能号
ja sret ;大于2返回
mov bl,ah ;通过bx计算功能号在table1中的偏移地址
mov bh,0
add bx,bx ;一个偏移地址占2个字节,所以bx*2
jmp word ptr cs:[si+bx] ;跳转到子程序的相应功能:入栈,出栈,显示
charpush:
mov si,cs:[di] ;di指向栈顶
mov cs:[bp+si],al ;将al内的字符入栈
inc word ptr cs:[di] ;栈顶上移一个字节
jmp near ptr sret ;返回
charpop:
cmp word ptr cs:[di],0 ;出栈前判断字符栈是否为空
je sret ;字符栈为空,不用出栈,直接返回
dec word ptr cs:[di] ;字符栈不空,栈指针top下移指向栈顶元素
jmp near ptr sret ;返回
charshow:
mov bx,0b800h
mov es,bx ;es指向显存缓冲区
mov al,160
mov ah,0
mul dh ;计算行数160*dh,结果在ax中
mov bx,ax ;bx存放显示的位置
add dl,dl ;计算列数 dl*2
mov dh,0
add bx,dx ;计算出字符要显示的位置:160*dh+dl*2
mov si,0
charshows:
cmp si,cs:[di]
jne noempty
mov byte ptr es:[bx],' '
jmp near ptr sret
noempty:
mov al,cs:[bp+si]
mov es:[bx],al
mov byte ptr es:[bx+2],' '
inc si
add bx,2
jmp near ptr charshows
sret:
pop es
pop di
pop dx
pop bx
ret
cls:
push ax
push bx
push cx
push es
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s:
mov byte ptr es:[bx],' '
mov byte ptr es:[bx+1],7
add bx,2
loop s
pop es
pop cx
pop bx
pop ax
ret
a_green:
push bx
push es
mov bx,0b800h
mov es,bx
mov bl,'A'
mov bh,2
mov es:[160*12+40],bx
pop es
pop bx
ret
code ends
end setup