第3章 | 显示字符

file

BIOS

Base Input & Output System
主要工作是检测、初始化硬件。BIOS还有中断向量表,通过“int 中断号”来实现相关的硬件调用。

BIOS是计算机上第一个运行的软件,由硬件ROM(只读存储器)加载。开机的时候处于实模式。

MBR

MBR在0盘0道1扇区(就是最开始的扇区),被BIOS加载,要求扇区的最后两个是0x55和0xaa。MBR选择在32K的最后1K被存放(0x7c00)

直接和硬件交互

然后mbr里面就可以写一些汇编了,比如一开始的输出字符。

;2023.5.10
;主引导程序
; 第一版
;----------------------------------

; 引导加载程序
; 0x7c00 是启动时的引导程序
SECTION MBR vstart=0x7c00
    mov ax,cs   ; cs指向加载程序当前的代码段
    mov ds,ax   ; Data Segment 数据段寄存器
    mov es,ax   ; Extra Segment 扩展段寄存器
    mov ss,ax   ; Stack Segment 堆栈段寄存器
    mov fs,ax   ; F Segment 拓展段寄存器
    mov sp,0x7c00   ; 堆栈指针寄存器

; 清屏利用 0x60 号功能,上卷全部行,则可清屏
; ---------------------
; INT 0x10  功能号:0x06       功能描述:上卷窗口
;----------------------
; 输入:
; AH 功能号 = 0x06
; BH = 上卷行属性
; (CL, CH) = 窗口左上角的(X,Y)位置
; (DL, DH) = 窗口右下角的(X,Y)位置
; 无返回值
    mov ax, 0x600       ; 一种显示方案
    mov bx, 0x700       
    mov cx, 0       ; 左上角 (0,0)
    mov dx, 0x184f      ; 右下角 (80,25)
                ; 一行只能80个字符,共25行
                ; 下标从0开始,0x18=24, 0x4f=79
    int     0x10        ; BIOS的系统调用接口 

;;;;;; 下面这三行代码获取光标的位置
; get_cursor获取当前光标位置,在光标位置处打印字符
    mov ah, 3       ; 输入: 3号子功能是获取光标位置,需要纯如ah寄存器
    mov bh, 0       ; bh寄存器存储的是待获取的光标的页号

    int     0x10        ; 输出: ch=光标开始行,cl=光标结束行
                ; dh=光标所在行号,dl=光标所在列号

;;;;;;;;   获取光标位置结束 ;;;;;;;;;

;;;;;;;;   打印字符串        ;;;;;;;;
    ; 还是用10h中断,不过这次调用13号子功能打印字符串
    mov ax, message
    mov bp, ax      ; es:bp 为串首地址,es此时通cs一致
                ; 开头时已经为sreg初始化

    ; 光标主要用到dx寄存器中内容,cx中的光标位置可忽略
    mov cx, 5       ; cx 为串长度,不包括结束符0的字符个数
    mov ax, 0x1301      ; 子功能号13是显示字符及属性,要存入ah寄存器
                ; al 设置谢字符方式al=01: 显示字符串,光标跟随移动
    mov bx, 0x2     ; bh 存储要显示的页号,此处是第0页
                ; bl 中是字符属性, 属性黑底绿字(bl = 02h)
    int 0x10        ; 执行BIOS 0x10 号中断
;;;;;;;  打印字符结束   ;;;;;;;
    jmp $           ; 使程序悬停在此

    message db "1 MBR"
    times 510-($-$$) db 0
    db 0x55,0xaa

显卡

不过这样直接与硬件打交道还是比较恐怖的,可以通过IO接口。改进下通过显卡操作。

;2023.5.11
;主引导程序
;直接操作显卡
;----------------------------------
SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax   ; gs段寄存器,可以使用gs段寄存器来访问显存

; 清屏利用 0x60 号功能,上卷全部行,则可清屏
; ---------------------
; INT 0x10  功能号:0x06       功能描述:上卷窗口
;----------------------
; 输入:
; AH 功能号 = 0x06
; BH = 上卷行属性
; (CL, CH) = 窗口左上角的(X,Y)位置
; (DL, DH) = 窗口右下角的(X,Y)位置
; 无返回值
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0       ; 左上角 (0,0)
    mov dx, 184fh       ; 右下角 (80,25)
                ; 一行只能80个字符,共25行
                ; 下标从0开始,0x18=24, 0x4f=79
    int     10h

; 输出背景色绿色, 前景色红色, 并且跳动的字符串"1 MBR"

    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0x20 ; A表示绿色背景闪烁, 4表示前景色为红色

    mov byte [gs:0x02],' '
    mov byte [gs:0x03],0x91

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0x96

    mov byte [gs:0x06],'B'
    mov byte [gs:0x07],0x94

    mov byte [gs:0x08],'R'
    mov byte [gs:0x09],0x94

    jmp $           ; 跳转到当前指令的地址, 通过死循环使程序悬停在此

    times 510-($-$$) db 0   ; $代表当前位置指令的地址, $$代表当前节段的起始地址
                ; MBR 的最后两字节是固定的 0x55 0xaa 牵引扇区签名
    db 0x55,0xaa

硬件

给mbr加上读写硬盘的功能。mbr负责加载loader,loader负责为内核准备好环境和加载内核。

把loader放到第2扇区,离mbr这个第0扇区远一点。

更改mbr

;2023.5.11
;主引导程序
;从硬盘加载一个内核程序并跳转到该程序的入口点
;----------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax   ; gs段寄存器,可以使用gs段寄存器来访问显存

; 清屏利用 0x60 号功能,上卷全部行,则可清屏
; ---------------------
; INT 0x10  功能号:0x06       功能描述:上卷窗口
;----------------------
; 输入:
; AH 功能号 = 0x06
; BH = 上卷行属性
; (CL, CH) = 窗口左上角的(X,Y)位置
; (DL, DH) = 窗口右下角的(X,Y)位置
; 无返回值
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0       ; 左上角 (0,0)
    mov dx, 184fh       ; 右下角 (80,25)
                ; 一行只能80个字符,共25行
                ; 下标从0开始,0x18=24, 0x4f=79
    int     10h

; 输出背景色绿色, 前景色红色, 并且跳动的字符串"1 MBR"

    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁, 4表示前景色为红色

    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 eax, LOADER_START_SECTOR    ; 起始扇区lba地址
    mov bx, LOADER_BASE_ADDR    ; 写入的地址
    mov cx, 1           ; 代写入的扇区数
    call rd_disk_m_16       ; rd_disk_m_16是函数,实现了从硬盘读取一个扇区的数据到内存中
                    ; 以下读取程序的起始部分(一个扇区)

    jmp LOADER_BASE_ADDR        ; 将读取的数据写入

;--------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;--------------------
                    ;eax=LBA 扇区号
                    ;bx=将数据写入的内存地址
                    ;cx=读入的扇区数
    mov esi,eax     ;备份eax
    mov di,cx       ;备份cx
;读写硬盘
;第1步:设置要读取的扇区数
    mov dx, 0x1f2
    mov al, cl      ;cl是cx的低8位
    out dx, al      ;out用于将数据传输到指定端口
                ;读取扇区数

    mov eax,esi     ;恢复eax

;第2步:将LBA地址存入0x1f3~0x1f6

    ;LBA地址7~0位写入端口0x1f3
    mov dx, 0x1f3
    out dx, al

    ;LBA地址15~8位写入端口0x1f4
    mov cl, 8
    shr eax, cl     ;将eax中存储的扇区数量向右移动8位
    mov dx, 0x1f4   
    out dx, al

    ;LBA地址23~16位写入端口0x1f5
    shr eax, cl
    mov dx, 0x1f5
    out dx, al

    shr eax, cl
    and al, 0x0f        ; lba第24~27位
    or al, 0xe0     ; 设置7~4位为1110,表示lba模式
    mov dx, 0x1f6
    out dx, al

; 第3步:向0x1f7端口写入读命令,0x20
    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

; 第4步:检测硬件状态
    .not_ready:
    ; 同一端口,写时表示写入命令字,读时表示读入硬盘状态
    nop                 ; 不做任何事情,填充下
    in al, dx           ; 从端口读取数据
    and al, 0x88        ; 第3位为1表示硬盘控制器已准备好数据传输
                ; 第7位为1表示硬盘忙
    cmp al, 0x08        ; 相等时ZF被置1  大于CF 1  小于SF 1
    jnz .not_ready      ; 若未准备好,继续等

; 第5步:从0x1f0端口读数据
    mov ax, di
    mov dx, 256
    mul dx
    mov cx, ax
; di为要读取的扇区数,一个扇区有512字节,每次读入一个字
; 共需 di*512/2 次,所以di*256

    mov dx, 0x1f0
    .go_on_read:
    in ax, dx
    mov [bx], ax
    add bx, 2
    loop .go_on_read
    ret

    times 510-($-$$) db 0   ; $代表当前位置指令的地址, $$代表当前节段的起始地址
                ; MBR 的最后两字节是固定的 0x55 0xaa 牵引扇区签名
    db 0x55,0xaa

这里的loader也只是打印下字。

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR

; 输出背景绿色, 前背景红色, 并且跳动的字符串"1 MBR"
mov byte [gs:0x00], '2'
mov byte [gs:0x01], 0xA4    ; A表示绿色背景闪烁, 4表示前景色为红色

mov byte [gs:0x02], ' '
mov byte [gs:0x03], 0xA4

mov byte [gs:0x04], 'L'
mov byte [gs:0x05], 0xA4

mov byte [gs:0x06], 'O'
mov byte [gs:0x07], 0xA4

mov byte [gs:0x08], 'A'
mov byte [gs:0x09], 0xA4

mov byte [gs:0x0a], 'D'
mov byte [gs:0x0b], 0xA4

mov byte [gs:0x0c], 'E'
mov byte [gs:0x0d], 0xA4

mov byte [gs:0x0e], 'R'
mov byte [gs:0x0f], 0xA4

jmp $

头文件里面写的两个宏,loader将被加载到0x900,在第二扇区。

;---------------- loader 和 kernel -----------------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