├── .gitignore ├── OS ├── Backup │ ├── dingst(empty).vhd │ └── dingst(init).vhd ├── day1 │ ├── Makefile │ ├── day1.asm │ ├── day1.md │ ├── make.bat │ └── src │ │ └── VMWare.png ├── day2 │ ├── Makefile │ ├── day2.asm │ ├── day2.md │ ├── loader.asm │ ├── make.bat │ └── src │ │ ├── OKMBR.png │ │ └── diskandmemory.png ├── day3 │ ├── Makefile │ ├── day2.asm │ ├── day3.asm │ ├── day3.md │ ├── loader.asm │ ├── make.bat │ └── src │ │ └── DX.jpg ├── day4 │ ├── Makefile │ ├── day2.asm │ ├── day4.asm │ ├── day4.c │ ├── day4.md │ ├── loader.asm │ ├── make.bat │ └── src │ │ ├── KRAW.BIN.png │ │ ├── OS.png │ │ └── kernel.bin.png ├── day5 │ ├── Makefile │ ├── com.sh │ ├── day2.asm │ ├── day4.asm │ ├── day5.md │ ├── liba.asm │ ├── libc.c │ ├── loader.asm │ ├── make.bat │ ├── osKernel.asm │ ├── src │ │ ├── OS.png │ │ └── callFrame.png │ └── stringio.h ├── day6 │ ├── Makefile │ ├── day2.asm │ ├── day4.asm │ ├── day6.md │ ├── liba.asm │ ├── libc.c │ ├── loader.asm │ ├── make.bat │ ├── osKernel.asm │ └── stringio.h ├── day7 │ ├── Makefile │ ├── day2.asm │ ├── day4.asm │ ├── day7.md │ ├── liba.asm │ ├── libc.c │ ├── loader.asm │ ├── make.bat │ ├── osKernel.asm │ ├── pm.asm │ ├── src │ │ ├── DW.png │ │ ├── OS.png │ │ ├── SELECTOR.png │ │ └── descriptor.png │ └── stringio.h ├── day8 │ └── day8.md └── tools │ ├── dd.exe │ ├── ld.exe │ ├── make.exe │ └── nasm │ ├── LICENSE │ ├── Uninstall.exe │ ├── contrib │ └── VSrules │ │ ├── nasm.README │ │ └── nasm.rules │ ├── nasm.exe │ ├── nasm.ico │ ├── nasmdoc.pdf │ ├── nasmpath.bat │ ├── ndisasm.exe │ └── rdoff │ ├── ldrdf.exe │ ├── rdf2bin.exe │ ├── rdf2com.exe │ ├── rdf2ihx.exe │ ├── rdf2ith.exe │ ├── rdf2srec.exe │ ├── rdfdump.exe │ ├── rdflib.exe │ └── rdx.exe └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | temp/ 2 | dingst.vhd 3 | *.bin 4 | -------------------------------------------------------------------------------- /OS/Backup/dingst(empty).vhd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/Backup/dingst(empty).vhd -------------------------------------------------------------------------------- /OS/Backup/dingst(init).vhd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/Backup/dingst(init).vhd -------------------------------------------------------------------------------- /OS/day1/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 默认动作 3 | 4 | default : 5 | ../tools/make.exe boot 6 | 7 | # 镜像文件生成 8 | boot.bin : day1.asm Makefile 9 | ../tools/nasm/nasm.exe day1.asm -o boot.bin 10 | 11 | # 其他指令 12 | boot: 13 | ../tools/make.exe -r boot.bin 14 | 15 | 16 | -------------------------------------------------------------------------------- /OS/day1/day1.asm: -------------------------------------------------------------------------------- 1 | ORG 07C00H ;告诉编译器程序被加载的地址为0x7c00 2 | MOV AX, CS 3 | MOV DS, AX 4 | MOV ES, AX 5 | CALL SHOW 6 | JMP $ 7 | SHOW: 8 | MOV AX, MESSAGE 9 | MOV BP, AX 10 | MOV CX, 16 11 | MOV AX, 01301H ;AH = 13H AL = 01H 12 | MOV BX, 000CH ;页号为0 BH = 0 红底黑字 BL = 0CH 13 | MOV DL, 0 14 | INT 10H 15 | RET 16 | MESSAGE: DB "HELLO, OS WORLD!" 17 | TIMES 510 - ($ - $$) DB 0 ;剩下的字节全部使用0填充 18 | 19 | DW 0XAA55 ; 20 | -------------------------------------------------------------------------------- /OS/day1/day1.md: -------------------------------------------------------------------------------- 1 | # Day 1 2 | 3 | 自制一个操作系统,你需要以下工具: 4 | 5 | * `Notepad++`,当然,其它文本编辑器也可以替代 6 | * `VMWare`虚拟机 7 | * `16`进制编辑器 8 | * `nasm`,可以去网上下载 9 | 10 | ## 建立一个汇编文件 11 | 12 | ```assembly 13 | ORG 07C00H ;告诉编译器程序被加载的地址为0x7c00 14 | MOV AX, CS 15 | MOV DS, AX 16 | MOV ES, AX 17 | CALL SHOW 18 | JMP $ 19 | SHOW: 20 | MOV AX, MESSAGE 21 | MOV BP, AX 22 | MOV CX, 16 23 | MOV AX, 01301H ;AH = 13H AL = 01H 24 | MOV BX, 000CH ;页号为0 BH = 0 红底黑字 BL = 0CH 25 | MOV DL, 0 26 | INT 10H 27 | RET 28 | MESSAGE: DB "HELLO, OS WORLD!" 29 | TIMES 510 - ($ - $$) DB 0 ;剩下的字节全部使用0填充 30 | DW 0XAA55 ; 31 | ``` 32 | 33 | 这里我们简单理解一下汇编代码,如果有汇编的基础,上面代码应该容易理解。首先,一条`ORG`指令,将设定程序的加载地址,这一地址也是默认的地址。 34 | 35 | 紧接着,初始化寄存器,然后调用子过程,最后死循环(`jmp $`) 36 | 37 | 子过程中,会调用`BIOS`系统调用`0x10`,向屏幕打印一行字符串`HELLO, OS WORLD!`。 38 | 39 | 最后,用0填充文件大小,最后两个字节分别是`0x55`和`0xaa`。为什么是这两个数字呢,这就类似于`PE`文件或者`ELF`文件的幻数(`Magic number`)一样,只有设定为这一值,系统才会认为这是一个可引导的扇区。汇编中注意小端序`0xAA55`。 40 | 41 | ## 用 nasm 编译文件 42 | 43 | 这里如果没有配置环境变量,可以在`nasm`文件夹下,打开`cmd`,输入命令,注意文件路径 44 | 45 | ```bash 46 | nasm.exe day1.asm -o boot.bin 47 | ``` 48 | 49 | 当然,我们也可以使用`Makefile`,写好脚本,帮助我们执行这一流程 50 | 51 | ```makefile 52 | # 默认动作 53 | default : 54 | ../tools/make.exe boot 55 | 56 | # 镜像文件生成 57 | boot.bin : day1.asm Makefile 58 | ../tools/nasm/nasm.exe day1.asm -o boot.bin 59 | 60 | # 其他指令 61 | boot: 62 | ../tools/make.exe -r boot.bin 63 | ``` 64 | 65 | ## 加电开机 66 | 67 | 最后,打开`VMWare`,创建一个虚拟机,类别均选择其它,采用软盘加载我们的`boot.bin`文件。开机后,我们便能够看到以下画面。 68 | 69 | ![VMWare](src/VMWare.png) 70 | 71 | 这也说明我们主引导扇区加载完成。 72 | 73 | ## [Day 2](OS/Day2/day2.md) -------------------------------------------------------------------------------- /OS/day1/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day1/src/VMWare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day1/src/VMWare.png -------------------------------------------------------------------------------- /OS/day2/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 默认动作 3 | TOOL_PATH = ../tools/ 4 | ASSEMBLE = ../tools/nasm/ 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | default : 13 | $(MAKE) mdr 14 | $(MAKE) loader 15 | 16 | # 镜像文件生成 17 | mdr.bin : day2.asm Makefile 18 | $(NASM) day2.asm -o mdr.bin 19 | 20 | loader.bin : loader.asm Makefile 21 | $(NASM) loader.asm -o loader.bin 22 | # 其他指令 23 | mdr: 24 | $(MAKE) mdr.bin 25 | 26 | loader: 27 | $(MAKE) loader.bin 28 | 29 | clean: 30 | -$(DEL) *.bin 31 | 32 | add: 33 | $(DD) if=mdr.bin of=dingst.vhd bs=512 count=1 34 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 35 | 36 | backup: 37 | -$(DEL) dingst.vhd 38 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 39 | 40 | run: 41 | -$(DEL) ..\dingst.vhd 42 | $(COPY) dingst.vhd ..\dingst.vhd -------------------------------------------------------------------------------- /OS/day2/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day2/day2.md: -------------------------------------------------------------------------------- 1 | # Day 2 2 | 3 | ## 磁盘加载 4 | 5 | 在上一讲中,我们只是制作了一个大小仅为`512 Byte`的软盘,现在我们需要给我们的操作系统分配一定空间的硬盘。 6 | 7 | 打开`cmd`,输入`diskpart`,创建一个虚拟磁盘,给出指定路径,然后给出大小,这里为`10 MB`。 8 | 9 | ```bash 10 | create vdisk file = E:\Git\gitcode\blog\docs\OS\dingst.vhd maximum = 10 type=fixed 11 | ``` 12 | 13 | 然后,我们将上一次得到的软盘内容,写入到这个磁盘里面,这里可以使用`dd.exe`工具(其实是`linux`的一个命令)。这里使用`git bash`可能会失败,最好还是下载一个`dd.exe`。 14 | 15 | ```bash 16 | dd if=boot.bin of=dingst.vhd bs=512 count=1 17 | ``` 18 | 19 | 此时,我们准备好了一块可装载的磁盘,在创建虚拟机时,采用已有磁盘,并添加我们的`dingst.vhd`即可。加电开机,可以发现,屏幕成功输出了对应字符串。 20 | 21 | ## 突破 512 Byte 的限制 22 | 23 | 现在我们的磁盘只有第一个扇区,即前`512 Byte`是有效的,其它部分基本都是`0`。我们将继续写入一部分内容。这里我们依旧会用到`Makefile`,方便我们不断调试。这一块的思路如下: 24 | 25 | 在主引导扇区中,我们需要新增一些代码,`day1`中,我们仅仅输出了一个字符串,`512 Byte`还能做更多的事,因此我们添加的程序要能够帮我们读磁盘的第二个扇区,将第二个扇区的内容,加载至内存`0x900`处,这个地址可以自己选择,但是有一定的范围。 26 | 27 | 我们可以先看看哪些地址是你可以选择的。 28 | 29 | | 起始 | 结束 | 大小 | 用途 | 30 | | :-----: | :-----: | :--------: | :----------------------------------------------------------: | 31 | | `FFFF0` | `FFFFF` | `16 B` | `BIOS` 入口地址,此地址也属于`BIOS`代码,这`16`字节的内容是为了执行跳转指令 | 32 | | `F0000` | `FFFEF` | `64KB-16B` | 系统`BIOS`的地址范围实际上是`F000-FFFFF`,上面是入口地址,所以单独列出 | 33 | | `C8000` | `EFFFF` | `160KB` | 映射硬件适配器的`ROM`或内存映射式`I/O` | 34 | | `C0000` | `C7FFF` | `32KB` | 显示适配器`BIOS` | 35 | | `B8000` | `BFFFF` | `32KB` | 文本模式显示适配器 | 36 | | `B0000` | `B7FFF` | `32KB` | 黑白显示适配器 | 37 | | `A0000` | `AFFFF` | `64KB` | 彩色显示适配器 | 38 | | `9FC00` | `9F000` | `1KB` | `EBDA(Extended BIOS Data Area)` 扩展`BIOS`数据区 | 39 | | `7E00` | `9FBFF` | `≈608KB` | 可区域用 | 40 | | `7C00` | `7DFF` | `512B` | `MBR`被`BIOS`加载区域 | 41 | | `500` | `7BFF` | `≈30KB` | 可区域用 | 42 | | `400` | `4FF` | `256B` | `BIOS Data Area` | 43 | | `000` | `3FF` | `1KB` | `Interrupt Vector Table` 中断向量表 | 44 | 45 | 在这张实模式下内存布局的图中,我们可以看到,只有两个区域是我们可以使用的,其它区域均有数据。而我们选择的区域`0x9000`确实是落在可用区域中。 46 | 47 | 接下来,我们创建一个`day2.asm`汇编文件。 48 | 49 | ```assembly 50 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 51 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 52 | 53 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 54 | ;初始化寄存器 55 | MOV AX, CS 56 | MOV DS, AX 57 | MOV ES, AX 58 | MOV SS, AX 59 | MOV FS, AX 60 | MOV SP, 0X7C00 61 | MOV AX, 0XB800 ;显存位置 62 | MOV GS, AX 63 | ;调用BIOS 0x10中断 64 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 65 | MOV BX, 0X0700 ;BH 上卷行的属性 66 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 67 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 68 | INT 10H ;调用BIOS 0x10中断 69 | ;输出 70 | MOV BYTE [GS: 0X00], '*' 71 | MOV BYTE [GS: 0X01], 0XA4 72 | 73 | MOV BYTE [GS: 0X02], '*' 74 | MOV BYTE [GS: 0X03], 0XA4 75 | 76 | MOV BYTE [GS: 0X04], 'M' 77 | MOV BYTE [GS: 0X05], 0XA4 78 | 79 | MOV BYTE [GS: 0X06], 'B' 80 | MOV BYTE [GS: 0X07], 0XA4 81 | 82 | MOV BYTE [GS: 0X08], 'R' 83 | MOV BYTE [GS: 0X09], 0XA4 84 | 85 | ;读LOADER 86 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 87 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 88 | MOV CX, 1 ;读一个扇区 89 | CALL READ_DISK 90 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 91 | 92 | ;读扇区 93 | READ_DISK: 94 | ;EAX LBA 扇区号 95 | ;BX 数据写入内存的地址 96 | ;CX 读入扇区数 97 | 98 | ;保存寄存器 EAX CX 99 | MOV ESI, EAX 100 | MOV DI, CX 101 | ;设置读取扇区的数量 102 | ;读写硬盘 103 | MOV DX, 0X1F2 ; 104 | MOV AL, CL ; 105 | OUT DX, AL 106 | 107 | ;恢复 EAX 108 | MOV EAX, ESI 109 | 110 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 111 | ;0-7 写入0x1f3 112 | MOV DX, 0X1F3 113 | OUT DX, AL 114 | ;8 - 15 写入0x1f4 115 | MOV CL, 8 116 | SHR EAX, CL 117 | MOV DX, 0X1F4 118 | OUT DX, AL 119 | ;16 - 23 写入0x1f5 120 | SHR EAX, CL 121 | AND AL, 0X0F 122 | MOV DX, 0X1F5 123 | OUT DX, AL 124 | ;24 - 27 125 | SHR EAX, CL 126 | AND AL, 0X0F 127 | OR AL, 0XE0 ;AL = 1110 0000 128 | MOV DX, 0X1F6 129 | OUT DX, AL 130 | ;向0x1f7 写入读命令 131 | MOV DX, 0X1F7 132 | MOV AL, 0X20 ;表示读命令 133 | 134 | OUT DX, AL 135 | ;检测硬盘状态 136 | .NOT_READY: 137 | NOP 138 | ;读取端口,查看状态 139 | IN AL, DX 140 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 141 | CMP AL, 0X08 142 | JNZ .NOT_READY 143 | ;读数据 144 | MOV AX, DI ;这里 DI = 1 145 | MOV DX, 512 146 | MUL DX 147 | MOV CX, AX 148 | MOV DX, 0X1F0 149 | .GO_ON: 150 | IN AX, DX 151 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 152 | ADD BX, 2 153 | LOOP .GO_ON 154 | RET 155 | 156 | TIMES 510 - ($ - $$) DB 0 157 | DB 0X55, 0XAA 158 | ``` 159 | 160 | 汇编代码有些长,我们一段一段的看,首先第一部分是初始化寄存器,并且清空屏幕,向屏幕中写入`**MBR`,这里是将段寄存器设置为`0XB800`,然后向这块地址写入数据,这个地址的含义可以看上面的表格,其实就是文本模式的首地址。 161 | 162 | 第二部分就是读取`Loader`,即第二个扇区。这里用到的`I/O`读写。所以会有`IN/OUT`这样的指令。这一块的内容,端口号的含义部分可以参考一些[博客](https://blog.csdn.net/fjlq1994/article/details/49472827),其实就是对特定的一些端口操作。 163 | 164 | 这里`EAX = 2`,表示读入的`LBA`编号,即第`2`个扇区;`BX = 0X900`,这是我们要写入的内存起始地址。这个`LOOP`部分,就是在不断循环将磁盘的内容读入到内存。 165 | 166 | 第三部分,这个`CALL READ_DISK`子过程调用完毕以后,程序会跳转至内存`0x900`,也就是我们加载的第二个扇区的地方。 167 | 168 | 最后一部分的代码,我们在上一节就很熟悉了,填充`0`,最后两个字节是魔术字段`55aa`。 169 | 170 | 上面这个汇编文件最后汇编会产生一个`512 Byte`的二进制文件被存储在磁盘的第一个扇区。至此,我们还差一个`loader.asm`,这个汇编文件汇编得到的二进制文件将被存储在磁盘的第二个扇区。 171 | 172 | ```assembly 173 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 174 | SECTION LOADER VSTART=LOADER_BASE_ADDR 175 | 176 | MOV AX, 0XB800 ;显存位置 177 | MOV ES, AX 178 | 179 | MOV BYTE[ES: 0X00], 'O' 180 | MOV BYTE[ES: 0X01], 0X07 181 | MOV BYTE[ES: 0X02], 'K' 182 | MOV BYTE[ES: 0X03], 0X06 183 | JMP $ 184 | ``` 185 | 186 | 这个`loader.asm`的代码和`day2.asm`的起始部分有些相似,起始就是向屏幕输出一些内容。所以如果我们能够突破`512 Byte`,成功加载第二个扇区,那么屏幕上应该显示`OKMBR`,即开始的两个`**`被`OK`所覆盖。 187 | 188 | 这里稍微有一点绕,让我们画一张图。 189 | 190 | ![memoryanddisk](src/diskandmemory.png) 191 | 192 | 扇区`1`中存放着向屏幕输出字符串`**MBR`和加载扇区`2`到内存`0x900`的代码,之后跳转到内存`0x900`,而扇区`2`中,存放着向屏幕输出字符串`OK`。逻辑就是这样的。 193 | 194 | ## Makefile帮助我们调试 195 | 196 | 在上一节中,我们已经用到了`Makefile`,这里我们继续使用,当然你也可以自己每次都去重新逐个汇编、写入磁盘。 197 | 198 | ```makefile 199 | 200 | # 默认动作 201 | TOOL_PATH = ../tools/ 202 | ASSEMBLE = ../tools/nasm/ 203 | 204 | MAKE = $(TOOL_PATH)make.exe -r 205 | NASM = $(ASSEMBLE)nasm.exe 206 | DD = $(TOOL_PATH)dd.exe 207 | DEL = del 208 | COPY = copy 209 | 210 | default : 211 | $(MAKE) mdr 212 | $(MAKE) loader 213 | 214 | # 镜像文件生成 215 | mdr.bin : day2.asm Makefile 216 | $(NASM) day2.asm -o mdr.bin 217 | 218 | loader.bin : loader.asm Makefile 219 | $(NASM) loader.asm -o loader.bin 220 | # 其他指令 221 | mdr: 222 | $(MAKE) mdr.bin 223 | 224 | loader: 225 | $(MAKE) loader.bin 226 | 227 | clean: 228 | -$(DEL) *.bin 229 | 230 | add: 231 | $(DD) if=mdr.bin of=dingst.vhd bs=512 count=1 232 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 233 | 234 | backup: 235 | -$(DEL) dingst.vhd 236 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 237 | 238 | run: 239 | -$(DEL) ..\dingst.vhd 240 | $(COPY) dingst.vhd ..\dingst.vhd 241 | ``` 242 | 243 | 这里大家根据自己的路径编写即可,主要工作: 244 | 245 | 将两个汇编文件汇编为二进制文件,同时使用`dd.exe`,将二进制文件写入到我们创建好的虚拟磁盘中,为了避免实验失败,我们对虚拟磁盘做了备份,可以使用`make backup`,使磁盘恢复空的状态。运行的命令如下: 246 | 247 | ```bash 248 | make 249 | make add 250 | make run 251 | ``` 252 | 253 | 此后,打开虚拟机,我们就能够看到结果。 254 | 255 | ![OKMBR](src/OKMBR.png) 256 | 257 | 这表明,我们已经能够突破`512 Byte`的限制,当然你可以加载更多的磁盘扇区。 258 | 259 | ## [Day 3](OS/day3/day3.md) -------------------------------------------------------------------------------- /OS/day2/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | SECTION LOADER VSTART=LOADER_BASE_ADDR 3 | 4 | MOV AX, 0XB800 ;显存位置 5 | MOV ES, AX 6 | 7 | MOV BYTE[ES: 0X00], 'O' 8 | MOV BYTE[ES: 0X01], 0X07 9 | MOV BYTE[ES: 0X02], 'K' 10 | MOV BYTE[ES: 0X03], 0X06 11 | JMP $ 12 | -------------------------------------------------------------------------------- /OS/day2/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day2/src/OKMBR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day2/src/OKMBR.png -------------------------------------------------------------------------------- /OS/day2/src/diskandmemory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day2/src/diskandmemory.png -------------------------------------------------------------------------------- /OS/day3/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 默认动作 3 | TOOL_PATH = ../tools/ 4 | ASSEMBLE = ../tools/nasm/ 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | default : 13 | $(MAKE) mbr 14 | $(MAKE) kernel 15 | $(MAKE) loader 16 | 17 | # 镜像文件生成 18 | kernel.bin : day3.asm Makefile 19 | $(NASM) day3.asm -o kernel.bin 20 | 21 | loader.bin: loader.asm Makefile 22 | $(NASM) loader.asm -o loader.bin 23 | 24 | mbr.bin : day2.asm Makefile 25 | $(NASM) day2.asm -o mbr.bin 26 | 27 | 28 | # 其他指令 29 | kernel: 30 | $(MAKE) kernel.bin 31 | 32 | loader: 33 | $(MAKE) loader.bin 34 | 35 | mbr: 36 | $(MAKE) mbr.bin 37 | 38 | clean: 39 | -$(DEL) *.bin 40 | 41 | add: 42 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 43 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 44 | $(DD) if=kernel.bin of=dingst.vhd bs=512 count=1 seek=9 45 | 46 | backup: 47 | -$(DEL) dingst.vhd 48 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 49 | 50 | run: 51 | -$(DEL) ..\dingst.vhd 52 | $(COPY) dingst.vhd ..\dingst.vhd 53 | -------------------------------------------------------------------------------- /OS/day3/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day3/day3.asm: -------------------------------------------------------------------------------- 1 | KERNEL_BASE_ADDR EQU 0X1500 2 | SECTION LOADER VSTART=KERNEL_BASE_ADDR 3 | MOV AX, 0XB800 4 | MOV ES, AX 5 | 6 | MOV BYTE [ES: 0X00], 'D' 7 | MOV BYTE [ES: 0X01], 0X07 8 | MOV BYTE [ES: 0X02], 'X' 9 | MOV BYTE [ES: 0X03], 0X06 10 | JMP $ 11 | -------------------------------------------------------------------------------- /OS/day3/day3.md: -------------------------------------------------------------------------------- 1 | # Day 3 2 | 3 | ## 模拟加载 kernel 4 | 5 | 上一节中,我们通过磁盘,加载了额外的一个扇区到内存,但操作系统的内核远不止一个扇区那么大,毕竟`512 Byte`的大小还是很有限,因此我们还需要进一步扩增加载数量。 6 | 7 | 这一节,不会有太多的新内容,只是对上一节的一个延续,也是为后续做准备。 8 | 9 | 首先创建一个`day3.asm` 10 | 11 | ```assembly 12 | KERNEL_BASE_ADDR EQU 0X1500 13 | SECTION LOADER VSTART=KERNEL_BASE_ADDR 14 | MOV AX, 0XB800 15 | MOV ES, AX 16 | 17 | MOV BYTE [ES: 0X00], 'D' 18 | MOV BYTE [ES: 0X01], 0X07 19 | MOV BYTE [ES: 0X02], 'X' 20 | MOV BYTE [ES: 0X03], 0X06 21 | JMP $ 22 | ``` 23 | 24 | 上面这个文件和`loader.asm`是不是有些相似,只是改变了起始地址,我们把`kernel`的起始地址设置位`0x1500`,比`loader`往后了一些。 25 | 26 | 同时我们修改原来的`loader.asm`文件,这里的读磁盘的子过程没有变化,变化的是读取的磁盘扇区偏移量和加载到内存的地址。 27 | 28 | ```assembly 29 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 30 | KERNEL_BASE_ADDR EQU 0X1500 31 | KERNEL_START_SECTOR EQU 0X9 ;kernel从第9个扇区开始读 32 | SECTION LOADER VSTART=LOADER_BASE_ADDR 33 | 34 | MOV AX, 0XB800 ;显存位置 35 | MOV ES, AX 36 | 37 | MOV BYTE[ES: 0X00], 'O' 38 | MOV BYTE[ES: 0X01], 0X07 39 | MOV BYTE[ES: 0X02], 'K' 40 | MOV BYTE[ES: 0X03], 0X06 41 | 42 | MOV EAX, KERNEL_START_SECTOR ;LBA 读入的扇区个数 43 | MOV BX, KERNEL_BASE_ADDR ;KERNEL的起始地址 44 | MOV CX, 1 45 | 46 | CALL READ_DISK 47 | JMP KERNEL_BASE_ADDR 48 | 49 | ;读扇区 50 | READ_DISK: 51 | ;同day2.asm文件 52 | ``` 53 | 54 | 这里需要修改`Makefile`文件,主要变化体现在`dd.exe`需要将`kernel`写入第9个扇区。 55 | 56 | ```makefile 57 | # 默认动作 58 | TOOL_PATH = ../tools/ 59 | ASSEMBLE = ../tools/nasm/ 60 | 61 | MAKE = $(TOOL_PATH)make.exe -r 62 | NASM = $(ASSEMBLE)nasm.exe 63 | DD = $(TOOL_PATH)dd.exe 64 | DEL = del 65 | COPY = copy 66 | 67 | default : 68 | $(MAKE) mbr 69 | $(MAKE) kernel 70 | $(MAKE) loader 71 | 72 | # 镜像文件生成 73 | kernel.bin : day3.asm Makefile 74 | $(NASM) day3.asm -o kernel.bin 75 | 76 | loader.bin: loader.asm Makefile 77 | $(NASM) loader.asm -o loader.bin 78 | 79 | mbr.bin : day2.asm Makefile 80 | $(NASM) day2.asm -o mbr.bin 81 | 82 | 83 | # 其他指令 84 | kernel: 85 | $(MAKE) kernel.bin 86 | 87 | loader: 88 | $(MAKE) loader.bin 89 | 90 | mbr: 91 | $(MAKE) mbr.bin 92 | 93 | clean: 94 | -$(DEL) *.bin 95 | 96 | add: 97 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 98 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 99 | $(DD) if=kernel.bin of=dingst.vhd bs=512 count=1 seek=9 100 | 101 | backup: 102 | -$(DEL) dingst.vhd 103 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 104 | 105 | run: 106 | -$(DEL) ..\dingst.vhd 107 | $(COPY) dingst.vhd ..\dingst.vhd 108 | ``` 109 | 110 | 最后只需要执行以下命令即可。 111 | 112 | ```bash 113 | make add 114 | make 115 | make run 116 | ``` 117 | 118 | 再次开启虚拟机,可以看到程序成功输出了`DX`,表明`kernel`加载是成功的。 119 | 120 | ![DX](src/DX.jpg) 121 | 122 | ## [Day 4](OS/day4/day4.md) -------------------------------------------------------------------------------- /OS/day3/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | KERNEL_BASE_ADDR EQU 0X1500 3 | KERNEL_START_SECTOR EQU 0X9 ;kernel从第9个扇区开始读 4 | SECTION LOADER VSTART=LOADER_BASE_ADDR 5 | 6 | MOV AX, 0XB800 ;显存位置 7 | MOV ES, AX 8 | 9 | MOV BYTE[ES: 0X00], 'O' 10 | MOV BYTE[ES: 0X01], 0X07 11 | MOV BYTE[ES: 0X02], 'K' 12 | MOV BYTE[ES: 0X03], 0X06 13 | 14 | MOV EAX, KERNEL_START_SECTOR ;LBA 读入的扇区个数 15 | MOV BX, KERNEL_BASE_ADDR ;KERNEL的起始地址 16 | MOV CX, 1 17 | 18 | CALL READ_DISK 19 | JMP KERNEL_BASE_ADDR 20 | 21 | 22 | 23 | ;--------------------读扇区 24 | READ_DISK: 25 | ;EAX LBA 扇区号 26 | ;BX 数据写入内存的地址 27 | ;CX 读入扇区数 28 | 29 | ;保存寄存器 EAX CX 30 | MOV ESI, EAX 31 | MOV DI, CX 32 | ;设置读取扇区的数量 33 | ;读写硬盘 34 | MOV DX, 0X1F2 ; 35 | MOV AL, CL ; 36 | OUT DX, AL 37 | 38 | ;恢复 EAX 39 | MOV EAX, ESI 40 | 41 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 42 | ;0-7 写入0x1f3 43 | MOV DX, 0X1F3 44 | OUT DX, AL 45 | ;8-15 写入0x1f4 46 | MOV CL, 8 47 | SHR EAX, CL 48 | MOV DX, 0X1F4 49 | OUT DX, AL 50 | ;16-23 写入0x1f5 51 | SHR EAX, CL 52 | AND AL, 0X0F 53 | MOV DX, 0X1F5 54 | OUT DX, AL 55 | ;24 - 27 56 | SHR EAX, CL 57 | AND AL, 0X0F 58 | OR AL, 0XE0 ;AL = 1110 0000 59 | MOV DX, 0X1F6 60 | OUT DX, AL 61 | ;向0x1f7 写入读命令 62 | MOV DX, 0X1F7 63 | MOV AL, 0X20 ;表示读命令 64 | 65 | OUT DX, AL 66 | ;检测硬盘状态 67 | .NOT_READY: 68 | NOP 69 | ;读取端口,查看状态 70 | IN AL, DX 71 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 72 | CMP AL, 0X08 73 | JNZ .NOT_READY 74 | ;读数据 75 | MOV AX, DI ;这里 DI = 1 76 | MOV DX, 512 77 | MUL DX 78 | MOV CX, AX 79 | MOV DX, 0X1F0 80 | .GO_ON: 81 | IN AX, DX 82 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 83 | ADD BX, 2 84 | LOOP .GO_ON 85 | RET 86 | -------------------------------------------------------------------------------- /OS/day3/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day3/src/DX.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day3/src/DX.jpg -------------------------------------------------------------------------------- /OS/day4/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 默认动作 3 | TOOL_PATH = ../tools/ 4 | ASSEMBLE = ../tools/nasm/ 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | 13 | default : 14 | $(MAKE) mbr 15 | $(MAKE) loader 16 | 17 | # 镜像文件生成 18 | 19 | loader.bin: loader.asm Makefile 20 | $(NASM) loader.asm -o loader.bin 21 | 22 | mbr.bin : day2.asm Makefile 23 | $(NASM) day2.asm -o mbr.bin 24 | 25 | 26 | # 其他指令 27 | 28 | loader: 29 | $(MAKE) loader.bin 30 | 31 | mbr: 32 | $(MAKE) mbr.bin 33 | 34 | clean: 35 | -$(DEL) *.bin 36 | 37 | add: 38 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 39 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 40 | $(DD) if=KRAW.bin of=dingst.vhd bs=512 count=1 seek=9 41 | 42 | backup: 43 | -$(DEL) dingst.vhd 44 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 45 | 46 | run: 47 | -$(DEL) ..\dingst.vhd 48 | $(COPY) dingst.vhd ..\dingst.vhd -------------------------------------------------------------------------------- /OS/day4/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day4/day4.asm: -------------------------------------------------------------------------------- 1 | EXTERN DISPLAY ;FROM C 2 | [BITS 16] 3 | [SECTION .TEXT] 4 | GLOBAL _start 5 | GLOBAL MYPRINT 6 | _start: 7 | CALL DISPLAY ;ASM CALL CALL 8 | MYPRINT: 9 | MOV AX, 0XB800 10 | MOV ES, AX 11 | MOV BYTE [ES: 0X00], 'O' 12 | MOV BYTE [ES: 0X01], 0X07 13 | MOV BYTE [ES: 0X02], 'K' 14 | MOV BYTE [ES: 0X03], 0X06 15 | RET 16 | -------------------------------------------------------------------------------- /OS/day4/day4.c: -------------------------------------------------------------------------------- 1 | void MYPRINT(); 2 | int DISPLAY(){ 3 | MYPRINT(); 4 | while(1){ 5 | } 6 | } 7 | 8 | 9 | -------------------------------------------------------------------------------- /OS/day4/day4.md: -------------------------------------------------------------------------------- 1 | # Day 4 2 | 3 | ## 前言及工具安装 4 | 5 | 前`3`章的学习中,我们走完了第一小步,也就是加载所谓的"内核",但内核的编写我们并不想全部使用汇编(`Assembly`)。因此这一章,我们将导入`C`语言,帮助我们编写更多的功能。 6 | 7 | 这里使用`C`语言,并不是像平时那样,用`C`语言各种库编程,而是使用`C`语言的框架,因为它的逻辑结构比汇编可读性要高很多!遗憾的是,我们不能用任何库函数,因为这些库函数也依赖于`OS`。 8 | 9 | 这里我们使用的工具是[`ubuntu-16.04.6-desktop-i386`](http://releases.ubuntu.com/16.04/),在我的尝试中,`64`位的`linux`使用`-m32`编译出的`32 bit`文件和本身使用`32`的`linux`编译的结果还是有差异,最后的结果并不能很好兼容。因此最好能够使用这一版本的虚拟机,并且它也很常见,提醒一点,虚拟机开机如果黑屏,直接关机,修改其设置,不要使用加速`3D`。其余安装教程都比较简易,可以参考网络的一些教程。 10 | 11 | ## 正式开始 12 | 13 | 这里我们用`C`语言实现一个简单的内核,及向屏幕输出两个字符`OK`,然后进入死循环。我们想用这段`C`语言编译后的结果,替代之前用汇编写的`kernel.asm`文件。这看似没有太大的意义,但是如果这一步能够实现,之后我们可以用类似的方式,`C + Assembly`联合编程,帮助我们高效开发。 14 | 15 | `day4.c`代码如下: 16 | 17 | ```c 18 | void MYPRINT(); 19 | int DISPLAY(){ 20 | MYPRINT(); 21 | while(1){ 22 | } 23 | } 24 | ``` 25 | 26 | 这段代码你再熟悉不过了吧,但函数`MYPRINT`的实现不在这个文件中,而是用汇编实现。同时我们看到,程序中,没有`main`函数,因为程序的调用入口点不在这个文件中,而是被定义在了汇编文件`_start`,当然这些也不是很重要,暂时不理解也没有关系。 27 | 28 | 接下来我们看看汇编`day4.asm`。 29 | 30 | ```assembly 31 | EXTERN DISPLAY ;FROM C 32 | [BITS 16] 33 | [SECTION .TEXT] 34 | GLOBAL _start 35 | GLOBAL MYPRINT 36 | _start: 37 | CALL DISPLAY ;ASM CALL 38 | MYPRINT: 39 | MOV AX, 0XB800 40 | MOV ES, AX 41 | MOV BYTE [ES: 0X00], 'O' 42 | MOV BYTE [ES: 0X01], 0X07 43 | MOV BYTE [ES: 0X02], 'K' 44 | MOV BYTE [ES: 0X03], 0X06 45 | RET 46 | ``` 47 | 48 | 这段汇编你也不会陌生,毕竟前面都写了很多次了,向屏幕输出`OK`两个字符,但是有几点,首先这里`_start`会被我们作为程序的入口点,汇编程序会调用`C`语言编写的`DISPLAY`函数。所以一开始声明了`EXTERN DISPLAY`,说明这是一个外部符号。 49 | 50 | 其余`mbr.asm`和`loader.asm`没有变化,和上一章一样,这里我们只是用尝试用`C`语言去写内核。 51 | 52 | 或许你会有疑问,这两个程序能够拼接吗?当然,这有些困难,原则上当然可以使用`ld.exe`把二者链接在一起,接下来我们将这两个文件拷贝到我们的虚拟机中。 53 | 54 | * 将`day4.asm`汇编为一个`32`位的`elf`文件。 55 | 56 | ```bash 57 | nasm -f elf32 -o dst.elf.o day4.asm 58 | ``` 59 | 60 | * 将`day4.c`用`gcc`编译为一个`obj`目标文件。 61 | 62 | ```bash 63 | gcc -o kernel.elf.o -c day4.c 64 | ``` 65 | 66 | * 进一步,我们使用`ld`链接两个二进制文件。注意这个地方我们指定了程序的起始地址为`0x1500`,这个和我们在`loader.asm`中的地址是一致的,否则最后会跳转失败。如果程序找不到入口点,你可以用`-e _start`显示说明。 67 | 68 | ```bash 69 | ld -s -Ttext 0x1500 dst.elf.o kernel.elf.o 70 | ``` 71 | 72 | 接下来,我们使用`objdump -D a.out`,查看我们生成的二进制文件。 73 | 74 | ![kernel.bin](src/kernel.bin.png) 75 | 76 | 可以看到,这段反汇编的结果和`C`语言编写的逻辑是一致的。最后程序`jmp 0x150b`,也就是汇编中`jmp $`。 77 | 78 | 但这个文件实在是有些大,毕竟是一个标准的`elf 32bit`文件,你可以用`readelf -a a.out`查看其属性。 79 | 80 | 但回头看看我们上一章的`kernel.bin`,也就几十个字节,因此,我们不需要那些标识符,只需要一个纯粹的代码二进制文件。使用以下命令 81 | 82 | ```bash 83 | objcopy -O binary a.out KRAW.BIN 84 | ``` 85 | 86 | 最后可以看到,我们产生的`KRAE.BIN`仅仅有`100 Byte`左右,与`kernel.bin`非常类似。 87 | 88 | ## Makefile 89 | 90 | 最后,我们将得到的`KRAW.BIN`拷贝到外面,修改`Makefile`。 91 | 92 | ```makefile 93 | # 默认动作 94 | TOOL_PATH = ../tools/ 95 | ASSEMBLE = ../tools/nasm/ 96 | 97 | MAKE = $(TOOL_PATH)make.exe -r 98 | NASM = $(ASSEMBLE)nasm.exe 99 | DD = $(TOOL_PATH)dd.exe 100 | DEL = del 101 | COPY = copy 102 | 103 | default : 104 | $(MAKE) mbr 105 | $(MAKE) loader 106 | 107 | # 镜像文件生成 108 | loader.bin: loader.asm Makefile 109 | $(NASM) loader.asm -o loader.bin 110 | 111 | mbr.bin : day2.asm Makefile 112 | $(NASM) day2.asm -o mbr.bin 113 | 114 | # 其他指令 115 | loader: 116 | $(MAKE) loader.bin 117 | 118 | mbr: 119 | $(MAKE) mbr.bin 120 | 121 | clean: 122 | -$(DEL) *.bin 123 | 124 | add: 125 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 126 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 127 | $(DD) if=KRAW.bin of=dingst.vhd bs=512 count=1 seek=9 # 有变化! 128 | 129 | backup: 130 | -$(DEL) dingst.vhd 131 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 132 | 133 | run: 134 | -$(DEL) ..\dingst.vhd 135 | $(COPY) dingst.vhd ..\dingst.vhd 136 | ``` 137 | 138 | 依次执行以下命令。 139 | 140 | ```bash 141 | make backup 142 | make 143 | make add 144 | make run 145 | ``` 146 | 147 | 最后再次启动虚拟机!屏幕输出了`OK`。 148 | 149 | ![OS](src/OS.png) 150 | 151 | 通过上述的步骤,我们成功地将`C`语言和汇编联合编程,并能够被系统识别加载,后续我们将继续用这种方式,完善我们的内核! 152 | 153 | ## [Day 5](OS/day5/day5.md) -------------------------------------------------------------------------------- /OS/day4/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | KERNEL_BASE_ADDR EQU 0X1500 3 | KERNEL_START_SECTOR EQU 0X9;kernel从第9个扇区开始读 4 | SECTION LOADER VSTART=LOADER_BASE_ADDR 5 | 6 | MOV AX, 0XB800 ;显存位置 7 | MOV ES, AX 8 | 9 | MOV BYTE[ES: 0X00], 'O' 10 | MOV BYTE[ES: 0X01], 0X07 11 | MOV BYTE[ES: 0X02], 'K' 12 | MOV BYTE[ES: 0X03], 0X06 13 | 14 | MOV EAX,KERNEL_START_SECTOR;LBA 读入的扇区个数 15 | MOV BX,KERNEL_BASE_ADDR;KERNEL的起始地址 16 | MOV CX,1 17 | 18 | CALL READ_DISK 19 | JMP KERNEL_BASE_ADDR 20 | 21 | 22 | 23 | ;--------------------读扇区 24 | READ_DISK: 25 | ;EAX LBA 扇区号 26 | ;BX 数据写入内存的地址 27 | ;CX 读入扇区数 28 | 29 | ;保存寄存器 EAX CX 30 | MOV ESI, EAX 31 | MOV DI, CX 32 | ;设置读取扇区的数量 33 | ;读写硬盘 34 | MOV DX, 0X1F2 ; 35 | MOV AL, CL ; 36 | OUT DX, AL 37 | 38 | ;恢复 EAX 39 | MOV EAX, ESI 40 | 41 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 42 | ;0-7 写入0x1f3 43 | MOV DX, 0X1F3 44 | OUT DX, AL 45 | ;8-15 写入0x1f4 46 | MOV CL, 8 47 | SHR EAX, CL 48 | MOV DX, 0X1F4 49 | OUT DX, AL 50 | ;16-23 写入0x1f5 51 | SHR EAX, CL 52 | AND AL, 0X0F 53 | MOV DX, 0X1F5 54 | OUT DX, AL 55 | ;24 - 27 56 | SHR EAX, CL 57 | AND AL, 0X0F 58 | OR AL, 0XE0 ;AL = 1110 0000 59 | MOV DX, 0X1F6 60 | OUT DX, AL 61 | ;向0x1f7 写入读命令 62 | MOV DX, 0X1F7 63 | MOV AL, 0X20 ;表示读命令 64 | 65 | OUT DX, AL 66 | ;检测硬盘状态 67 | .NOT_READY: 68 | NOP 69 | ;读取端口,查看状态 70 | IN AL, DX 71 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 72 | CMP AL, 0X08 73 | JNZ .NOT_READY 74 | ;读数据 75 | MOV AX, DI ;这里 DI = 1 76 | MOV DX, 512 77 | MUL DX 78 | MOV CX, AX 79 | MOV DX, 0X1F0 80 | .GO_ON: 81 | IN AX, DX 82 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 83 | ADD BX, 2 84 | LOOP .GO_ON 85 | RET 86 | -------------------------------------------------------------------------------- /OS/day4/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day4/src/KRAW.BIN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day4/src/KRAW.BIN.png -------------------------------------------------------------------------------- /OS/day4/src/OS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day4/src/OS.png -------------------------------------------------------------------------------- /OS/day4/src/kernel.bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day4/src/kernel.bin.png -------------------------------------------------------------------------------- /OS/day5/Makefile: -------------------------------------------------------------------------------- 1 | # 默认动作 2 | TOOL_PATH = ../tools/ 3 | ASSEMBLE = ../tools/nasm/ 4 | 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | 13 | default : 14 | $(MAKE) mbr 15 | $(MAKE) loader 16 | 17 | # 镜像文件生成 18 | 19 | loader.bin: loader.asm Makefile 20 | $(NASM) loader.asm -o loader.bin 21 | 22 | mbr.bin: day2.asm Makefile 23 | $(NASM) day2.asm -o mbr.bin 24 | 25 | # 其他指令 26 | 27 | loader: 28 | $(MAKE) loader.bin 29 | 30 | mbr: 31 | $(MAKE) mbr.bin 32 | 33 | clean: 34 | -$(DEL) loader.bin 35 | -$(DEL) mbr.bin 36 | 37 | 38 | add: 39 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 40 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 41 | $(DD) if=osKernel.bin of=dingst.vhd bs=512 count=100 seek=9 42 | 43 | backup: 44 | -$(DEL) dingst.vhd 45 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 46 | 47 | run: 48 | -$(DEL) ..\dingst.vhd 49 | $(COPY) dingst.vhd ..\dingst.vhd 50 | 51 | all: 52 | $(MAKE) backup 53 | $(MAKE) clean 54 | $(MAKE) mbr 55 | $(MAKE) loader 56 | $(MAKE) add 57 | $(MAKE) run -------------------------------------------------------------------------------- /OS/day5/com.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | rm -rf temp; 3 | mkdir temp; 4 | chmod 777 temp; 5 | 6 | nasm -f elf32 osKernel.asm -o ./temp/osKernel.o; 7 | nasm -f elf32 liba.asm -o ./temp/liba.o; 8 | 9 | gcc -c -m16 -march=i386 -masm=intel -nostdlib -ffreestanding -mpreferred-stack-boundary=2 -lgcc -shared libc.c -o ./temp/libc.o; 10 | 11 | chmod 777 ./temp/osKernel.o; 12 | chmod 777 ./temp/liba.o; 13 | chmod 777 ./temp/libc.o; 14 | 15 | ld -m elf_i386 -N -Ttext 0x8000 --oformat binary ./temp/osKernel.o ./temp/liba.o ./temp/libc.o -o temp/osKernel.bin; 16 | 17 | echo "have Dome!" 18 | 19 | -------------------------------------------------------------------------------- /OS/day5/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day5/day4.asm: -------------------------------------------------------------------------------- 1 | EXTERN DISPLAY ;FROM C 2 | [BITS 16] 3 | [SECTION .TEXT] 4 | GLOBAL _start 5 | GLOBAL MYPRINT 6 | _start: 7 | CALL DISPLAY ;ASM CALL CALL 8 | MYPRINT: 9 | MOV AX, 0XB800 10 | MOV ES, AX 11 | MOV BYTE [ES: 0X00], 'O' 12 | MOV BYTE [ES: 0X01], 0X07 13 | MOV BYTE [ES: 0X02], 'K' 14 | MOV BYTE [ES: 0X03], 0X06 15 | RET 16 | -------------------------------------------------------------------------------- /OS/day5/day5.md: -------------------------------------------------------------------------------- 1 | # Day 5 2 | 3 | ## 前言 4 | 5 | 在这一章,我们会进一步开发独立内核,首先我们再次回顾我们的思路`Boot->mbr->kernel`。我们要编写的正是`kernel`。 6 | 7 | `C + Assembly`联合编程,我们主要解决两个部分,一个是`C`语言,一个是汇编。由于不能够使用一般的库函数,我们会用汇编对`BIOS`的功能进行封装,供C语言调用。 8 | 9 | 首先是要说明几点,由于我们最后的内核不算太小,所以我们将内核加载至内存的`0x8000`的位置,具体内存布局可以参考[`day2`](OS/day2/day2.md)的部分。其次,内核大小超过了一个扇区,因此我们需要修改加载扇区的个数。上面两个部分的修改,对应着`loader.asm`。 10 | 11 | ```assembly 12 | KERNEL_BASE_ADDR EQU 0x8000 13 | ;....... 14 | MOV CX,8 15 | ``` 16 | 17 | 修改内核加载的基地址,同时修改加载的扇区个数,这里由原来的`1`改为了`8` 18 | 19 | ## 汇编部分 20 | 21 | 汇编部分需要做好`I/O`接口,供上层调用。这里我们一共定义了三个基本的`I/O`操作。 22 | 23 | * `getch()`得到键盘键入的一个字符。 24 | * `putchar()`向屏幕输出一个字符。 25 | * `printInPos`向屏幕当前位置输出一个字符串。 26 | 27 | ```assembly 28 | BITS 16 29 | [GLOBAL printInPos] ;Print content at the cursor 30 | [GLOBAL putchar] ;Output one character 31 | [GLOBAL getch] ;Get keyboard input 32 | 33 | ;Get keyboard input 34 | getch: 35 | MOV AH, 0 ;Function number 36 | INT 16H 37 | MOV AH, 0 ;Read characters, AL = characters 38 | RETF 39 | 40 | ;Print characters at the cursor 41 | putchar: 42 | PUSHA ;Protect the scene 43 | MOV BP, SP ;Save the top of the stack 44 | ADD BP, 16 + 4 ;BP = 第一个参数的地址 45 | MOV AL, [BP] ;AL = Print characters 46 | MOV BH, 0 ;BH = page number 47 | MOV AH, 0EH ;function number 48 | INT 10H 49 | POPA 50 | RETF 51 | 52 | ;Display the string at the specified position 53 | printInPos: 54 | PUSHA 55 | MOV SI, SP ;SI 保存 SP 的开始位置 56 | ADD SI, 16 + 4 ;这其实是第一个参数的地址,SI也就是msg的地址 57 | MOV AX, CS 58 | MOV DS, AX 59 | MOV BP, [SI] ;BP = 栈底指针 60 | MOV AX, DS ; 61 | MOV ES, AX ;ES = DS 62 | MOV CX, [SI + 4] ;CX = 字符串长度,SI + 4也就是第二个参数的地址 63 | MOV AX, 1301H 64 | ;functon number AH = 13 AL = 01H; 65 | ;Indicates that the cursor displays the end of the string 66 | MOV BX, 0007H ;BH = page number BL = 07 black and white 67 | MOV DH, [SI + 8] ;Line number= 0 同理,这里是第三个参数 68 | MOV DL, [SI + 12] ;Column number = 0 这里是第四个参数 69 | INT 10H ;BIOS 10H interrupt call 70 | POPA 71 | RETF 72 | ``` 73 | 74 | 这一部分应该说是我们的"地基",也是我们上层代码最直接的依赖。 75 | 76 | 上面的三段代码基本都是调用`BIOS`的`10H`输出功能和`16H`输入功能。在这里,我们要详细地解释一下这段代码,但如果你对汇编和`C`语言地关系足够了解,可以跳过至下一段。 77 | 78 | 首先代码为什么是这样的?这与我们设计的函数有关系,三个函数的模型如下: 79 | 80 | ```c 81 | // Print content at the cursor 82 | extern void printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); 83 | extern void putchar(char c); // Output one character 84 | extern char getch(); // Get keyboard input 85 | ``` 86 | 87 | 我们以第一个函数`printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); `为例。看看函数的调用栈帧。 88 | 89 | ![callFrame](src/callFrame.png) 90 | 91 | 请大家结合对应汇编代码看,我们基本能够明白这段汇编的含义,比如为什么`SI`要向上偏移`20 Byte`,因为这样才能访问到第一个参数的地址,而第一个参数正式我们字符串的首地`址,后面的参数访问是类似的道理。 92 | 93 | 而至于为什么要用`ES`,以及`DX`,这些寄存器,这与我们使用`BIOS`的[功能](https://zh.wikipedia.org/wiki/INT_10H)有关系,这些寄存器正是`10H`中断的`13H` 94 | 95 | 功能调用所规定的!当然,这里还有很多细节部分,比如调用约定。例如函数的参数是从右往左入栈的等等,这些细节可以在《程序员的自我修养》中得到进一步的解释说明! 96 | 97 | ## 自己编写C库函数 98 | 99 | 上一部分,基本揭示了汇编和`C`语言之间的关系,我们不妨编写一个供上层调用的`C`语言的库`stringio.h`。 100 | 101 | ```c 102 | #include //这个库并不依赖于操作系统 103 | 104 | extern void printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); 105 | // Print content at the cursor 106 | extern void putchar(char c); // Output one character 107 | extern char getch(); // Get keyboard input 108 | 109 | // Calculate string length 110 | uint16_t strlen(char *str){ 111 | uint16_t count = 0; 112 | while(str[count++] != 0); 113 | return count - 1; 114 | } 115 | //... ... TODO 116 | /* 117 | // 这里可以用C语言编写大量的函数,可以使用上面三个extern外部导入的函数 118 | // 具体的函数你可以参考源文件 119 | */ 120 | ``` 121 | 122 | 在工具链,也就是`stringio.h`上,我们进一步实现`libc.c`,具体代码请移步源文件,没有太多难度。 123 | 124 | ## 最后一部分,编写 kernel入口点 125 | 126 | 这里涉及到的两个函数`startUp`和`shell`均在`libc.c`有具体实现,这里作为外部符号引入。 127 | 128 | ```assembly 129 | BITS 16 130 | [EXTERN startUp] 131 | [EXTERN shell] 132 | 133 | GLOBAL _start 134 | ;entry 135 | _start: 136 | CALL DWORD startUp 137 | ;wait the keyboard 138 | KeyBoard: 139 | MOV AH, 0 140 | INT 16H 141 | CMP AL, 0DH 142 | JNE KeyBoard 143 | CALL DWORD shell 144 | JMP KeyBoard 145 | ``` 146 | 147 | 整个汇编也很简单,首先调用一个显示信息的函数,然后等待键盘键入。 148 | 149 | 这里有几点我们要说明,`_start`是默认的入口点,但你可以不必这么命名,在最后链接时,用这样的形式`-e _start`,便可以指定程序的入口点。 150 | 151 | ## 在Ubuntu i386上编译链接 152 | 153 | 这里,我们再次用到`day4`中的工具,将这4个文件,分别是`stringio.h`,`lib.asm`,`libc.c`,`osKernel.asm`。 154 | 155 | 在虚拟机中,打开`vim`,写入编译和链接的规则`shell`文件`com.sh`。 156 | 157 | ```shell 158 | # !/bin/bash 159 | rm -rf temp; 160 | mkdir temp; 161 | chmod 777 temp; 162 | 163 | nasm -f elf32 osKernel.asm -o ./temp/osKernel.o; 164 | nasm -f elf32 liba.asm -o ./temp/liba.o; 165 | 166 | gcc -c -m16 -march=i386 -masm=intel -nostdlib -ffreestanding -mpreferred-stack-boundary=2 -lgcc -shared libc.c -o ./temp/libc.o; 167 | 168 | chmod 777 ./temp/osKernel.o; 169 | chmod 777 ./temp/liba.o; 170 | chmod 777 ./temp/libc.o; 171 | 172 | ld -m elf_i386 -N -Ttext 0x8000 --oformat binary ./temp/osKernel.o ./temp/liba.o ./temp/libc.o -o temp/osKernel.bin; 173 | 174 | echo "have Dome!" 175 | ``` 176 | 177 | 这段代码就类似于我们编写的`Makefile`一样,很方便,让我们不必每一次都重复敲命令。这里的代码我们主要解释`gcc`过程。这里几个参数的含义: 178 | 179 | * `-m16` 编译为`16`位。 180 | * `-march` 编译为`i386` 181 | * `-nostdlib` 无需链接标准库 182 | * `-mpreferred-stack-boundary` 对齐字节是2字节 183 | * `-lgcc -shared` 编译并链接我们的自己的库`libc.c` 184 | 185 | ## 初步结果 186 | 187 | ![OS](src/OS.png) 188 | 189 | 到这里,我们基本搭建好了开发框架。 190 | 191 | ## [Day 6](OS/day6/day6.md) -------------------------------------------------------------------------------- /OS/day5/liba.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [GLOBAL printInPos] ;Print content at the cursor 3 | [GLOBAL putchar] ;Output one character 4 | [GLOBAL getch] ;Get keyboard input 5 | 6 | ;Get keyboard input 7 | getch: 8 | MOV AH, 0 ;Function number 9 | INT 16H 10 | MOV AH, 0 ;Read characters, AL = characters 11 | RETF 12 | 13 | ;Print characters at the cursor 14 | putchar: 15 | PUSHA ;Protect the scene 16 | MOV BP, SP ;Save the top of the stack 17 | ADD BP, 16 + 4 ;Parameter address 18 | MOV AL, [BP] ;AL = Print characters 19 | MOV BH, 0 ;BH = page number 20 | MOV AH, 0EH ;function number 21 | INT 10H 22 | POPA 23 | RETF 24 | 25 | ;Display the string at the specified position 26 | printInPos: 27 | PUSHA 28 | MOV SI, SP ;use SI 29 | ADD SI, 16 + 4 ;First parameter address 30 | MOV AX, CS 31 | MOV DS, AX 32 | MOV BP, [SI] ;BP = offset 33 | MOV AX, DS ; 34 | MOV ES, AX ;ES = DS 35 | MOV CX, [SI + 4] ;CX = String length 36 | MOV AX, 1301H ;functon number AH = 13 AL = 01H,Indicates that the cursor displays the end of the string 37 | MOV BX, 0007H ;BH = page number BL = 07 black and white 38 | MOV DH, [SI + 8] ;Line number= 0 39 | MOV DL, [SI + 12] ;Column number = 0 40 | INT 10H ;BIOS 10H interrupt call 41 | POPA 42 | RETF 43 | -------------------------------------------------------------------------------- /OS/day5/libc.c: -------------------------------------------------------------------------------- 1 | /* Using C language expansion kernel 2 | * if you have any problem,contact me at djh113@126.com 3 | * or visit my homepage at https://github.com/djh-sudo 4 | */ 5 | #include "stringio.h" 6 | #define BUF_LEN 32 7 | 8 | // start menu 9 | void startUp(){ 10 | char* title = "TinyOS Oerating System version 1.0"; 11 | char* subTitle = "Designed by DJH-sudo"; 12 | char* copyRight = "Coypleft by GNU"; 13 | char* hint = "System is ready.Press ENTER\r\n"; 14 | 15 | printInPos(title,strlen(title),5,23); 16 | printInPos(subTitle,strlen(subTitle),6,23); 17 | printInPos(copyRight,strlen(copyRight),8,23); 18 | printInPos(hint,strlen(hint),15,23); 19 | } 20 | 21 | //print shell 22 | void promptString(){ 23 | char*p_string = "TinyOS v1#>"; 24 | print(p_string); 25 | } 26 | 27 | // help me 28 | void showHelp(){ 29 | char * help = "shell for OS version 1.1 x86 PC\r\n" 30 | "Use `help` to see the list \r\n" 31 | "\r\n" 32 | "clc - clear the ternimal \r\n" 33 | "time - get current time \r\n" 34 | "drawpic - draw a animate pic \r\n" 35 | "power off - force the OS shut down" 36 | "\r\n"; 37 | print(help); 38 | } 39 | 40 | void shell(){ 41 | showHelp(); 42 | } 43 | -------------------------------------------------------------------------------- /OS/day5/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | KERNEL_BASE_ADDR EQU 0x8000 3 | KERNEL_START_SECTOR EQU 0X9;kernel从第9个扇区开始读 4 | SECTION LOADER VSTART=LOADER_BASE_ADDR 5 | 6 | MOV AX, 0XB800 ;显存位置 7 | MOV ES, AX 8 | 9 | MOV BYTE[ES: 0X00], '8' 10 | MOV BYTE[ES: 0X01], 0X07 11 | MOV BYTE[ES: 0X02], '0' 12 | MOV BYTE[ES: 0X03], 0X06 13 | 14 | MOV EAX,KERNEL_START_SECTOR;LBA 读入的扇区个数 15 | MOV BX,KERNEL_BASE_ADDR;KERNEL的起始地址 16 | MOV CX,8 17 | 18 | CALL READ_DISK 19 | JMP KERNEL_BASE_ADDR 20 | 21 | 22 | 23 | ;--------------------读扇区 24 | READ_DISK: 25 | ;EAX LBA 扇区号 26 | ;BX 数据写入内存的地址 27 | ;CX 读入扇区数 28 | 29 | ;保存寄存器 EAX CX 30 | MOV ESI, EAX 31 | MOV DI, CX 32 | ;设置读取扇区的数量 33 | ;读写硬盘 34 | MOV DX, 0X1F2 ; 35 | MOV AL, CL ; 36 | OUT DX, AL 37 | 38 | ;恢复 EAX 39 | MOV EAX, ESI 40 | 41 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 42 | ;0-7 写入0x1f3 43 | MOV DX, 0X1F3 44 | OUT DX, AL 45 | ;8-15 写入0x1f4 46 | MOV CL, 8 47 | SHR EAX, CL 48 | MOV DX, 0X1F4 49 | OUT DX, AL 50 | ;16-23 写入0x1f5 51 | SHR EAX, CL 52 | AND AL, 0X0F 53 | MOV DX, 0X1F5 54 | OUT DX, AL 55 | ;24 - 27 56 | SHR EAX, CL 57 | AND AL, 0X0F 58 | OR AL, 0XE0 ;AL = 1110 0000 59 | MOV DX, 0X1F6 60 | OUT DX, AL 61 | ;向0x1f7 写入读命令 62 | MOV DX, 0X1F7 63 | MOV AL, 0X20 ;表示读命令 64 | 65 | OUT DX, AL 66 | ;检测硬盘状态 67 | .NOT_READY: 68 | NOP 69 | ;读取端口,查看状态 70 | IN AL, DX 71 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 72 | CMP AL, 0X08 73 | JNZ .NOT_READY 74 | ;读数据 75 | MOV AX, DI ;这里 DI = 1 76 | MOV DX, 512 77 | MUL DX 78 | MOV CX, AX 79 | MOV DX, 0X1F0 80 | .GO_ON: 81 | IN AX, DX 82 | MOV[BX], AX ;向BASE_ADDR(内存0x8000)写数据 83 | ADD BX, 2 84 | LOOP .GO_ON 85 | RET 86 | -------------------------------------------------------------------------------- /OS/day5/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day5/osKernel.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [EXTERN startUp] 3 | [EXTERN shell] 4 | 5 | GLOBAL _start 6 | ;entry 7 | _start: 8 | CALL DWORD startUp 9 | ;wait the keyboard 10 | KeyBoard: 11 | MOV AH, 0 12 | INT 16H 13 | CMP AL, 0DH 14 | JNE KeyBoard 15 | CALL DWORD shell 16 | JMP KeyBoard 17 | -------------------------------------------------------------------------------- /OS/day5/src/OS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day5/src/OS.png -------------------------------------------------------------------------------- /OS/day5/src/callFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day5/src/callFrame.png -------------------------------------------------------------------------------- /OS/day5/stringio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Realize some functions of C language string 3 | * if you have any problem,contact me at djh113@126.com 4 | * or visit my homepage at https://github.com/djh-sudo 5 | */ 6 | #include 7 | 8 | extern void printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); // Print content at the cursor 9 | extern void putchar(char c); // Output one character 10 | extern char getch(); // Get keyboard input 11 | 12 | 13 | // Calculate string length 14 | uint16_t strlen(char *str){ 15 | uint16_t count = 0; 16 | while(str[count++] != 0); 17 | return count - 1; 18 | } 19 | 20 | // Compare strings 21 | uint8_t strcmp(char* str1,char* str2){ 22 | int i = 0; 23 | while(1){ 24 | if(str1[i] == 0 || str2[i] == 0) 25 | break; 26 | if(str1[i] != str2[i]) 27 | break; 28 | i++; 29 | } 30 | return (str1[i] - str2[i]); 31 | } 32 | 33 | // Display characters at the cursor 34 | void print(char*str){ 35 | uint16_t len = strlen(str); 36 | for(int i = 0; i < len; i++){ 37 | putchar(str[i]); 38 | } 39 | } 40 | 41 | // Get the first word of the string 42 | void getFirstWord(char*str,char*buf){ 43 | int i = 0; 44 | while(str[i] && str[i] != ' ' && str[i] != '\n'){ 45 | buf[i] = str[i]; 46 | i++; 47 | } 48 | buf[i] = 0; 49 | } 50 | 51 | // Read the string into the buffer 52 | void read2Buf(char*buffer,uint16_t maxLen){ 53 | int i = 0; 54 | while(1){ 55 | char tempc = getch();// Read character by character 56 | if(!(tempc == 0x0D || tempc == '\b' || (tempc >= 32 && tempc <= 127))){ 57 | continue;// Invaild symbol 58 | } 59 | if(i > 0 && i < maxLen - 1){ 60 | if(tempc == 0x0D){// press enter stop! 61 | break; 62 | }else if(tempc == '\b'){// back 63 | putchar('\b'); 64 | putchar(' '); 65 | putchar('\b'); 66 | i--; 67 | }else{// Valid characters 68 | putchar(tempc); 69 | buffer[i] = tempc; 70 | i++; 71 | } 72 | }else if(i >= maxLen - 1){ 73 | // Buffer exceeded 74 | if(tempc == '\b'){ 75 | putchar('\b'); 76 | putchar(' '); 77 | putchar('\b'); 78 | i--; 79 | }else if(tempc == 0x0D){ 80 | break; 81 | } 82 | }else if(i <= 0){ 83 | if(tempc == 0x0D) 84 | break; 85 | else if(tempc != '\b'){ 86 | putchar(tempc); 87 | buffer[i] = tempc; 88 | i++; 89 | } 90 | } 91 | } 92 | putchar('\r'); 93 | putchar('\n'); 94 | buffer[i] = 0; 95 | } 96 | -------------------------------------------------------------------------------- /OS/day6/Makefile: -------------------------------------------------------------------------------- 1 | # 默认动作 2 | TOOL_PATH = ../tools/ 3 | ASSEMBLE = ../tools/nasm/ 4 | 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | 13 | default : 14 | $(MAKE) mbr 15 | $(MAKE) loader 16 | 17 | # 镜像文件生成 18 | 19 | loader.bin: loader.asm Makefile 20 | $(NASM) loader.asm -o loader.bin 21 | 22 | mbr.bin: day2.asm Makefile 23 | $(NASM) day2.asm -o mbr.bin 24 | 25 | # 其他指令 26 | 27 | loader: 28 | $(MAKE) loader.bin 29 | 30 | mbr: 31 | $(MAKE) mbr.bin 32 | 33 | clean: 34 | -$(DEL) loader.bin 35 | -$(DEL) mbr.bin 36 | 37 | 38 | add: 39 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 40 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 41 | $(DD) if=osKernel.bin of=dingst.vhd bs=512 count=100 seek=9 42 | 43 | backup: 44 | -$(DEL) dingst.vhd 45 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 46 | 47 | run: 48 | -$(DEL) ..\dingst.vhd 49 | $(COPY) dingst.vhd ..\dingst.vhd 50 | 51 | all: 52 | $(MAKE) backup 53 | $(MAKE) clean 54 | $(MAKE) mbr 55 | $(MAKE) loader 56 | $(MAKE) add 57 | $(MAKE) run -------------------------------------------------------------------------------- /OS/day6/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day6/day4.asm: -------------------------------------------------------------------------------- 1 | EXTERN DISPLAY ;FROM C 2 | [BITS 16] 3 | [SECTION .TEXT] 4 | GLOBAL _start 5 | GLOBAL MYPRINT 6 | _start: 7 | CALL DISPLAY ;ASM CALL CALL 8 | MYPRINT: 9 | MOV AX, 0XB800 10 | MOV ES, AX 11 | MOV BYTE [ES: 0X00], 'O' 12 | MOV BYTE [ES: 0X01], 0X07 13 | MOV BYTE [ES: 0X02], 'K' 14 | MOV BYTE [ES: 0X03], 0X06 15 | RET 16 | -------------------------------------------------------------------------------- /OS/day6/day6.md: -------------------------------------------------------------------------------- 1 | # Day 6 2 | 3 | 这一章也会比较轻松,因为我们只是对`day5`完善,实现一些简单的功能。 4 | 5 | 这里我们将实现几个简单的函数,包括清屏、获取时间调用,以及`shell`提示符。 6 | 7 | 先看上层结构,这段`C`语言可谓再熟悉不过了。 8 | 9 | ```c 10 | void shell(){ 11 | clearScreen(); 12 | showHelp(); 13 | char cmdStr[BUF_LEN + 1] = {0}; 14 | char cmdFirstWord[BUF_LEN + 1] = {0}; 15 | enum{help,clear,time,power_off}; 16 | char*command[] = {"help","cls","time","poweroff"}; 17 | while(1){ 18 | promptString(); 19 | read2Buf(cmdStr,BUF_LEN); 20 | getFirstWord(cmdStr,cmdFirstWord); 21 | if(strcmp(cmdFirstWord,command[clear]) == 0){ 22 | clearScreen(); 23 | } 24 | else if(strcmp(cmdFirstWord,command[help]) == 0){ 25 | showHelp(); 26 | } 27 | else if(strcmp(cmdFirstWord,command[power_off]) == 0){ 28 | poweroff(); 29 | } 30 | else if(strcmp(cmdFirstWord,command[time]) == 0){ 31 | systime(); 32 | } 33 | else{ 34 | if(cmdFirstWord[0] != 0){ 35 | char*errMsg = ": command not dound\r\n"; 36 | print(cmdFirstWord); 37 | print(errMsg); 38 | } 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | 但上面这些函数`poweroff()`,`systime()`,`clearScreen`的具体实现在`liba.asm`中,也就是依旧需要汇编语言的支持。 45 | 46 | 更确切地说,其实就是借助`BIOS`的各种中断调用、端口读写,完成我们的功能。 47 | 48 | 这里我们简单说明这几个函数的实现。 49 | 50 | * `system` 51 | 52 | ```assembly 53 | MOV AL,4 54 | OUT 70H,AL 55 | IN AL,71H 56 | MOV AH,AL 57 | MOV CL,4 58 | SHR AH,CL 59 | AND AL,00001111B 60 | ADD AH,30H 61 | ADD AL,30H 62 | ;AX = hour 63 | MOV DX,AX 64 | MOV AL,DH 65 | MOV BH,0 66 | MOV AH,0EH 67 | INT 10H 68 | 69 | MOV AL,DL 70 | MOV BH,0 71 | MOV AH,0EH 72 | INT 10H 73 | 74 | MOV AL,':' 75 | MOV BH,0 76 | MOV AH,0EH 77 | INT 10H 78 | ;... ... TODO 79 | ``` 80 | 81 | 这里我们使用端口读写,将时钟的小时`hour`两位数读到`AL`寄存器中,在分别显示。显示使用的是`BIOS`的`10H`中断。而后面的分钟`minute`和秒`second`是类似的,只需要修改一开始的`AL`的值即可,这些方法都可以去查询`X86`汇编手册。 82 | 83 | 关机和清屏的操作,比较简单,调用`BIOS`对应的系统调用即可,具体细节你可以参考源代码。 84 | 85 | 最后将`liba.asm`和`libc.c`拷贝至虚拟机中,重新编译。这里便能够看到脚本`com.sh`的优势,每次只需要重新执行即可。 86 | 87 | 执行`Makefile`中命令。 88 | 89 | ```makefile 90 | make all 91 | ``` 92 | 93 | 再次开机,可以发现,这些命令都能够使用了。 94 | 95 | 写到这里,我们基本在实模式下实现了一个简易版本的操作系统,系统还很小、很简陋,但这的确是操作系统的原型。 96 | 97 | ## [Day 7](OS/day7/day7.md) 98 | 99 | -------------------------------------------------------------------------------- /OS/day6/liba.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [GLOBAL printInPos] ;Print content at the cursor 3 | [GLOBAL putchar] ;Output one character 4 | [GLOBAL getch] ;Get keyboard input 5 | [GLOBAL clearScreen] ;clear the screen 6 | [GLOBAL poweroff] ;power off 7 | [GLOBAL systime] ;systime 8 | [GLOBAL getDate] ;get date 9 | ;getDate 10 | getDate: 11 | PUSHA 12 | ;---- year ---- 13 | MOV AL,'2' 14 | MOV BH,0 15 | MOV AH,0EH 16 | INT 10H 17 | 18 | MOV AL,'0' 19 | MOV BH,0 20 | MOV AH,0EH 21 | INT 10H 22 | 23 | MOV AL,09H 24 | OUT 70H,AL 25 | IN AL,71H 26 | MOV AH,AL 27 | MOV CL,4 28 | SHR AH,CL 29 | AND AL,00001111B 30 | ADD AH,30H 31 | ADD AL,30H 32 | ;AX = year 33 | MOV DX,AX 34 | MOV AL,DH 35 | MOV BH,0 36 | MOV AH,0EH 37 | INT 10H 38 | 39 | MOV AL,DL 40 | MOV BH,0 41 | MOV AH,0EH 42 | INT 10H 43 | 44 | MOV AL,'-' 45 | MOV BH,0 46 | MOV AH,0EH 47 | INT 10H 48 | ; ---- month ---- 49 | MOV AL,08H 50 | OUT 70H,AL 51 | IN AL,71H 52 | MOV AH,AL 53 | MOV CL,4 54 | SHR AH,CL 55 | AND AL,00001111B 56 | ADD AH,30H 57 | ADD AL,30H 58 | ;AX = month 59 | MOV DX,AX 60 | MOV AL,DH 61 | MOV BH,0 62 | MOV AH,0EH 63 | INT 10H 64 | 65 | MOV AL,DL 66 | MOV BH,0 67 | MOV AH,0EH 68 | INT 10H 69 | 70 | MOV AL,'-' 71 | MOV BH,0 72 | MOV AH,0EH 73 | INT 10H 74 | 75 | ;date 76 | MOV AL,07H 77 | OUT 70H,AL 78 | IN AL,71H 79 | MOV AH,AL 80 | MOV CL,4 81 | SHR AH,CL 82 | AND AL,00001111B 83 | ADD AH,30H 84 | ADD AL,30H 85 | ;AX = date 86 | MOV DX,AX 87 | MOV AL,DH 88 | MOV BH,0 89 | MOV AH,0EH 90 | INT 10H 91 | 92 | MOV AL,DL 93 | MOV BH,0 94 | MOV AH,0EH 95 | INT 10H 96 | 97 | MOV AL,0AH 98 | MOV BH,0 99 | MOV AH,0EH 100 | INT 10H 101 | 102 | MOV AL,0DH 103 | MOV BH,0 104 | MOV AH,0EH 105 | INT 10H 106 | POPA 107 | RETF 108 | ;systime 109 | systime: 110 | PUSHA 111 | ;hour 112 | MOV AL,4 113 | OUT 70H,AL 114 | IN AL,71H 115 | MOV AH,AL 116 | MOV CL,4 117 | SHR AH,CL 118 | AND AL,00001111B 119 | ADD AH,30H 120 | ADD AL,30H 121 | ;AX = hour 122 | MOV DX,AX 123 | MOV AL,DH 124 | MOV BH,0 125 | MOV AH,0EH 126 | INT 10H 127 | 128 | MOV AL,DL 129 | MOV BH,0 130 | MOV AH,0EH 131 | INT 10H 132 | 133 | MOV AL,':' 134 | MOV BH,0 135 | MOV AH,0EH 136 | INT 10H 137 | ;---- minute ---- 138 | MOV AL,2 139 | OUT 70H,AL 140 | IN AL,71H 141 | MOV AH,AL 142 | MOV CL,4 143 | SHR AH,CL 144 | AND AL,00001111B 145 | ADD AH,30H 146 | ADD AL,30H 147 | ;AX = minute 148 | MOV DX,AX 149 | MOV AL,DH 150 | MOV BH,0 151 | MOV AH,0EH 152 | INT 10H 153 | 154 | MOV AL,DL 155 | MOV BH,0 156 | MOV AH,0EH 157 | INT 10H 158 | 159 | MOV AL,':' 160 | MOV BH,0 161 | MOV AH,0EH 162 | INT 10H 163 | ; ----- second ---- 164 | MOV AL,0 165 | OUT 70H,AL 166 | IN AL,71H 167 | MOV AH,AL 168 | MOV CL,4 169 | SHR AH,CL 170 | AND AL,00001111B 171 | ADD AH,30H 172 | ADD AL,30H 173 | ;AX = second 174 | MOV DX,AX 175 | MOV AL,DH 176 | MOV BH,0 177 | MOV AH,0EH 178 | INT 10H 179 | 180 | MOV AL,DL 181 | MOV BH,0 182 | MOV AH,0EH 183 | INT 10H 184 | 185 | MOV AL,0AH 186 | MOV BH,0 187 | MOV AH,0EH 188 | INT 10H 189 | 190 | MOV AL,0DH 191 | MOV BH,0 192 | MOV AH,0EH 193 | INT 10H 194 | POPA 195 | RETF 196 | ;power off 197 | poweroff: 198 | MOV AX, 5307H 199 | MOV BX, 0001H 200 | MOV CX, 0003H 201 | INT 15H 202 | 203 | ;clear the screen 204 | clearScreen: 205 | PUSH AX 206 | MOV AX, 0003H 207 | INT 10H 208 | POP AX 209 | RETF 210 | ;Get keyboard input 211 | getch: 212 | MOV AH, 0 ;Function number 213 | INT 16H 214 | MOV AH, 0 ;Read characters, AL = characters 215 | RETF 216 | 217 | ;Print characters at the cursor 218 | putchar: 219 | PUSHA ;Protect the scene 220 | MOV BP, SP ;Save the top of the stack 221 | ADD BP, 16 + 4 ;Parameter address 222 | MOV AL, [BP] ;AL = Print characters 223 | MOV BH, 0 ;BH = page number 224 | MOV AH, 0EH ;function number 225 | INT 10H 226 | POPA 227 | RETF 228 | 229 | ;Display the string at the specified position 230 | printInPos: 231 | PUSHA 232 | MOV SI, SP ;use SI 233 | ADD SI, 16 + 4 ;First parameter address 234 | MOV AX, CS 235 | MOV DS, AX 236 | MOV BP, [SI] ;BP = offset 237 | MOV AX, DS ; 238 | MOV ES, AX ;ES = DS 239 | MOV CX, [SI + 4] ;CX = String length 240 | MOV AX, 1301H ;functon number AH = 13 AL = 01H,Indicates that the cursor displays the end of the string 241 | MOV BX, 0007H ;BH = page number BL = 07 black and white 242 | MOV DH, [SI + 8] ;Line number= 0 243 | MOV DL, [SI + 12] ;Column number = 0 244 | INT 10H ;BIOS 10H interrupt call 245 | POPA 246 | RETF 247 | -------------------------------------------------------------------------------- /OS/day6/libc.c: -------------------------------------------------------------------------------- 1 | /* Using C language expansion kernel 2 | * if you have any problem,contact me at djh113@126.com 3 | * or visit my homepage at https://github.com/djh-sudo 4 | */ 5 | #include "stringio.h" 6 | #define BUF_LEN 32 7 | extern void clearScreen(); 8 | extern void poweroff(); 9 | extern void systime(); 10 | extern void drawPic(); 11 | extern void getDate(); 12 | // start menu 13 | void startUp(){ 14 | clearScreen(); 15 | char* title = "TinyOS Oerating System version 1.0"; 16 | char* subTitle = "Designed by DJH-sudo"; 17 | char* copyRight = "Coypleft by GNU"; 18 | char* hint = "System is ready.Press ENTER\r\n"; 19 | 20 | printInPos(title,strlen(title),5,23); 21 | printInPos(subTitle,strlen(subTitle),6,23); 22 | printInPos(copyRight,strlen(copyRight),8,23); 23 | printInPos(hint,strlen(hint),15,23); 24 | } 25 | 26 | //print shell 27 | void promptString(){ 28 | char*p_string = "TinyOS v1#>"; 29 | print(p_string); 30 | } 31 | 32 | // help me 33 | void showHelp(){ 34 | char * help = "shell for OS version 1.1 x86 PC\r\n" 35 | "This is the shell command!\r\n" 36 | "Use `help` to see the list \r\n" 37 | "\r\n" 38 | "\rcls - clear the ternimal \r\n" 39 | "\rtime - get current time \r\n" 40 | "\rdrawpic - draw a animate pic \r\n" 41 | "\rpoweroff - force the OS shut down" 42 | "\r\n"; 43 | print(help); 44 | } 45 | 46 | void shell(){ 47 | clearScreen(); 48 | showHelp(); 49 | char cmdStr[BUF_LEN + 1] = {0}; 50 | char cmdFirstWord[BUF_LEN + 1] = {0}; 51 | enum{help,clear,time,power_off,date}; 52 | char*command[] = {"help","cls","time","poweroff","date"}; 53 | while(1){ 54 | promptString(); 55 | read2Buf(cmdStr,BUF_LEN); 56 | getFirstWord(cmdStr,cmdFirstWord); 57 | if(strcmp(cmdFirstWord,command[clear]) == 0){ 58 | clearScreen(); 59 | } 60 | else if(strcmp(cmdFirstWord,command[help]) == 0){ 61 | showHelp(); 62 | } 63 | else if(strcmp(cmdFirstWord,command[power_off]) == 0){ 64 | poweroff(); 65 | } 66 | else if(strcmp(cmdFirstWord,command[time]) == 0){ 67 | systime(); 68 | } 69 | else if(strcmp(cmdFirstWord,command[date]) == 0){ 70 | getDate(); 71 | } 72 | else{ 73 | if(cmdFirstWord[0] != 0){ 74 | char*errMsg = ": command not dound\r\n"; 75 | print(cmdFirstWord); 76 | print(errMsg); 77 | } 78 | } 79 | } 80 | } 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /OS/day6/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | KERNEL_BASE_ADDR EQU 0x8000 3 | KERNEL_START_SECTOR EQU 0X9;kernel从第9个扇区开始读 4 | SECTION LOADER VSTART=LOADER_BASE_ADDR 5 | 6 | MOV AX, 0XB800 ;显存位置 7 | MOV ES, AX 8 | 9 | MOV BYTE[ES: 0X00], '8' 10 | MOV BYTE[ES: 0X01], 0X07 11 | MOV BYTE[ES: 0X02], '0' 12 | MOV BYTE[ES: 0X03], 0X06 13 | 14 | MOV EAX,KERNEL_START_SECTOR;LBA 读入的扇区个数 15 | MOV BX,KERNEL_BASE_ADDR;KERNEL的起始地址 16 | MOV CX,8 17 | 18 | CALL READ_DISK 19 | JMP KERNEL_BASE_ADDR 20 | 21 | 22 | 23 | ;--------------------读扇区 24 | READ_DISK: 25 | ;EAX LBA 扇区号 26 | ;BX 数据写入内存的地址 27 | ;CX 读入扇区数 28 | 29 | ;保存寄存器 EAX CX 30 | MOV ESI, EAX 31 | MOV DI, CX 32 | ;设置读取扇区的数量 33 | ;读写硬盘 34 | MOV DX, 0X1F2 ; 35 | MOV AL, CL ; 36 | OUT DX, AL 37 | 38 | ;恢复 EAX 39 | MOV EAX, ESI 40 | 41 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 42 | ;0-7 写入0x1f3 43 | MOV DX, 0X1F3 44 | OUT DX, AL 45 | ;8-15 写入0x1f4 46 | MOV CL, 8 47 | SHR EAX, CL 48 | MOV DX, 0X1F4 49 | OUT DX, AL 50 | ;16-23 写入0x1f5 51 | SHR EAX, CL 52 | AND AL, 0X0F 53 | MOV DX, 0X1F5 54 | OUT DX, AL 55 | ;24 - 27 56 | SHR EAX, CL 57 | AND AL, 0X0F 58 | OR AL, 0XE0 ;AL = 1110 0000 59 | MOV DX, 0X1F6 60 | OUT DX, AL 61 | ;向0x1f7 写入读命令 62 | MOV DX, 0X1F7 63 | MOV AL, 0X20 ;表示读命令 64 | 65 | OUT DX, AL 66 | ;检测硬盘状态 67 | .NOT_READY: 68 | NOP 69 | ;读取端口,查看状态 70 | IN AL, DX 71 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 72 | CMP AL, 0X08 73 | JNZ .NOT_READY 74 | ;读数据 75 | MOV AX, DI ;这里 DI = 1 76 | MOV DX, 512 77 | MUL DX 78 | MOV CX, AX 79 | MOV DX, 0X1F0 80 | .GO_ON: 81 | IN AX, DX 82 | MOV[BX], AX ;向BASE_ADDR(内存0x8000)写数据 83 | ADD BX, 2 84 | LOOP .GO_ON 85 | RET 86 | -------------------------------------------------------------------------------- /OS/day6/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day6/osKernel.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [EXTERN startUp] 3 | [EXTERN shell] 4 | 5 | GLOBAL _start 6 | ;entry 7 | _start: 8 | CALL DWORD startUp 9 | ;wait the keyboard 10 | KeyBoard: 11 | MOV AH, 0 12 | INT 16H 13 | CMP AL, 0DH 14 | JNE KeyBoard 15 | CALL DWORD shell 16 | JMP KeyBoard 17 | -------------------------------------------------------------------------------- /OS/day6/stringio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Realize some functions of C language string 3 | * if you have any problem,contact me at djh113@126.com 4 | * or visit my homepage at https://github.com/djh-sudo 5 | */ 6 | #include 7 | 8 | extern void printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); // Print content at the cursor 9 | extern void putchar(char c); // Output one character 10 | extern char getch(); // Get keyboard input 11 | 12 | 13 | // Calculate string length 14 | uint16_t strlen(char *str){ 15 | uint16_t count = 0; 16 | while(str[count++] != 0); 17 | return count - 1; 18 | } 19 | 20 | // Compare strings 21 | uint8_t strcmp(char* str1,char* str2){ 22 | int i = 0; 23 | while(1){ 24 | if(str1[i] == 0 || str2[i] == 0) 25 | break; 26 | if(str1[i] != str2[i]) 27 | break; 28 | i++; 29 | } 30 | return (str1[i] - str2[i]); 31 | } 32 | 33 | // Display characters at the cursor 34 | void print(char*str){ 35 | uint16_t len = strlen(str); 36 | for(int i = 0; i < len; i++){ 37 | putchar(str[i]); 38 | } 39 | } 40 | 41 | // Get the first word of the string 42 | void getFirstWord(char*str,char*buf){ 43 | int i = 0; 44 | while(str[i] && str[i] != ' ' && str[i] != '\n'){ 45 | buf[i] = str[i]; 46 | i++; 47 | } 48 | buf[i] = 0; 49 | } 50 | 51 | // Read the string into the buffer 52 | void read2Buf(char*buffer,uint16_t maxLen){ 53 | int i = 0; 54 | while(1){ 55 | char tempc = getch();// Read character by character 56 | if(!(tempc == 0x0D || tempc == '\b' || (tempc >= 32 && tempc <= 127))){ 57 | continue;// Invaild symbol 58 | } 59 | if(i > 0 && i < maxLen - 1){ 60 | if(tempc == 0x0D){// press enter stop! 61 | break; 62 | }else if(tempc == '\b'){// back 63 | putchar('\b'); 64 | putchar(' '); 65 | putchar('\b'); 66 | i--; 67 | }else{// Valid characters 68 | putchar(tempc); 69 | buffer[i] = tempc; 70 | i++; 71 | } 72 | }else if(i >= maxLen - 1){ 73 | // Buffer exceeded 74 | if(tempc == '\b'){ 75 | putchar('\b'); 76 | putchar(' '); 77 | putchar('\b'); 78 | i--; 79 | }else if(tempc == 0x0D){ 80 | break; 81 | } 82 | }else if(i <= 0){ 83 | if(tempc == 0x0D) 84 | break; 85 | else if(tempc != '\b'){ 86 | putchar(tempc); 87 | buffer[i] = tempc; 88 | i++; 89 | } 90 | } 91 | } 92 | putchar('\r'); 93 | putchar('\n'); 94 | buffer[i] = 0; 95 | } 96 | -------------------------------------------------------------------------------- /OS/day7/Makefile: -------------------------------------------------------------------------------- 1 | # 默认动作 2 | TOOL_PATH = ../tools/ 3 | ASSEMBLE = ../tools/nasm/ 4 | 5 | 6 | MAKE = $(TOOL_PATH)make.exe -r 7 | NASM = $(ASSEMBLE)nasm.exe 8 | DD = $(TOOL_PATH)dd.exe 9 | DEL = del 10 | COPY = copy 11 | 12 | 13 | default : 14 | $(MAKE) mbr 15 | $(MAKE) loader 16 | $(MAKE) pm 17 | 18 | # 镜像文件生成 19 | 20 | loader.bin: loader.asm Makefile 21 | $(NASM) loader.asm -o loader.bin 22 | 23 | mbr.bin: day2.asm Makefile 24 | $(NASM) day2.asm -o mbr.bin 25 | 26 | pm.bin:pm.asm Makefile 27 | $(NASM) pm.asm -o pm.bin 28 | 29 | # 其他指令 30 | 31 | loader: 32 | $(MAKE) loader.bin 33 | 34 | pm: 35 | $(MAKE) pm.bin 36 | 37 | mbr: 38 | $(MAKE) mbr.bin 39 | 40 | clean: 41 | -$(DEL) loader.bin 42 | -$(DEL) mbr.bin 43 | -$(DEL) pm.bin 44 | 45 | 46 | add: 47 | $(DD) if=mbr.bin of=dingst.vhd bs=512 count=1 48 | $(DD) if=loader.bin of=dingst.vhd bs=512 count=1 seek=2 49 | $(DD) if=osKernel.bin of=dingst.vhd bs=512 count=100 seek=9 50 | $(DD) if=pm.bin of=dingst.vhd bs=512 count=100 seek=20 51 | 52 | backup: 53 | -$(DEL) dingst.vhd 54 | $(COPY) ..\Backup\dingst(empty).vhd dingst.vhd 55 | 56 | run: 57 | -$(DEL) ..\dingst.vhd 58 | $(COPY) dingst.vhd ..\dingst.vhd 59 | 60 | all: 61 | $(MAKE) backup 62 | $(MAKE) clean 63 | $(MAKE) mbr 64 | $(MAKE) loader 65 | $(MAKE) pm 66 | $(MAKE) add 67 | $(MAKE) run -------------------------------------------------------------------------------- /OS/day7/day2.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | LOADER_START_SECTOR EQU 0X2 ;以LBA方式,loader存在第2个扇区,第一个是主引导扇区 3 | 4 | 5 | SECTION MBR VSTART=0X7C00 ;程序加载到0x7c00 6 | ;初始化寄存器 7 | MOV AX, CS 8 | MOV DS, AX 9 | MOV ES, AX 10 | MOV SS, AX 11 | MOV FS, AX 12 | MOV SP, 0X7C00 13 | MOV AX, 0XB800 ;显存位置 14 | MOV GS, AX 15 | ;调用BIOS 0x10中断 16 | MOV AX, 0X0600 ;AH 功能 AL是内容,这里都是0 17 | MOV BX, 0X0700 ;BH 上卷行的属性 18 | MOV CX, 0 ;(CL,CH)->(X0,Y0)左上角 19 | MOV DX, 184FH ;(DL,DH)->(X1,Y1) 右下角 (80,25) 20 | INT 10H ;调用BIOS 0x10中断 21 | ;输出 22 | MOV BYTE [GS: 0X00], '*' 23 | MOV BYTE [GS: 0X01], 0XA4 24 | 25 | MOV BYTE [GS: 0X02], '*' 26 | MOV BYTE [GS: 0X03], 0XA4 27 | 28 | MOV BYTE [GS: 0X04], 'M' 29 | MOV BYTE [GS: 0X05], 0XA4 30 | 31 | MOV BYTE [GS: 0X06], 'B' 32 | MOV BYTE [GS: 0X07], 0XA4 33 | 34 | MOV BYTE [GS: 0X08], 'R' 35 | MOV BYTE [GS: 0X09], 0XA4 36 | 37 | ;读LOADER 38 | MOV EAX, LOADER_START_SECTOR ;LBA方式读入扇区 39 | MOV BX, LOADER_BASE_ADDR ;LBA方式写入地址 40 | MOV CX, 1 ;读一个扇区 41 | CALL READ_DISK 42 | JMP LOADER_BASE_ADDR ;跳转至 LOADER 43 | 44 | ;读扇区 45 | READ_DISK: 46 | ;EAX LBA 扇区号 47 | ;BX 数据写入内存的地址 48 | ;CX 读入扇区数 49 | 50 | ;保存寄存器 EAX CX 51 | MOV ESI, EAX 52 | MOV DI, CX 53 | ;设置读取扇区的数量 54 | ;读写硬盘 55 | MOV DX, 0X1F2 ; 56 | MOV AL, CL ; 57 | OUT DX, AL 58 | 59 | ;恢复 EAX 60 | MOV EAX, ESI 61 | 62 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 63 | ;0-7 写入0x1f3 64 | MOV DX, 0X1F3 65 | OUT DX, AL 66 | ;8-15 写入0x1f4 67 | MOV CL, 8 68 | SHR EAX, CL 69 | MOV DX, 0X1F4 70 | OUT DX, AL 71 | ;16-23 写入0x1f5 72 | SHR EAX, CL 73 | AND AL, 0X0F 74 | MOV DX, 0X1F5 75 | OUT DX, AL 76 | ;24 - 27 77 | SHR EAX, CL 78 | AND AL, 0X0F 79 | OR AL, 0XE0 ;AL = 1110 0000 80 | MOV DX, 0X1F6 81 | OUT DX, AL 82 | ;向0x1f7 写入读命令 83 | MOV DX, 0X1F7 84 | MOV AL, 0X20 ;表示读命令 85 | 86 | OUT DX, AL 87 | ;检测硬盘状态 88 | .NOT_READY: 89 | NOP 90 | ;读取端口,查看状态 91 | IN AL, DX 92 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 93 | CMP AL, 0X08 94 | JNZ .NOT_READY 95 | ;读数据 96 | MOV AX, DI ;这里 DI = 1 97 | MOV DX, 512 98 | MUL DX 99 | MOV CX, AX 100 | MOV DX, 0X1F0 101 | .GO_ON: 102 | IN AX, DX 103 | MOV[BX], AX ;向BASE_ADDR(内存0x900)写数据 104 | ADD BX, 2 105 | LOOP .GO_ON 106 | RET 107 | 108 | TIMES 510 - ($ - $$) DB 0 109 | DB 0X55, 0XAA 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OS/day7/day4.asm: -------------------------------------------------------------------------------- 1 | EXTERN DISPLAY ;FROM C 2 | [BITS 16] 3 | [SECTION .TEXT] 4 | GLOBAL _start 5 | GLOBAL MYPRINT 6 | _start: 7 | CALL DISPLAY ;ASM CALL CALL 8 | MYPRINT: 9 | MOV AX, 0XB800 10 | MOV ES, AX 11 | MOV BYTE [ES: 0X00], 'O' 12 | MOV BYTE [ES: 0X01], 0X07 13 | MOV BYTE [ES: 0X02], 'K' 14 | MOV BYTE [ES: 0X03], 0X06 15 | RET 16 | -------------------------------------------------------------------------------- /OS/day7/day7.md: -------------------------------------------------------------------------------- 1 | # Day 7 2 | 3 | ## 前言 4 | 5 | 从这里开始,又进入了比较难的一部分,也就是实模式向保护模式的转化。我们之前的程序都是基于实模式下编写的,这也是`x86`汇编最经典的部分。但是实模式的缺点有很多,例如`segement + offset`的寻址大小是有限的,只有`1M`字节。同时,程序的内存之间不是隔离的,有很大的操作空间,显然这是不安全的。 6 | 7 | ## 保护模式 8 | 9 | 在实模式中,我们可以对任意寄存器操作,同时也可以访问任意的内存空间,这虽然方便,但从安全角度而言,很危险,各个程序之间的内存不是隔离开来的。 10 | 11 | 所以从`.386`及以后,开始有了保护模式。保护模式访问内存的方式和实模式有一定的区别。寄存器的长度扩展到了`32 bit`。首先我们要认识几个新的改变: 12 | 13 | 在实模式下,逻辑地址就是实际的物理地址。即`segement + offset`的方式寻址。 14 | 15 | 但是保护模式下,每个应用程序的地址需要独立,它们从逻辑上有`4G(2^32)`的独立空间。要实现这一目的,首先硬件要支持(`MMU`),即内存管理单元,负责将虚拟页面映射至物理页面。 16 | 17 | ### 平坦模型 18 | 19 | * 首先,我们的要设置段的界限,以及每个段的访问权限,防止越界访问。 20 | 21 | * 其次,`CS,DS`,这些段寄存器,不再是段基址,而是段选择子,这也就意味者放弃了直接访问物理内存的机会。因为只有段选择字和段描述符结合起来才能访问真实的物理地址。一个描述符是`8 Byte`,它们共同构成了全局描述符表。下面是段描述符的结构。 22 | 23 | * | `31 ~ 24` | `23` | `22` | `21` | `20` | `19 ~ 16` | `15` | `14 ~ 13` | `12` | `11~8` | `7 ~ 0` | 24 | | :-----------: | :--: | ----- | :--: | :---: | :-------: | :--: | :-------: | :--: | :----: | :-----------: | 25 | | 段基址`31-24` | `G` | `D/B` | `L` | `AVL` | 段界限 | `P` | `DPL` | `S` | `TYPE` | 段基址`23-16` | 26 | 27 | * | `31 ~ 16` | `15 ~ 0` | 28 | | :-----------: | :-----------: | 29 | | 段基址 `15-0` | 段界限 `15-0` | 30 | 31 | * 1. `DPL`表示`CPU`工作在特权级还是用户级。 32 | 2. `S`,`0`:是系统段,`1`:表示代码段/数据段。 33 | 3. `G`,表示段界限的单位,`0`:字节为单位,`1`:`4KB`为单位。 34 | 4. `L`,表示64的代码段标志。 35 | 5. `TYPE`,表示描述符的字类型,`4`中组合方式,代码段/数据段,可读/可写/可执行。 36 | 6. `P`,异常中断的检测。 37 | 38 | * 为了支持上述复杂的功能,需要一个数据结构去描述,即`GDT`(全局描述符表),这个数据结构在内存中。 39 | 40 | ### 流程 41 | 42 | 1. `CR0`寄存器将原有的`Segment + offset`寻址模式变为段选择子。 43 | 2. 初始化`GDT` 44 | 3. 打开`A20`地址线。 45 | 4. 打开`CR0`寄存器。 46 | 5. 进入保护模式。 47 | 48 | ## 汇编部分 49 | 50 | ### 全局描述符 51 | 52 | 让我们再次回归一下段描述符,这是一个`64 bit`的结构。而`GDT`的每一项就是这样一个结构,所以顾名思义叫`Global Descriptor Table`。 53 | 54 | ![](src/descriptor.png) 55 | 56 | 而之前提到的段选择子的概念如下: 57 | 58 | 原有的段寄存器,如`CS`,`DS`,`SS`等,其不在是段基址,因为段基址在段描述符(上图)中已经规定好了,而这一个个`16`位的寄存器其结构变成了下面的形式。 59 | 60 | ![SELECTOR](src/SELECTOR.png) 61 | 62 | 其中高`13`位是索引值,也就是在`GDT`中的下标。`TI`表示是局部还是全局,即这个选择子是在`GDT`中,还是`LDT`(局部描述符表)中。低两位,是请求特权级,也就是`R0 ~ R3`。 63 | 64 | 接下来正式进入汇编部分,首先是一个宏。 65 | 66 | ```assembly 67 | %MACRO DESCRIPTOR 3 68 | DW %2 & 0FFFFH ;段界限1 (2字节) 69 | DW %1 & 0FFFFH ;段基址1 (2字节) 70 | DB (%1 >> 16) & 0FFH ;段基址2 (1字节) 71 | DW ((%2 >> 8) & 0F00H) | (%3 & 0F0FFH) ;属性1 + 段界限2 + 属性2 (2字节) 72 | DB (%1 >> 24) & 0FFH ;段基址3 73 | %ENDMACRO 74 | ``` 75 | 76 | 这里宏有三个入口参数,即 `%1 %2 %3`。分别表示段基址,段界限(段内最大偏移量)以及段属性。我们知道一个描述符的长度是`64 bit`,也就是`8`字节。每一个位置的含义在上面这个图都有说明,而上面这段汇编的含义就是根据传入的这三个参数,填写对应的位置的值。后续我们一起看看实例。 77 | 78 | 这里我们定义了一些属性 79 | 80 | ```assembly 81 | DA_32 EQU 4000H ;32位 82 | DA_C EQU 98H ;只执行代码段的属性 83 | DA_DRW EQU 92H ;可读写的数据段 84 | DA_DRWA EQU 93H ;存在的已访问的可读写的 85 | [SECTION .GDT] 86 | ;GDT 87 | ; 段基址, 段界限 属性 88 | PM_GDT: DESCRIPTOR 0, 0, 0 89 | PM_DESC_CODE32: DESCRIPTOR 0, SEGCODE32LEN -1, DA_C + DA_32 90 | PM_DESC_DATA: DESCRIPTOR 0, DATALEN-1, DA_DRW 91 | PM_DESC_STACK: DESCRIPTOR 0, TOPOFSTACK, DA_DRWA + DA_32 92 | PM_DESC_TEST: DESCRIPTOR 0200000H, 0FFFFH, DA_DRW 93 | PM_DESC_VIDEO: DESCRIPTOR 0B8000H, 0FFFFH, DA_DRW 94 | ;end of definiton gdt 95 | GDTLEN EQU $ - PM_GDT 96 | ``` 97 | 98 | 这里我们在写段描述符,这里我们传入的三个参数依次就是段基址、段界限以及段属性。利用`DESCRIPTOR`这个宏,能够帮助我们填写描述符的各个字段。 99 | 100 | 以我们的自己的段`PM_DESC_TEST`为例,这里段基址是`0200000H`,这个地址已经超出了`1M`的限制。值得一说的是,段界限一共是`20 bit`,最多就是`1M`,但是我们还没有说单位,如果属性字段`G = 1`,单位就是页`4KB`,否则就是字节。这里我们只是测试,所以填入了`0xFFFFH`。最后一个字段是属性。我们对应着汇编语句看。 101 | 102 | * ```assembly 103 | DW %2 & 0FFFFH ;段界限1 (2字节) 104 | ``` 105 | 106 | ​ 这句话的意思是,描述符的前两个字节的内容是 `%2 & 0FFFFH`,即第`2`个参数与`0xFFFF`按位与。 107 | 108 | * ```assembly 109 | DW %1 & 0FFFFH ;段基址1 (2字节) 110 | ``` 111 | 112 | 段描述符的接下来两个字节,填入段基址的低`16`位,所以是`%1 & 0FFFFH`。这与上图的一致的。 113 | 114 | * ```assembly 115 | DB (%1 >> 16) & 0FFH ;段基址2 (1字节) 116 | ``` 117 | 118 | ​ 接下来的一个字节,是段基址`16 ~ 23`位,因此,我们的参数一,也就是段基址首先要右移`16`位,然后在按位与`0xFFH`,这样才能取到对应的位置。 119 | 120 | * ```assembly 121 | DW ((%2 >> 8) & 0F00H) | (%3 & 0F0FFH) ;属性1 + 段界限2 + 属性2 (2字节) 122 | ``` 123 | 124 | ​ 接下来的两个字节,也就是下面这张图。 125 | 126 | ![DW](src/DW.png) 127 | 128 | 那么首先是段界限左移`8`位,这样能够拿到段界限的高`4`位,然后与上`0F00H`,将其余位屏蔽,只保留段界限的位置。之后的`(%3 & 0F0FFH)`,起始就是属性位,这里可以看到,这`16 bit`刚好屏蔽了段界限,最后将二者拼接起来,用`|`(或运算)。 129 | 130 | `DA_DRW EQU 92H`这样一个属性为什么就是可读可写呢。展开,即`0000 0000 1001 0010 B`,可以看到,这里`type = 2`,正是[可读可写的编码](https://blog.csdn.net/qq_35733751/article/details/91347768)。 131 | 132 | * ```assembly 133 | DB (%1 >> 24) & 0FFH ;段基址3 134 | ``` 135 | 136 | ​ 最后一个字节,是段基址的最高`8`位,因此需要将段基址右移`24 bit`。 137 | 138 | 其余的段的分析是类似的,我们也能看到宏定义的好处,那就是具备扩展性。 139 | 140 | ### 选择子 141 | 142 | ```assembly 143 | ;GDT 选择子 144 | SELECTOERCODE32 EQU PM_DESC_CODE32 - PM_GDT 145 | SELECTOERDATA EQU PM_DESC_DATA - PM_GDT 146 | SELECTOERSTACK EQU PM_DESC_STACK - PM_GDT 147 | SELECTOERTEST EQU PM_DESC_TEST - PM_GDT 148 | SELECTOERVIDEO EQU PM_DESC_VIDEO - PM_GDT 149 | ;END of [SECTION .gdt] 150 | ``` 151 | 152 | 选择子,如果不看请求权限,其实就是索引,表明在`GDT`中的位置。 153 | 154 | ### 数据段 155 | 156 | ​ 接下来的部分就是自己定义的段,这里我们以数据段为例。 157 | 158 | ```assembly 159 | [SECTION .DATA1] 160 | ALIGN 32 161 | [BITS 32] 162 | PM_DATA: 163 | PMMESSAGE : DB "POTECT MODE", 0 ; 164 | OFFSETPMESSAGE EQU PMMESSAGE - $$ 165 | DATALEN EQU $- PM_DATA 166 | ;END of [SECTION .data] 167 | ``` 168 | 169 | 在数据段中,我们定义了一个字符串,最后为了方便打印字符串,我们给出了字符串的相对偏移地址`offset`。 170 | 171 | ### 初始化代码段 172 | 173 | ​ 初始化代码段的汇编如下: 174 | 175 | ```assembly 176 | XOR EAX, EAX 177 | MOV AX, CS 178 | SHL EAX, 4 179 | ADD EAX, PM_SEG_CODE32 180 | MOV WORD[PM_DESC_CODE32+2], AX 181 | SHR EAX, 16 182 | MOV BYTE [PM_DESC_CODE32+4], AL 183 | MOV BYTE [PM_DESC_CODE32+7], AH 184 | ``` 185 | 186 | 我们逐句简单看看吧,首先是`XOR`,清零`EAX`寄存器操作,为什么要用`EAX`,这刚好是一个`32`位的寄存器,同时我们的基地址也是`32 bit`。 187 | 188 | 接下来把程序的实际运行的地址`CS`放入`AX`寄存器中,随后左移四位,因为我们知道实际这段程序还是在`16 bit`模式下运行的,我们还没有真正跳入保护模式,所以寻址方式依旧是`16 * segement + offset`。左移`4`位也就很正常了。而后面的`PM_SEG_CODE32`正是代码段的偏移地址,因此也要加给`EAX`寄存器。 189 | 190 | 之后,`EAX`中保存了我们代码段的地址,此时我们需要将这段地址写入描述符中,首先写入低`16`位,然后再分别写入其它两个字节。为什么要这么做呢,可以再看看这个图。 191 | 192 | ![](src/descriptor.png) 193 | 194 | ` MOV WORD[PM_DESC_CODE32+2], AX`,这里`+ 2`是跳过了前面两字节的段界限,而后面的`+ 4/ + 7`,道理是一样的,因为这里段基址不是连续的。 195 | 196 | 而初始化其它的段,和上面的代码没有实质性区别,需要修改的不过只是对应段的标号。 197 | 198 | ### 加载 GDTR 199 | 200 | ```assembly 201 | XOR EAX, EAX 202 | MOV AX, DS 203 | SHL EAX, 4 204 | ADD EAX, PM_GDT 205 | MOV DWORD [GDTPTR +2], EAX 206 | LGDT [GDTPTR] 207 | ``` 208 | 这一段代码,我们利用指令`LGDT`,将`GDT`的初始位置,加载至特定的寄存器中。这里前面的初始化和代码段初始化是类似的,这里一次性把`GDT`地址写入内存,为什么要`+ 2`,这是因为寄存器`gdtr`一共`48 bit`,其中低`16`位用于存放全局描述符的边界,高`32`位用于存放`gdt`地址。所以这里`+ 2`也就是跳过低位的两个字节。 209 | 210 | 之后是对`A20`地址线和`CR0`寄存器操作,最后跳入保护模式。 211 | 212 | ```assembly 213 | CLI 214 | IN AL, 92H 215 | OR AL, 00000010B 216 | OUT 92H, AL 217 | 218 | ;切换到保护模式 219 | MOV EAX, CR0 220 | OR EAX, 1 221 | MOV CR0, EAX 222 | JMP DWORD SELECTOERCODE32: 0 223 | ``` 224 | 225 | ## 修改相应的地址 226 | 227 | 最后,在本地用`nasm`编译即可,将这个产生的二进制文件,写入磁盘的第20个扇区。 228 | 229 | 同时,我们在`liba.asm`中添加一个函数`callpm`,也就是跳转为保护模式。 230 | 231 | ```assembly 232 | CALLPM: 233 | ;进入保护模式 234 | MOV EAX,20 ;LBA 读入的扇区起始偏移 235 | MOV BX,0X9000 ;保护模式下KERNEL的起始地址 236 | MOV CX,10 ;读入扇区个数 237 | 238 | CALL READ_DISK 239 | JMP 0x9000 240 | ``` 241 | 242 | 这段代码,会将磁盘的第20个扇区开始,连续读入10个扇区,加载至内存的`0x9000`处。 243 | 244 | 同时,我们的`pm.asm`程序的入口地址,正好也是`0x9000`。这样,一旦扇区读写完成后,程序会自动跳转到这一地址,继续执行这里的程序,而`0x9000`处的程序,正是我们编写的实模式跳转至保护模式的程序。 245 | 246 | ```assembly 247 | ORG 0X9000 ;起始地址 248 | JMP PM_BEGIN ;跳入到标号为PM_BEGIN的代码段开始推进 249 | ``` 250 | 251 | 最后,重新编译内核`liba.asm`,修改并执行`Makefile`,开机。程序已成功进入保护模式。 252 | 253 | ![OS](src/OS.png) 254 | 255 | ## [Day 8 结语](OS/day8/day8.md) 256 | 257 | -------------------------------------------------------------------------------- /OS/day7/liba.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [GLOBAL printInPos] ;Print content at the cursor 3 | [GLOBAL putchar] ;Output one character 4 | [GLOBAL getch] ;Get keyboard input 5 | [GLOBAL clearScreen] ;clear the screen 6 | [GLOBAL poweroff] ;power off 7 | [GLOBAL systime] ;systime 8 | [GLOBAL CALLPM] ;call pm 9 | [GLOBAL getDate] ;get date 10 | ;getDate 11 | getDate: 12 | PUSHA 13 | ;---- year ---- 14 | MOV AL,'2' 15 | MOV BH,0 16 | MOV AH,0EH 17 | INT 10H 18 | 19 | MOV AL,'0' 20 | MOV BH,0 21 | MOV AH,0EH 22 | INT 10H 23 | 24 | MOV AL,09H 25 | OUT 70H,AL 26 | IN AL,71H 27 | MOV AH,AL 28 | MOV CL,4 29 | SHR AH,CL 30 | AND AL,00001111B 31 | ADD AH,30H 32 | ADD AL,30H 33 | ;AX = year 34 | MOV DX,AX 35 | MOV AL,DH 36 | MOV BH,0 37 | MOV AH,0EH 38 | INT 10H 39 | 40 | MOV AL,DL 41 | MOV BH,0 42 | MOV AH,0EH 43 | INT 10H 44 | 45 | MOV AL,'-' 46 | MOV BH,0 47 | MOV AH,0EH 48 | INT 10H 49 | ; ---- month ---- 50 | MOV AL,08H 51 | OUT 70H,AL 52 | IN AL,71H 53 | MOV AH,AL 54 | MOV CL,4 55 | SHR AH,CL 56 | AND AL,00001111B 57 | ADD AH,30H 58 | ADD AL,30H 59 | ;AX = month 60 | MOV DX,AX 61 | MOV AL,DH 62 | MOV BH,0 63 | MOV AH,0EH 64 | INT 10H 65 | 66 | MOV AL,DL 67 | MOV BH,0 68 | MOV AH,0EH 69 | INT 10H 70 | 71 | MOV AL,'-' 72 | MOV BH,0 73 | MOV AH,0EH 74 | INT 10H 75 | 76 | ;date 77 | MOV AL,07H 78 | OUT 70H,AL 79 | IN AL,71H 80 | MOV AH,AL 81 | MOV CL,4 82 | SHR AH,CL 83 | AND AL,00001111B 84 | ADD AH,30H 85 | ADD AL,30H 86 | ;AX = date 87 | MOV DX,AX 88 | MOV AL,DH 89 | MOV BH,0 90 | MOV AH,0EH 91 | INT 10H 92 | 93 | MOV AL,DL 94 | MOV BH,0 95 | MOV AH,0EH 96 | INT 10H 97 | 98 | MOV AL,0AH 99 | MOV BH,0 100 | MOV AH,0EH 101 | INT 10H 102 | 103 | MOV AL,0DH 104 | MOV BH,0 105 | MOV AH,0EH 106 | INT 10H 107 | POPA 108 | RETF 109 | ;systime 110 | systime: 111 | PUSHA 112 | ;hour 113 | MOV AL,4 114 | OUT 70H,AL 115 | IN AL,71H 116 | MOV AH,AL 117 | MOV CL,4 118 | SHR AH,CL 119 | AND AL,00001111B 120 | ADD AH,30H 121 | ADD AL,30H 122 | ;AX = hour 123 | MOV DX,AX 124 | MOV AL,DH 125 | MOV BH,0 126 | MOV AH,0EH 127 | INT 10H 128 | 129 | MOV AL,DL 130 | MOV BH,0 131 | MOV AH,0EH 132 | INT 10H 133 | 134 | MOV AL,':' 135 | MOV BH,0 136 | MOV AH,0EH 137 | INT 10H 138 | ;---- minute ---- 139 | MOV AL,2 140 | OUT 70H,AL 141 | IN AL,71H 142 | MOV AH,AL 143 | MOV CL,4 144 | SHR AH,CL 145 | AND AL,00001111B 146 | ADD AH,30H 147 | ADD AL,30H 148 | ;AX = minute 149 | MOV DX,AX 150 | MOV AL,DH 151 | MOV BH,0 152 | MOV AH,0EH 153 | INT 10H 154 | 155 | MOV AL,DL 156 | MOV BH,0 157 | MOV AH,0EH 158 | INT 10H 159 | 160 | MOV AL,':' 161 | MOV BH,0 162 | MOV AH,0EH 163 | INT 10H 164 | ; ----- second ---- 165 | MOV AL,0 166 | OUT 70H,AL 167 | IN AL,71H 168 | MOV AH,AL 169 | MOV CL,4 170 | SHR AH,CL 171 | AND AL,00001111B 172 | ADD AH,30H 173 | ADD AL,30H 174 | ;AX = second 175 | MOV DX,AX 176 | MOV AL,DH 177 | MOV BH,0 178 | MOV AH,0EH 179 | INT 10H 180 | 181 | MOV AL,DL 182 | MOV BH,0 183 | MOV AH,0EH 184 | INT 10H 185 | 186 | MOV AL,0AH 187 | MOV BH,0 188 | MOV AH,0EH 189 | INT 10H 190 | 191 | MOV AL,0DH 192 | MOV BH,0 193 | MOV AH,0EH 194 | INT 10H 195 | POPA 196 | RETF 197 | ;power off 198 | poweroff: 199 | MOV AX, 5307H 200 | MOV BX, 0001H 201 | MOV CX, 0003H 202 | INT 15H 203 | 204 | ;clear the screen 205 | clearScreen: 206 | PUSH AX 207 | MOV AX, 0003H 208 | INT 10H 209 | POP AX 210 | RETF 211 | ;Get keyboard input 212 | getch: 213 | MOV AH, 0 ;Function number 214 | INT 16H 215 | MOV AH, 0 ;Read characters, AL = characters 216 | RETF 217 | 218 | ;Print characters at the cursor 219 | putchar: 220 | PUSHA ;Protect the scene 221 | MOV BP, SP ;Save the top of the stack 222 | ADD BP, 16 + 4 ;Parameter address 223 | MOV AL, [BP] ;AL = Print characters 224 | MOV BH, 0 ;BH = page number 225 | MOV AH, 0EH ;function number 226 | INT 10H 227 | POPA 228 | RETF 229 | 230 | ;Display the string at the specified position 231 | printInPos: 232 | PUSHA 233 | MOV SI, SP ;use SI 234 | ADD SI, 16 + 4 ;First parameter address 235 | MOV AX, CS 236 | MOV DS, AX 237 | MOV BP, [SI] ;BP = offset 238 | MOV AX, DS ; 239 | MOV ES, AX ;ES = DS 240 | MOV CX, [SI + 4] ;CX = String length 241 | MOV AX, 1301H ;functon number AH = 13 AL = 01H,Indicates that the cursor displays the end of the string 242 | MOV BX, 0007H ;BH = page number BL = 07 black and white 243 | MOV DH, [SI + 8] ;Line number= 0 244 | MOV DL, [SI + 12] ;Column number = 0 245 | INT 10H ;BIOS 10H interrupt call 246 | POPA 247 | RETF 248 | 249 | 250 | 251 | CALLPM: 252 | ;进入保护模式 253 | MOV EAX,20 ;LBA 读入的扇区起始偏移 254 | MOV BX,0X9000 ;保护模式下KERNEL的起始地址 255 | MOV CX,10 ;读入扇区个数 256 | 257 | CALL READ_DISK 258 | JMP 0x9000 259 | READ_DISK: 260 | ;EAX LBA 扇区号 261 | ;BX 数据写入内存的地址 262 | ;CX 读入扇区数 263 | 264 | ;保存寄存器 EAX CX 265 | MOV ESI, EAX 266 | MOV DI, CX 267 | ;设置读取扇区的数量 268 | ;读写硬盘 269 | MOV DX, 0X1F2 ; 270 | MOV AL, CL ; 271 | OUT DX, AL 272 | 273 | ;恢复 EAX 274 | MOV EAX, ESI 275 | 276 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 277 | ;0-7 写入0x1f3 278 | MOV DX, 0X1F3 279 | OUT DX, AL 280 | ;8-15 写入0x1f4 281 | MOV CL, 8 282 | SHR EAX, CL 283 | MOV DX, 0X1F4 284 | OUT DX, AL 285 | ;16-23 写入0x1f5 286 | SHR EAX, CL 287 | AND AL, 0X0F 288 | MOV DX, 0X1F5 289 | OUT DX, AL 290 | ;24 - 27 291 | SHR EAX, CL 292 | AND AL, 0X0F 293 | OR AL, 0XE0 ;AL = 1110 0000 294 | MOV DX, 0X1F6 295 | OUT DX, AL 296 | ;向0x1f7 写入读命令 297 | MOV DX, 0X1F7 298 | MOV AL, 0X20 ;表示读命令 299 | 300 | OUT DX, AL 301 | ;检测硬盘状态 302 | .NOT_READY: 303 | NOP 304 | ;读取端口,查看状态 305 | IN AL, DX 306 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 307 | CMP AL, 0X08 308 | JNZ .NOT_READY 309 | ;读数据 310 | MOV AX, DI ;这里 DI = 1 311 | MOV DX, 512 312 | MUL DX 313 | MOV CX, AX 314 | MOV DX, 0X1F0 315 | .GO_ON: 316 | IN AX, DX 317 | MOV[BX], AX ;向BASE_ADDR(内存0x8000)写数据 318 | ADD BX, 2 319 | LOOP .GO_ON 320 | RET 321 | -------------------------------------------------------------------------------- /OS/day7/libc.c: -------------------------------------------------------------------------------- 1 | /* Using C language expansion kernel 2 | * if you have any problem,contact me at djh113@126.com 3 | * or visit my homepage at https://github.com/djh-sudo 4 | */ 5 | #include "stringio.h" 6 | #define BUF_LEN 32 7 | extern void clearScreen(); 8 | extern void poweroff(); 9 | extern void systime(); 10 | extern void drawPic(); 11 | extern void CALLPM(); 12 | extern void getDate(); 13 | // start menu 14 | void startUp(){ 15 | clearScreen(); 16 | char* title = "TinyOS Oerating System version 1.0"; 17 | char* subTitle = "Designed by DJH-sudo"; 18 | char* copyRight = "Coypleft by GNU"; 19 | char* hint = "System is ready.Press ENTER\r\n"; 20 | 21 | printInPos(title,strlen(title),5,23); 22 | printInPos(subTitle,strlen(subTitle),6,23); 23 | printInPos(copyRight,strlen(copyRight),8,23); 24 | printInPos(hint,strlen(hint),15,23); 25 | } 26 | 27 | //print shell 28 | void promptString(){ 29 | char*p_string = "TinyOS v1#>"; 30 | print(p_string); 31 | } 32 | 33 | // help me 34 | void showHelp(){ 35 | char * help = "shell for OS version 1.1 x86 PC\r\n" 36 | "This is the shell command!\r\n" 37 | "Use `help` to see the list \r\n" 38 | "\r\n" 39 | "\rcls - clear the ternimal \r\n" 40 | "\rtime - get current time \r\n" 41 | "\rdrawpic - draw a animate pic \r\n" 42 | "\rpoweroff - force the OS shut down" 43 | "\r\n"; 44 | print(help); 45 | } 46 | 47 | void shell(){ 48 | clearScreen(); 49 | showHelp(); 50 | char cmdStr[BUF_LEN + 1] = {0}; 51 | char cmdFirstWord[BUF_LEN + 1] = {0}; 52 | enum{help,clear,time,power_off,enterpm,date}; 53 | char*command[] = {"help","cls","time","poweroff","enterpm","date"}; 54 | while(1){ 55 | promptString(); 56 | read2Buf(cmdStr,BUF_LEN); 57 | getFirstWord(cmdStr,cmdFirstWord); 58 | if(strcmp(cmdFirstWord,command[clear]) == 0){ 59 | clearScreen(); 60 | } 61 | else if(strcmp(cmdFirstWord,command[help]) == 0){ 62 | showHelp(); 63 | } 64 | else if(strcmp(cmdFirstWord,command[power_off]) == 0){ 65 | poweroff(); 66 | } 67 | else if(strcmp(cmdFirstWord,command[time]) == 0){ 68 | systime(); 69 | } 70 | else if(strcmp(cmdFirstWord,command[enterpm]) == 0){ 71 | CALLPM(); 72 | } 73 | else if(strcmp(cmdFirstWord,command[date]) == 0){ 74 | getDate(); 75 | } 76 | else{ 77 | if(cmdFirstWord[0] != 0){ 78 | char*errMsg = ": command not dound\r\n"; 79 | print(cmdFirstWord); 80 | print(errMsg); 81 | } 82 | } 83 | } 84 | } 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /OS/day7/loader.asm: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR EQU 0X900 ;加载第2个段的地址 2 | KERNEL_BASE_ADDR EQU 0x8000 3 | KERNEL_START_SECTOR EQU 0X9;kernel从第9个扇区开始读 4 | SECTION LOADER VSTART=LOADER_BASE_ADDR 5 | 6 | MOV AX, 0XB800 ;显存位置 7 | MOV ES, AX 8 | 9 | MOV BYTE[ES: 0X00], '8' 10 | MOV BYTE[ES: 0X01], 0X07 11 | MOV BYTE[ES: 0X02], '0' 12 | MOV BYTE[ES: 0X03], 0X06 13 | 14 | MOV EAX,KERNEL_START_SECTOR;LBA 读入的扇区个数 15 | MOV BX,KERNEL_BASE_ADDR;KERNEL的起始地址 16 | MOV CX,8 17 | 18 | CALL READ_DISK 19 | JMP KERNEL_BASE_ADDR 20 | 21 | 22 | 23 | ;--------------------读扇区 24 | READ_DISK: 25 | ;EAX LBA 扇区号 26 | ;BX 数据写入内存的地址 27 | ;CX 读入扇区数 28 | 29 | ;保存寄存器 EAX CX 30 | MOV ESI, EAX 31 | MOV DI, CX 32 | ;设置读取扇区的数量 33 | ;读写硬盘 34 | MOV DX, 0X1F2 ; 35 | MOV AL, CL ; 36 | OUT DX, AL 37 | 38 | ;恢复 EAX 39 | MOV EAX, ESI 40 | 41 | ;将LBA的地址存入 0x1f3-0x1f6,设置起始扇区编号 42 | ;0-7 写入0x1f3 43 | MOV DX, 0X1F3 44 | OUT DX, AL 45 | ;8-15 写入0x1f4 46 | MOV CL, 8 47 | SHR EAX, CL 48 | MOV DX, 0X1F4 49 | OUT DX, AL 50 | ;16-23 写入0x1f5 51 | SHR EAX, CL 52 | AND AL, 0X0F 53 | MOV DX, 0X1F5 54 | OUT DX, AL 55 | ;24 - 27 56 | SHR EAX, CL 57 | AND AL, 0X0F 58 | OR AL, 0XE0 ;AL = 1110 0000 59 | MOV DX, 0X1F6 60 | OUT DX, AL 61 | ;向0x1f7 写入读命令 62 | MOV DX, 0X1F7 63 | MOV AL, 0X20 ;表示读命令 64 | 65 | OUT DX, AL 66 | ;检测硬盘状态 67 | .NOT_READY: 68 | NOP 69 | ;读取端口,查看状态 70 | IN AL, DX 71 | AND AL, 0X88 ;第4位为1表示可以传输,第7位为1表示繁忙 72 | CMP AL, 0X08 73 | JNZ .NOT_READY 74 | ;读数据 75 | MOV AX, DI ;这里 DI = 1 76 | MOV DX, 512 77 | MUL DX 78 | MOV CX, AX 79 | MOV DX, 0X1F0 80 | .GO_ON: 81 | IN AX, DX 82 | MOV[BX], AX ;向BASE_ADDR(内存0x8000)写数据 83 | ADD BX, 2 84 | LOOP .GO_ON 85 | RET 86 | -------------------------------------------------------------------------------- /OS/day7/make.bat: -------------------------------------------------------------------------------- 1 | ..\tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /OS/day7/osKernel.asm: -------------------------------------------------------------------------------- 1 | BITS 16 2 | [EXTERN startUp] 3 | [EXTERN shell] 4 | 5 | GLOBAL _start 6 | ;entry 7 | _start: 8 | CALL DWORD startUp 9 | ;wait the keyboard 10 | KeyBoard: 11 | MOV AH, 0 12 | INT 16H 13 | CMP AL, 0DH 14 | JNE KeyBoard 15 | CALL DWORD shell 16 | JMP KeyBoard 17 | -------------------------------------------------------------------------------- /OS/day7/pm.asm: -------------------------------------------------------------------------------- 1 | DA_32 EQU 4000H ;32位 2 | DA_C EQU 98H ; 只执行代码段的属性 3 | DA_DRW EQU 92H ;可读写的数据段 4 | DA_DRWA EQU 93H ;存在的已访问的可读写的 5 | 6 | %MACRO DESCRIPTOR 3 7 | DW %2 & 0FFFFH ;段界限1 (2字节) 8 | DW %1 & 0FFFFH ;段基址1 (2字节) 9 | DB (%1 >> 16) & 0FFH ;段基址2 (1字节) 10 | DW ((%2 >> 8) & 0F00H) | (%3 & 0F0FFH) ;属性1 + 段界限2 + 属性2 (2字节) 11 | DB (%1 >> 24) & 0FFH ;段基址3 12 | %ENDMACRO 13 | 14 | 15 | ORG 0X9000 ;起始地址 16 | JMP PM_BEGIN ;跳入到标号为PM_BEGIN的代码段开始推进 17 | 18 | 19 | [SECTION .GDT] 20 | ;GDT 21 | ; 段基址,段界限,属性 22 | PM_GDT: DESCRIPTOR 0, 0, 0 23 | PM_DESC_CODE32: DESCRIPTOR 0, SEGCODE32LEN -1, DA_C+DA_32 24 | PM_DESC_DATA: DESCRIPTOR 0, DATALEN-1, DA_DRW 25 | PM_DESC_STACK: DESCRIPTOR 0, TOPOFSTACK, DA_DRWA+DA_32 26 | PM_DESC_TEST: DESCRIPTOR 0200000H, 0FFFFH, DA_DRW 27 | PM_DESC_VIDEO: DESCRIPTOR 0B8000H, 0FFFFH, DA_DRW 28 | ;end of definiton gdt 29 | GDTLEN EQU $ - PM_GDT 30 | GDTPTR DW GDTLEN - 1 31 | DD 0 ; GDT 基地址 32 | 33 | ;GDT 选择子 34 | SELECTOERCODE32 EQU PM_DESC_CODE32 - PM_GDT 35 | SELECTOERDATA EQU PM_DESC_DATA - PM_GDT 36 | SELECTOERSTACK EQU PM_DESC_STACK - PM_GDT 37 | SELECTOERTEST EQU PM_DESC_TEST - PM_GDT 38 | SELECTOERVIDEO EQU PM_DESC_VIDEO - PM_GDT 39 | ;END of [SECTION .gdt] 40 | 41 | [SECTION .DATA1] 42 | ALIGN 32 43 | [BITS 32] 44 | PM_DATA: 45 | PMMESSAGE : DB "POTECT MODE", 0 ; 46 | OFFSETPMESSAGE EQU PMMESSAGE - $$ 47 | DATALEN EQU $- PM_DATA 48 | ;END of [SECTION .data] 49 | 50 | ;全局的堆栈段 51 | [SECTION .GS] 52 | ALIGN 32 53 | [BITS 32] 54 | PM_STACK: 55 | TIMES 512 DB 0 56 | TOPOFSTACK EQU $ - PM_STACK -1 57 | ;END of STACK 58 | 59 | [SECTION .S16] 60 | [BITS 16] 61 | PM_BEGIN: 62 | MOV AX, CS 63 | MOV DS, AX 64 | MOV ES, AX 65 | MOV SS, AX 66 | MOV SP, 0100H 67 | 68 | ;初始化32位的代码段 69 | XOR EAX, EAX 70 | MOV AX, CS 71 | SHL EAX, 4 72 | ADD EAX, PM_SEG_CODE32 73 | MOV WORD[PM_DESC_CODE32+2], AX 74 | SHR EAX, 16 75 | MOV BYTE [PM_DESC_CODE32+4], AL 76 | MOV BYTE [PM_DESC_CODE32+7], AH 77 | 78 | 79 | ;初始化32位的数据段 80 | XOR EAX, EAX 81 | MOV AX, DS 82 | SHL EAX, 4 83 | ADD EAX, PM_DATA 84 | MOV WORD[PM_DESC_DATA+2], AX 85 | SHR EAX, 16 86 | MOV BYTE [PM_DESC_DATA+4], AL 87 | MOV BYTE [PM_DESC_DATA+7], AH 88 | 89 | ;初始化32位的stack段 90 | XOR EAX, EAX 91 | MOV AX, DS 92 | SHL EAX, 4 93 | ADD EAX, PM_STACK 94 | MOV WORD[PM_DESC_STACK + 2], AX 95 | SHR EAX, 16 96 | MOV BYTE [PM_DESC_STACK + 4], AL 97 | MOV BYTE [PM_DESC_STACK + 7], AH 98 | 99 | ;加载GDTR 100 | XOR EAX, EAX 101 | MOV AX, DS 102 | SHL EAX, 4 103 | ADD EAX, PM_GDT 104 | MOV DWORD [GDTPTR + 2], EAX 105 | LGDT [GDTPTR] 106 | 107 | ;A20 108 | CLI 109 | 110 | IN AL, 92H 111 | OR AL, 00000010B 112 | OUT 92H, AL 113 | 114 | ;切换到保护模式 115 | MOV EAX, CR0 116 | OR EAX, 1 117 | MOV CR0, EAX 118 | 119 | JMP DWORD SELECTOERCODE32: 0 120 | 121 | 122 | 123 | [SECTION .S32] ;32位的代码段 124 | [BITS 32] 125 | PM_SEG_CODE32 : 126 | MOV AX, SELECTOERDATA ;通过数据段的选择子放入ds寄存器,就可以用段+偏移进行寻址 127 | MOV DS, AX 128 | 129 | MOV AX, SELECTOERTEST ;通过测试段的选择子放入es寄存器,就可以用段+偏移进行寻址 130 | MOV ES, AX 131 | 132 | MOV AX, SELECTOERVIDEO 133 | MOV GS, AX 134 | 135 | MOV AX, SELECTOERSTACK 136 | MOV SS, AX 137 | MOV ESP, TOPOFSTACK 138 | 139 | MOV AH, 0CH 140 | XOR ESI, ESI 141 | XOR EDI, EDI 142 | MOV ESI, OFFSETPMESSAGE 143 | MOV EDI, (80*10 +0) *2 144 | CLD 145 | 146 | .1: 147 | LODSB 148 | TEST AL, AL 149 | JZ .2 150 | MOV [GS: EDI], AX 151 | ADD EDI, 2 152 | JMP .1 153 | 154 | .2: ;显示完毕 155 | 156 | ;测试段的寻址 157 | MOV AX, 'P' 158 | MOV [ES: 0], AX 159 | MOV AX, SELECTOERVIDEO 160 | MOV GS, AX 161 | MOV EDI, (80*15 +0) *2 162 | MOV AH, 0CH 163 | MOV AL, [ES: 0] 164 | MOV [GS: EDI], AX 165 | 166 | JMP $ 167 | 168 | 169 | SEGCODE32LEN EQU $ - PM_SEG_CODE32 170 | -------------------------------------------------------------------------------- /OS/day7/src/DW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day7/src/DW.png -------------------------------------------------------------------------------- /OS/day7/src/OS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day7/src/OS.png -------------------------------------------------------------------------------- /OS/day7/src/SELECTOR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day7/src/SELECTOR.png -------------------------------------------------------------------------------- /OS/day7/src/descriptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/day7/src/descriptor.png -------------------------------------------------------------------------------- /OS/day7/stringio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Realize some functions of C language string 3 | * if you have any problem,contact me at djh113@126.com 4 | * or visit my homepage at https://github.com/djh-sudo 5 | */ 6 | #include 7 | 8 | extern void printInPos(char*msg,uint16_t len,uint8_t row,uint8_t col); // Print content at the cursor 9 | extern void putchar(char c); // Output one character 10 | extern char getch(); // Get keyboard input 11 | 12 | 13 | // Calculate string length 14 | uint16_t strlen(char *str){ 15 | uint16_t count = 0; 16 | while(str[count++] != 0); 17 | return count - 1; 18 | } 19 | 20 | // Compare strings 21 | uint8_t strcmp(char* str1,char* str2){ 22 | int i = 0; 23 | while(1){ 24 | if(str1[i] == 0 || str2[i] == 0) 25 | break; 26 | if(str1[i] != str2[i]) 27 | break; 28 | i++; 29 | } 30 | return (str1[i] - str2[i]); 31 | } 32 | 33 | // Display characters at the cursor 34 | void print(char*str){ 35 | uint16_t len = strlen(str); 36 | for(int i = 0; i < len; i++){ 37 | putchar(str[i]); 38 | } 39 | } 40 | 41 | // Get the first word of the string 42 | void getFirstWord(char*str,char*buf){ 43 | int i = 0; 44 | while(str[i] && str[i] != ' ' && str[i] != '\n'){ 45 | buf[i] = str[i]; 46 | i++; 47 | } 48 | buf[i] = 0; 49 | } 50 | 51 | // Read the string into the buffer 52 | void read2Buf(char*buffer,uint16_t maxLen){ 53 | int i = 0; 54 | while(1){ 55 | char tempc = getch();// Read character by character 56 | if(!(tempc == 0x0D || tempc == '\b' || (tempc >= 32 && tempc <= 127))){ 57 | continue;// Invaild symbol 58 | } 59 | if(i > 0 && i < maxLen - 1){ 60 | if(tempc == 0x0D){// press enter stop! 61 | break; 62 | }else if(tempc == '\b'){// back 63 | putchar('\b'); 64 | putchar(' '); 65 | putchar('\b'); 66 | i--; 67 | }else{// Valid characters 68 | putchar(tempc); 69 | buffer[i] = tempc; 70 | i++; 71 | } 72 | }else if(i >= maxLen - 1){ 73 | // Buffer exceeded 74 | if(tempc == '\b'){ 75 | putchar('\b'); 76 | putchar(' '); 77 | putchar('\b'); 78 | i--; 79 | }else if(tempc == 0x0D){ 80 | break; 81 | } 82 | }else if(i <= 0){ 83 | if(tempc == 0x0D) 84 | break; 85 | else if(tempc != '\b'){ 86 | putchar(tempc); 87 | buffer[i] = tempc; 88 | i++; 89 | } 90 | } 91 | } 92 | putchar('\r'); 93 | putchar('\n'); 94 | buffer[i] = 0; 95 | } 96 | -------------------------------------------------------------------------------- /OS/day8/day8.md: -------------------------------------------------------------------------------- 1 | # Day 8 2 | 3 | ## 结语 4 | 5 | 到这里,我们对操作系统的概念有了比较直观的理解。但这仅仅是一个开始。实际的操作系统内核功能还会多很多,并且代码需要精简复用,而不是像现在这样,只是简单堆叠。 6 | 7 | 限于时间和精力,我们并没有继续开发和研究下去,但以后说不定会继续...... 8 | 9 | ``` 10 | OS is a complex system;putting a programming layer on top of the API doesn't eliminate the complexity - it merely hides it. Sonner or later that complexity is going to jump out and bite you in the leg. 11 | ``` -------------------------------------------------------------------------------- /OS/tools/dd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/dd.exe -------------------------------------------------------------------------------- /OS/tools/ld.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/ld.exe -------------------------------------------------------------------------------- /OS/tools/make.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/make.exe -------------------------------------------------------------------------------- /OS/tools/nasm/LICENSE: -------------------------------------------------------------------------------- 1 | NASM is now licensed under the 2-clause BSD license, also known as the 2 | simplified BSD license. 3 | 4 | Copyright 1996-2010 the NASM Authors - All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following 8 | conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 18 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 20 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /OS/tools/nasm/Uninstall.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/Uninstall.exe -------------------------------------------------------------------------------- /OS/tools/nasm/contrib/VSrules/nasm.README: -------------------------------------------------------------------------------- 1 | Visual Studio 2008 NASM integration 2 | 3 | 4 | In order to use nasm seamlessly in your VS2k8, follow the steps below. 5 | 6 | 1. First install nasm by running its installer 7 | 2. copy nasm.rules to c:\Program Files\Microsoft Visual Studio 2008\VC\VCProjectDefaults 8 | 3. Start Visual Studio 2008 9 | 4. go to Tools->Options->VC++ Directories 10 | 5. click on Show Directories for Executables 11 | 6. add C:\Program Files\NASM to the list of paths 12 | 7. Open a solution that you want to use NASM with 13 | 8. Right click on the project name and select Custom Build Rules 14 | 9. Check the box next to the NASM line 15 | 10. Add any .asm files to the project 16 | 11. click on build to test 17 | -------------------------------------------------------------------------------- /OS/tools/nasm/contrib/VSrules/nasm.rules: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 25 | 34 | 43 | 51 | 60 | 67 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /OS/tools/nasm/nasm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/nasm.exe -------------------------------------------------------------------------------- /OS/tools/nasm/nasm.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/nasm.ico -------------------------------------------------------------------------------- /OS/tools/nasm/nasmdoc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/nasmdoc.pdf -------------------------------------------------------------------------------- /OS/tools/nasm/nasmpath.bat: -------------------------------------------------------------------------------- 1 | @set path=E:\Git\gitcode\blog\docs\OS\nasm;%path% 2 | @%comspec% -------------------------------------------------------------------------------- /OS/tools/nasm/ndisasm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/ndisasm.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/ldrdf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/ldrdf.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdf2bin.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdf2bin.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdf2com.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdf2com.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdf2ihx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdf2ihx.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdf2ith.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdf2ith.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdf2srec.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdf2srec.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdfdump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdfdump.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdflib.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdflib.exe -------------------------------------------------------------------------------- /OS/tools/nasm/rdoff/rdx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djh-sudo/tiny-OS-kernel/1234129f87b8ff569a0b1c2ca5f51be409e36240/OS/tools/nasm/rdoff/rdx.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Making your tiny OS 2 | 3 | > Let's start! 4 | 5 | ## what should know before 6 | 7 | * `C`语言和`x86`汇编 8 | 9 | 当然,只需要简单的汇编基础就可以,至于`C`语言,也不需要很多知识,了解源文件到可执行文件的流程。 10 | 11 | * `linux`基本命令 12 | 13 | 在简单的内核编译中,我们用到了`linux`下的一些工具,比如`nasm`,当然这些`windows`下也有,包括`gcc`。但编译过程是在`linux`虚拟机中完成的。 14 | 15 | ## Tools 16 | 17 | 我们会用到什么工具呢? 18 | 19 | * 文本编辑器,如`Notepad++`,`sublime`等等,最简单的记事本也可以。 20 | * 二进制查看器,当然,这个也不是很关键,一来`sublime`工具本身就能已`16`进制形式显示,同时虚拟机也有帮助我们解析的工具。 21 | * `linux`虚拟机,这里我们用到的是`Ubuntu i386`。需要安装工具`nasm`、`vim`、`gcc`、`readelf`等。 22 | 23 | 当然,你不必担心,用到这些工具或者资料的时候,文中都会有超链接,你可以去下载查阅。同时互联网上也有大量的资料供你翻阅。 24 | 25 | ## Result 26 | 27 | 最后我们做的结果是? 28 | 29 | 最后当然不是一个非常完整的操作系统,这更像是一次研究探索和对`OS`的了解,以及对`C`语言和汇编的关系的认识,最后是对硬件和软件如何打交道的一种认识。 30 | 31 | `7`个部分,我们仅仅完成了加电开机自启,加载主引导扇区,然后加载更多的信息到内存,最后简单实现了从实模式进入保护模式,仅此而已,但操作系统的雏形大致也是如此。 32 | 33 | ## Reference 34 | 35 | 在整个过程中,我们参考了一些视频和电子课本 36 | 37 | `[1]` 《`30`天自制操作系统》 38 | 39 | `[2]` 《`Oranges`一个操作系统的实现》 40 | 41 | `[3]` `51CTO` 42 | 43 | 等等。 44 | 45 | 其中主要参考的是电子书`[2]`。`[1]`中的内容更加全面,最后完成的效果也更加完善。 46 | 47 | ## Last 48 | 49 | 源文件和笔记一并上传至[`GitHub`](https://github.com/djh-sudo/tiny-OS-kernel/),如果您有任何问题,也可以通过`djh113@126.com`联系我。每个文件夹是独立的结构,编译的二进制文件并未上传,但源码和工具都已上传。 50 | --------------------------------------------------------------------------------