《匯編語言》課程設計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,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

 

總結

  匯編的難度並不大,我認為在有編程的基礎上,學習匯編要做到細致,細致的理解計算機編程的編譯過程,對於我理解其他編程語言也有很大的幫助。歡迎大家關注,一起交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM