突破512字节

简介: 突破512字节

先写个 Makefile

  • 为啥先来个 Makefile ,原因就是省事,一个 “make” 就可以可以自动实现起多条命令
  • 这里提供一个 Makefile ,功能:编译 boot.asm 生成 boot.bin ,创建一个 60M 的虚拟硬盘,将 boot.bin 写入虚拟硬盘的第一个扇区。
  • 后面我们默认使用虚拟硬盘,不再考虑虚拟软盘了,毕竟软盘已经被抛弃了

打印前清屏

  • 前面我们已经成功的写出来一个程序 boot.asm
  • 现在我们就开始完善这个程序吧
  • 首先,在程序开始处 org 0x7c00 和 标签 start 之间加一条指令:jmp start ,在 0x7c00 处加一条跳转指令就可以保证即便在 satrt 前有别的程序或数据,程序会跳转到 start 标签处运行
  • 我们看到在我们实现的打印信息:Welcome to KOS. 前还有一堆打印信息,清掉它们

  • 利用 BIOS 提供的 int 0x10 中断的 0x06号 功能,上卷全部行,则可实现清屏。
; 功能:清屏
mov ah, 0x06      ; AH 功能号 = 0x06
mov bh, 0x07      ; 清屏属性
mov al, 0x00      ; AL = 上卷的行数(如果为0,表示全部)
mov cx, 0x0000    ; 窗口左上角(CL, CH)位置,即(0, 0)
mov dx, 0x184f    ; 窗口右下角(DL, DH)位置
                  ; VGA 文本模式中,一行只能容纳 80 个字节,共 25 行 ,
                  ; 下标从 0 开始,所以 0x18 =24, 0x4f = 79
int 0x10
  • 加入清屏功能后的新程序:boot.asm
  • 有了前面的 Makefile ,这里就不用执行那么多步骤了, 执行 “make” 命令,然后运行 “bochs -f bochsrc.disk” 即可。哈哈,是不是清屏成功了,但是打印起始处并不是在窗口起始处。

显存

  • 原先我们使用 int 0x10 的 0x0e 号功能来实现打印的,现在我们直接操作显存,显存地址:0xb8000,把字符串数据放到显存开始处,那么是不是就实现了在窗口起始处打印。
  • 关于显存,在【8086汇编基础】中有讲解,这里做个简单讲解
  • 显存就是一段特殊内存,操作这段内存中的数据将导致显示器上内容改变
  • 有“文本模式”和“图形模式”两种工作模式可选,我们暂时使用文本模式
  • 在文本模式下
  • 显存的地址映射范围:[0xB8000, 0xBFFFF]
  • 一个屏幕最多可以显示 25 行 * 80 列的字符
  • 每两个字节为一组,第一个字节为要显示的字符,第二个字节为显示属性
  • 最终实现了清屏并从屏幕起始处打印字符串的程序:boot.asm
  • 又发现了一个问题,光标位置不对,想想也是,直接操作显存关人家光标什么事
  • 可以使用 BIOS 提供的 int 0x10 中断的 0x02 功能号设置光标位置,不过这种方式你还得自己计算光标位置,我嫌麻烦,不搞
mov ah, 0x02    ; AH 功能号 = 0x02,设置光标位置 (dl, dh)
mov dh, 0x0     ; 设置光标行号
mov dl, 0x0     ; 设置光标列号
mov bh, 0x0     ; 页码
int 0x10
  • 还是直接使用 BIOS 提供的 int 0x10 中断的 0x13 功能号来打印字符吧,程序:boot.asm
  • 用函数的思想将打印功能封装一下,想用函数,首先得指定栈位置:mov sp, 0x7c00 ,0x7c00 地址往下一段空间都是安全的,没人用,入栈时栈指针往下走 0x7c00 地址往上当然就是 MBR 了。程序:boot.asm

