自己动手做OS——Day2

自己动手做OS——Day2

我们在昨天做了一个简单的输出一句话的小扇区,现在我们需要继续学习。

1 介绍文本编辑器

这个我们就不写了,我推荐VSCode,听说notepad++的作者是港独,鄙视他所以不用np++,不多谈了sublime也不错的。

2 继续开发

接下来,作者想让我们继续学习他写的代码,我们把这些代码直接po上来(我根据我昨天写的代码进行了一些更改,与作者的并不完全相同):

; hello-os
; TAB=4

        ORG     0x7c00          ; 指明程序的装载地址
; 以下这段是标准FAT12格式软盘专用的代码

        JMP     entry
        DB     "HELLOSON"     ; 启动区的名称可以是任意的字符串(8字节)
        DW     512            ; 每个扇区(sector)的大小(必须为512字节)
        DB     1              ; 簇(cluster)的大小(必须为1个扇区)
        DW     1        ; FAT的起始位置(一般从第一个扇区开始)
        DB     2        ; FAT的个数(必须为2)
        DW     224      ; 根目录的大小(一般设成224项)
        DW     2880     ; 该磁盘的大小(必须是2880扇区)
        DB     0xf0     ; 磁盘的种类(必须是0xf0)
        DW     9        ; FAT的长度(必须是9扇区)
        DW     18       ; 1个磁道(track)有几个扇区(必须是18)
        DW     2        ; 磁头数(必须是2)
        DD     0        ; 不使用分区,必须是0
        DD     2880     ; 重写一次磁盘大小
        DB     0,0,0x29       ; 意义不明,固定
        DD     0xffffffff     ;(可能是)卷标号码
        DB     "HELLO-OS   "  ; 磁盘的名称(11字节)
        DB     "FAT12   "     ; 磁盘格式名称(8字节)
        RESB   18             ; 先空出18字节

; 程序主体
entry:
        MOV     AX,0            ; 初始化寄存器
        MOV     SS,AX
        MOV     SP,0x7c00
        MOV     DS,AX
        MOV     ES,AX

        MOV     SI,msg
putloop:
        MOV     AL,[SI]
        ADD     SI,1            ; 给SI加1
        CMP     AL,0

        JE      fin
        MOV     AH,0x0e         ; 显示一个文字
        MOV     BX,15           ; 指定字符颜色
        INT     0x10            ; 调用显卡BIOS
        JMP     putloop
fin:
        HLT                     ; 让CPU停止,等待指令
        JMP     fin             ; 无限循环

msg:
        DB      0x0a, 0x0a      ; 换行2次
        DB      "wo shi ni die"
        DB      0x0a            ; 换行
        DB      0

		RESB	0x7dfe-$		; 补全

		DB		0x55, 0xaa

; 其余部分

		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	4600
		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	1469432

我们将它与昨天的程序做一下对比,我们不难发现,它和昨天的是一样的,只不过中间原本直接书写二进制的部分换成了一些指令罢了。那么接下来我们就来看一下这些指令都是干啥的。

  • ORG 0x7c00:告诉nask(也就是编译器)把程序装载到指定的地址,也就是说这个程序会被装载到内存中0x7c00这个地方,至于为啥,等后面再看吧。
  • entry::学过一点点汇编的都知道,这就是个标识符,用来做跳转之类的
  • MOV rs rd:就是简单的赋值语句,MOV AX,0表示把0赋给AX寄存器

下面呢就可以介绍一下一些常用的寄存器了(在这个nask指令集架构下的):

  • AX——accumulator,累加寄存器
  • CX——counter,计数寄存器
  • DX——data,数据寄存器
  • BX——base,基址寄存器
  • SP——stack pointer,栈指针寄存器
  • BP——base pointer,基址指针寄存器
  • SI——source index,源变址寄存器
  • DI——destination index,目的变址寄存器

这些寄存器都是十六位寄存器,可以用于储存、操作16位的二进制数。

除此之外,还有八个八位寄存器:

  • AL——累加寄存器低位(accumulator low)

  • CL——计数寄存器低位(counter low)

  • DL——数据寄存器低位(data low)

  • BL——基址寄存器低位(base low)

  • AH——累加寄存器高位(accumulator high)

  • CH——计数寄存器高位(counter high)

  • DH——数据寄存器高位(data high)

  • BH——基址寄存器高位(base high)

大家可能注意到了,这里的ABCD都在上面出现过,没错,这里的AL和AH共同组成了上面的AX,也就是说你更改AL的值,上面的AX也会随之改变。

