diff --git a/.gitignore b/.gitignore index 7cf5001..341a0d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.bin *.img +aA1.txt \ No newline at end of file diff --git a/Makefile b/Makefile index 2081f39..9fcb3a7 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,10 @@ everything : $(BOOT_BIN) $(LDR_BIN) @dd if=/dev/zero of=a.img bs=512 count=2880 @mkfs -t vfat a.img @dd if=$(BOOT_BIN) of=a.img bs=512 count=1 conv=notrunc + @dd if=/dev/zero of=aA1.txt bs=4096 count=1 @sudo mount -o loop a.img /mnt @sudo cp $(LDR_BIN) /mnt -v + @sudo cp aA1.txt /mnt -v @sudo umount /mnt clean : diff --git a/loader.asm b/loader.asm index 49cd5d6..d1c34a7 100644 --- a/loader.asm +++ b/loader.asm @@ -1,9 +1,345 @@ -org 0400h +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 - mov ax, 0B800h - mov gs, ax - mov ah, 0Fh ; 0000: 黑底 1111: 白字 - mov al, 'L' - mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列 + pop es + popa + pop bp + ret +;---------------------------------------------------------------------------- +; @param ax: the int to print +;---------------------------------------------------------------------------- +; print a word as decimal to current cursor - jmp $ ; 到此停住 +DispInt: + push bp + mov bp, sp + pusha + mov di, 0 +.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 +;---------------------------------------------------------------------------- +; 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 + +;---------------------------------------------------------------------------- +; 函数名: 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 +;------------------------------------------------------------------------------ +; 作用: +; 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. + +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 -> "LOADER BIN" + 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 + +MAIN: + mov ax, cs ; cs <- 0 + mov ds, ax ; ds <- 0 + mov ss, ax ; ss <- 0 + mov ax, BaseOfSectorBuf + mov es, ax ; es <- BaseOfSectorBuf + + call DispNewline + mov ax, cs + push ax + mov ax, StudentString + push ax + mov ax, _endStudentString - StudentString + push ax + call DispStr + add sp, 6 ; pop for 3 times + + mov ax, LoaderFileName + push ax + call FuncFindFile + mov dx, ax ; temporarily save read result to dx + pop ax + + call DispNewline + mov ax, cs + push ax + mov ax, clusternoString + push ax + mov ax, _endclusternoString - clusternoString + push ax + call DispStr + add sp, 6 ; pop for 3 times + mov ax, dx ; dx holds the read result + call DispInt + + mov ax, AA1FileName + push ax + call FuncFindFile + mov dx, ax ; temporarily save read result to dx + pop ax + + call DispNewline + mov ax, cs + push ax + mov ax, clusternoString + push ax + mov ax, _endclusternoString - clusternoString + push ax + call DispStr + add sp, 6 ; pop for 3 times + mov ax, dx ; dx holds the read result + call DispInt + 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 0000h ; diff from where we are here +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 "LOADE 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 \ No newline at end of file