-
Notifications
You must be signed in to change notification settings - Fork 2
/
p109.asm
221 lines (191 loc) · 8.97 KB
/
p109.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
%define _BOOT_DEBUG_ ; 做 Boot Sector 时一定将此行注释掉!将此行打开后可用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试
%ifdef _BOOT_DEBUG_
org 0100h ; 调试状态, 做成 .COM 文件, 可调试
%else
org 07c00h ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif
;================================================================================================
%ifdef _BOOT_DEBUG_
BaseOfStack equ 0100h ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长, 压栈是从高地址 向 低地址 增长)
%else
BaseOfStack equ 07c00h ; 堆栈基地址(栈底, 从这个位置向低地址生长)
%endif
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors equ 14 ; 根目录占用空间
SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号
;================================================================================================
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName DB 'ForrestY' ; 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 'OrangeS0.02'; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
xor ah, ah ; `.
xor dl, dl ; | 软驱复位
int 13h ; /
; 下面在 A 盘的根目录寻找 LOADER.BIN
; wSectorNo dw 0 == 要读取的扇区号
; 这里将要读取的扇区号赋值为19,且es:bx=9000h:0100 ,则是把扇区号为19的内容读到es:bx中;而且es:bx 按地址计算的话,应该在数据区去了;
mov word [wSectorNo], SectorNoOfRootDirectory ; SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号
LABEL_SEARCH_IN_ROOT_DIR_BEGIN: ; es:bx 始终都没有改变
; wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的扇区数, RootDirSectors equ 14
; BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
; OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
; RootDirSectors equ 14 ; 根目录占用空间
; SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号
cmp word [wRootDirSizeForLoop], 0 ; `. 判断根目录区是不是已经读完, 根目录区的条目是14个,
jz LABEL_NO_LOADERBIN ; / 如果读完表示没有找到 LOADER.BIN, 否则 wRootDirSizeForLoop - 1
dec word [wRootDirSizeForLoop] ; /
; 以下用于初始化 int13h 的寄存器值
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader
; 构建缓冲区外部 es:bx over
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1 ; 每次读取的扇区数目 cl = 1 ;
call ReadSector ; ReadSector 从 dh-磁头号,ch-磁道号,cl-起始扇区号定位的扇区为起点,读取al个扇区到es:bx中,es:bx=9000h:0100
; 字符串
; LoaderFileName db "LOADER BIN", 0 ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
; MessageLength equ 9
; BootMessage: db "Booting " ; 9字节, 不够则用空格补齐. 序号 0
; Message1 db "Ready. " ; 9字节, 不够则用空格补齐. 序号 1
; Message2 db "No LOADER" ; 9字节, 不够则用空格补齐. 序号 2
mov si, LoaderFileName ; ds:si -> "LOADER BIN"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100
cld ; cld 清空,df=0,内存地址向高地址增长;
mov dx, 10h ; dx = 16 ,总循环次数
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx, 0 ; `. 循环次数控制,
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; / 如果已经读完了一个 Sector, 就跳到下一个 Sector
dec dx ; 否则 dx--
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到 ,filename = LOADER BIN;
dec cx
lodsb ; ds:si -> al , lodsb把DS:SI指向的存储单元中的数据装入AL或AX,然后根据DF标志增减SI
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 下一个目录条目 20h=0010 0000
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 后便来到这里继续
jmp $ ; 代码暂时停在这里
;============================================================================
;变量
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的扇区数,
; 在循环中会递减至零
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
;字符串
LoaderFileName db "LOADER BIN", 0 ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength equ 9
BootMessage: db "Booting " ; 9字节, 不够则用空格补齐. 序号 0
Message1 db "Ready. " ; 9字节, 不够则用空格补齐. 序号 1
Message2 db "No LOADER" ; 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 中, ax初始值=19
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 ; 在调用者中, cl 被赋值为1, mov cl, 1
push bx ; 保存 bx
mov bl, [BPB_SecPerTrk] ; bl: 除数 , BPB_SecPerTrk DW 18 ; 每磁道扇区数
div ; y 在 al 中, z 在 ah 中 , div 被除数A 默认存放在AX中(16位以内) 或 AX 和 DX中(32位,DX存放高16位,AX存放低16位)
inc ah ; z ++
mov cl, ah ; cl <- 起始扇区号
mov dh, al ; dh <- y
shr al, 1 ; y >> 1 (y/BPB_NumHeads)
mov ch, al ; ch <- 柱面号
and dh, 1 ; dh & 1 = 磁头号
pop bx ; 恢复 bx
; 至此, "柱面号, 起始扇区, 磁头号" 全部得到
mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘) ,BS_DrvNum DB 0; 中断 13 的驱动器号
.GoOnReading:
mov ah, 2 ; 读
mov al, byte [bp-2] ; 读 al 个扇区
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1,
; 这时就不停地读, 直到正确为止
add esp, 2
pop bp
ret
times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 0xaa55 ; 结束标志