除此之外,还有三十二位的寄存器:

EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,正如刚才所讲,这个实质上就是拓展后的AX等等,AX也是他们的一部分,但是不幸的是,这些寄存器的高位并不能进行直接的读取、操作。我们如果想要知道高16位是什么,就需要右移16位,使用高16位的内容覆盖掉低16位的内容才能看到高16位是什么。

当然,除了这八种之外,还有一种叫做段寄存器,下面是名字,具体的内容作者还没有讲述:

  • ES——附加段寄存器(extra segment)

  • CS——代码段寄存器(code segment)

  • SS——栈段寄存器(stack segment)

  • DS——数据段寄存器(data segment)

  • FS——没有名称(segment part 2)

  • GS——没有名称(segment part 3)

接下来,我们需要回到我们的程序,来看看我们的程序在执行过程中发生了什么。在此之前,我们先来顺序解释一下,我们看的懂的部分:

  • 最开始,告诉程序我们的代码需要装载到内存的指定位置
  • 接下来,我们进入了JMP entry语句,JMP就是jump的缩写,通过这条语句,我们跳转到了entry:标签所在的代码段
    • 在这里,我们可以加以理解,这里的entry实质上就是一个助记符,如果我们使用MOV AX, entry那么我们就会将entry所在的地址赋值给AX
    • 同理,下面的MOV AX msg就是如此,他将这段代码的起始地址赋给了AX
  • 随后,进入了entry,开始了一系列的初始化

接下来我们需要理解的是:MOV SS,AX这样的语句的作用。我们先来看一个其他的语句:MOV BYTE [678],123,首先BYTE、WORD、DWORD等英文词也都是汇编语言的保留字。这个指令是要用内存的“678”号地址来保存“123”这个数值。之所以是八位,这是因为指令里指定了“BYTE”。

当然,我们来看这样的语句:MOV WORD [678],123,这里就相当于将123这个数字,以16位的形式存储在678开头的内存区域中,如下图所示

自己动手做OS——Day2_第1张图片

如果大家自己手写过mips的cpu的话,应该非常容易理解这个,这里实际上就是SB、SH、SW的区别。那么返回我们刚才的语句**MOV SS,AX,这个实质上,就是把AX中的值赋值给了SS,但是如果语句是MOV AL, BYTE [BX]**,就是将内存BX所在的块的值赋值给AL。当然,需要注意的是:只有BX、BP、SI、DI这几个寄存器可以在这个过程中用于指定地址,也就是说如果我们想把DX内存中的值赋值给AL,需要这样写:

MOV BX, DX
MOV AL, BYTE [BX]

再往后看,

  • ADD SI,1 就是SI = SI + 1

  • CMP AL,0就是将AL中的值与0进行比较

  • CMP后面紧跟着JE fin这两句语句共同组成了一个判断语句,用C语言来写就是:if(AL == 0) goto fin;

  • 再往后看INT 0x10,这里的int就是软件中断,在电脑中有个名为BIOS的程序,出厂时就组装在电脑主板上的ROM中,里面有一些程序,这里我们使用INT 0x10实际上做的就是停止掉我们现在在干的事情,转到BIOS中的16号程序。

  • 关于上面的INT,我们可以通过这种方法来现实一些文字:

    • AH=0x0e;
    • AL=character code;
    • BH=0;
    • BL=color code;
    • 然后再调用INT x10

到此为止,如果大家用心读的话那么这个程序就非常好懂了:

首先我们来看:

msg:
        DB      0x0a, 0x0a      ; 换行2次
        DB      "wo shi ni die"
        DB      0x0a            ; 换行
        DB      0

这里是msg部分,这里就是在给他附了一堆二进制数字,这里实际上不是程序,而是一些存储字符的地方。我们需要注意的是,这里以纯0结束.

进入putloop之前,我们执行了MOV SI,msg也就是说,我们把msg的首地址赋值给了SI

接下来就进入了PUTLOOP:

putloop:
        MOV     AL,[SI]
        ADD     SI,1            ; 给SI加1
        CMP     AL,0

        JE      fin
        MOV     AH,0x0e         ; 显示一个文字
        MOV     BX,15           ; 指定字符颜色
        INT     0x10            ; 调用显卡BIOS
        JMP     putloop

这里我们会把对应内存部分(也就是代码段中的内容存储到AL中),然后再进行输出的例程,当然,如果我们的内容为0的话,就会退出,这也说明,如果是有字符的话,这里不会全0的。

如果全0的话,就会进入fin:

