注册 登录
编程论坛 操作系统内核开发

编写操作系统?有趣:第一天

AXRZ 发布于 2016-03-23 12:33, 14938 次点击
直接进入正题,再考虑过操作系统的具体的功能分块后,第一件需要考虑的事情就是引导扇区了。引导扇区位于硬盘的0磁头,0柱面上的第一个扇区,并由电脑在开启电源,完成BIOS自检后加载到内存并执行,我们现在并不需要过多了解BIOS自检时的过程(因为这对于编写操作系统来说没什么意义),但我们需要了解一下关于引导扇区的知识点:

-引导扇区的最后两个字节必须为55H,AAH,这两字节为BIOS检查该硬盘是否为可引导硬盘的唯一条件。若硬盘最后这两个字节不为此,则BIOS将视其为不可引导硬盘

-引导扇区由BIOS加载到内存地址0000:7C00处,因此在设置编译器是必须将程序的偏移设为0x7C00

-引导扇区的长度只能为一个扇区(512字节),这个限制虽然非常不人性,但是足够引导操作系统的加载程序

-在引导扇区被加载到内存后,DL寄存器中将存放目前硬盘(引导的硬盘),这可以用来进行后面对操作系统硬盘的读写

此外关于硬件中断的内容我们在写操作系统本身时再提及

 

那么了解了基本的知识点后,我们正式开始编写引导扇区的代码。在这里,你需要的工具有(推荐):

-Disk Explorer for NTF-下载地址:https://www.        ;用于对IMG文件进行读写(这个软件我之后会破解)本文暂时不需要

-NASM-下载地址:http://www.nasm.us/pub/nasm/releasebuilds/2.12/nasm-2.12.zip        ;  用于编译汇编语言

-BOCHS-下载地址:https://

-任何文本编辑器,这里推荐NOTEPAD++

 

以下是BOOTLOADER.ASM(引导程序)的代码
ORG 7C00H

ENTRY:
    XOR BX,BX
    MOV DS,BX
    MOV SS,BX
    MOV ES,BX
    MOV BP,0FFFFH
    MOV SP,BP
    MOV [DriveNumber],DL
CHECKOSDISK:
    MOV SI,CODMessage0
    CALL WRITESTRING
    CALL RESETDRIVE
    MOV BX,513
    MOV CX,1
    MOV AL,1
    CALL READSECTOR
    JC CODERROR
    CMP BYTE [BX+508],42H
    JNE CODERROR
    CMP BYTE [BX+509],5AH
    JE CODNEXT
CODERROR:
    MOV SI,CODErrorMsg
    CALL WRITESTRING
    JMP ENDDELAY
CODNEXT:
    MOV SI,CODOKMessage
    CALL WRITESTRING
LOADOSMAIN:
    MOV SI,LOMessage
    CALL RESETDRIVE
    CALL WRITESTRING
    MOV BX,[OSMEntryOffset]
    MOV CX,2
    MOV AL,[OSMSectorCount]
    CALL READSECTOR
    JNC JUMPOSMAIN
LOMERROR:
    MOV SI,LOMErrorMsg
    CALL WRITESTRING
    JMP ENDDELAY
JUMPOSMAIN:
    MOV SP,BP
    JMP FAR [OSMEntryAddress]
ENDDELAY:
    MOV SI,EDMessage
    CALL WRITESTRING
    JMP $

WRITECHAR:                                ;AL = Character ASCII
    PUSH BX
    PUSH AX
    MOV BX,0FH
    MOV AH,0EH
    INT 10H
    POP AX
    POP BX
    RETN

WRITESTRING:                            ;DS:SI = Address Of String
    PUSH AX
WSLOOP:
    CLD
    LODSB
    TEST AL,AL
    JZ WSRET
    CALL WRITECHAR
    JMP WSLOOP
WSRET:
    POP AX
    RETN

WRITEHEXNUM:                            ;AX = Hex Number To Output
    PUSHAW
    CLD
    MOV CL,16
    MOV DX,AX
    MOV DI,HexNumTable
