[2012-08-30 12:43] 保护模式下的课程设计2
花了四天时间,总算把保护模式下的课程设计2给做好了。
共写了两个程序,第一个是主程序,需要事先保存在A盘(FAT12)下,存为LOADER.COM。第二个是引导安装程序,运行后会把引导程序写入A盘引导扇区。
PS:中间有的注释是错的,有时间改过来!
环境:NASM
参考了《自己动手写操作系统》。
;主程序
DA_32 EQU 4000h ; 32 位段
DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节
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 ; ┛
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P EQU 1 ; 页存在属性位
PG_RWR EQU 0 ; R/W 属性位值, 读/执行
PG_RWW EQU 2 ; R/W 属性位值, 读/写/执行
PG_USS EQU 0 ; U/S 属性位值, 系统级
PG_USU EQU 4 ; U/S 属性位值, 用户级
;----------------------------------------------------------------------------
; =========================================
; FLAGS - Intel 8086 Family Flags Register
; =========================================
;
; |11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
; | | | | | | | | | | | | | | | | | '--- CF……Carry Flag
; | | | | | | | | | | | | | | | | '--- 1
; | | | | | | | | | | | | | | | '--- PF……Parity Flag
; | | | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | | | '--- AF……Auxiliary Flag
; | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | '--- ZF……Zero Flag
; | | | | | | | | | | '--- SF……Sign Flag
; | | | | | | | | | '--- TF……Trap Flag (Single Step)
; | | | | | | | | '--- IF……Interrupt Flag
; | | | | | | | '--- DF……Direction Flag
; | | | | | | '--- OF……Overflow flag
; | | | | '----- IOPL……I/O Privilege Level (286+ only)
; | | | '----- NT……Nested Task Flag (286+ only)
; | | '----- 0
; | '----- RF……Resume Flag (386+ only)
; '------ VM……Virtual Mode Flag (386+ only)
;
; 注: see PUSHF POPF STI CLI STD CLD
;
; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; 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 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
org 0100h
jmp LABEL_BEGIN
%macro cls 0
mov al,0
mov ecx,10000h
mov bx,0
%%l:
mov [gs:bx],al
xor al,7
inc bx
loop %%l
%endmacro
%macro settime 2
mov ah,[%1]
mov cl,4
shl ah,cl
add ah,[%1+1]
mov al,%2
out 70h,al
mov al,ah
out 71h,al
%endmacro
%macro skipmark 0
cmp si,2
jz %%skip
cmp si,4
jz %%skip
cmp si,6
jz %%skip
cmp si,8
jz %%skip
cmp si,10
jz %%skip
jmp %%ctn
%%skip:
add edi,2
%%ctn:
%endmacro
%macro skipmark_d 0
cmp si,1
jz %%skip
cmp si,3
jz %%skip
cmp si,5
jz %%skip
cmp si,7
jz %%skip
cmp si,9
jz %%skip
jmp %%ctn
%%skip:
sub edi,2
%%ctn:
%endmacro
%macro slt 2
mov bx, 0b800h
mov ds,bx
mov byte [(80 * 15 + %2) * 2],%1
%endmacro
[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_CODE16_2: 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
SelectorCode16_2 equ LABEL_DESC_CODE16_2 - 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
; 字符串
m1: db "1)Reset PC", 0
m2: db "2)Start system",0
m3: db "3)Clock",0
m4: db "4)Set clock",0
o1 equ m1 - $$
o2 equ m2 - $$
o3 equ m3 - $$
o4 equ m4 - $$
tm: db "Enter new time(yy/mm/dd hh:mm:ss): "
ot equ tm - $$
stl equ $ - tm
tmc: db " / / : :",0
td: times 12 db 0
od equ td - $$
menu db 0
om equ menu-$$
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 [LABEL_GO_BACK_TO_REAL_2+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
mov ax, cs
movzx eax, ax
shl eax, 4
add eax, LABEL_SEG_CODE16_2
mov word [LABEL_DESC_CODE16_2 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16_2 + 4], al
mov byte [LABEL_DESC_CODE16_2 + 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 ; 开中断
db 0eah,0,0,0ffh,0ffh
LABEL_REAL_ENTRY_2:
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 al,1
mov ch,0
mov cl,1
mov dh,0
mov dl,80h
mov bx,0
mov es,bx
mov bx,7c00h
mov ah,2
int 13h
mov ax,[es:7c00h+510d]
cmp ax,0aa55h
jnz error
db 0eah,0,7ch,0,0
error:
slt 'S',1
slt 'y',2
slt 's',3
slt 't',4
slt 'e',5
slt 'm',6
slt ' ',7
slt 'e',8
slt 'r',9
slt 'r',10
slt 'o',11
slt 'r',12
slt '.',13
jmp $
; 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
ls:
mov ah,7
mov al,0
mov [gs:(80 * 10 + 18)*2],ax
mov [gs:(80 * 11 + 18)*2],ax
mov [gs:(80 * 12 + 18)*2],ax
mov [gs:(80 * 13 + 18)*2],ax
mov bl,[om]
mov bh,0
mov dx,bx
mov ecx,159
timesl:
add bx,dx
loop timesl
mov ah,7
mov al,'>'
mov [gs:bx+(80 * 10 + 18) * 2],ax
; 下面显示一个字符串
mov ah, 7
xor esi, esi
xor edi, edi
mov esi, o1 ; 源数据偏移
mov edi, (80 * 10 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l1:
lodsb
test al, al
jz l2
mov [gs:edi], ax
add edi, 2
jmp l1
l2:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o2 ; 源数据偏移
mov edi, (80 * 11 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l3:
lodsb
test al, al
jz l4
mov [gs:edi], ax
add edi, 2
jmp l3
l4:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o3 ; 源数据偏移
mov edi, (80 * 12 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l5:
lodsb
test al, al
jz l6
mov [gs:edi], ax
add edi, 2
jmp l5
l6:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o4 ; 源数据偏移
mov edi, (80 * 13 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l7:
lodsb
test al, al
jz l8
mov [gs:edi], ax
add edi, 2
jmp l7
l8:
in al,64h
test al,1
jz l8
in al,60h
cmp al,48h
jz up
cmp al,50h
jz down
cmp al,1ch
jz enter
cmp al,02
jnz l9
sub1: jmp SelectorCode16:0
l9:
cmp al,03
jnz l10
sub2: jmp SelectorCode16_2:0
l10:
cmp al,04
jnz l11;g1
sub3: cls
clock:
mov al, 0
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 69) * 2)], bl
mov [gs:((80 * 0 + 70) * 2)], al
; jmp ctn6
;l8g3: jmp l8
;ctn6:
; jmp ct3
;cg3: jmp clock
;ct3:
mov al, 2
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
; jmp ctn1
;l11g1: jmp l11g2
;ctn1:
add bl,30h
add al,30h
mov [gs:((80 * 0 + 66) * 2)], bl
mov [gs:((80 * 0 + 67) * 2)], al
mov al, 4
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
; jmp ct2
;cg2: jmp cg3
;ct2:
; jmp ctn5
;l8g2: jmp l8g3
;ctn5:
add bl,30h
add al,30h
mov [gs:((80 * 0 + 63) * 2)], bl
mov [gs:((80 * 0 + 64) * 2)], al
mov al,":"
mov [gs:((80 * 0 + 65) * 2)], al
mov [gs:((80 * 0 + 68) * 2)], al
mov al,"/"
mov [gs:((80 * 0 + 56) * 2)], al
mov [gs:((80 * 0 + 59) * 2)], al
; jmp ctn2
;l11g2: jmp l11g3
;ctn2:
mov al, 7
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 60) * 2)], bl
mov [gs:((80 * 0 + 61) * 2)], al
; jmp ct1
;cg1: jmp cg2
;ct1:
mov al, 8
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
; jmp ctn4
;l8g1: jmp l8g2
;ctn4:
mov [gs:((80 * 0 + 57) * 2)], bl
mov [gs:((80 * 0 + 58) * 2)], al
; jmp ctn3
;l11g3: jmp l11
;ctn3:
mov al, 9
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 54) * 2)], bl
mov [gs:((80 * 0 + 55) * 2)], al
in al,60h
cmp al,01
jz continue
cmp al,3bh
jnz clock
mov ecx,8000h
mov bx,1
mov al,1
loo:
add [gs:bx],al
add bx,2
loop loo
jmp clock;g1
continue:
cls
jmp ls
l11: cmp al,5
jnz l13
sub4: cls
mov ah, 7
xor esi, esi
xor edi, edi
mov esi, ot ; 源数据偏移
mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l:
lodsb
test al, al
jz l12
mov [gs:edi], ax
add edi, 2
jmp l
l12: mov edi, (80 * 10 + stl) * 2
mov ah,11111111b
mov [gs:edi+1],ah
mov si,0
setclk:
wait_: in al,64h
test al,1
jz wait_
in al,60h
cmp al,01
jz l13_c
cmp al,4bh
jz left
cmp al,4dh
jz right
cmp al,0eh
jz del
cmp al,53h
jz clear
cmp al,1ch
jz set
cmp al,2
jb setclk
cmp si,11
ja setclk
cmp al,0bh
ja setclk
jz zero
sub al,1
mov [od+si],al
add al,48
mov [gs:edi],al
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
inc si
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
zero:
mov al,0
mov [od+si],al
add al,48
mov [gs:edi],al
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
inc si
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
set: settime od,9
settime od+2,8
settime od+4,7
settime od+6,4
settime od+8,2
settime od+10,0
l13_c:
cls
l13:
jmp ls
del:
cmp si,0
jz setclk
mov ah,0
dec si
mov al,[gs:edi+1]
and al,00000111b
mov [gs:edi+1],al
mov [od+si],ah
sub edi,2
skipmark_d
mov [gs:edi],ah
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
left:
cmp si,0
jz setclk
dec si
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
sub edi,2
skipmark_d
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
right:
cmp si,11
ja setclk
inc si
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
clear:
cmp si,11
ja setclk
mov ah,0
mov [od+si],ah
mov [gs:edi],ah
jmp setclk
up:
mov al,[om]
dec al
cmp al,11111111b
jz ovfl
upret:
mov [om],al
jmp ls
down:
mov al,[om]
inc al
and al,11b
mov [om],al
jmp ls
ovfl:
mov al,3
jmp upret
enter:
mov al,[om]
cmp al,0
jz sub1
cmp al,1
jz sub2
cmp al,2
jz sub3
cmp al,3
jz sub4
jmp ls
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]
[SECTION .s16code_2]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16_2:
; 跳回实模式:
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_2:
jmp 0:LABEL_REAL_ENTRY_2 ; 段地址会在程序开始处被设置成正确的值
Code16Len_2 equ $ - LABEL_SEG_CODE16_2
; END of [SECTION .s16code]
;引导程序
org 100h
;================================================================================================
%ifdef _BOOT_DEBUG_
BaseOfStack equ 0100h ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack equ 07c00h ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endif
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors equ 14 ; 根目录占用空间
SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号
SectorNoOfFAT1 equ 1 ; FAT1 的第一个扇区号 = BPB_RsvdSecCnt
DeltaSectorNo equ 17 ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo
;================================================================================================
mov al,1
mov ch,0
mov cl,1
mov dx,0
mov bx,cs
mov es,bx
mov bx,main
mov ah,3
int 13h
mov ax,4c00h
int 21h
times 07c00h-($-$$)-100h db 0
main:
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName DB 'ShineFan' ; OEM String, 必须 8 个字节
BPB_BytsPerSec DW 512 ; 每扇区字节数
BPB_SecPerClus DB 1 ; 每簇多少扇区
BPB_RsvdSecCnt DW 1 ; Boot 记录占用多少扇区
BPB_NumFATs DB 2 ; 共有多少 FAT 表
BPB_RootEntCnt DW 224 ; 根目录文件数最大值
BPB_TotSec16 DW 2880 ; 逻辑扇区总数
BPB_Media DB 0xF0 ; 媒体描述符
BPB_FATSz16 DW 9 ; 每FAT扇区数
BPB_SecPerTrk DW 18 ; 每磁道扇区数
BPB_NumHeads DW 2 ; 磁头数(面数)
BPB_HiddSec DD 0 ; 隐藏扇区数
BPB_TotSec32 DD 0 ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
BS_DrvNum DB 0 ; 中断 13 的驱动器号
BS_Reserved1 DB 0 ; 未使用
BS_BootSig DB 29h ; 扩展引导标记 (29h)
BS_VolID DD 0 ; 卷序列号
BS_VolLab DB 'MyOS 0.01 '; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
; 清屏
mov ax, 0600h ; AH = 6, AL = 0h
mov bx, 0700h ; 黑底白字(BL = 07h)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0184fh ; 右下角: (80, 50)
int 10h ; int 10h
mov dh, 0 ; "Booting "
call DispStr ; 显示字符串
xor ah, ah ; ┓
xor dl, dl ; ┣ 软驱复位
int 13h ; ┛
; 下面在 A 盘的根目录寻找 LOADER.BIN
mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], 0 ; ┓
jz LABEL_NO_LOADERBIN ; ┣ 判断根目录区是不是已经读完
dec word [wRootDirSizeForLoop] ; ┛ 如果读完表示没有找到 LOADER.BIN
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1
call ReadSector
mov si, LoaderFileName ; ds:si -> "LOADER BIN"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
cld
mov dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx, 0 ; ┓循环次数控制,
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣如果已经读完了一个 Sector,
dec dx ; ┛就跳到下一个 Sector
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME ; 继续循环
LABEL_DIFFERENT:
and di, 0FFE0h ; else ┓ di &= E0 为了让它指向本条目开头
add di, 20h ; ┃
mov si, LoaderFileName ; ┣ di += 20h 下一个目录条目
jmp LABEL_SEARCH_FOR_LOADERBIN; ┛
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo], 1
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
LABEL_NO_LOADERBIN:
mov dh, 2 ; "No LOADER."
call DispStr ; 显示字符串
%ifdef _BOOT_DEBUG_
mov ax, 4c00h ; ┓
int 21h ; ┛没有找到 LOADER.BIN, 回到 DOS
%else
jmp $ ; 没有找到 LOADER.BIN, 死循环在这里
%endif
LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便来到这里继续
mov ax, RootDirSectors
and di, 0FFE0h ; di -> 当前条目的开始
add di, 01Ah ; di -> 首 Sector
mov cx, word [es:di]
push cx ; 保存此 Sector 在 FAT 中的序号
add cx, ax
add cx, DeltaSectorNo ; 这句完成时 cl 里面变成 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
mov ax, cx ; ax <- Sector 号
LABEL_GOON_LOADING_FILE:
push ax ; ┓
push bx ; ┃
mov ah, 0Eh ; ┃ 每读一个扇区就在 "Booting " 后面打一个点, 形成这样的效果:
mov al, '.' ; ┃
mov bl, 0Fh ; ┃ Booting ......
int 10h ; ┃
pop bx ; ┃
pop ax ; ┛
mov cl, 1
call ReadSector
pop ax ; 取出此 Sector 在 FAT 中的序号
call GetFATEntry
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
push ax ; 保存 Sector 在 FAT 中的序号
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
mov dh, 1 ; "Ready."
call DispStr ; 显示字符串
mov bh,0
mov dx,0
mov ah,2
int 10h
; *****************************************************************************************************
jmp BaseOfLoader:OffsetOfLoader ; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
; 开始执行 LOADER.BIN 的代码
; Boot Sector 的使命到此结束
; *****************************************************************************************************
;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName db "LOADER COM", 0 ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength equ 17
BootMessage: db "Loading system "; 9字节, 不够则用空格补齐. 序号 0
Message1 db "Done. "; 9字节, 不够则用空格补齐. 序号 1
Message2 db "System not found."; 9字节, 不够则用空格补齐. 序号 2
;============================================================================
;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
; 显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:
mov ax, MessageLength
mul dh
add ax, BootMessage
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串地址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
int 10h ; int 10h
ret
;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:
; -----------------------------------------------------------------------
; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
; -----------------------------------------------------------------------
; 设扇区号为 x
; ┌ 柱面号 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁头号 = y & 1
; 每磁道扇区数 │
; └ 余 z => 起始扇区号 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]
mov byte [bp-2], cl
push bx ; 保存 bx
mov bl, [BPB_SecPerTrk] ; bl: 除数
div bl ; y 在 al 中, z 在 ah 中
inc ah ; z ++
mov cl, ah ; cl <- 起始扇区号
mov dh, al ; dh <- y
shr al, 1 ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
mov ch, al ; ch <- 柱面号
and dh, 1 ; dh & 1 = 磁头号
pop bx ; 恢复 bx
; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
mov ah, 2 ; 读
mov al, byte [bp-2] ; 读 al 个扇区
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
add esp, 2
pop bp
ret
;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
; 找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
; 需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfLoader ; ┓
sub ax, 0100h ; ┣ 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
mov es, ax ; ┛
pop ax
mov byte [bOdd], 0
mov bx, 3
mul bx ; dx:ax = ax * 3
mov bx, 2
div bx ; dx:ax / 2 ==> ax <- 商, dx <- 余数
cmp dx, 0
jz LABEL_EVEN
mov byte [bOdd], 1
LABEL_EVEN:;偶数
xor dx, dx ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
mov bx, [BPB_BytsPerSec]
div bx ; dx:ax / BPB_BytsPerSec ==> ax <- 商 (FATEntry 所在的扇区相对于 FAT 来说的扇区号)
; dx <- 余数 (FATEntry 在扇区内的偏移)。
push dx
mov bx, 0 ; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00 = (BaseOfLoader - 100) * 10h
add ax, SectorNoOfFAT1 ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
mov cl, 2
call ReadSector ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [bOdd], 1
jnz LABEL_EVEN_2
shr ax, 4
LABEL_EVEN_2:
and ax, 0FFFh
LABEL_GET_FAT_ENRY_OK:
pop bx
pop es
ret
共写了两个程序,第一个是主程序,需要事先保存在A盘(FAT12)下,存为LOADER.COM。第二个是引导安装程序,运行后会把引导程序写入A盘引导扇区。
PS:中间有的注释是错的,有时间改过来!
环境:NASM
参考了《自己动手写操作系统》。
;主程序
DA_32 EQU 4000h ; 32 位段
DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节
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 ; ┛
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P EQU 1 ; 页存在属性位
PG_RWR EQU 0 ; R/W 属性位值, 读/执行
PG_RWW EQU 2 ; R/W 属性位值, 读/写/执行
PG_USS EQU 0 ; U/S 属性位值, 系统级
PG_USU EQU 4 ; U/S 属性位值, 用户级
;----------------------------------------------------------------------------
; =========================================
; FLAGS - Intel 8086 Family Flags Register
; =========================================
;
; |11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
; | | | | | | | | | | | | | | | | | '--- CF……Carry Flag
; | | | | | | | | | | | | | | | | '--- 1
; | | | | | | | | | | | | | | | '--- PF……Parity Flag
; | | | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | | | '--- AF……Auxiliary Flag
; | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | '--- ZF……Zero Flag
; | | | | | | | | | | '--- SF……Sign Flag
; | | | | | | | | | '--- TF……Trap Flag (Single Step)
; | | | | | | | | '--- IF……Interrupt Flag
; | | | | | | | '--- DF……Direction Flag
; | | | | | | '--- OF……Overflow flag
; | | | | '----- IOPL……I/O Privilege Level (286+ only)
; | | | '----- NT……Nested Task Flag (286+ only)
; | | '----- 0
; | '----- RF……Resume Flag (386+ only)
; '------ VM……Virtual Mode Flag (386+ only)
;
; 注: see PUSHF POPF STI CLI STD CLD
;
; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; 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 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
org 0100h
jmp LABEL_BEGIN
%macro cls 0
mov al,0
mov ecx,10000h
mov bx,0
%%l:
mov [gs:bx],al
xor al,7
inc bx
loop %%l
%endmacro
%macro settime 2
mov ah,[%1]
mov cl,4
shl ah,cl
add ah,[%1+1]
mov al,%2
out 70h,al
mov al,ah
out 71h,al
%endmacro
%macro skipmark 0
cmp si,2
jz %%skip
cmp si,4
jz %%skip
cmp si,6
jz %%skip
cmp si,8
jz %%skip
cmp si,10
jz %%skip
jmp %%ctn
%%skip:
add edi,2
%%ctn:
%endmacro
%macro skipmark_d 0
cmp si,1
jz %%skip
cmp si,3
jz %%skip
cmp si,5
jz %%skip
cmp si,7
jz %%skip
cmp si,9
jz %%skip
jmp %%ctn
%%skip:
sub edi,2
%%ctn:
%endmacro
%macro slt 2
mov bx, 0b800h
mov ds,bx
mov byte [(80 * 15 + %2) * 2],%1
%endmacro
[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_CODE16_2: 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
SelectorCode16_2 equ LABEL_DESC_CODE16_2 - 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
; 字符串
m1: db "1)Reset PC", 0
m2: db "2)Start system",0
m3: db "3)Clock",0
m4: db "4)Set clock",0
o1 equ m1 - $$
o2 equ m2 - $$
o3 equ m3 - $$
o4 equ m4 - $$
tm: db "Enter new time(yy/mm/dd hh:mm:ss): "
ot equ tm - $$
stl equ $ - tm
tmc: db " / / : :",0
td: times 12 db 0
od equ td - $$
menu db 0
om equ menu-$$
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 [LABEL_GO_BACK_TO_REAL_2+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
mov ax, cs
movzx eax, ax
shl eax, 4
add eax, LABEL_SEG_CODE16_2
mov word [LABEL_DESC_CODE16_2 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16_2 + 4], al
mov byte [LABEL_DESC_CODE16_2 + 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 ; 开中断
db 0eah,0,0,0ffh,0ffh
LABEL_REAL_ENTRY_2:
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 al,1
mov ch,0
mov cl,1
mov dh,0
mov dl,80h
mov bx,0
mov es,bx
mov bx,7c00h
mov ah,2
int 13h
mov ax,[es:7c00h+510d]
cmp ax,0aa55h
jnz error
db 0eah,0,7ch,0,0
error:
slt 'S',1
slt 'y',2
slt 's',3
slt 't',4
slt 'e',5
slt 'm',6
slt ' ',7
slt 'e',8
slt 'r',9
slt 'r',10
slt 'o',11
slt 'r',12
slt '.',13
jmp $
; 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
ls:
mov ah,7
mov al,0
mov [gs:(80 * 10 + 18)*2],ax
mov [gs:(80 * 11 + 18)*2],ax
mov [gs:(80 * 12 + 18)*2],ax
mov [gs:(80 * 13 + 18)*2],ax
mov bl,[om]
mov bh,0
mov dx,bx
mov ecx,159
timesl:
add bx,dx
loop timesl
mov ah,7
mov al,'>'
mov [gs:bx+(80 * 10 + 18) * 2],ax
; 下面显示一个字符串
mov ah, 7
xor esi, esi
xor edi, edi
mov esi, o1 ; 源数据偏移
mov edi, (80 * 10 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l1:
lodsb
test al, al
jz l2
mov [gs:edi], ax
add edi, 2
jmp l1
l2:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o2 ; 源数据偏移
mov edi, (80 * 11 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l3:
lodsb
test al, al
jz l4
mov [gs:edi], ax
add edi, 2
jmp l3
l4:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o3 ; 源数据偏移
mov edi, (80 * 12 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l5:
lodsb
test al, al
jz l6
mov [gs:edi], ax
add edi, 2
jmp l5
l6:
mov ah, 7 ; 0000: 黑底 1100: 红字
xor esi, esi
xor edi, edi
mov esi, o4 ; 源数据偏移
mov edi, (80 * 13 + 20) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l7:
lodsb
test al, al
jz l8
mov [gs:edi], ax
add edi, 2
jmp l7
l8:
in al,64h
test al,1
jz l8
in al,60h
cmp al,48h
jz up
cmp al,50h
jz down
cmp al,1ch
jz enter
cmp al,02
jnz l9
sub1: jmp SelectorCode16:0
l9:
cmp al,03
jnz l10
sub2: jmp SelectorCode16_2:0
l10:
cmp al,04
jnz l11;g1
sub3: cls
clock:
mov al, 0
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 69) * 2)], bl
mov [gs:((80 * 0 + 70) * 2)], al
; jmp ctn6
;l8g3: jmp l8
;ctn6:
; jmp ct3
;cg3: jmp clock
;ct3:
mov al, 2
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
; jmp ctn1
;l11g1: jmp l11g2
;ctn1:
add bl,30h
add al,30h
mov [gs:((80 * 0 + 66) * 2)], bl
mov [gs:((80 * 0 + 67) * 2)], al
mov al, 4
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
; jmp ct2
;cg2: jmp cg3
;ct2:
; jmp ctn5
;l8g2: jmp l8g3
;ctn5:
add bl,30h
add al,30h
mov [gs:((80 * 0 + 63) * 2)], bl
mov [gs:((80 * 0 + 64) * 2)], al
mov al,":"
mov [gs:((80 * 0 + 65) * 2)], al
mov [gs:((80 * 0 + 68) * 2)], al
mov al,"/"
mov [gs:((80 * 0 + 56) * 2)], al
mov [gs:((80 * 0 + 59) * 2)], al
; jmp ctn2
;l11g2: jmp l11g3
;ctn2:
mov al, 7
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 60) * 2)], bl
mov [gs:((80 * 0 + 61) * 2)], al
; jmp ct1
;cg1: jmp cg2
;ct1:
mov al, 8
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
; jmp ctn4
;l8g1: jmp l8g2
;ctn4:
mov [gs:((80 * 0 + 57) * 2)], bl
mov [gs:((80 * 0 + 58) * 2)], al
; jmp ctn3
;l11g3: jmp l11
;ctn3:
mov al, 9
out 70h,al
in al,71h
mov bl,al
shr bl,4
and al,1111b
add bl,30h
add al,30h
mov [gs:((80 * 0 + 54) * 2)], bl
mov [gs:((80 * 0 + 55) * 2)], al
in al,60h
cmp al,01
jz continue
cmp al,3bh
jnz clock
mov ecx,8000h
mov bx,1
mov al,1
loo:
add [gs:bx],al
add bx,2
loop loo
jmp clock;g1
continue:
cls
jmp ls
l11: cmp al,5
jnz l13
sub4: cls
mov ah, 7
xor esi, esi
xor edi, edi
mov esi, ot ; 源数据偏移
mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
l:
lodsb
test al, al
jz l12
mov [gs:edi], ax
add edi, 2
jmp l
l12: mov edi, (80 * 10 + stl) * 2
mov ah,11111111b
mov [gs:edi+1],ah
mov si,0
setclk:
wait_: in al,64h
test al,1
jz wait_
in al,60h
cmp al,01
jz l13_c
cmp al,4bh
jz left
cmp al,4dh
jz right
cmp al,0eh
jz del
cmp al,53h
jz clear
cmp al,1ch
jz set
cmp al,2
jb setclk
cmp si,11
ja setclk
cmp al,0bh
ja setclk
jz zero
sub al,1
mov [od+si],al
add al,48
mov [gs:edi],al
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
inc si
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
zero:
mov al,0
mov [od+si],al
add al,48
mov [gs:edi],al
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
inc si
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
set: settime od,9
settime od+2,8
settime od+4,7
settime od+6,4
settime od+8,2
settime od+10,0
l13_c:
cls
l13:
jmp ls
del:
cmp si,0
jz setclk
mov ah,0
dec si
mov al,[gs:edi+1]
and al,00000111b
mov [gs:edi+1],al
mov [od+si],ah
sub edi,2
skipmark_d
mov [gs:edi],ah
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
left:
cmp si,0
jz setclk
dec si
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
sub edi,2
skipmark_d
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
right:
cmp si,11
ja setclk
inc si
mov ah,[gs:edi+1]
and ah,00000111b
mov [gs:edi+1],ah
add edi,2
skipmark
mov ah,[gs:edi+1]
or ah,11111000b
mov [gs:edi+1],ah
jmp setclk
clear:
cmp si,11
ja setclk
mov ah,0
mov [od+si],ah
mov [gs:edi],ah
jmp setclk
up:
mov al,[om]
dec al
cmp al,11111111b
jz ovfl
upret:
mov [om],al
jmp ls
down:
mov al,[om]
inc al
and al,11b
mov [om],al
jmp ls
ovfl:
mov al,3
jmp upret
enter:
mov al,[om]
cmp al,0
jz sub1
cmp al,1
jz sub2
cmp al,2
jz sub3
cmp al,3
jz sub4
jmp ls
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]
[SECTION .s16code_2]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16_2:
; 跳回实模式:
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_2:
jmp 0:LABEL_REAL_ENTRY_2 ; 段地址会在程序开始处被设置成正确的值
Code16Len_2 equ $ - LABEL_SEG_CODE16_2
; END of [SECTION .s16code]
;引导程序
org 100h
;================================================================================================
%ifdef _BOOT_DEBUG_
BaseOfStack equ 0100h ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack equ 07c00h ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endif
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors equ 14 ; 根目录占用空间
SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号
SectorNoOfFAT1 equ 1 ; FAT1 的第一个扇区号 = BPB_RsvdSecCnt
DeltaSectorNo equ 17 ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo
;================================================================================================
mov al,1
mov ch,0
mov cl,1
mov dx,0
mov bx,cs
mov es,bx
mov bx,main
mov ah,3
int 13h
mov ax,4c00h
int 21h
times 07c00h-($-$$)-100h db 0
main:
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName DB 'ShineFan' ; OEM String, 必须 8 个字节
BPB_BytsPerSec DW 512 ; 每扇区字节数
BPB_SecPerClus DB 1 ; 每簇多少扇区
BPB_RsvdSecCnt DW 1 ; Boot 记录占用多少扇区
BPB_NumFATs DB 2 ; 共有多少 FAT 表
BPB_RootEntCnt DW 224 ; 根目录文件数最大值
BPB_TotSec16 DW 2880 ; 逻辑扇区总数
BPB_Media DB 0xF0 ; 媒体描述符
BPB_FATSz16 DW 9 ; 每FAT扇区数
BPB_SecPerTrk DW 18 ; 每磁道扇区数
BPB_NumHeads DW 2 ; 磁头数(面数)
BPB_HiddSec DD 0 ; 隐藏扇区数
BPB_TotSec32 DD 0 ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
BS_DrvNum DB 0 ; 中断 13 的驱动器号
BS_Reserved1 DB 0 ; 未使用
BS_BootSig DB 29h ; 扩展引导标记 (29h)
BS_VolID DD 0 ; 卷序列号
BS_VolLab DB 'MyOS 0.01 '; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
; 清屏
mov ax, 0600h ; AH = 6, AL = 0h
mov bx, 0700h ; 黑底白字(BL = 07h)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0184fh ; 右下角: (80, 50)
int 10h ; int 10h
mov dh, 0 ; "Booting "
call DispStr ; 显示字符串
xor ah, ah ; ┓
xor dl, dl ; ┣ 软驱复位
int 13h ; ┛
; 下面在 A 盘的根目录寻找 LOADER.BIN
mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], 0 ; ┓
jz LABEL_NO_LOADERBIN ; ┣ 判断根目录区是不是已经读完
dec word [wRootDirSizeForLoop] ; ┛ 如果读完表示没有找到 LOADER.BIN
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1
call ReadSector
mov si, LoaderFileName ; ds:si -> "LOADER BIN"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
cld
mov dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx, 0 ; ┓循环次数控制,
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣如果已经读完了一个 Sector,
dec dx ; ┛就跳到下一个 Sector
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME ; 继续循环
LABEL_DIFFERENT:
and di, 0FFE0h ; else ┓ di &= E0 为了让它指向本条目开头
add di, 20h ; ┃
mov si, LoaderFileName ; ┣ di += 20h 下一个目录条目
jmp LABEL_SEARCH_FOR_LOADERBIN; ┛
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo], 1
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
LABEL_NO_LOADERBIN:
mov dh, 2 ; "No LOADER."
call DispStr ; 显示字符串
%ifdef _BOOT_DEBUG_
mov ax, 4c00h ; ┓
int 21h ; ┛没有找到 LOADER.BIN, 回到 DOS
%else
jmp $ ; 没有找到 LOADER.BIN, 死循环在这里
%endif
LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便来到这里继续
mov ax, RootDirSectors
and di, 0FFE0h ; di -> 当前条目的开始
add di, 01Ah ; di -> 首 Sector
mov cx, word [es:di]
push cx ; 保存此 Sector 在 FAT 中的序号
add cx, ax
add cx, DeltaSectorNo ; 这句完成时 cl 里面变成 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
mov ax, cx ; ax <- Sector 号
LABEL_GOON_LOADING_FILE:
push ax ; ┓
push bx ; ┃
mov ah, 0Eh ; ┃ 每读一个扇区就在 "Booting " 后面打一个点, 形成这样的效果:
mov al, '.' ; ┃
mov bl, 0Fh ; ┃ Booting ......
int 10h ; ┃
pop bx ; ┃
pop ax ; ┛
mov cl, 1
call ReadSector
pop ax ; 取出此 Sector 在 FAT 中的序号
call GetFATEntry
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
push ax ; 保存 Sector 在 FAT 中的序号
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
mov dh, 1 ; "Ready."
call DispStr ; 显示字符串
mov bh,0
mov dx,0
mov ah,2
int 10h
; *****************************************************************************************************
jmp BaseOfLoader:OffsetOfLoader ; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
; 开始执行 LOADER.BIN 的代码
; Boot Sector 的使命到此结束
; *****************************************************************************************************
;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName db "LOADER COM", 0 ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength equ 17
BootMessage: db "Loading system "; 9字节, 不够则用空格补齐. 序号 0
Message1 db "Done. "; 9字节, 不够则用空格补齐. 序号 1
Message2 db "System not found."; 9字节, 不够则用空格补齐. 序号 2
;============================================================================
;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
; 显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:
mov ax, MessageLength
mul dh
add ax, BootMessage
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串地址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
int 10h ; int 10h
ret
;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:
; -----------------------------------------------------------------------
; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
; -----------------------------------------------------------------------
; 设扇区号为 x
; ┌ 柱面号 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁头号 = y & 1
; 每磁道扇区数 │
; └ 余 z => 起始扇区号 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]
mov byte [bp-2], cl
push bx ; 保存 bx
mov bl, [BPB_SecPerTrk] ; bl: 除数
div bl ; y 在 al 中, z 在 ah 中
inc ah ; z ++
mov cl, ah ; cl <- 起始扇区号
mov dh, al ; dh <- y
shr al, 1 ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
mov ch, al ; ch <- 柱面号
and dh, 1 ; dh & 1 = 磁头号
pop bx ; 恢复 bx
; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
mov ah, 2 ; 读
mov al, byte [bp-2] ; 读 al 个扇区
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
add esp, 2
pop bp
ret
;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
; 找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
; 需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfLoader ; ┓
sub ax, 0100h ; ┣ 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
mov es, ax ; ┛
pop ax
mov byte [bOdd], 0
mov bx, 3
mul bx ; dx:ax = ax * 3
mov bx, 2
div bx ; dx:ax / 2 ==> ax <- 商, dx <- 余数
cmp dx, 0
jz LABEL_EVEN
mov byte [bOdd], 1
LABEL_EVEN:;偶数
xor dx, dx ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
mov bx, [BPB_BytsPerSec]
div bx ; dx:ax / BPB_BytsPerSec ==> ax <- 商 (FATEntry 所在的扇区相对于 FAT 来说的扇区号)
; dx <- 余数 (FATEntry 在扇区内的偏移)。
push dx
mov bx, 0 ; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00 = (BaseOfLoader - 100) * 10h
add ax, SectorNoOfFAT1 ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
mov cl, 2
call ReadSector ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [bOdd], 1
jnz LABEL_EVEN_2
shr ax, 4
LABEL_EVEN_2:
and ax, 0FFFh
LABEL_GET_FAT_ENRY_OK:
pop bx
pop es
ret
评论次数(1) |
浏览次数(663) |
类型(汇编作业) |
收藏此文 |