fin:
        HLT                     ; 让CPU停止,等待指令
        JMP     fin             ; 无限循环

这里的HLT就是halt,这个就是停止的意思,这条语句会让计算机基本处于睡眠状态,可以省很多电。如果不加的话,计算机就会疯狂执行jump,这样负荷非常高。

最后再来解释ORG 0x7c00,这里是要把我们的程序装配到这个地方,之所以是0x7c00是因为在前面的部分还装配着bios的程序。

0x00007c00-0x00007dff :启动区内容的装载地址

如果你真正的理解这个程序的话,你就会发现我前面写的那个长的代码有错,具体来说是多了一块:

; hello-os
; TAB=4

        ORG     0x7c00          ; 指明程序的装载地址
; 以下这段是标准FAT12格式软盘专用的代码

        JMP     entry
        DB     "HELLOSON"     ; 启动区的名称可以是任意的字符串(8字节)
        DW     512            ; 每个扇区(sector)的大小(必须为512字节)
        DB     1              ; 簇(cluster)的大小(必须为1个扇区)
        DW     1        ; FAT的起始位置(一般从第一个扇区开始)
        DB     2        ; FAT的个数(必须为2)
        DW     224      ; 根目录的大小(一般设成224项)
        DW     2880     ; 该磁盘的大小(必须是2880扇区)
        DB     0xf0     ; 磁盘的种类(必须是0xf0)
        DW     9        ; FAT的长度(必须是9扇区)
        DW     18       ; 1个磁道(track)有几个扇区(必须是18)
        DW     2        ; 磁头数(必须是2)
        DD     0        ; 不使用分区,必须是0
        DD     2880     ; 重写一次磁盘大小
        DB     0,0,0x29       ; 意义不明,固定
        DD     0xffffffff     ;(可能是)卷标号码
        DB     "HELLO-OS   "  ; 磁盘的名称(11字节)
        DB     "FAT12   "     ; 磁盘格式名称(8字节)
        RESB   18             ; 先空出18字节

; 程序主体
entry:
        MOV     AX,0            ; 初始化寄存器
        MOV     SS,AX
        MOV     SP,0x7c00
        MOV     DS,AX
        MOV     ES,AX

        MOV     SI,msg
putloop:
        MOV     AL,[SI]
        ADD     SI,1            ; 给SI加1
        CMP     AL,0

        JE      fin
        MOV     AH,0x0e         ; 显示一个文字
        MOV     BX,15           ; 指定字符颜色
        INT     0x10            ; 调用显卡BIOS
        JMP     putloop
fin:
        HLT                     ; 让CPU停止,等待指令
        JMP     fin             ; 无限循环

msg:
        DB      0x0a, 0x0a      ; 换行2次
        DB      "wo shi ni die"
        DB      0x0a            ; 换行
        DB      0

        RESB	0x7dfe-$		; 补全

        DB		0x55, 0xaa

接下来是第二部分。

3 先制作启动区

这里作者做的事情就是把这个文件改了个名,然后改了一下所谓的编译方式,我们来看一下他这个改后的编译方式是怎么样的。

首先:

..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst

这里同时将这个nas 编译成了 .bin.lst文件。这个.lst是一个文本文件,可以用来简单地确认每个指令是怎样翻译成机器语言的。

所以我们改一下我们的asm.bat

C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools\nask.exe .\ipl.nas .\ipl.bin  .\ipl.lst