WHLOOP:
    MOV AX,DX
    MOV SI,DI
    SUB CL,4
    JZ WHNEXT
    SHR AX,CL
WHNEXT:
    AND AX,0000000000001111B
    ADD SI,AX
    LODSB
    CALL WRITECHAR
    TEST CL,CL
    JNZ WHLOOP
    POPAW
    RETN

WRAPTEXT:
    MOV AL,0DH
    CALL WRITECHAR
    MOV AL,0AH
    CALL WRITECHAR
    RETN

RESETDRIVE:                                ;DL = Drive Number
    XOR AH,AH
    INT 13H
    RETN                                ;Succeed: CF = 0, AH = 0; Failed: CF = 1, AH = Error Code

READSECTOR:                                ;AL = Number Of Sectors To Read, CH = Low 8 Bits Of Cylinder Number, CL = Sector Number(Bit 0-5) High 2 Bits Of Cylinder Number, DH = Head Number, DL = Drive Number, ES:BX = Address Of Buffer
    PUSH BP
    PUSH 2
    MOV BP,SP
RSLOOP:
    PUSHAW
    MOV AH,2
    INT 13H
    JNC RSRET
RSRETRY:
    DEC WORD [BP]
    POPAW
    JNZ RSLOOP
RSRET:
    ADD SP,18
    POP BP
    RETN                                ;Succeed: CF = 0, AH = 0, AL = Number Of Sectors Transferred; Failed: CF = 1, AH = Error Code

WRITESECTOR:                            ;AL = Number Of Sectors To Write, CH = Low 8 Bits Of Cylinder Number, CL = Sector Number(Bit 0-5) High 2 Bits Of Cylinder Number, DH = Head Number, DL = Drive Number, ES:BX = Address Of Buffer
    PUSH BP
    PUSH 2
    MOV BP,SP
WSELOOP:
    PUSHAW
    MOV AH,3
    INT 13H
    JNC WSERET
WSERETRY:
    DEC WORD [BP]
    POPAW
    JNZ WSELOOP
WSERET:
    ADD SP,18
    POP BP
    RETN                                ;Succeed: CF = 0, AH = 0, AL = Number Of Sectors Transferred; Failed: CF = 1, AH = Error Code

DriveInfo:
DriveNumber:
    DB 0
MaxCylinderNum:
    DW 0
MaxSectorNum:
    DB 0
MaxHeadNum:
    DB 0

OSMainInfo:
OSMEntryOffset:
    DW 7E00H
OSMSectorCount:
    DB 4
OSMEntryAddress:
    DD 07E00000H
   

Tables:
CharTable:
    DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ",0
HexNumTable:
    DB "0123456789ABCDEF",0

Strings:
EDMessage:
    DB "SYSTEM PENDING...",0DH,0AH,0
CODMessage0:
    DB "CHECKING OS DRIVE...",0DH,0AH,0
CODOKMessage:
    DB "OS DRIVE IS NORMAL.",0DH,0AH,0
LOMessage:
    DB "LOADING OS MAIN, SYSTEM KERNEL...",0DH,0AH,0
CODErrorMsg:
    DB "ERROR: CURRENT DRIVE UNAVAILABLE.",0DH,0AH,0
LOMErrorMsg:
    DB "ERROR: OS MAIN LOAD FAILED.",0DH,0AH,0


BootSectSignature:
    TIMES 508-($-$$) DB 0
    DB 42H,5AH
    DB 55H,0AAH

 


如上代码是我编写的引导扇区,其中包括一些有必要的子程序和一些可扩展用的子程序,若部分子程序不需要可以去除。

将如上代码保存为.ASM文件后,我们用命令行(在NASM文件的文件夹里SHIFT加右键鼠标并选择“OPEN COMMAND WINDOWS HERE“)打开NASM并输入命令行:

nasm -f bin -o [路径/目标文件名] [路径/源代码文件名]

在这里,我输入

nasm -f bin -o BOOTLOADER.IMG BOOTLOADER.ASM

若没有不编译错误,则生成目标文件



随后我们运行位于BOCHS文件夹下的BOCHSDBG来调试我们的引导扇区

