2020301918-os/loader.asm
2022-09-21 22:15:05 +08:00

395 lines
14 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

org 0400h
jmp MAIN
;----------------------------------------------------------------------------
; @param bp+8: selector
; @param bp+6: string addr
; @param bp+4: length
;----------------------------------------------------------------------------
; Display string to current cursor
DispStr:
push bp
mov bp, sp
pusha
push es
mov ax, 0300h
mov bh, 0
int 10h
mov ax, [bp + 6]
mov cx, [bp + 4]
mov es, [bp + 8]
mov bp, ax
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
int 10h
pop es
popa
pop bp
ret
;----------------------------------------------------------------------------
; @param bp + 4: the word to print as uint_16t
;----------------------------------------------------------------------------
; print a word as decimal to current cursor
DispInt:
push bp
mov bp, sp
pusha
mov di, 0
mov ax, [bp + 4]
.loop1:
mov dx, 0
mov bx, 10
div bx ; {dx, ax} / bx(=10) -> ax ... dx
push dx
inc di
test ax, ax
jnz .loop1
.loop2:
pop dx
mov ax, dx
mov ah, 0Eh
add al, '0'
mov bl, 0Fh
int 10H
dec di
test di, di
jnz .loop2
popa
pop bp
ret
;----------------------------------------------------------------------------
; no param
;----------------------------------------------------------------------------
; give you a new line
DispNewline:
push bp
mov bp, sp
pusha
mov ax, 0300h
mov bh, 0
int 10h
add dh, 1
mov dl, 0
mov ax, 0200h
int 10h
popa
pop bp
ret
;----------------------------------------------------------------------------
; @param bp + 4: low byte holds the ascii to print
;----------------------------------------------------------------------------
; putchar to current cursor pos
DispPutchar:
push bp
mov bp, sp
pusha
; get cursor pos
mov ax, 0300h
mov bh, 0
int 10h
; putchar
mov ax, [bp + 4]
mov ah, 0Eh ; no need to set al, ax holds the param
mov bl, 0Fh
int 10H
popa
pop bp
ret
;----------------------------------------------------------------------------
; 函数名: StringCmp
;----------------------------------------------------------------------------
; 作用:
; 比较 ds:si 和 es:di 处的字符串比较长度为11仅为loader.bin所用
; 如果两个字符串相等ax返回1否则ax返回0
StringCmp:
push bp
mov bp, sp
pusha
mov cx, 11 ; 比较长度为11
cld ; 清位保险一下
.STARTCMP:
lodsb ; ds:si -> al
cmp al, byte [es:di]
jnz .DIFFERENT
inc di
dec cx
cmp cx, 0
jz .SAME
jmp .STARTCMP
.DIFFERENT:
mov word [bp - 2], 0 ; 这里用了一个技巧这样在popa的时候ax也顺便更新了
jmp .ENDCMP
.SAME:
mov word [bp - 2], 1 ; 下一步就是ENDCMP了就懒得jump了
.ENDCMP:
popa
pop bp
ret
;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 将磁盘的数据读入到内存中
; ax: 从哪个扇区开始
; cx: 读入多少个扇区
; (es:bx): 读入的缓冲区的起始地址
;
; 中断调用传入的参数规范请参考本节实验指导书的实验参考LBA部分
ReadSector:
push bp
mov bp, sp
pusha
mov si, BufferPacket ; ds:si 指向的是BufferPacket的首地址
mov word [si + 0], 010h ; buffer_packet_size
mov word [si + 2], cx ; sectors
mov word [si + 4], bx ; buffer-offset
mov word [si + 6], es ; buffer-segment
mov word [si + 8], ax ; start_sectors
mov dl, BS_DrvNum ; 驱动号
mov ah, 42h ; 扩展读
int 13h
jc .ReadFail ; 读取失败简单考虑就默认bios坏了
popa
pop bp
ret
.ReadFail:
jmp $ ; 如果cf位置1就意味着读入错误这个时候建议直接开摆
;------------------------------------------------------------------------------
; 函数名: GetNextCluster
; data => BaseOfSectorBuf : 0
;------------------------------------------------------------------------------
; 作用:
; ax存放的是当前的簇(cluster)号根据当前的簇号在fat表里查找找到下一个簇的簇号并将返回值存放在ax
GetNextCluster:
push bp
mov bp, sp
pusha
mov bx, 3 ; 一个FAT项长度为1.5字节
mul bx
mov bx, 2 ; ax = floor(clus_number * 1.5)
div bx ; 这个时候ax里面放着的是FAT项基地址相对于FAT表开头的字节偏移量
; 如果clus_number为奇数则dx为1否则为0
push dx ; 临时保存奇数标识信息
mov dx, 0 ; 下面除法要用到
mov bx, BPB_BytsPerSec
div bx ; dx:ax / BPB_BytsPerSec
; ax <- 商 (基地址在FAT表的第几个扇区)
; dx <- 余数 (基地址在扇区内的偏移)
mov bx, 0 ; bx <- 0 于是, es:bx = BaseOfSectorBuf:0
add ax, SectorNoOfFAT1 ; 此句之后的 ax 就是FAT项所在的扇区号
mov cx, 2 ; 读取FAT项所在的扇区, 一次读两个, 避免在边界
call ReadSector ; 发生错误, 因为一个FAT项可能跨越两个扇区
mov bx, dx ; 将偏移量搬回bx
mov ax, [es:bx]
pop bx ; 取回奇数标识信息
cmp bx, 0 ; 如果是第奇数个FAT项还得右移四位
jz .EvenCluster ; 可能是微软(FAT是微软创建的)第一个亲儿子的原因,有它的历史局限性
shr ax, 4 ; 当时的磁盘很脆弱经常容易写坏所以需要两张FAT表备份而且人们能够制作的存储设备的容量很小
.EvenCluster:
and ax, 0FFFh ; 读完需要与一下因为高位是未定义的防止ax值有误
mov word [bp - 2], ax ; 这里用了一个技巧这样在popa的时候ax也顺便更新了
popa
pop bp
ret
;------------------------------------------------------------------------------
; @param bp + 4: Filename string addr
; @retval ax: first cluster no
;------------------------------------------------------------------------------
; Find File by name, return first cluster no.
; data => BaseOfSectorBuf:OffsetOfSectorBuf
FuncFindFile:
push bp
mov bp, sp
pusha
mov word [RootDirSectorNow], SectorNoOfRootDirectory
mov word [LeftRootDirSectors], RootDirSectors
.FindLoaderInRootDir:
mov ax, [RootDirSectorNow] ; ax <- 现在正在搜索的扇区号
mov bx, OffsetOfSectorBuf ; es:bx = BaseOfSectorBuf:OffsetOfSectorBuf
mov cx, 1
call ReadSector
mov si, [bp + 4] ; ds:si
mov di, OffsetOfSectorBuf ; es:di -> BaseOfSectorBuf:400h = BaseOfSectorBuf*10h+400h
mov dx, 10h ; 32(目录项大小) * 16(dx) = 512(BPB_BytsPerSec)
.CompareFilename:
call StringCmp
cmp ax, 1
jz .LoaderFound ; ax == 1 -> 比对成了
dec dx
cmp dx, 0
jz .GotoNextRootDirSector ; 该扇区的所有目录项都探索完了,去探索下一个扇区
add di, 20h ; 32 -> 目录项大小
jmp .CompareFilename
.GotoNextRootDirSector:
inc word [RootDirSectorNow] ; 改变正在搜索的扇区号
dec word [LeftRootDirSectors] ; ┓
cmp word [LeftRootDirSectors], 0 ; ┣ 判断根目录区是不是已经读完
jz .NoLoader ; ┛ 如果读完表示没有找到 LOADER.BIN就直接开摆
jmp .FindLoaderInRootDir
.NoLoader:
call DispNewline
mov ax, cs
push ax
mov ax, NotFoundString
push ax
mov ax, _endNotFoundString - NotFoundString
push ax
call DispStr
add sp, 6
jmp $
.LoaderFound: ; 找到 LOADER.BIN 后便来到这里继续
add di, 01Ah ; 0x1a = 26 这个 26 在目录项里偏移量对应的数据是起始簇号RTFM
mov dx, word [es:di] ; 起始簇号占2字节读入到dx里
mov word [bp - 2], dx ; set ax([bp-2]) as retval, popa will set that
popa
pop bp
ret
;------------------------------------------------------------------------------
; @param bp + 4: first cluster number
;------------------------------------------------------------------------------
; Find File by name, return first cluster no.
; data => BaseOfSectorBuf:OffsetOfSectorBuf
FuncPrintClusterNumbers:
push bp
mov bp, sp
pusha
mov ax, [bp + 4]
.loop:
mov ax, dx
push ax
call DispInt ; ax now holds the current cluster number
add sp, 2
push ' '
call DispPutchar
add sp, 2
call GetNextCluster ; 根据数据区簇号获取文件下一个簇的簇号
mov dx, ax ; dx <- 下一个簇的簇号
cmp dx, 0FFFh ; 判断是否读完了根据文档理论上dx只要在0xFF8~0xFFF都行但是这里直接偷懒只判断0xFFF
jnz .loop
popa
pop bp
ret
MAIN:
mov ax, cs ; cs <- 0
mov ds, ax ; ds <- 0
mov ss, ax ; ss <- 0
mov ax, BaseOfSectorBuf
mov es, ax ; es <- BaseOfSectorBuf
; Display "This is {name}'s boot"
call DispNewline
push cs
push StudentString
push _endStudentString - StudentString
call DispStr
add sp, 6 ; pop for 3 times
; Read loader.bin's cluster number
push LoaderFileName
call FuncFindFile
add sp, 2
mov dx, ax ; temporarily save read result to dx
; Print first cluster number
call DispNewline
push cs
push clusternoString
push _endclusternoString - clusternoString
call DispStr
add sp, 6 ; pop for 3 times
push dx ; dx holds the read result
call FuncPrintClusterNumbers
add sp, 2
; Read aA1.txt's cluster number
push AA1FileName
call FuncFindFile
add sp, 2
mov dx, ax ; temporarily save read result to dx
; Print first cluster number
call DispNewline
push cs
push clusternoString
push _endclusternoString - clusternoString
call DispStr
add sp, 6
push dx ; dx holds the read result
call FuncPrintClusterNumbers
add sp, 2
jmp $
;==============================================================================
; CONSTANTS
;==============================================================================
BaseOfStack equ 07c00h ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
; according to osdev, memory from 0x0000:0x7e00 to 0x7000:0xFFFF is free to use
BaseOfSectorBuf equ 07000h ; diff from where we are here
OffsetOfSectorBuf equ 0400h ; the first 400h(=1024d) bytes left for fat1 read buf
RootDirSectors equ 14 ; 19~32
SectorNoOfRootDirectory equ 19 ; Root Sector starts from 19
SectorNoOfFAT1 equ 1
DeltaSectorNo equ 31
BPB_BytsPerSec equ 512 ; 每扇区字节数
BPB_SecPerClus equ 1 ; 每簇多少扇区
BPB_RsvdSecCnt equ 1 ; Boot 记录占用多少扇区
BPB_NumFATs equ 2 ; 共有多少 FAT 表
BPB_RootEntCnt equ 224 ; 根目录文件数最大值
BPB_TotSec16 equ 2880 ; 逻辑扇区总数
BPB_Media equ 0xF0 ; 媒体描述符
BPB_FATSz16 equ 9 ; 每FAT扇区数
BPB_SecPerTrk equ 18 ; 每磁道扇区数
BPB_NumHeads equ 2 ; 磁头数(面数)
BPB_HiddSec equ 0 ; 隐藏扇区数
BPB_TotSec32 equ 0 ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
BS_DrvNum equ 80h ; 中断 13 的驱动器号
BS_Reserved1 equ 0 ; 未使用
BS_BootSig equ 29h ; 扩展引导标记 (29h)
BS_VolID equ 0 ; 卷序列号
;==============================================================================
; dw means initialized DATA
;==============================================================================
LeftRootDirSectors dw RootDirSectors ; 还未搜索的根目录扇区数
RootDirSectorNow dw SectorNoOfRootDirectory ; 目前正在搜索的根目录扇区
BufferPacket times 010h db 0 ; ReadSector函数会用到的用于向int 13h中断的一个缓冲区
LoaderFileName db "LOADER BIN", 0 ; 8:3, fill with whitespace
AA1FileName db "AA1 TXT", 0 ; the 0 at end is for StrCmp
StudentString db "This is LiMaoliang's boot" ; size = 25
_endStudentString
clusternoString db "cluster no:"
_endclusternoString
NotFoundString db "not found"
_endNotFoundString