; 主引导程序 ; ---------------------------------------------------------------- %include "boot.inc" SECTION MBR vstart=0x7c00 mov ax, cs ; 0x00000000 mov ds, ax mov es, ax mov ss, ax mov fs, ax mov sp, 0x7c00 mov ax, 0xb800 ;文本模式显示 mov gs, ax call clean_display ; call get_cursor_position call disp_string mov eax, LOADER_START_SECTOR ; 起始扇区地址: LBA 地址 mov bx, LOADER_BASE_ADDR ; 写入的地址 mov cx, 4 ; 待写入的扇区数(4 个扇区) call rd_disk_m_16 ; 读取硬盘数据 jmp LOADER_BASE_ADDR+0x300 ; 直接跳转到 loader 的 loader_start 位置 ;清屏,利用 0x06 号功能,上巻全部行,则可清屏 ; ---------------------------------------------------------------- ; INT 0X10 功能号:0x06 功能描述:上巻窗口 ; AL=上卷行数 ; AL=0 整个窗口空白 ; BH=卷入行属性 ; CH=左上角行号 (CH、CL)=窗口的左上角位置(Y坐标,X坐标) ; CL=左上角列号 ; DH=右下角行号 (DH、DL)=窗口的右下角位置(Y坐标,X坐标) ; DL=右下角列号 ; ---------------------------------------------------------------- clean_display: mov ah, 0x06 ; 功能号 mov al, 0x00 ; 上巻的行数(如果为 0,表示全部) mov bx, 0x700 mov cx, 0 ; 左上角:(0,0) mov dx, 0x184f ; 右下角: (80, 25) ; VGA 文本模式中,一行只能容纳 80 个字符,共 25 行. ;下标、从 0 开始,所以 Ox18=24, 0x4f=79 int 10h ; 调用 INT 0x10 功能 ret ;;;;;;;;;;;; 获取光标位置 ;;;;;;;;;;;;; ; .get_cursor_position, 在光标位置打印字符 get_cursor_position: mov ah, 0x3 ; 3 号功能是获取光标位置,需要 mov bh, 0 ; 待获取光标的页号 int 0x10 ret ;;;;;;;;;;;; 获取光标位置结束 ;;;;;;;;; ; 直接操作显卡显示 ;-------------------------------------------------------- ; 输出背景色绿色,前景色红色, 并且跳动的字符串 "1 MBR" ;-------------------------------------------------------- disp_string: mov byte [gs:0x00], '1' mov byte [gs:0x01], 0xA4 ; A(K:1 R:0 G:1 B:0) 绿色背景闪烁, 4(K:0 R:1 G:0 B:0)前景红色 mov byte [gs:0x02], ' ' mov byte [gs:0x03], 0xA4 mov byte [gs:0x04], 'M' mov byte [gs:0x05], 0xA4 mov byte [gs:0x06], 'B' mov byte [gs:0x07], 0xA4 mov byte [gs:0x08], 'R' mov byte [gs:0x09], 0xA4 mov byte [gs:0x0A], '.' mov byte [gs:0x0B], 0xA4 mov byte [gs:0x0C], '.' mov byte [gs:0x0D], 0xA4 mov byte [gs:0x0E], '.' mov byte [gs:0x0F], 0xA4 ret ;---------------------------------------------------------------- ; 读取硬盘数据 ; EAX=LBA 地址(扇区号) ; BX=将数据写入的内存地址 ; CX=读取的扇区数 rd_disk_m_16: ;---------------------------------------------------------------- mov esi, eax ; 备份 eax mov di, cx ; 备份 cx ; 第 1 步:设置要读取的扇区数 mov dx, 0x1f2 ; Primary 通道 Sector count mov al, cl out dx, al ; 读取的扇区数 mov eax, esi ; 恢复 eax ; 第 2 步:设置 LBA 地址,存入 0x1f3 - 0x1f6 端口中 ; LBA 地址 7 ~ 0 位写入端口 0x1f3 mov dx, 0x1f3 ; Primary 通道 LBA low out dx, al ; LBA 地址 15 ~ 8 位写入端口 0x1f4 mov cl, 8 shr eax, cl mov dx, 0x1f4 ; Primary 通道 LBA mid out dx, al ; LBA 地址 23 ~ 16 位写入端口 0x1f5 shr eax, cl mov dx, 0x1f5 ; Primary 通道 LBA high out dx, al shr eax, cl and al, 0x0f ; LBA 第 24~27 位 or al, 0xe0 ; 设置 7-4 位为 1110, 表示 LBA 模式 mov dx, 0x1f6 ; Device out dx, al ; 第 3 步:向 0x1f7 端口写入命令 0x20 mov dx, 0x1f7 ; Command mov al, 0x20 out dx, al ; 第 4 步:检测硬盘状态 .not_ready: ; 同一端口,写时表示写入命令,读时表示读入硬盘状态 nop in al, dx ; 0x1f7 Status and al, 0x88 ; 第 4 位为 1 表示硬盘控制器已准备好数据传输,第 7 位为 1 表示硬盘忙 cmp al, 0x08 jnz .not_ready ; 第 5 步:从 0x1f0 端口读取数据 ; di 为要读取的扇区数,一个扇区 512 字节,每次读入一个字,共需要 di*512/2 次,所以 di*256 mov ax, di mov dx, 256 mul dx ; dx:ax = dx * 256 mov cx, ax mov dx, 0x1f0 .go_on_read: in ax, dx mov [bx], ax ; LOADER_BASE_ADDR (0~FFFFh) add bx, 2 loop .go_on_read ret times 510 - ($-$$) db 0 db 0x55, 0xaa