若程式越來越大,並超過512個位元組,則難以在開機磁區執行。
解決方法至少有兩種:
解決方法至少有兩種:
- 寫一個開機磁區,可讀取程式並執行它,就好像這程式是個作業系統核心似的
- 借助別的東西,例如 DOS。我們可將程式編譯為 COM 檔,讓 DOS 執行它。
這裡採用第二種,以達到目的。
預先準備
- 下載 FreeDos,改名為 freedos.img
- 用 bximage 產成一個軟碟映像檔(floopy image),命名為 pm.img
- 若第一次使用 pm.img,可利用 freedos.img 開機後,格式化 pm.img (ex, format b:)
程式碼
pm.inc
; 描述符圖示 ; 圖示一 ; ; ------ ┏━━┳━━┓高地址 ; ┃ 7 ┃ 段 ┃ ; ┣━━┫ ┃ ; 基 ; 字節 7 ┆ ┆ ┆ ; 址 ; ┣━━┫ ② ┃ ; ┃ 0 ┃ ┃ ; ------ ┣━━╋━━┫ ; ┃ 7 ┃ G ┃ ; ┣━━╉──┨ ; ┃ 6 ┃ D ┃ ; ┣━━╉──┨ ; ┃ 5 ┃ 0 ┃ ; ┣━━╉──┨ ; ┃ 4 ┃ AVL┃ ; 字節 6 ┣━━╉──┨ ; ┃ 3 ┃ ┃ ; ┣━━┫ 段 ┃ ; ┃ 2 ┃ 界 ┃ ; ┣━━┫ 限 ┃ ; ┃ 1 ┃ ┃ ; ┣━━┫ ② ┃ ; ┃ 0 ┃ ┃ ; ------ ┣━━╋━━┫ ; ┃ 7 ┃ P ┃ ; ┣━━╉──┨ ; ┃ 6 ┃ ┃ ; ┣━━┫ DPL┃ ; ┃ 5 ┃ ┃ ; ┣━━╉──┨ ; ┃ 4 ┃ S ┃ ; 字節 5 ┣━━╉──┨ ; ┃ 3 ┃ ┃ ; ┣━━┫ T ┃ ; ┃ 2 ┃ Y ┃ ; ┣━━┫ P ┃ ; ┃ 1 ┃ E ┃ ; ┣━━┫ ┃ ; ┃ 0 ┃ ┃ ; ------ ┣━━╋━━┫ ; ┃ 23 ┃ ┃ ; ┣━━┫ ┃ ; ┃ 22 ┃ ┃ ; ┣━━┫ 段 ┃ ; ; 字節 ┆ ┆ 基 ┆ ; 2, 3, 4 ; ┣━━┫ 址 ┃ ; ┃ 1 ┃ ① ┃ ; ┣━━┫ ┃ ; ┃ 0 ┃ ┃ ; ------ ┣━━╋━━┫ ; ┃ 15 ┃ ┃ ; ┣━━┫ ┃ ; ┃ 14 ┃ ┃ ; ┣━━┫ 段 ┃ ; ; 字節 0,1┆ ┆ 界 ┆ ; ; ┣━━┫ 限 ┃ ; ┃ 1 ┃ ① ┃ ; ┣━━┫ ┃ ; ┃ 0 ┃ ┃ ; ------ ┗━━┻━━┛低地址 ; ; 圖示二 ; 高地址………………………………………………………………………低地址 ; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ; |7654321076543210765432107654321076543210765432107654321076543210| <- 共 8 字節 ; |--------========--------========--------========--------========| ; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓ ; ┃31..24┃ (見下圖) ┃ 段基址(23..0) ┃ 段界限(15..0)┃ ; ┃ ┃ ┃ ┃ ┃ ; ┃ 基址2┃③│②│ ①┃基址1b│ 基址1a ┃ 段界限1 ┃ ; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫ ; ┃ %6 ┃ %5 ┃ %4 ┃ %3 ┃ %2 ┃ %1 ┃ ; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛ ; │ \_________ ; │ \__________________ ; │ \________________________________________________ ; │ \ ; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓ ; ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ ; ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫ ; ┃ G ┃D/B ┃ 0 ┃ AVL┃ 段界限 2 (19..16) ┃ P ┃ DPL ┃ S ┃ TYPE ┃ ; ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫ ; ┃ ③: 屬性 2 ┃ ②: 段界限 2 ┃ ①: 屬性1 ┃ ; ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛ ; 高地址 低地址 ; ; ; 說明: ; ; (1) P: 存在(Present)位。 ; P=1 表示描述符對地址轉換是有效的,或者說該描述符所描述的段存在,即在內存中; ; P=0 表示描述符對地址轉換無效,即該段不存在。使用該描述符進行內存訪問時會引起異常。 ; ; (2) DPL: 表示描述符特權級(Descriptor Privilege level),共2位。它規定了所描述段的特權級,用於特權檢查,以決定對該段能否訪問。 ; ; (3) S: 說明描述符的類型。 ; 對於存儲段描述符而言,S=1,以區別與系統段描述符和門描述符(S=0)。 ; ; (4) TYPE: 說明存儲段描述符所描述的存儲段的具體屬性。 ; ; ; 數據段類型 類型值 說明 ; ---------------------------------- ; 0 只讀 ; 1 只讀、已訪問 ; 2 讀/寫 ; 3 讀/寫、已訪問 ; 4 只讀、向下擴展 ; 5 只讀、向下擴展、已訪問 ; 6 讀/寫、向下擴展 ; 7 讀/寫、向下擴展、已訪問 ; ; ; 類型值 說明 ; 代碼段類型 ---------------------------------- ; 8 只執行 ; 9 只執行、已訪問 ; A 執行/讀 ; B 執行/讀、已訪問 ; C 只執行、一致碼段 ; D 只執行、一致碼段、已訪問 ; E 執行/讀、一致碼段 ; F 執行/讀、一致碼段、已訪問 ; ; ; 系統段類型 類型編碼 說明 ; ---------------------------------- ; 0 <未定義> ; 1 可用286TSS ; 2 LDT ; 3 忙的286TSS ; 4 286調用門 ; 5 任務門 ; 6 286中斷門 ; 7 286陷阱門 ; 8 未定義 ; 9 可用386TSS ; A <未定義> ; B 忙的386TSS ; C 386調用門 ; D <未定義> ; E 386中斷門 ; F 386陷阱門 ; ; (5) G: 段界限粒度(Granularity)位。 ; G=0 表示界限粒度為字節; ; G=1 表示界限粒度為4K 字節。 ; 注意,界限粒度只對段界限有效,對段基地址無效,段基地址總是以字節為單位。 ; ; (6) D/B: 此位是一個很特殊的位,在描述可執行段、向下擴展數據段或由SS寄存器尋址的段(通常是堆棧段)的三種描述符中的意義各不相同。 ; ⑴ 在描述可執行段的描述符中,D位決定了指令使用的地址及操作數所默認的大小。 ; ① D=1表示默認情況下指令使用32位地址及32位或8位操作數,這樣的代碼段也稱為32位代碼段; ; ② D=0 表示默認情況下,使用16位地址及16位或8位操作數,這樣的代碼段也稱為16位代碼段,它與80286兼容。可以使用地址大小前綴和操作數大小前綴分別改變默認的地址或操作數的大小。 ; ⑵ 在向下擴展數據段的描述符中,D位決定段的上部邊界。 ; ① D=1表示段的上部界限為4G; ; ② D=0表示段的上部界限為64K,這是為了與80286兼容。 ; ⑶ 在描述由SS寄存器尋址的段描述符中,D位決定隱式的堆棧訪問指令(如PUSH和POP指令)使用何種堆棧指針寄存器。 ; ① D=1表示使用32位堆棧指針寄存器ESP; ; ② D=0表示使用16位堆棧指針寄存器SP,這與80286兼容。 ; ; (7) AVL: 軟件可利用位。80386對該位的使用未左規定,Intel公司也保證今後開發生產的處理器只要與80386兼容,就不會對該位的使用做任何定義或規定。 ; ;---------------------------------------------------------------------------- ; 描述符類型值說明 ; 其中: ; DA_ : Descriptor Attribute ; D : 數據段 ; C : 代碼段 ; S : 系統段 ; R : 只讀 ; RW : 讀寫 ; A : 已訪問 ; 其它 : 可按照字面意思理解 ;---------------------------------------------------------------------------- DA_32 EQU 4000h ; 32 位段 DA_DPL0 EQU 00h ; DPL = 0 DA_DPL1 EQU 20h ; DPL = 1 DA_DPL2 EQU 40h ; DPL = 2 DA_DPL3 EQU 60h ; DPL = 3 ;---------------------------------------------------------------------------- ; 存儲段描述符類型值說明 ;---------------------------------------------------------------------------- DA_DR EQU 90h ; 存在的只讀數據段類型值 DA_DRW EQU 92h ; 存在的可讀寫數據段屬性值 DA_DRWA EQU 93h ; 存在的已訪問可讀寫數據段類型值 DA_C EQU 98h ; 存在的只執行代碼段屬性值 DA_CR EQU 9Ah ; 存在的可執行可讀代碼段屬性值 DA_CCO EQU 9Ch ; 存在的只執行一致代碼段屬性值 DA_CCOR EQU 9Eh ; 存在的可執行可讀一致代碼段屬性值 ;---------------------------------------------------------------------------- ; 系統段描述符類型值說明 ;---------------------------------------------------------------------------- DA_LDT EQU 82h ; 局部描述符表段類型值 DA_TaskGate EQU 85h ; 任務門類型值 DA_386TSS EQU 89h ; 可用 386 任務狀態段類型值 DA_386CGate EQU 8Ch ; 386 調用門類型值 DA_386IGate EQU 8Eh ; 386 中斷門類型值 DA_386TGate EQU 8Fh ; 386 陷阱門類型值 ;---------------------------------------------------------------------------- ; 選擇子圖示: ; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓ ; ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ ; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫ ; ┃ 描述符索引 ┃ TI ┃ RPL ┃ ; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛ ; ; RPL(Requested Privilege Level): 請求特權級,用於特權檢查。 ; ; TI(Table Indicator): 引用描述符表指示位 ; TI=0 指示從全局描述符表GDT中讀取描述符; ; TI=1 指示從局部描述符表LDT中讀取描述符。 ; ;---------------------------------------------------------------------------- ; 選擇子類型值說明 ; 其中: ; SA_ : Selector Attribute SA_RPL0 EQU 0 ; ┓ SA_RPL1 EQU 1 ; ┣ RPL SA_RPL2 EQU 2 ; ┃ SA_RPL3 EQU 3 ; ┛ SA_TIG EQU 0 ; ┓TI SA_TIL EQU 4 ; ┛ ;---------------------------------------------------------------------------- ; 宏 ------------------------------------------------------------------------------------------------------ ; ; 描述符 ; usage: Descriptor Base, Limit, Attr ; Base: dd ; Limit: dd (low 20 bits available) ; Attr: dw (lower 4 bits of higher byte are always 0) %macro Descriptor 3 dw %2 & 0FFFFh ; 段界限 1 (2 字節) dw %1 & 0FFFFh ; 段基址 1 (2 字節) db (%1 >> 16) & 0FFh ; 段基址 2 (1 字節) dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 屬性 1 + 段界限 2 + 屬性 2 (2 字節) db (%1 >> 24) & 0FFh ; 段基址 3 (1 字節) %endmacro ; 共 8 字節 ; ; 門 ; usage: Gate Selector, Offset, DCount, Attr ; Selector: dw ; Offset: dd ; DCount: db ; Attr: db %macro Gate 4 dw (%2 & 0FFFFh) ; 偏移 1 (2 字節) dw %1 ; 選擇子 (2 字節) dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 屬性 (2 字節) dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字節) %endmacro ; 共 8 字節 ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
pmtest2.inc
; ========================================== ; pmtest2.asm ; 編譯方法:nasm pmtest2.asm -o pmtest2.com ; ========================================== %include "pm.inc" ; 常量, 巨集, 以及一些說明 org 0100h jmp LABEL_BEGIN [SECTION .gdt] ; GDT ; 段基址, 段界限 , 屬性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32; 非一致程式碼段, 32 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致程式碼段, 16 LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32; Stack, 32 位 LABEL_DESC_TEST: Descriptor 0500000h, 0ffffh, DA_DRW LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 顯示卡記憶體首位址 ; GDT 結束 GdtLen equ $ - LABEL_GDT ; GDT長度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基位址 ; GDT 選擇子 SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT SelectorData equ LABEL_DESC_DATA - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorTest equ LABEL_DESC_TEST - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; END of [SECTION .gdt] [SECTION .data1] ; 資料段 ALIGN 32 [BITS 32] LABEL_DATA: SPValueInRealMode dw 0 ; 字元串 PMMessage: db "In Protect Mode now. ^-^", 0 ; 在保護模式中顯示 OffsetPMMessage equ PMMessage - $$ StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 OffsetStrTest equ StrTest - $$ DataLen equ $ - LABEL_DATA ; END of [SECTION .data1] ; 全局堆疊段 [SECTION .gs] ALIGN 32 [BITS 32] LABEL_STACK: times 512 db 0 TopOfStack equ $ - LABEL_STACK - 1 ; END of [SECTION .gs] [SECTION .s16] [BITS 16] LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov [LABEL_GO_BACK_TO_REAL+3], ax mov [SPValueInRealMode], sp ; 初始化 16 位程式碼段描述符 mov ax, cs movzx eax, ax shl eax, 4 add eax, LABEL_SEG_CODE16 mov word [LABEL_DESC_CODE16 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE16 + 4], al mov byte [LABEL_DESC_CODE16 + 7], ah ; 初始化 32 位程式碼段描述符 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 mov word [LABEL_DESC_CODE32 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE32 + 4], al mov byte [LABEL_DESC_CODE32 + 7], ah ; 初始化資料段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word [LABEL_DESC_DATA + 2], ax shr eax, 16 mov byte [LABEL_DESC_DATA + 4], al mov byte [LABEL_DESC_DATA + 7], ah ; 初始化堆疊段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_STACK mov word [LABEL_DESC_STACK + 2], ax shr eax, 16 mov byte [LABEL_DESC_STACK + 4], al mov byte [LABEL_DESC_STACK + 7], ah ; 為加載 GDTR 作準備 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT ; eax <- gdt 基位址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基位址 ; 加載 GDTR lgdt [GdtPtr] ; 關中斷 cli ; 打開位址線A20 in al, 92h or al, 00000010b out 92h, al ; 準備切換到保護模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 真正進入保護模式 jmp dword SelectorCode32:0 ; 執行這一句會把 SelectorCode32 裝入 cs, 並跳轉到 Code32Selector:0 處 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LABEL_REAL_ENTRY: ; 從保護模式跳回到實模式就到了這裡 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, [SPValueInRealMode] in al, 92h ; `. and al, 11111101b ; | 關閉 A20 位址線 out 92h, al ; / sti ; 開中斷 mov ax, 4c00h ; `. int 21h ; / 回到 DOS ; END of [SECTION .s16] [SECTION .s32]; 32 位程式碼段. 由實模式跳入. [BITS 32] LABEL_SEG_CODE32: mov ax, SelectorData mov ds, ax ; 資料段選擇子 mov ax, SelectorTest mov es, ax ; 測試段選擇子 mov ax, SelectorVideo mov gs, ax ; 視頻段選擇子 mov ax, SelectorStack mov ss, ax ; 堆疊段選擇子 mov esp, TopOfStack ; 下面顯示一個字元串 mov ah, 0Ch ; 0000: 黑底 1100: 紅字 xor esi, esi xor edi, edi mov esi, OffsetPMMessage ; 源資料偏移 mov edi, (80 * 10 + 0) * 2 ; 目的資料偏移。螢幕第 10 行, 第 0 列。 cld .1: lodsb test al, al jz .2 mov [gs:edi], ax add edi, 2 jmp .1 .2: ; 顯示完畢 call DispReturn call TestRead call TestWrite call TestRead ; 到此停止 jmp SelectorCode16:0 ; ------------------------------------------------------------------------ TestRead: xor esi, esi mov ecx, 8 .loop: mov al, [es:esi] call DispAL inc esi loop .loop call DispReturn ret ; TestRead 結束----------------------------------------------------------- ; ------------------------------------------------------------------------ TestWrite: push esi push edi xor esi, esi xor edi, edi mov esi, OffsetStrTest ; 源資料偏移 cld .1: lodsb test al, al jz .2 mov [es:edi], al inc edi jmp .1 .2: pop edi pop esi ret ; TestWrite 結束---------------------------------------------------------- ; ------------------------------------------------------------------------ ; 顯示 AL 中的數字 ; 默認地: ; 數字已經存在 AL 中 ; edi 始終指向要顯示的下一個字元的位置 ; 被改變的暫存器: ; ax, edi ; ------------------------------------------------------------------------ DispAL: push ecx push edx mov ah, 0Ch ; 0000: 黑底 1100: 紅字 mov dl, al shr al, 4 mov ecx, 2 .begin: and al, 01111b cmp al, 9 ja .1 add al, '0' jmp .2 .1: sub al, 0Ah add al, 'A' .2: mov [gs:edi], ax add edi, 2 mov al, dl loop .begin add edi, 2 pop edx pop ecx ret ; DispAL 結束------------------------------------------------------------- ; ------------------------------------------------------------------------ DispReturn: push eax push ebx mov eax, edi mov bl, 160 div bl and eax, 0FFh inc eax mov bl, 160 mul bl mov edi, eax pop ebx pop eax ret ; DispReturn 結束--------------------------------------------------------- SegCode32Len equ $ - LABEL_SEG_CODE32 ; END of [SECTION .s32] ; 16 位程式碼段. 由 32 位程式碼段跳入, 跳出後到實模式 [SECTION .s16code] ALIGN 32 [BITS 16] LABEL_SEG_CODE16: ; 跳回實模式: mov ax, SelectorNormal mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov eax, cr0 and al, 11111110b mov cr0, eax LABEL_GO_BACK_TO_REAL: jmp 0:LABEL_REAL_ENTRY ; 段位址會在程序開始處被設置成正確的值 Code16Len equ $ - LABEL_SEG_CODE16 ; END of [SECTION .s16code]
留言
張貼留言