文章目录
-
- 专栏博客链接
- 编写完的bochsrc.disk
- 编写完的boot.inc
- 编写完的Mbr.S
- 编写完的Loader.S
- 编写完的xxd.sh
- 编写完的MakeFile
- 常用编译链接命令
专栏博客链接
《操作系统真象还原》从零开始自制操作系统 全章节博客链接
编写完的bochsrc.disk
megs : 32romimage: file=/home/cooiboi/bochs/share/bochs/BIOS-bochs-latest
vgaromimage: file=/home/cooiboi/bochs/share/bochs/VGABIOS-lgpl-latestboot: disklog: bochs.outmouse:enabled=0
keyboard:keymap=/home/cooiboi/bochs/share/bochs/keymaps/x11-pc-us.mapata0:enabled=1,ioaddr1=0x1f0,ioaddr2=0x3f0,irq=14
ata0-master: type=disk, path="hd60M.img", mode=flat,cylinders=121,heads=16,spt=63
ata0-slave: type=disk, path="hd80M.img", mode=flat,cylinders=162,heads=16,spt=63#gdbstub:enabled=1,port=1234,text_base=0,data_base=0,bss_base=0
编写完的boot.inc
;------------------- 进入loader所需要的宏 --------------------------LOADER_START_SECTOR equ 2
LOADER_BASE_ADDR equ 0x600 ;博客名字是Love 6 干脆就把Loader设置加载到0x600;-------------------- gdt描述符属性 --------------------------------
;我查了查下划线的作用 其实没有任何作用 这里仅仅为了方便 确定哪些位为我们想要设置数而专门用的下划线分割
;上面的第多少位都是针对的高32位而言的 参照博客的图 DESC_G_4K equ 1_00000000000000000000000b ;第23位G 表示4K或者1MB位 段界限的单位值 此时为1则为4k
DESC_D_32 equ 1_0000000000000000000000b ;第22位D/B位 表示地址值用32位EIP寄存器 操作数与指令码32位
DESC_L equ 0_000000000000000000000b ;第21位 设置成0表示不设置成64位代码段 忽略
DESC_AVL equ 0_00000000000000000000b ;第20位 是软件可用的 操作系统额外提供的 可不设置DESC_LIMIT_CODE2 equ 1111_0000000000000000b ;第16-19位 段界限的最后四位 全部初始化为1 因为最大段界限*粒度必须等于0xffffffff
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ;相同的值 数据段与代码段段界限相同
DESC_LIMIT_VIDEO2 equ 0000_0000000000000000b ;第16-19位 显存区描述符VIDEO2 书上后面的0少打了一位 这里的全是0为高位 低位即可表示段基址DESC_P equ 1_000000000000000b ;第15位 P present判断段是否存在于内存
DESC_DPL_0 equ 00_0000000000000b ;第13-14位 这两位更是重量级 Privilege Level 0-3
DESC_DPL_1 equ 01_0000000000000b ;0为操作系统 权力最高 3为用户段 用于保护
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000bDESC_S_sys equ 0_000000000000b ;第12位为0 则表示系统段 为1则表示数据段
DESC_S_CODE equ 1_000000000000b ;第12位与type字段结合 判断是否为系统段还是数据段
DESC_S_DATA equ DESC_S_CODEDESC_TYPE_CODE equ 1000_00000000b ;第9-11位表示该段状态 1000 可执行 不允许可读 已访问位0
;x=1 e=0 w=0 a=0
DESC_TYPE_DATA equ 0010_00000000b ;第9-11位type段 0010 可写
;x=0 e=0 w=1 a=0;代码段描述符高位4字节初始化 (0x00共8位 <<24 共32位初始化0)
;4KB为单位 Data段32位操作数 初始化的部分段界限 最高权限操作系统代码段 P存在表示 状态
DESC_CODE_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0X00;数据段描述符高位4字节初始化
DESC_DATA_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0X00;显存段描述符高位4字节初始化
DESC_VIDEO_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0X0B ;整挺好 我看书上写的0x00 结果我自己推算出来这里末尾是B ;-------------------- 选择子属性 --------------------------------
;第0-1位 RPL 特权级比较是否允许访问 第2位TI 0表示GDT 1表示LDT 第3-15位索引值
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b;------------------ 开启页表所需要的宏 ---------------------------PAGE_DIR_TABLE_POS equ 0x100000 ;这里设置了页目录项的起始位置;------------------ 页表相关属性 ---------------------------------PG_P equ 1b ;PG目录项的属性 Present存在于当前物理内存
PG_RW_R equ 00b ;只可读不可写
PG_RW_W equ 10b ;可写可读
PG_US_S equ 000b ;Supervisor 超级用户
PG_US_U equ 100b ;User 普通用户
;不是很清楚 Global位为什么宏先不定义 但是剩下的PWT PCD 我们用不到即设置为0 A位是cpu操控的 页表项就算是弄完了;----------------- 加载内核宏定义 -------------------------------KERNEL_BIN_SECTOR equ 0x9
KERNEL_BIN_BASE_ADDR equ 0x70000
KERNEL_ENTER_ADDR equ 0xc0001500PT_NULL equ 0x0
编写完的Mbr.S
%include "boot.inc"
SECTION MBR vstart=0x7c00 ;起始地址编译在0x7c00mov ax,csmov ds,axmov es,axmov ss,axmov fs,axmov sp,0x7c00mov ax,0xb800 ; ax为文本信号起始区mov gs,ax ; gs = ax 充当段基址的作用; ah = 0x06 al = 0x00 想要调用int 0x06的BIOS提供的中断对应的函数 ; 即向上移动即完成清屏功能; cx dx 分别存储左上角与右下角的左边 详情看int 0x06函数调用mov ax,0600h mov bx,0700hmov cx,0mov dx,184fh;调用BIOS中断int 0x10 ;新增功能 直接操作显存部分;低位字节储存ascii字符 小端储存内存顺序相反mov byte [gs:0x00],'L' ;背景储存在第二个字节 含字符与背景属性mov byte [gs:0x01],0xA4 mov byte [gs:0x02],'O' mov byte [gs:0x03],0xA4mov byte [gs:0x04],'V' mov byte [gs:0x05],0xA4mov byte [gs:0x06],'E' mov byte [gs:0x07],0xA4mov byte [gs:0x08],'6' mov byte [gs:0x09],0xA4mov byte [gs:0x0A],' ' mov byte [gs:0x0B],0xA4mov byte [gs:0x0C],'O' mov byte [gs:0x0D],0xA4mov byte [gs:0x0E],'S' mov byte [gs:0x0F],0xA4; 思考一下 在输出完“Love 6OS后 按照书上的逻辑是要把磁盘读入 ; 先把配置信息给送到寄存器中”; 为什么要送到 eax中呢 因为IN OUT IO接口 ; 规定的就是dx里面存放的是端口号 ax是需要或者输送的信息mov eax,LOADER_START_SECTOR mov bx,LOADER_BASE_ADDR ;把要目标内存位置放进去 bx常作地址储存mov cx,4;读取磁盘数 cx常作计数call rd_disk_m_16jmp LOADER_BASE_ADDR ; 直接当跳跳熊 开跳0x600
;------------------------------------------------------------------------
;读取第二块硬盘
rd_disk_m_16:
;------------------------------------------------------------------------
;1 写入待操作磁盘数
;2 写入LBA 低24位寄存器 确认扇区
;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
;4 command 写指令
;5 读取status状态寄存器 判断是否完成工作
;6 完成工作 取出数据;;;;;;;;;;;;;;;;;;;;;;1 写入待操作磁盘数;;;;;;;;;;;;;;;;;;;;;mov esi,eax ; !!! 备份eaxmov di,cx ; !!! 备份cxmov dx,0x1F2 ; 0x1F2为Sector Count 端口号 送到dx寄存器中mov al,cl ; !!! 忘了只能由ax al传递数据out dx,al ; !!! 这里修改了 原out dx,clmov eax,esi ; !!!袄无! 原来备份是这个用 前面需要ax来传递数据 麻了;;;;;;;;;;;;;;;;;;;;;
;2 写入LBA 24位寄存器 确认扇区
;;;;;;;;;;;;;;;;;;;;;mov cl,0x8 ; shr 右移8位 把24位给送到 LBA low mid high 寄存器中mov dx,0x1F3 ; LBA lowout dx,al mov dx,0x1F4 ; LBA midshr eax,cl ; eax为32位 ax为16位 eax的低位字节 右移8位即8~15out dx,almov dx,0x1F5shr eax,clout dx,al;;;;;;;;;;;;;;;;;;;;;
;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
;;;;;;;;;;;;;;;;;;;;;; 24 25 26 27位 尽管我们知道ax只有2 但还是需要按规矩办事 ; 把除了最后四位的其他位置设置成0shr eax,cland al,0x0f or al,0xe0 ;!!! 把第四-七位设置成0111 转换为LBA模式mov dx,0x1F6 ; 参照硬盘控制器端口表 Device out dx,al;;;;;;;;;;;;;;;;;;;;;
;4 向Command写操作 Status和Command一个寄存器
;;;;;;;;;;;;;;;;;;;;;mov dx,0x1F7 ; Status寄存器端口号mov ax,0x20 ; 0x20是读命令out dx,al;;;;;;;;;;;;;;;;;;;;;
;5 向Status查看是否准备好惹
;;;;;;;;;;;;;;;;;;;;;;设置不断读取重复 如果不为1则一直循环.not_ready: nop ; !!! 空跳转指令 在循环中达到延时目的in al,dx ; 把寄存器中的信息返还出来and al,0x88 ; !!! 0100 0100 0x88cmp al,0x08jne .not_ready ; !!! jump not equal == 0;;;;;;;;;;;;;;;;;;;;;
;6 读取数据
;;;;;;;;;;;;;;;;;;;;;mov ax,di ;把 di 储存的cx 取出来mov dx,256mul dx ;与di 与 ax 做乘法 计算一共需要读多少次 方便作循环 低16位放ax 高16位放dxmov cx,ax ;loop 与 cx相匹配 cx-- 当cx == 0即跳出循环mov dx,0x1F0.go_read_loop:in ax,dx ;两字节dx 一次读两字mov [bx],axadd bx,2loop .go_read_loopret ;与call 配对返回原来的位置 跳转到call下一条指令times 510 - ($ - $$) db 0 db 0x55,0xaa
编写完的Loader.S
%include "boot.inc"
SECTION loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR ;是个程序都需要有栈区 我设置的0x600以下的区域到0x500区域都是可用空间 况且也用不到
jmp loader_start ;下面存放数据段 构建gdt 跳跃到下面的代码区 ;对汇编再复习 db define byte,dw define word,dd define dwordGDT_BASE : dd 0x00000000 ;刚开始的段选择子0不能使用 故用两个双字 来填充dd 0x00000000 CODE_DESC : dd 0x0000FFFF ;FFFF是与其他的几部分相连接 形成0XFFFFF段界限dd DESC_CODE_HIGH4DATA_STACK_DESC : dd 0x0000FFFFdd DESC_DATA_HIGH4VIDEO_DESC : dd 0x80000007 ;0xB8000 到0xBFFFF为文字模式显示内存 B只能在boot.inc中出现定义了 此处不够空间了 8000刚好够dd DESC_VIDEO_HIGH4 ;0x0007 (bFFFF-b8000)/4k = 0x7GDT_SIZE equ $ - GDT_BASE ;当前位置减去GDT_BASE的地址 等于GDT的大小GDT_LIMIT equ GDT_SIZE - 1 ;SIZE - 1即为最大偏移量times 59 dq 0 ;预留59个 define double四字型 8字描述符times 5 db 0 ;为了凑整数 0x800 导致前面少了三个total_mem_bytes dd 0;在此前经过计算程序内偏移量为0x200 我算了算 60*8+4*8=512 刚好是 0x200 说这里的之后还会用到;我们刚开始程序设置的地址位置为 0x600 那这就是0x800gdt_ptr dw GDT_LIMIT ;gdt指针 2字gdt界限放在前面 4字gdt地址放在后面 lgdt 48位格式 低位16位界限 高位32位起始地址dd GDT_BASEards_buf times 244 db 0 ;buf 记录内存大小的缓冲区ards_nr dw 0 ;nr 记录20字节结构体个数 计算了一下 4+2+4+244+2=256 刚好256字节;书籍作者有强迫症 哈哈 这里244的buf用不到那么多的 实属强迫症使然 哈哈SELECTOR_CODE equ (0X0001<<3) + TI_GDT + RPL0 ;16位寄存器 4位TI RPL状态 GDT剩下的选择子SELECTOR_DATA equ (0X0002<<3) + TI_GDT + RPL0SELECTOR_VIDEO equ (0X0003<<3) + TI_GDT + RPL0 loader_start:mov sp,LOADER_BASE_ADDR ;先初始化了栈指针xor ebx,ebx ;异或自己 即等于0mov ax,0 mov es,ax ;心有不安 还是把es给初始化一下mov di,ards_buf ;di指向缓冲区位置
.e820_mem_get_loop:mov eax,0x0000E820 ;每次都需要初始化mov ecx,0x14mov edx,0x534d4150int 0x15 ;调用了0x15中断jc .e820_failed_so_try_e801 ;这时候回去看了看jc跳转条件 就是CF位=1 carry flag = 1 中途失败了即跳转add di,cx ;把di的数值增加20 为了下一次作准备inc word [ards_nr]cmp ebx,0jne .e820_mem_get_loop ;直至读取完全结束 则进入下面的处理时间mov cx,[ards_nr] ;反正也就是5 cx足以mov ebx,ards_bufxor edx,edx
.find_max_mem_area:mov eax,[ebx] ;我也不是很清楚为什么用内存上限来表示操作系统可用部分add eax,[ebx+8] ;既然作者这样用了 我们就这样用add ebx,20 ;简单的排序cmp edx,eaxjge .next_ardsmov edx,eax.next_ards:loop .find_max_mem_areajmp .mem_get_ok.e820_failed_so_try_e801: ;地址段名字取的真的简单易懂 哈哈哈哈 mov ax,0xe801int 0x15jc .e801_failed_so_try_88;1 先算出来低15MB的内存 mov cx,0x400mul cx ;低位放在ax 高位放在了dxshl edx,16 ;dx把低位的16位以上的书往上面抬 变成正常的数and eax,0x0000FFFF ;把除了16位以下的 16位以上的数清零 防止影响or edx,eax ;15MB以下的数 暂时放到了edx中add edx,0x100000 ;加了1MB 内存空缺 mov esi,edx;2 接着算16MB以上的内存 字节为单位xor eax,eaxmov ax,bxmov ecx,0x10000 ;0x10000为64KB 64*1024 mul ecx ;高32位为0 因为低32位即有4GB 故只用加eaxmov edx,esiadd edx,eaxjmp .mem_get_ok.e801_failed_so_try_88:mov ah,0x88int 0x15jc .error_hltand eax,0x0000FFFFmov cx,0x400 ;1024mul cxshl edx,16or edx,eax add edx,0x100000.error_hlt:jmp $
.mem_get_ok:mov [total_mem_bytes],edx
; --------------------------------- 设置进入保护模式 -----------------------------
; 1 打开A20 gate
; 2 加载gdt
; 3 将cr0 的 pe位置1in al,0x92 ;端口号0x92 中 第1位变成1即可or al,0000_0010bout 0x92,allgdt [gdt_ptr]mov eax,cr0 ;cr0寄存器第0位设置位1or eax,0x00000001 mov cr0,eax;-------------------------------- 已经打开保护模式 ---------------------------------------jmp dword SELECTOR_CODE:p_mode_start ;刷新流水线[bits 32]p_mode_start: mov ax,SELECTOR_DATAmov ds,axmov es,axmov ss,axmov esp,LOADER_STACK_TOP;------------------------------- 加载内核到缓冲区 -------------------------------------------------mov eax, KERNEL_BIN_SECTORmov ebx, KERNEL_BIN_BASE_ADDRmov ecx,200call rd_disk_m_32;------------------------------- 启动分页 ---------------------------------------------------call setup_page;这里我再把gdtr的格式写一下 0-15位界限 16-47位起始地址sgdt [gdt_ptr] ;将gdt寄存器中的指 还是放到gdt_ptr内存中 我们修改相对应的 段描述符mov ebx,[gdt_ptr+2] ;32位内存先倒出来 为的就是先把显存区域描述法的值改了 可以点开boot.inc 和 翻翻之前的段描述符;段基址的最高位在高4字节 故or dword [ebx+0x18+4],0xc0000000add dword [gdt_ptr+2],0xc0000000 ;gdt起始地址增加 分页机制开启的前奏add esp,0xc0000000 ;栈指针也进入高1GB虚拟内存区mov eax,PAGE_DIR_TABLE_POSmov cr3,eaxmov eax,cr0or eax,0x80000000mov cr0,eaxlgdt [gdt_ptr]mov eax,SELECTOR_VIDEOmov gs,eaxmov byte [gs:160],'V'jmp SELECTOR_CODE:enter_kernel;------------------------------ 跳转到内核区 enter_kernel:call kernel_init ;根据我们的1M以下的内存分布区 综合考虑出的数据mov esp,0xc009f000jmp KERNEL_ENTER_ADDR;------------------------------- 创建页表 ------------------------------------------------
setup_page:mov ecx,0x1000 ;循环4096次 将页目录项清空 内存清0mov esi,0 .clear_page_dir_mem: ;dir directory 把页目录项清空mov byte [PAGE_DIR_TABLE_POS+esi],0inc esiloop .clear_page_dir_mem.create_pde: mov eax,PAGE_DIR_TABLE_POS ;页目录项 起始位置add eax,0x1000 ;页目录项刚好4k字节 add eax即得第一个页表项的地址;接下来我们要做的是 把虚拟地址1M下和3G+1M 两部分的1M内存在页目录项中都映射到物理地址0-0XFFFFFor eax, PG_P | PG_RW_W | PG_US_U ;哦 悟了 哈哈哈 这里设置为PG_US_U 是因为init在用户进程 如果这里设置成US_S 这样子连进内核都进不去了mov [PAGE_DIR_TABLE_POS+0x0],eax ;页目录项偏移0字节与偏移0xc00 对应0x 一条页目录项对应2^22位4MB 偏移由前10位*4字节得到 可自己推算一下mov [PAGE_DIR_TABLE_POS+0xc00],eax sub eax,0x1000 mov [PAGE_DIR_TABLE_POS+4092],eax ;虚拟内存最后一个目录项 指向页目录表自身 书上写的是为了动态操纵页表 我也不是很清楚 反正有用 先放放;这里就创建了一页页表 mov eax,PAGE_DIR_TABLE_POSadd eax,0x1000mov ecx,256mov esi,0mov ebx,PG_P | PG_RW_W | PG_US_U .create_kernel_pte: mov [eax+esi*4],ebxinc esiadd ebx,0x1000loop .create_kernel_pte ;这里对于我们这里填写的目录项所对应的页表 页表中我们还没填写的值
;为了实现 真正意义上的 内核空间被用户进程完全共享
;只是把页目录与页表的映射做出来了 mov eax,PAGE_DIR_TABLE_POSadd eax,0x2000 ;eax此时处于第二个页表or eax,PG_P | PG_RW_W | PG_US_U
;这里循环254次可以来分析一下 我们这里做的是 0xc0 以上部分的映射 0xc0 对应的是第768个页表项 页表项中一共有 2^10=1024项
;第1023项我们已经设置成 映射到页目录项本身位置了 即1022 - 769 +1 = 254mov ebx,PAGE_DIR_TABLE_POSmov ecx,254 mov esi,769.create_kernel_pde:mov [ebx+esi*4],eaxinc esiadd eax,0x1000loop .create_kernel_pde ret ;----------------------- 初始化内核 把缓冲区的内核代码放到0x1500区域 ------------------------------------------
;这个地方主要对elf文件头部分用的很多
;可以参照着书上给的格式 来比较对比
kernel_init:xor eax,eax ;全部清零xor ebx,ebxxor ecx,ecxxor edx,edx;这里稍微解释一下 因为0x70000 为64kb*7=448kb 而我们的内核映射区域是4MB 而在虚拟地址4MB以内的都可以当作1:1映射mov ebx,[KERNEL_BIN_BASE_ADDR+28]add ebx,KERNEL_BIN_BASE_ADDR ;ebx当前位置为程序段表mov dx,[KERNEL_BIN_BASE_ADDR+42] ;获取程序段表每个条目描述符字节大小mov cx,[KERNEL_BIN_BASE_ADDR+44] ;一共有几个段.get_each_segment:cmp dword [ebx+0],PT_NULLje .PTNULL ;空即跳转即可 不进行mem_cpymov eax,[ebx+8]cmp eax,0xc0001500jb .PTNULLpush dword [ebx+16] ;ebx+16在存储的数是filesz 可以翻到Loader刚开始mov eax,[ebx+4] add eax,KERNEL_BIN_BASE_ADDRpush eax ;p_offset 在文件中的偏移位置 源位置 push dword [ebx+8] ;目标位置call mem_cpyadd esp,12 ;把三个参数把栈扔出去 等于恢复栈指针.PTNULL:add ebx,edx ;edx是一个描述符字节大小loop .get_each_segment ;继续进行外层循环 retmem_cpy:cld ;向高地址自动加数字 cld std 向低地址自动移动push ebp ;保存ebp 因为访问的时候通过ebp 良好的编程习惯保存相关寄存器mov ebp,esp push ecx ;外层循环还要用 必须保存 外层eax存储着还有几个段;分析一下为什么是 8 因为进入的时候又重新push了ebp 所以相对应的都需要+4;并且进入函数时 还Push了函数返回地址 所以就那么多了mov edi,[ebp+8] ;目的指针 edi存储的是目的位置 4+4mov esi,[ebp+12] ;源指针 源位置 8+4mov ecx,[ebp+16] ;与Movsb好兄弟 互相搭配 12+4rep movsb ;一个一个字节复制pop ecx pop ebpret;------------------------ rd_disk_m_32 在mbr.S复制粘贴过来的 修改了点代码 ----------------------
rd_disk_m_32:
;1 写入待操作磁盘数
;2 写入LBA 低24位寄存器 确认扇区
;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
;4 command 写指令
;5 读取status状态寄存器 判断是否完成工作
;6 完成工作 取出数据;;;;;;;;;;;;;;;;;;;;;;1 写入待操作磁盘数;;;;;;;;;;;;;;;;;;;;;mov esi,eax ; !!! 备份eaxmov di,cx ; !!! 备份cxmov dx,0x1F2 ; 0x1F2为Sector Count 端口号 送到dx寄存器中mov al,cl ; !!! 忘了只能由ax al传递数据out dx,al ; !!! 这里修改了 原out dx,clmov eax,esi ; !!!袄无! 原来备份是这个用 前面需要ax来传递数据 麻了;;;;;;;;;;;;;;;;;;;;;
;2 写入LBA 24位寄存器 确认扇区
;;;;;;;;;;;;;;;;;;;;;mov cl,0x8 ; shr 右移8位 把24位给送到 LBA low mid high 寄存器中mov dx,0x1F3 ; LBA lowout dx,al mov dx,0x1F4 ; LBA midshr eax,cl ; eax为32位 ax为16位 eax的低位字节 右移8位即8~15out dx,almov dx,0x1F5shr eax,clout dx,al;;;;;;;;;;;;;;;;;;;;;
;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
;;;;;;;;;;;;;;;;;;;;;; 24 25 26 27位 尽管我们知道ax只有2 但还是需要按规矩办事 ; 把除了最后四位的其他位置设置成0shr eax,cland al,0x0f or al,0xe0 ;!!! 把第四-七位设置成0111 转换为LBA模式mov dx,0x1F6 ; 参照硬盘控制器端口表 Device out dx,al;;;;;;;;;;;;;;;;;;;;;
;4 向Command写操作 Status和Command一个寄存器
;;;;;;;;;;;;;;;;;;;;;mov dx,0x1F7 ; Status寄存器端口号mov ax,0x20 ; 0x20是读命令out dx,al;;;;;;;;;;;;;;;;;;;;;
;5 向Status查看是否准备好惹
;;;;;;;;;;;;;;;;;;;;;;设置不断读取重复 如果不为1则一直循环.not_ready: nop ; !!! 空跳转指令 在循环中达到延时目的in al,dx ; 把寄存器中的信息返还出来and al,0x88 ; !!! 0100 0100 0x88cmp al,0x08jne .not_ready ; !!! jump not equal == 0;;;;;;;;;;;;;;;;;;;;;
;6 读取数据
;;;;;;;;;;;;;;;;;;;;;mov ax,di ;把 di 储存的cx 取出来mov dx,256mul dx ;与di 与 ax 做乘法 计算一共需要读多少次 方便作循环 低16位放ax 高16位放dxmov cx,ax ;loop 与 cx相匹配 cx-- 当cx == 0即跳出循环mov dx,0x1F0.go_read_loop:in ax,dx ;两字节dx 一次读两字mov [ebx],axadd ebx,2loop .go_read_loopret ;与call 配对返回原来的位置 跳转到call下一条指令
编写完的xxd.sh
xxd -u -a -g 1 -s $2 -l $3 $1
编写完的MakeFile
BUILD_DIR = ./build
ENTRY_POINT = 0xc0001500
AS = nasm
CC = gcc
LD = ld
LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/
ASFLAGS = -f elf
CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \$(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o $(BUILD_DIR)/switch.o \$(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o $(BUILD_DIR)/memory.o \$(BUILD_DIR)/bitmap.o $(BUILD_DIR)/thread.o $(BUILD_DIR)/list.o \$(BUILD_DIR)/sync.o $(BUILD_DIR)/console.o $(BUILD_DIR)/keyboard.o \$(BUILD_DIR)/ioqueue.o $(BUILD_DIR)/tss.o $(BUILD_DIR)/process.o \$(BUILD_DIR)/syscall-init.o $(BUILD_DIR)/syscall.o $(BUILD_DIR)/stdio.o \$(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o $(BUILD_DIR)/fs.o $(BUILD_DIR)/inode.o \$(BUILD_DIR)/file.o $(BUILD_DIR)/dir.o $(BUILD_DIR)/fork.o $(BUILD_DIR)/shell.o \$(BUILD_DIR)/buildin_cmd.o $(BUILD_DIR)/exec.o############## c代码编译 ###############
$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \thread/thread.h kernel/interrupt.h device/console.h \device/keyboard.h device/ioqueue.h userprog/process.h \lib/user/syscall.h userprog/syscall-init.h lib/stdio.h \lib/kernel/stdio-kernel.h fs/file.h kernel/global.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \lib/stdint.h kernel/interrupt.h device/timer.h kernel/memory.h \thread/thread.h device/console.h device/keyboard.h userprog/tss.h \userprog/syscall-init.h device/ide.h fs/fs.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h \kernel/kernel.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/io.h lib/kernel/print.h \kernel/interrupt.h thread/thread.h kernel/debug.h kernel/global.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \lib/kernel/print.h lib/stdint.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/string.o: lib/string.c lib/string.h \kernel/debug.h kernel/global.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h kernel/global.h \thread/sync.h thread/thread.h lib/kernel/list.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h kernel/global.h \lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \lib/stdint.h lib/string.h kernel/global.h kernel/memory.h \kernel/debug.h kernel/interrupt.h lib/kernel/print.h \userprog/process.h thread/sync.h lib/user/syscall.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h \kernel/interrupt.h lib/stdint.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h \lib/stdint.h thread/thread.h kernel/debug.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/console.o: device/console.c device/console.h \lib/kernel/print.h thread/sync.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h \lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \kernel/global.h lib/stdint.h device/ioqueue.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h \kernel/interrupt.h kernel/global.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h \kernel/global.h thread/thread.h lib/kernel/print.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/process.o: userprog/process.c userprog/process.h \lib/string.h kernel/global.h kernel/memory.h lib/kernel/print.h \thread/thread.h kernel/interrupt.h kernel/debug.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \lib/user/syscall.h lib/stdint.h lib/kernel/print.h kernel/interrupt.h thread/thread.h \kernel/memory.h fs/file.h userprog/fork.h lib/kernel/stdio-kernel.h userprog/exec.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h fs/file.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h lib/string.h lib/user/syscall.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h \lib/stdio.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/ide.o: device/ide.c device/ide.h lib/stdint.h kernel/debug.h \lib/kernel/stdio-kernel.h lib/stdio.h kernel/global.h thread/sync.h \lib/kernel/io.h device/timer.h kernel/interrupt.h lib/kernel/list.h fs/fs.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/fs.o: fs/fs.c fs/fs.h lib/stdint.h kernel/global.h device/ide.h fs/inode.h fs/dir.h \fs/super_block.h lib/kernel/stdio-kernel.h lib/string.h kernel/debug.h lib/kernel/list.h \fs/file.h thread/thread.h device/ioqueue.h device/keyboard.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/inode.o: fs/inode.c fs/inode.h device/ide.h kernel/debug.h thread/thread.h \kernel/memory.h lib/string.h lib/kernel/list.h kernel/interrupt.h lib/kernel/bitmap.h \fs/file.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/file.o: fs/file.c fs/file.h lib/kernel/stdio-kernel.h thread/thread.h device/ide.h \fs/file.h kernel/global.h kernel/interrupt.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/dir.o: fs/dir.c fs/dir.h device/ide.h fs/fs.h fs/inode.h kernel/memory.h lib/string.h lib/stdint.h \lib/kernel/stdio-kernel.h kernel/debug.h fs/file.h kernel/memory.h lib/string.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/fork.o: userprog/fork.c userprog/fork.h kernel/global.h lib/stdint.h lib/string.h \kernel/memory.h kernel/interrupt.h thread/sync.h thread/thread.h kernel/debug.h userprog/process.h \lib/kernel/stdio-kernel.h fs/file.h lib/kernel/list.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/shell.o: shell/shell.c shell/shell.h kernel/global.h lib/stdint.h lib/string.h \lib/user/syscall.h lib/stdio.h fs/file.h kernel/debug.h shell/buildin_cmd.h fs/fs.h \userprog/exec.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/buildin_cmd.o: shell/buildin_cmd.c shell/buildin_cmd.h fs/file.h fs/fs.h kernel/debug.h \lib/string.h lib/user/syscall.h fs/dir.h fs/fs.h lib/stdio.h shell/shell.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/exec.o: userprog/exec.c userprog/exec.h kernel/global.h kernel/memory.h fs/fs.h \fs/file.h lib/kernel/stdio-kernel.h kernel/interrupt.h lib/user/syscall.h$(CC) $(CFLAGS) $< -o $@############## 汇编代码编译 ###############
$(BUILD_DIR)/kernel.o: kernel/kernel.S$(AS) $(ASFLAGS) $< -o $@$(BUILD_DIR)/print.o: lib/kernel/print.S$(AS) $(ASFLAGS) $< -o $@$(BUILD_DIR)/switch.o: thread/switch.S$(AS) $(ASFLAGS) $< -o $@############## 链接所有目标文件 #############
$(BUILD_DIR)/kernel.bin: $(OBJS)$(LD) $(LDFLAGS) $^ -o $@.PHONY : mk_dir hd clean allmk_dir:if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fihd:dd if=$(BUILD_DIR)/kernel.bin \of=/home/cooiboi/bochs/hd60M.img \bs=512 count=200 seek=9 conv=notruncclean:cd $(BUILD_DIR) && rm -f ./*build: $(BUILD_DIR)/kernel.binall: mk_dir build hd
常用编译链接命令
nasm -I include/ -o boot/loader.bin boot/loader.S
dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=3 seek=2 conv=notrunc
bin/bochs -f bochsrc.diskgcc -m32 -c -o kernel/main.o kernel/main.c
ld -m elf_i386 kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin
dd if=/home/cooiboi/bochs/kernel/kernel.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=200 seek=9 conv=notruncnasm -I include/ -o boot/loader.bin boot/loader.S
dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=3 seek=2 conv=notrunc
nasm -f elf -o lib/kernel/print.o lib/kernel/print.S
gcc -m32 -I lib/kernel/ -c -o kernel/main.o kernel/main.c
ld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel/kernel.bin \
kernel/main.o lib/kernel/print.o
dd if=/home/cooiboi/bochs/kernel/kernel.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc
bin/bochs -f bochsrc.diskgcc -m32 -I lib/kernel -c -o build/timer.o device/timer.c
gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/debug.o kernel/debug.c
nasm -f elf -o build/print.o lib/kernel/print.S
nasm -f elf -o build/kernel.o kernel/kernel.S
ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o \
build/print.o build/kernel.o build/timer.o build/debug.o
dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc
dd if=/home/cooiboi/bochs/command/prog_no_arg of=/home/cooiboi/bochs/hd60M.img bs=512 count=30 seek=300 conv=notruncmake all
bin/bochs -f bochsrc.disk