谢谢完成这篇代码前参阅的其它文章。没有别的老师和同学们的指导,就没有这个实验的完成。
我用的是win10,虚拟机virtualBox,开源免费不用破解,虚拟机也可以提供3.5软驱。至于如何共享文件,如何在虚拟机里下载XP及验证码,网上都提供了很多资料,搜一搜即可。给太多链接,涉及版权,就发表不了了。
好了,实验开始:
我用的masm5.0在Xp系统里不很好用,编译出错后给空白行,没有提示,在外面的dosBox里就可完全根据masm5.0调试。一度卡在了引导不成功,也找不到故障在哪的境地,直到直接拿别人的好用的源代码改,只保留框架,才发现了masm5.0的问题。
主要参考了这位大师的文章动态显示时间:所有地址标号只在代码段可用,且所有标号只记录了距离代码段开始的相对位置。脱离所在代码段,标号将不再可用,也给不出正确的结果。
要想动态显示时间,可以延时后输出时间,但这是个死循环,唯一可以影响中断死循环的就是int 9键盘中断事件,因此,只可以在int9 中更改esc 和 F1 键的标志位,而在循环显示时间中,也不断的检测这俩键的标志位。所以,为了标号有效,就要使新 int 9 具有和主系统相同的段地址。这样,程序远转移后,esc 和 F1 的数据标号,依然有意义。有点类似于C语言的全局变量的感觉。退回主菜单时,要先恢复原 int 9
网上也有写要在引导扇区末尾加aa55h标志的,可是课本上没有这个讲解,实验里也不用加这个,也能做出引导扇区。所以添加aa55h不是必要的。
下面是完整代码,并带有详细注释,缩进排列。可以和链接里的那篇好文结合着看
assume cs:try2
try2 segment
main:
mov ax,loadsys
mov es,ax
mov bx,0
mov ah,3;write
mov al,1;number
mov ch,0;track
mov cl,1;sector
mov dh,0;face
mov dl,0;3.5 A
int 13h
mov ax,mainsys
mov es,ax
mov al,15; 根据写完后的代码量,5个扇区就足够了
mov cl,2
mov ah,3
int 13h
mov ax,4c00h
int 21h
try2 ends
assume cs:loadsys
loadsys segment
mov ax,2000h
mov es,ax
mov bx,0; 2000:0
mov ah,2;read
mov al,15
mov ch,0
mov cl,2
mov dh,0
mov dl,0
int 13h
push es
push bx
retf
loadsys ends
assume cs:mainsys
mainsys segment
jmp near ptr mainsys_start
menu_name dw name0,name1,name2,name3,name4,name5;menuname[bp]
name0 db '--------welcome-------',0
name1 db '1) restart PC',0
name2 db '2) start winXP',0
name3 db '3) show time',0
name4 db '4) set time',0
name5 db '--------welcome-------',0
menu_choose dw menu_restart,menu_toXP,menu_show_time,menu_set_time
int9_location dw 0,0; 保存原 int 9 的入口地址
esc_check db 0
time_check db 0;验证时间的有效性,未实现,闰年呢?2月呢?
tip_esc_F1 db 'ESC-return to menu F1-change color',0
tip_set_timeA db "checking time's validity is difficult",0
tip_set_timeB db 'so,all your input is acceptted.',0
tip_set_timeC db 'Congratulations , best wishes for you ',0
tip_set_timeD db 'Press any key to return..... ',0
cmos_addr db 9,8,7,4,2,0
bcd db 6 dup (0); 年月日时分秒
asc dw 6 dup (0); 年月日时分秒
time_format db '// :: '
cursor_row db 5; 跟显示字符相关的参数
cursor_colomn db 20; 见明知意,还少占用寄存器
blrgb_irgb db 02h
mainsys_start:
mov ax,cs
mov ds,ax
mov blrgb_irgb,02h; 以特定属性刷新主菜单
call clr_screen
mov cursor_row,5
mov cursor_colomn,20
mov cx,6
mov bx,0
show_mainsys_lp:
mov si,menu_name[bx]; 利用了数据标号的性质
call show_str
add cursor_row,2
add bx,2
loop show_mainsys_lp
mainsys_input:
mov ah,0
int 16h
mov bl,al
sub bl,30h
dec bl
cmp bl,0
jb mainsys_input
cmp bl,3
ja mainsys_input
add bl,bl
sub bh,bh
call word ptr menu_choose[bx]
jmp mainsys_start; 跳转后附带刷新主菜单
clr_screen:
push ax
push cx
push es
push di
mov ax,0b800h
mov es,ax
mov di,0
mov ah,blrgb_irgb; 黑底蓝字 + 空格
mov al,' '
mov cx,2000
clrlp:
mov es:[di],ax
add di,2
loop clrlp
pop di
pop es
pop cx
pop ax
ret
show_str:
push ax
push si
push es
push di
mov ax,0b800h
mov es,ax
mov ax,0
mov al,160
mul cursor_row
mov di,ax
mov ax,0
mov al,cursor_colomn
add al,al
add di,ax; 得到显示位置
mov ah,blrgb_irgb
show_str_lp:
cmp byte ptr ds:[si],0
je show_str_end
mov al,ds:[si]
mov es:[di],ax
add di,2
inc si
jmp show_str_lp
show_str_end:
pop di
pop es
pop si
pop ax
ret
menu_restart:
mov ax,0ffffh
push ax
mov ax,0
push ax
retf; 修改cs:ip的巧妙语句
menu_toXP:
mov ax,0
mov es,ax
mov bx,7c00h
mov ah,2;read
mov al,1;num
mov ch,0;trace
mov cl,1;sector
mov dh,0;face
mov dl,80h;driver C
int 13h
push es
push bx; 这俩寄存器可以直接入栈的
retf
menu_show_time:
;安装新的 int 9 中断,用 int 9中断,终止显示时间的循环
;不再传送,修正中断向量表,指向本系统代码段内的新 int 9
mov ax,0
mov es,ax
push es:[9 * 4]
pop int9_location[0]
push es:[9 * 4 + 2]
pop int9_location[2]
cli
mov word ptr es:[9 * 4],offset int9_new
mov es:[9 * 4 + 2 ],cs
sti
mov blrgb_irgb,04h
call clr_screen
mov cursor_row,5
mov cursor_colomn,20
mov si,offset tip_esc_F1
call show_str
mov cursor_row,7
show_time_lp:
call getbcd
call bcdtoasc
call showasc
mov dx,5
mov cx,0
delay:
sub cx,1
sbb dx,0
cmp cx,0
jne delay
cmp dx,0
jne delay
cmp esc_check,1;esc 的扫描码即是1
je show_time_end
jmp show_time_lp
show_time_end: ;恢复原 int 9,学到的最重要的处理
mov esc_check,0
mov ax,0
mov es,ax
cli
push int9_location[0]
pop es:[9 * 4]
push int9_location[2]
pop es:[9 * 4 + 2]
sti
ret ; 菜单三结束
getbcd:
push ax
push cx
push si
mov si,0
mov cx,6
getbcdlp:
mov al,cmos_addr[si]
out 70h,al
in al,71h
mov bcd[si],al
inc si
loop getbcdlp
pop si
pop cx
pop ax
ret
bcdtoasc:
push ax
push cx
push si
mov si,0
mov cx,6
bcdtoasclp:
mov al,bcd[si]
mov ah,al
and al,00001111b
add al,30h
push cx
mov cl,4
shr ah,cl
add ah,30h
pop cx
push si
add si,si
mov asc[si],ax
pop si
inc si
loop bcdtoasclp
pop si
pop cx
pop ax
ret
showasc:
push ax
push bx
push cx
push si
push es
push di
mov ax,0b800h
mov es,ax
mov ax,0
mov al,160
mul cursor_row
mov di,ax
mov ax,0
mov al,cursor_colomn
add al,al
add di,ax; 得到显示位置
mov si,0
mov bh,blrgb_irgb
mov cx,6
shasclp:
push si
add si,si
mov ax,asc[si]
pop si
mov bl,ah
mov es:[di],bx
add di,2
mov bl,al
mov es:[di],bx
add di,2
mov bl,time_format[si]
mov es:[di],bx
add di,2
inc si
loop shasclp
pop di
pop es
pop si
pop cx
pop bx
pop ax
ret
int9_new:
push ax
push si
pushf
pushf
pop ax
and ah,11111100b
push ax
popf
; call far ptr int9_location ???这么做不对,下面就对 跟课本不一致
mov si,offset int9_location
call dword ptr ds:[si]
in al,60h
cmp al,1; 1--esc
je esc_clicked
cmp al,3bh; 3bh--F1
je F1_clicked
int9_new_end:
pop si
pop ax
iret
esc_clicked:
mov esc_check,al
jmp int9_new_end
F1_clicked:
inc blrgb_irgb
mov ah,0
int 16h ;可见在int 9 中仍可以调用中断
jmp int9_new_end
menu_set_time:
mov blrgb_irgb,3dh
call clr_screen
mov cursor_row,5
mov cursor_colomn,10
mov si,offset tip_set_timeA
call show_str
mov cursor_row,6
mov cursor_colomn,10
mov si,offset tip_set_timeB
call show_str
mov cursor_row,10
mov cursor_colomn,15
call getbcd
call bcdtoasc
call showasc
call newtime
call savasc
call asctobcd
call setcmos
mov cursor_row,15
mov cursor_colomn,10
mov si,offset tip_set_timeC
call show_str
mov cursor_row,17
mov cursor_colomn,10
mov si,offset tip_set_timeD
call show_str
mov ah,0; 实现延时
int 16h
ret
newtime: ;按enter键完成输入,支持箭头<-- 和-->选择时间
push ax ;并能跳过时间格式字符,准确落在数字上
push es ;移动到两边时,并不会越界,
push di ;能够使待修改字符闪烁,提醒修改位置
push bp
mov ax,0b800h
mov es,ax
mov ax,0
mov al,160
mul cursor_row
mov di,ax
mov ax,0
mov al,cursor_colomn
add al,al
add di,ax; 得到显示位置
mov bp,0; es:[di+bp]
newtimelp:
or byte ptr es:[di+bp+1],10000000b; 实现待修改字符的闪烁
mov ah,0
int 16h
cmp ah,4bh; <-- 通码4bH
je toleft
cmp ah,4dh; --> 通码4dh
je toright
cmp ah,1ch; enter 通码 1ch
je timeover
cmp ah,2h; 1~9,0 范围是 2~Bh
jb newtimelp
cmp ah,0bh
ja newtimelp
mov es:[di+bp],al
jmp toright
toleft:
cmp bp,0
jna newtimelp ; 空格20h
cmp byte ptr es:[di+bp-2],'0';/29h < 数字 < :3ah
jb leftone
cmp byte ptr es:[di+bp-2],'9'
ja leftone
and byte ptr es:[di+bp+1],01111111b
sub bp,2
jmp newtimelp
leftone:
and byte ptr es:[di+bp+1],01111111b
sub bp,4
jmp newtimelp
toright:
cmp bp,32; 秒的最后一位
jnb newtimelp
cmp byte ptr es:[di+bp+2],'0'
jb rightone
cmp byte ptr es:[di+bp+2],'9'
ja rightone
and byte ptr es:[di+bp+1],01111111b
add bp,2
jmp newtimelp
rightone:
and byte ptr es:[di+bp+1],01111111b
add bp,4
jmp newtimelp
timeover:
and byte ptr es:[di+bp+1],01111111b
pop bp
pop di
pop es
pop ax
ret
savasc:
push ax
push cx
push si
push es
push di
mov ax,0b800h
mov es,ax
mov ax,0
mov al,160
mul cursor_row
mov di,ax
mov ax,0
mov al,cursor_colomn
add al,al
add di,ax; 得到显示位置
mov si,offset asc
mov cx,6
savasclp:
mov ah,es:[di]
mov al,es:[di+2]
mov ds:[si],ax
add si,2
add bp,6
loop savasclp
pop di
pop es
pop si
pop cx
pop ax
ret
asctobcd:
push ax
push cx
push si
mov si,0
mov cx,6
asctobcdlp:
push si
add si,si
mov ax,asc[si]
pop si
sub ah,30h
push cx
mov cl,4
shl ah,cl
pop cx
sub al,30h
or al,ah
mov bcd[si],al
inc si
loop asctobcdlp
pop si
pop cx
pop ax
ret
setcmos:
push ax
push cx
push si
mov si,0
mov cx,6
setcmoslp:
mov al,cmos_addr[si]
out 70h,al
mov al,bcd[si]
out 71h,al
inc si
loop setcmoslp
pop si
pop cx
pop ax
ret
mainsys ends
end main
下面是演示效果:
谢谢阅读。并向网络上分享知识的您们致以深深的谢意。