首先设置硬盘与引导,选择Disk & Boot, 并在新弹出的窗口中我们使用如下设置一下虚拟镜像:将只需将"Type of Floppy Media"设置成1.44M,然后将映象的路径添加到"First floppy image/device",并把"Type of Floppy Media"也设置为1.44M,最后把"Status"设置为"inserted"即可

在保存设置(保存至BOCHS目录下)后我们就可以点击start来开始调试了

 

开始调试后会弹出两个窗口,可以输入命令行的窗口是调试窗口,以及另一个是引导程序输出的窗口

我们首先往调试窗口中输入“b 0x7C00",在偏移0x7C00处放下一个断点

然后我们输入两次“c”来继续(一次是BOCHS加载程序后挂起用,一次是遇上了断点来继续程序)



可以看到输出窗口处成功的输出了我们引导程序的信息,但是输出后似乎挂起了,原因是我的代码内最后一条引导程序里的指令是JMP FAR [OSENADDR](意义是跳转至07E0:0000处,因为[OSENADDR]为07E00000),一条跳转至主操作系统的程序代码,但是我们还没有编写出主操作系统,其在我们的IMG镜像文件中是不存在的,所以0x7E00处是没有代码的。

-查看更多BOCHS调试命令行:http://

 

当然有兴趣的朋友可以自己试着编写自己的子程序和完全属于自己的操作系统,我在这里为你们提供:
-实模式内存布局(全英文):http://wiki.(x86)

-BIOS中断大全查询的网址(全英文):http://www.

了解硬件I/O的朋友可以在以下网址查询到各个I/O端口:

-硬件I/O端口大全(全英文):http://bochs.

另外,可以将NASM编译器的文件夹添加到系统环境目录的PATH项下,这样以后无论在哪个目录打开命令行,都可以运行NASM编译器。


以下为各个文件的备用下载地址:
-WinHex(代替NtExplorer)-下载地址:
只有本站会员才能查看附件,请 登录


-NASM-下载地址:
只有本站会员才能查看附件,请 登录


-BOCHS-下载地址:
只有本站会员才能查看附件,请 登录


-NotePad++-下载地址:
只有本站会员才能查看附件,请 登录


-BOOTLOADER.ASM和BOOTLOADER.IMG-下载地址:
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2016-5-8 02:55编辑过]

23 回复
#2
wmf20142016-03-25 13:56
很久前研究过这些的,真的是没日没夜。现在年纪大了,没那个冲劲了。
小朋友,挺你一下,加油
#3
AXRZ2016-03-27 23:39
有谁知道从单线程进入多线程的具体方法吗?
在网上翻了个遍,但是始终没找到关于操作系统通过修改APIC来进入多线程模式的信息
#4
zhulei19782016-04-12 06:15
NASM的语法与MASM语法是不是有不同
#5
zhulei19782016-04-12 06:21
请问那个LODSB指令是做什么用的
#6
zhulei19782016-04-12 06:42
我查了一下书,LODS是从串取指令
#7
zhulei19782016-04-12 07:21
它那个数据段式定义在最后的么,我看的书不是这样的
#8
AXRZ2016-04-12 11:37
回复 7楼 zhulei1978
数据段定义的话看个人(有些编译器一定要你定义在代码段前面),一般来讲操作系统的引导扇区前三个字节是一个JMP NEAR的指令,跳开数据段。但是我比较倾向与这样写,个人喜好。

另外,数据和代码实际上是没有区别的(在内存中都是一样的储存方式),你在代码中间插一段数据段都是可以的。

NASM的语法和MASM确实有些许不同,编写操作系统不推荐MASM。我更喜欢NASM,因为比较倾向与纯正的汇编。
语法常用的不同点:
1. 段寄存器运用    MASM: MOV DS:[BX],AX    NASM: MOV [DS:BX],AX
2. 数据类型标明    MASM: MOV WORD PTR [BX],1234    NASM MOV WORD [BX],1234
3. 没有OFFSET!     MASM: MOV AX,OFFSET [DATA]    NASM:MOV AX,DATA
其他的不同都是少用的,更多语法转换: http://