我们看一下这个ipl.lst

     1 00000000                                 ; hello-os
     2 00000000                                 ; TAB=4
     3 00000000                                 
     4                                                  ORG     0x7c00          ; 指明程序的装载地址
     5 00007C00                                 ; 以下这段是标准FAT12格式软盘专用的代码
     6 00007C00                                 
     7 00007C00 EB 4D                                   JMP     entry
     8 00007C02 48 45 4C 4C 4F 53 4F 4E                 DB     "HELLOSON"     ; 启动区的名称可以是任意的字符串(8字节)
     9 00007C0A 0200                                    DW     512            ; 每个扇区(sector)的大小(必须为512字节)
    10 00007C0C 01                                      DB     1              ; 簇(cluster)的大小(必须为1个扇区)
    11 00007C0D 0001                                    DW     1        ; FAT的起始位置(一般从第一个扇区开始)
    12 00007C0F 02                                      DB     2        ; FAT的个数(必须为2)
    13 00007C10 00E0                                    DW     224      ; 根目录的大小(一般设成224项)
    14 00007C12 0B40                                    DW     2880     ; 该磁盘的大小(必须是2880扇区)
    15 00007C14 F0                                      DB     0xf0     ; 磁盘的种类(必须是0xf0)
    16 00007C15 0009                                    DW     9        ; FAT的长度(必须是9扇区)
    17 00007C17 0012                                    DW     18       ; 1个磁道(track)有几个扇区(必须是18)
    18 00007C19 0002                                    DW     2        ; 磁头数(必须是2)
    19 00007C1B 00000000                                DD     0        ; 不使用分区,必须是0
    20 00007C1F 00000B40                                DD     2880     ; 重写一次磁盘大小
    21 00007C23 00 00 29                                DB     0,0,0x29       ; 意义不明,固定
    22 00007C26 FFFFFFFF                                DD     0xffffffff     ;(可能是)卷标号码
    23 00007C2A 48 45 4C 4C 4F 2D 4F 53 20 20           DB     "HELLO-OS   "  ; 磁盘的名称(11字节)
       00007C34 20 
    24 00007C35 46 41 54 31 32 20 20 20                 DB     "FAT12   "     ; 磁盘格式名称(8字节)
    25 00007C3D 00 00 00 00 00 00 00 00 00 00           RESB   18             ; 先空出18字节
       00007C47 00 00 00 00 00 00 00 00 
    26 00007C4F                                 
    27 00007C4F                                 ; 程序主体
    28 00007C4F                                 entry:
    29 00007C4F B8 0000                                 MOV     AX,0            ; 初始化寄存器
    30 00007C52 8E D0                                   MOV     SS,AX
    31 00007C54 BC 7C00                                 MOV     SP,0x7c00
    32 00007C57 8E D8                                   MOV     DS,AX
    33 00007C59 8E C0                                   MOV     ES,AX
    34 00007C5B                                 
    35 00007C5B BE 7C73                                 MOV     SI,msg
    36 00007C5E                                 putloop:
    37 00007C5E 8A 04                                   MOV     AL,[SI]
    38 00007C60 83 C6 01                                ADD     SI,1            ; 给SI加1
    39 00007C63 3C 00                                   CMP     AL,0
    40 00007C65                                 
    41 00007C65 74 09                                   JE      fin
    42 00007C67 B4 0E                                   MOV     AH,0x0e         ; 显示一个文字
    43 00007C69 BB 000F                                 MOV     BX,15           ; 指定字符颜色
    44 00007C6C CD 10                                   INT     0x10            ; 调用显卡BIOS
    45 00007C6E EB EE                                   JMP     putloop
    46 00007C70                                 fin:
    47 00007C70 F4                                      HLT                     ; 让CPU停止,等待指令
    48 00007C71 EB FD                                   JMP     fin             ; 无限循环
    49 00007C73                                 
    50 00007C73                                 msg:
    51 00007C73 0A 0A                                   DB      0x0a, 0x0a      ; 换行2次
    52 00007C75 77 6F 20 73 68 69 20 6E 69 20           DB      "wo shi ni die"
       00007C7F 64 69 65 
    53 00007C82 0A                                      DB      0x0a            ; 换行
    54 00007C83 00                                      DB      0
    55 00007C84                                 
    56 00007C84 00 00 00 00 00 00 00 00 00 00           RESB	0x7dfe-$		; 补全
       00007C8E 00 00 00 00 00 00 00 00 00 00 
       00007C98 00 00 00 00 00 00 00 00 00 00 
       00007CA2 00 00 00 00 00 00 00 00 00 00 
       00007CAC 00 00 00 00 00 00 00 00 00 00 
       00007CB6 00 00 00 00 00 00 00 00 00 00 
       00007CC0 00 00 00 00 00 00 00 00 00 00 
       00007CCA 00 00 00 00 00 00 00 00 00 00 
       00007CD4 00 00 00 00 00 00 00 00 00 00 
       00007CDE 00 00 00 00 00 00 00 00 00 00 
       00007CE8 00 00 00 00 00 00 00 00 00 00 
       00007CF2 00 00 00 00 00 00 00 00 00 00 
       00007CFC 00 00 00 00 00 00 00 00 00 00 
       00007D06 00 00 00 00 00 00 00 00 00 00 
       00007D10 00 00 00 00 00 00 00 00 00 00 
       00007D1A 00 00 00 00 00 00 00 00 00 00 
       00007D24 00 00 00 00 00 00 00 00 00 00 
       00007D2E 00 00 00 00 00 00 00 00 00 00 
       00007D38 00 00 00 00 00 00 00 00 00 00 
       00007D42 00 00 00 00 00 00 00 00 00 00 
       00007D4C 00 00 00 00 00 00 00 00 00 00 
       00007D56 00 00 00 00 00 00 00 00 00 00 
       00007D60 00 00 00 00 00 00 00 00 00 00 
       00007D6A 00 00 00 00 00 00 00 00 00 00 
       00007D74 00 00 00 00 00 00 00 00 00 00 
       00007D7E 00 00 00 00 00 00 00 00 00 00 
       00007D88 00 00 00 00 00 00 00 00 00 00 
       00007D92 00 00 00 00 00 00 00 00 00 00 
       00007D9C 00 00 00 00 00 00 00 00 00 00 
       00007DA6 00 00 00 00 00 00 00 00 00 00 
       00007DB0 00 00 00 00 00 00 00 00 00 00 
       00007DBA 00 00 00 00 00 00 00 00 00 00 
       00007DC4 00 00 00 00 00 00 00 00 00 00 
       00007DCE 00 00 00 00 00 00 00 00 00 00 
       00007DD8 00 00 00 00 00 00 00 00 00 00 
       00007DE2 00 00 00 00 00 00 00 00 00 00 
       00007DEC 00 00 00 00 00 00 00 00 00 00 
       00007DF6 00 00 00 00 00 00 00 00 
    57 00007DFE                                 
    58 00007DFE 55 AA                                   DB		0x55, 0xaa

