终于把完整代码写了,太懒了。
assume cs:code,ds:data,ss:stack
data segment
db 128 dup(0)
data ends
stack segment
db 128 dup(0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
mov bx,offset Boot_end-offset Boot
call cpy_introduce_toDiskA
call cpy_boot_toDiskA
mov ax,4c00h
int 21h
;=======================================================
introduce:
mov bx,0
mov ss,bx
mov sp,7c00h
call sav_old_int9
call cpy_boot_FromDiskA
mov bx,0
push bx
mov bx,7e00h
push bx
retf
;======================================================
cpy_boot_FromDiskA:
mov bx,0
mov es,bx
mov bx,7e00h
mov al,2 ;读取的扇区数
mov ch,0 ;磁道号
mov cl,2 ;扇区号
mov dl,0 ;驱动器号,0:软驱A
mov dh,0 ;磁头号(对于软盘即面号,因为一个面用一个磁头来读写)
mov ah,2 ;2表示读扇区,3表示写扇区
int 13h
ret
;=======================================================
sav_old_int9:
mov bx,0
mov es,bx
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
ret
db 512 dup(0)
introduce_end:nop
;=======================================================
cpy_introduce_toDiskA:
mov bx,cs
mov es,bx
mov bx,offset introduce
mov al,1 ;读取的扇区数
mov ch,0 ;磁道号
mov cl,1 ;扇区号
mov dl,0 ;磁头号(面号)
mov dh,0 ;软驱A
mov ah,3 ;写扇区 ,软驱A 0道0面1扇区
int 13h
ret
;======================================================
cpy_boot_toDiskA:
mov bx,cs
mov es,bx
mov bx,offset Boot
mov al,2
mov ch,0
mov cl,2
mov dl,0
mov dh,0
mov ah,3
int 13h
ret
;======================================================
Boot: jmp BOOT_START
;======================================================
;======================================================
;======================================================
OPTION_1 db '1) reset pc ',0 ;show_string
OPTION_2 db '2) start system ',0
OPTION_3 db '3) clock',0
OPTION_4 db '4) set clock ',0
ADDRESS_OPTION dw offset OPTION_1-offset Boot + 7e00h ;ADDRESS_OPTION[0]
dw offset OPTION_2-offset Boot + 7e00h
dw offset OPTION_3-offset Boot + 7e00h
dw offset OPTION_4-offset Boot + 7e00h
TIME_CMOS db 9,8,7,4,2,0
TIME_STYLE db 'YY/MM/DD HH:MM:SS',0
STRING_STACK db 12 dup('0'),0
;======================================================
;======================================================
;======================================================
BOOT_START: call init_reg
call clear_screen
call show_option
jmp choose_option
mov ax,4c00h
int 21h
;=====================================================
choose_option:
call clear_buff
mov ah,0
int 16h
cmp al,'1'
je isChooseOne
cmp al,'2'
je isChooseTwo
cmp al,'3'
je isChooseThree
cmp al,'4'
je isChooseFour
jmp choose_option
;====================================================
isChooseOne:
mov di,160*3
mov byte ptr es:[di],'1'
mov bx,0ffffh
push bx
mov bx,0
push bx
retf
jmp choose_option
isChooseTwo:
mov di,160*3
mov byte ptr es:[di],'2'
call start_system
jmp choose_option
isChooseThree:
mov di,160*3
mov byte ptr es:[di],'3'
call show_clock
jmp BOOT_START ;返回主屏幕
isChooseFour:
mov di,160*3
mov byte ptr es:[di],'4'
call set_clock
jmp choose_option
;====================================================
start_system:
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 ;pop ip=7c00h pop cx=0
ret
;====================================================
set_clock: call clear_string_stack
call show_string_stack
call get_string
call set_time
ret
;====================================================
set_time: mov bx,offset TIME_CMOS-offset Boot +7e00H
mov si,offset STRING_STACK-offset Boot+7e00h
mov cx,6
setTime: mov dx,ds:[si] ;ASCII码
sub dh,30h ;ds:[si]=12
sub dl,30h ;dh=2
shl dl,1 ;dl=1
shl dl,1
shl dl,1
shl dl,1
and dh,00001111b ;这步可以不要,不影响
or dl,dh
mov al,ds:[bx]
out 70h,al
mov al,dl
out 71h,al ;把BCD码写入
add si,2
inc bx
loop setTime
ret
;====================================================
;====================================================
get_string: mov si,offset STRING_STACK-offset Boot+7e00h
mov bx,0 ;Top
getString: call clear_buff
mov ah,0
int 16h
cmp al,'0'
jb notNumber
cmp al,'9'
ja notNumber
call char_push
call show_string_stack
jmp getString
getStringRet:ret ;这里大小写错了,找了20分钟
notNumber: cmp ah,0eh
je isBackSpace ;BackSpace
cmp ah,1ch
je getStringRet
jmp getString
isBackSpace: call char_pop
call show_string_stack
jmp getString
;====================================================
char_pop: cmp bx,0
je charPopRet
dec bx
mov byte ptr ds:[si+bx],'0'
charPopRet: ret
;====================================================
char_push: cmp bx,11 ;记录字符个数,一共11个,最后一个当作0
ja charPushRet
mov ds:[si+bx],al
inc bx
charPushRet: ret
;====================================================
show_string_stack:
push si
push di
mov si,offset STRING_STACK-offset Boot+7e00h
mov di,160*4
call show_string
pop di
pop si
ret
;====================================================
clear_string_stack:
push bx
push cx
push es
push si
push di
mov si,offset STRING_STACK-offset Boot +7e00h
mov dx,3030h
mov cx,6 ;6*2=12个字符都是0
clearStringStack:
mov ds:[si],dx
add si,2
loop clearStringStack
pop di
pop si
pop es
pop cx
pop bx
ret
;====================================================
show_clock: call show_style
call set_new_int9
mov bx,offset TIME_CMOS-offset Boot+7e00h
showTime: mov si,bx
mov di,160*20
mov cx,6
showDate: mov al,ds:[si]
out 70h,al
in al,71h
mov ah,al
shr ah,1
shr ah,1
shr ah,1
shr ah,1
and al,00001111b
add ah,30h
add al,30h
mov es:[di],ah
mov es:[di+2],al
add di,6
inc si
loop showDate
jmp showTime
showTimeRet:call set_old_int9
ret
;====================================================
set_old_int9:
push bx
push es
mov bx,0
mov es,bx
cli
push es:[200h]
pop es:[9*4]
push es:[202h]
pop es:[9*4+2]
sti
pop es
pop bx
ret
;====================================================
set_new_int9:
push bx
push es
mov bx,0
mov es,bx
cli
mov word ptr es:[9*4],offset new_int9-offset Boot+7e00h
mov word ptr es:[9*4+2],0
sti
pop es
pop bx
ret
;====================================================
new_int9: push ax
call clear_buff
in al,60h ;把扫描码从60h端口读出
pushf
call dword ptr cs:[200h]
cmp al,01h ;Esc
je isEsc
cmp al,3bh
jne int9Ret
call change_time_color
int9Ret: pop ax
iret
isEsc: pop ax
add sp,4 ;刚开始中断的时候 标志寄存器入栈 if=0,tf=0,CS,IP入栈。所以栈顶要向下移4个字节,才是flag的正确值
popf
jmp showTimeRet
;====================================================
change_time_color:
push bx
push cx
push es
mov bx,0b800h ;这个地方又写成08b00,哎,真傻。
mov es,bx
mov bx,160*20+1
mov cx,17
changeTimeColor:
inc byte ptr es:[bx]
add bx,2
loop changeTimeColor
pop es
pop cx
pop bx
ret
;====================================================
show_style:
mov si,offset TIME_STYLE-offset Boot +7e00h
mov di,160*20
call show_string
ret
;====================================================
clear_buff: mov ah,1 ;用来查询键盘缓冲区,对键盘扫描但不等待
int 16h ;并设置ZF标志。若有按键操作(即键盘缓冲区不空),zf=0,无键按下则zf=1
jz clearBuffRet
mov ah,0
int 16h ;清除键盘缓存区的字符
jmp clear_buff
clearBuffRet:ret
show_option:
mov bx,offset ADDRESS_OPTION - offset Boot + 7e00h
mov di,160*10+30*2
mov cx,4
showOption: mov si,ds:[bx] ;si存放了OPTION_n的偏移地址
call show_string
add bx,2
add di,160
loop showOption
ret
;=====================================================
show_string:push dx
push ds
push es
push si
push di
showString: mov dl,ds:[si]
cmp dl,0
je showStringRet
mov es:[di],dl ;es=0b800h
add di,2
inc si
jmp showString
showStringRet:pop di
pop si
pop es
pop ds
pop dx
ret
;=====================================================
clear_screen:
mov bx,0
mov dx,0700h ;07h(黑底白字), 00表示无字符
mov cx,2000
clearScreen:mov es:[bx],dx
add bx,2
loop clearScreen
ret
;=====================================================
init_reg: mov bx,0b800h
mov es,bx
mov bx,0
mov ds,bx
ret
db 512 dup(0)
Boot_end: nop
;=======================================================
cpy_Boot:
mov ax,cs
mov ds,ax
mov si,offset Boot
mov bx,0
mov es,bx
mov di,7e00h
mov cx,offset Boot_end-offset Boot
cld
rep movsb
ret
code ends
end start
下面是演示视频
这个程序总体可以分成两部分,一部分是引导程序,另一部分就功能得实现。在然后从上到下,慢慢细分。