[此贴子已经被作者于2016-4-29 07:44编辑过]

#9
zhulei19782016-04-12 13:33
你在代码中间插一段数据段都是可以的

 那样的话逻辑上不会混乱吗
#10
AXRZ2016-04-12 21:40
回复 9楼 zhulei1978
确实会,只是打个比方。例如,我可以在操作系统子过程的开头插入一段子过程名字的字符串,然后就可以供为其他之后运行的程序查找和使用,虽然这样效率并不高。

[此贴子已经被作者于2016-4-12 21:44编辑过]

#11
zhulei19782016-04-29 02:46
MOV AH,2
     INT 13H中断是做什么的
#12
zhulei19782016-04-29 02:52
你那个显示字符串都调用一个子程序:
 LEA SI,[CODERMSG]
 CALL WRITESTRING

汇编里面不是有中断可以直接显示字符串的吗
mov dx,offset CODERMSG
 mov ah,09h
 int 21h
 

#13
AXRZ2016-04-29 04:13
回复 12楼 zhulei1978
MOV AH,2
INT 13H
为INT13驱动器操作中的读盘操作。因为不知道硬盘的内存映射地址或I/O口,所以用INT13是最兼容的方法

INT 21里的中断为DOS中断,是DOS系统内核管理的,在编写内核是不能使用

[此贴子已经被作者于2016-4-29 04:14编辑过]

#14
zhulei19782016-04-29 04:52
哦,谢谢,又学到新的知识了。
#15
zhulei19782016-05-02 13:13
你这一句是做什么用的:
  MOV BP,0FFFFH
  MOV SP,BP
为什么要将sp置0FFFFH
#16
zhulei19782016-05-02 13:31
  PUSHAW指令是做什么的啊,是NASM里的指令吗,MASM李没有这个指令
#17
AXRZ2016-05-03 03:14
回复 15楼 zhulei1978
这是设置堆栈的指令,意义是将栈底设置为0FFFFH,并初始化栈顶。此时堆栈底的位置是0000H:FFFFH,将SP置BP(0FFFFH)其实就是初始化堆栈,此时堆栈顶的位置和堆栈底一致,没有数据被保存在堆栈中。(INTEL架构中堆栈向低地址扩展)。

堆栈其实和普通内存没有大区别(区别是数据的读取和写入方式),你可以将一块普通的内存区域变成堆栈,或反过来。

设置堆栈的原因是我们可以用比MOV访问普通内存更优化的PUSH和POP指令,这两条指令比MOV更小,并且比MOV快,在读写暂时数据(例如函数的参数)时更有优势。缺点是要按照堆栈顺序进行读写(这也是为何它们比MOV快的原因)。

[此贴子已经被作者于2016-5-3 03:21编辑过]

#18
AXRZ2016-05-03 03:26
回复 16楼 zhulei1978
PUSHAW的意义是Push All Word:
其将所有16位的通用寄存器入栈,是16位PUSHA指令的明确写法,明确入栈的寄存器为16位。

注:如果你编写的是32位的程序,那PUSHA就是入栈所有32位的通用寄存器;如果你编写的是十六位的程序,那PUSHA就是入站是所有16位的通用寄存器。但是如果是PUSHAW的就明确了入栈的为16位,PUSHAD明确入栈的为32位。
#19
zhulei19782016-05-03 04:37
恩,谢谢
还在研究你的程序
#20
dadongwushen2016-05-13 21:09
那如果用c语言编写操作系统,我们用什么程序编译啊?
#21
dadongwushen2016-05-13 22:28
顺便问一下,这操作系统的交叉编译环境c语言和汇编的用什么软件
#22
AXRZ2016-05-13 22:36
回复 21楼 dadongwushen
给个链接给你,看看有无帮助:
https://bbs.bccn.net/thread-228696-1-1.html
#23
dadongwushen2016-05-14 23:19
谢谢楼主分享,但是在Windows上搞起来还是比较麻烦
#24
xinpeng06162020-01-20 20:00
回复 楼主 AXRZ
楼主 ,那个I/O端口大全那网页好像打不开了
1