加载器

  • 我们的 MBR 受限于 512 字节大小的,在那么小的空间中,没法为内核准备好环境,更没法将内核成功加载到内存并运行。所以我们要在另一个程序中完成初始化环境及加载内核的任务,这个程序我们称之为 loader,即加载器。Loader 我们会在后面实现。那么问题来了,loader 在哪里?如何跳过去执行?这就是新款 MBR 的使命,简而言之就是,负责从硬盘上把loader 加载到内存,并将接力棒交给它。
  • MBR 占据了硬盘第 0 扇区(以逻辑 LBA 方式,扇区从 0 开始编号,若以物理 CHS 方式,扇区则从 1 开始编号)
  • loader 放在硬盘哪里呢?这个就是完全我们自己规定了,MBR 都是我们自己写的,想读哪个扇区就读哪个扇区。loader 就放在扇区 2 吧,中间隔一个扇区不用,数据安全一些,心里安慰一下。
  • 确定了 loader 存放在硬盘中的位置,接下来我们还有确定把 loader 从硬盘中读取到哪片内存中,先看一下内存布局

  • 随着咱们不断添加功能,内核必然越来越大,其所在的内存地址也会向越来越高的地方发展,难免会超过可用区域的上限,咱们尽量把loader 放在低处,多留出一些空间给内核。
  • 从上图中我们可以找到两片可用区域, 0x500 ~ 0x7BFF 和 0x7E00 ~ 0x9FBFF。就选择把 loader 加载到内存 0x900 开始处吧,别问为啥,这个地址你随意。
  • 最终,将 loader 加载到内存中的程序为:boot.asm【rd_disk_to_mem 中涉及硬盘驱动相关知识,关于硬盘驱动,我暂时不想研究,反正也是通用的,就从别的地方把驱动代码移植过来先用着】
  • 如果你稍微细心一点就会发现,rd_disk_to_mem 是不是没用入栈和出栈?哈哈哈,原因就是懒得写,rd_disk_to_mem 最后执行,就算弄脏了别的寄存器,下面这些寄存器也没人用了,脏了就脏了吧。
  • 怎么测试程序读硬盘成功了呢?
  • 目前虚拟硬盘扇区 2 中还没有数据,所以把 boot.asm 程序的第 41 行 读扇区号改为 0 ,即把 “mov eax, 0x02” 改为 “mov eax, 0x00”,这样子程序运行起来后,利用 bochs 调试 xp 命令读取 0x900 开始处的内存,看看跟 a.img 中的数据是否相同,可以利用二进制工具查看 a.img 中的内容,具体查看方法自己搜索
  • 由于硬盘扇区 2 中 loader 程序还没有实现,所以暂时还不能跳转到 0x900 处执行,等后面 loader 实现后再增加 “jmp 0x900” 这条指令

简单实现一个 loader

  • 这里并不是实现真正的 loader 程序,仅仅是实现一个打印提示信息,证明 loader 程序成功被 MBR 加载并执行
  • 把前面实现的 boot.asm 拿过来改改,最开始处改为 “org 0x7c00” 改为 “org 0x900” ,将打印信息改为“Loader...”
  • 改完后程序:loader.asm
  • 编译 loader.asm
nasm loader.asm -o loader.bin
  • 将生成的 loader.bin 写入硬盘第 2 个扇区。
dd if=loader.bin of=a.img bs=512 count=1 seek=2 conv=notrunc
  • 运行 bochs 验证一下
bochs -f bochsrc.disk
  • 成功显示信息: “Loader...”
  • 注意, boot.asm 中 “jmp 0x900” 这条指令要恢复
  • 将上述过程写入 Makefile ,产生新的 Makefile
目录
相关文章
|
算法 前端开发 Java
在字节当了几个月的牛马,醒悟了。
以前也分享过不少实习体验,比如去年就分享了一位师弟的美团实习体验:美团实习三个月,我受益良多,今天来分享一下一位学习圈中学弟的字节实习体验。
200 0
单字节,双字节,四字节能够表示的数值大小范围分别是多少
单字节,双字节,四字节能够表示的数值大小范围分别是多少
|
存储 数据处理
位,字节与字
位、字节、字(bits, Bytes, words)是计算机数据存储的单位。位是最小的存储单位,每一个位存储一个1位的二进制码(0 or 1),一个字节由8位(8个二进制0 or 1 串)组成。而字通常为16、32或64个位组成。
809 0
|
存储 Linux Windows
网络字节
网络字节
132 0
|
Java API
一个汉字占几个字节你真的记住了吗?
一个汉字占几个字节是不是不太好记呢,编码不一样则占字节位就不一样。下面用一段简短的代码了解一下一个汉字占几个字节。
395 0
一个汉字占几个字节你真的记住了吗?
|
C++
c++ 数据字节
#include using namespace std; void main() { cout
847 0
一个汉字占多少字节?
原文:一个汉字占多少字节? GBK编码,一个汉字占两个字节。 UTF-16编码,通常汉字占两个字节,CJKV扩展B区、扩展C区、扩展D区中的汉字占四个字节(一般字符的Unicode范围是U+0000至U+FFFF,而这些扩展部分的范围大于U+20000,因而要用两个UTF-16)。
2412 0