当前位置: 代码迷 >> 综合 >> 《操作系统真象还原》从零开始自制操作系统 自写源码实现 (基础汇编文件 + 编译链接命令 + MakeFile)
  详细解决方案

《操作系统真象还原》从零开始自制操作系统 自写源码实现 (基础汇编文件 + 编译链接命令 + MakeFile)

热度:103   发布时间:2023-11-17 17:47:02.0

文章目录

    • 专栏博客链接
    • 编写完的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字节初始化 (0x008<<2432位初始化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         		   ;0xB80000xBFFFF为文字模式显示内存 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                                              ;0x1000064KB  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^224MB 偏移由前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;这里稍微解释一下 因为0x7000064kb*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

  相关解决方案