可以看到,左边的是二进制,右边的是相应的代码

接下来有一个用这个bin来做一个img的文件:

C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools\edimg.exe imgin:C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools\fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img 

然后启动部分不变就可以了。

4 Makefile入门

我嫌每次写路径都太麻烦了,于是我就将z_tools这个文件夹加到环境变量里面了,这样我们就可以不用再去写路径,而是直接make或是直接nask就可以调用我们之前的那个.exe文件了。

接下来我们学习一下作者的这个文件:

#文件生成规则

ipl.bin : ipl.nas Makefile
    nask ipl.nas ipl.bin ipl.lst

helloos.img : ipl.bin Makefile
    edimg.exe   imgin:fdimg0at.tek \
        wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img

这里两个高亮的,就是我们想要生成的东西也就是ipl.binhelloos.img

那么需要生成ipl.bin需要先检查是否有ipl.nas和Makefile两个文件,如果没有的话会报错,为了生成它执行的语句是nask ipl.nas ipl.bin ipl.lst

为了方便删除,我们将这个文件改成这样:

#文件生成规则

helloos.img : ipl.bin Makefile
	C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools\edimg.exe   imgin:C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools\fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img

ipl.bin : ipl.nas Makefile
	nask.exe .\ipl.nas .\ipl.bin .\ipl.lst

clean:
	rm ipl.bin ipl.lst helloos.img

我们可以输入make来生成,同时可以输入make clean来删除输入的东西,接下来,我们将原有的功能进行整合,做一个新的文件就可以了。

当然,在此之前,我们需要先来研究一下这个makefile文件的格式,第一行是我们的这个最终的生成目标,或者是叫做默认的生成目标,它需要依赖的是ipl.bin,如果这个文件夹下有ipl.bin,那么它就拿来直接用了,如果没有他就会从其他的目标中去寻找ipl.bin,并且执行相应语句进行生成。当然除此之外还可以像clean一样,这种不生成目标而执行语句的,我们可以使用make clean的方法来调用下面相应的语句。

那么我们现在希望能够达到的效果是:

make clean
make 
make run

输入这三句话,第一句话负责将原来的东西删除掉

第二句话负责生成相应的文件

第三句话进行装配与启动,我们现在还需要来写一个run,说白了就是直接把原来的run.bat直接搬运过来就好了

最后整完之后,我们调整了一下,变成了这个样子:

#文件生成规则

TOOLS_DIR=C:\Users\wangsy\Desktop\WSY-OS\REF\tolset\z_tools
QEMU_DIR=$(TOOLS_DIR)\qemu

helloos.img : ipl.bin Makefile
	edimg.exe   imgin:$(TOOLS_DIR)\fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img

ipl.bin : ipl.nas Makefile
	nask.exe .\ipl.nas .\ipl.bin .\ipl.lst


clean:
	rm ipl.bin ipl.lst helloos.img

run: 
	rm $(QEMU_DIR)\fdimage0.bin
	cp helloos.img $(QEMU_DIR)\fdimage0.bin
	make.exe -C $(QEMU_DIR)

这里面涉及到了一点点变量的概念,聪明如你,肯定能看得出来

你可能感兴趣的:(OS)