文章目录

  • 第一周
    • 第一天-从计算机结构到汇编程序入门
      • HelloWorld
      • 引入汇编
      • 添加注释
    • 第二天-汇编语言学习与Makefile入门
      • 文本编辑器
      • 初识汇编
        • 寄存器
        • 数据大小
        • 指令
      • 制作启动区
    • 第三天-进入32位模式并导入C语言
      • 制作IPL
      • 磁盘
      • 显示
      • 导入c语言
      • 实现HLT
    • 第四天-C语言与画面显示
      • 白屏
      • 线条屏
      • RGB屏幕
      • 桌面雏形
    • 第5天:结构体、文字显示与GDT/IDT初始化
      • 接受启动信息
      • 试用结构体
      • 显示字符
      • 显示字符串
      • 显示变量
      • 显示指针
    • 第六天-分割编译和中断处理
      • 分割源文件
    • 第七天-FIFO鼠标控制
      • 鼠标解读
      • 加快中断
      • 制作FIFO缓冲区

第一周

我感觉肯定很难不会简单;

第一天-从计算机结构到汇编程序入门

HelloWorld

  1. 安装虚拟机wmware和挂载xp系统(主要是考虑系统的安全,虚拟机方便保存快照随时回滚和学习汇编的方便,在xp上不用装其他的东西直接就可以汇编链接)可以跳过
  2. 然后就是准备工作,工欲善其事便先利其器.如下图
    • code文件夹是我每天编代码
    • tool文件夹用于存放工具类
    • makeOS用于是存放官方的资料和网上借鉴的资料
    • masm5用于汇编学习

  1. 认识二进制编译器 可以直接对二进制进行编译的软件

  1. 然后编辑如下二进制代码然后另存为helloos.img下述代码你也不需要知道什么意思;只要知道下面的功能是为了保存下来制作软盘映像文件[当然这有点老了,有兴趣的可以试试硬盘装入].这里我采用的是虚拟机QEMU

  1. 存放于day01目录下的helloos0 里面其他的目录就是关于QEMU软件 也是我们下面定义在.bat的文件路径可以理解为java的jdk路径就好了

  1. 编写run.batintall.bat内容如下;可以理解为java的jdk路径就好了,为了映射一些工具或者虚拟机等其他软件
..\z_tools\imgtol.com w a: helloos.imgcopy helloos.img ..\z_tools\qemu\fdimage0.bin
..\z_tools\make.exe -C ../z_tools/qemu

  1. 打开双击!cons_nt.bat运行run.bat就可以出现一个十分常见的hello world

引入汇编

如果大家仔细看helloos.img的代码的话就发现这个代码是在太长了又不是编写

例如一个helloworld 汇编文件1kb,二进制文件1440kb

所以就就引入了汇编

这里我们使用的nask软件将汇编代码-> 机器代码

..\z_tools\nask.exe helloos.nas helloos.img

helloos.nas->helloos.img

然后老规矩在run一下也可以继续得到hello world

添加注释

为了让别人读懂你的程序需要添加必要的注释

; hello-os
; TAB=4; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeDB     0xeb, 0x4e, 0x90DB      "HELLOIPL"        ; 启动扇区名称(8字节)DW       512             ; 每个扇区(sector)大小(必须512字节)DB     1               ; 簇(cluster)大小(必须为1个扇区)DW       1               ; FAT起始位置(一般为第一个扇区)DB     2               ; FAT个数(必须为2)DW       224             ; 根目录大小(一般为224项)DW        2880            ; 该磁盘大小(必须为2880扇区1440*1024/512)DB     0xf0            ; 磁盘类型(必须为0xf0)DW     9               ; FAT的长度(必须是9扇区)DW        18              ; 一个磁道(track)有几个扇区(必须为18)DW     2               ; 磁头数(必须是2)DD     0               ; 不使用分区,必须是0DD       2880            ; 重写一次磁盘大小DB        0,0,0x29        ; 意义不明(固定)DD      0xffffffff      ; (可能是)卷标号码DB     "HELLO-OS   " ; 磁盘的名称(必须为11字节,不足填空格)DB       "FAT12   "        ; 磁盘格式名称(必须是8字节,不足填空格)RESB 18              ; 先空出18字节; 程序主体DB       0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7cDB        0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8aDB        0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09DB        0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xebDB        0xee, 0xf4, 0xeb, 0xfd; 信息显示部分DB        0x0a, 0x0a      ; 换行两次DB        "hello, world"DB      0x0a            ; 换行DB      0RESB   0x1fe-$         ; 填写0x00直到0x001feDB     0x55, 0xaa; 启动扇区以外部分输出DB        0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00RESB  4600DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00RESB  1469432

今天就到此结束了2020年1月11日17:27:26

第二天-汇编语言学习与Makefile入门

文本编辑器

书中推荐使用teraPad,这里我们推荐使用Notepad++

初识汇编

; hello-os
; TAB=4ORG     0x7c00          ; 指明程序装载地址,告诉nask机器机器指令装在到那个内存地址; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP        entryDB     0x90DB      "HELLOIPL"        ; 启动扇区名称(8字节)DW       512             ; 每个扇区(sector)大小(必须512字节)DB     1               ; 簇(cluster)大小(必须为1个扇区)DW       1               ; FAT起始位置(一般为第一个扇区)DB     2               ; FAT个数(必须为2)DW       224             ; 根目录大小(一般为224项)DW        2880            ; 该磁盘大小(必须为2880扇区1440*1024/512)DB     0xf0            ; 磁盘类型(必须为0xf0)DW     9               ; FAT的长度(必??9扇区)DW        18              ; 一个磁道(track)有几个扇区(必须为18)DW     2               ; 磁头数(必??2)DD     0               ; 不使用分区,必须是0DD       2880            ; 重写一次磁盘大小DB        0,0,0x29        ; 意义不明(固定)DD      0xffffffff      ; (可能是)卷标号码DB     "HELLO-OS   " ; 磁盘的名称(必须为11字?,不足填空格)DB       "FAT12   "        ; 磁盘格式名称(必??8字?,不足填空格)RESB 18              ; 先空出18字节; 程序主体entry:MOV        AX,0            ; 初始化寄存器MOV     SS,AXMOV        SP,0x7c00MOV        DS,AXMOV        ES,AXMOV        SI,msg
putloop:MOV     AL,[SI]ADD      SI,1            ; 给SI加1CMP      AL,0JE      finMOV      AH,0x0e         ; 显示一个文字MOV     BX,15           ; 指定字符颜色INT     0x10            ; 调用显卡BIOSJMP       putloop
fin:HLT                     ; 让CPU停止,等待指令JMP     fin             ; 无限循环msg:DB        0x0a, 0x0a      ; 换行两次DB        "hello, world"DB      0x0a            ; 换行DB      0RESB   0x7dfe-$        ; 填写0x00直到0x001feDB     0x55, 0xaa; 以下是引导扇区以外的部分的记叙DB       0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00RESB  4600DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00RESB  1469432

寄存器

Ax Bx Cx Dx X 表示扩展的意思(extend) 之前的cpu的寄存器都是8位, 而现在一个16位,扩展了一位;

另外一方面,cpu还有8个8位寄存器

段寄存器,都是16位的寄存器

数据大小

在汇编语言中声明内存大小时,一般显示地使用

  • DB(字母 D 表示 Data,字母 B 表示 Byte)、
  • DW(字母 W 表示 Word,2Bytes)
  • DD(第二个字母 D 表示 Double Word,4Bytes),这样就能

很好地指导编译器分配内存空间,但是对于:

mov [ebx], 2;

若无特殊标识,则不确定常数 2 是单字节、双字节,还是双字。对于这种情况,X86 提供了
三个指示规则标记,分别为 BYTE PTR、WORD PTR 和 DWORD PTR,如将上例写成:

mov byte ptr [ebx], 2 ;将 2 以单字节形式传送到 ebx 值指示的内存地址中

mov word ptr [ebx], 2 ;将 2 以双字节形式传送到 ebx 值指示的内存地址中

mov dword ptr [ebx], 2 ;将 2 以四字节形式传送到 ebx 值指示的内存地址中

指令

1)数据传送指令
mov 指令
mov 指令将第二个操作数(寄存器的内容、内存中的内容或常数值)复制到第一个操作数(寄
存器或内存)。但不能用于直接从内存复制到内存,其语法如下:

mov <reg>,<reg>mov <reg>,<mem>mov <mem>,<reg>mov <reg>,<con>mov <mem>,<con>

举例:

mov eax, ebx  ;将 ebx 值拷贝到 eaxmov byte pty [var], 5 ;将 5 保存到 var 值指示的内存地址的一个字节中

push 指令
push 指令将操作数压入内存的栈中,常用于函数调用。ESP 是栈顶,压栈前先将 ESP 值减 4
(栈增长方向与内存地址增长方向相反),然后将操作数压入 ESP 指示的地址。其语法如下:

push <reg32>push <mem>push <con32>

举例(注意,栈中元素固定为 32 位):

push eax ;将 eax 值压栈push [var] ;将 var 值指示的内存地址的四字节值内容压栈

pop 指令
与 push 指令相反,pop 指令执行的是出栈的工作,出栈前先将 ESP 指示的地址中的内容出
栈,然后将 ESP 值加 4。其语法如下:

pop edi ;弹出栈顶元素送到 edipop [ebx] ;弹出栈顶元素送到 ebx 值指示的内存地址的四个字节中

2)算术和逻辑运算指令
add/sub 指令
add 指令将两个操作数相加,相加的结果保存到第一个操作数中。sub 指令用于两个操作数相
减,相减的结果保存到第一个操作数中。它们的语法格式类似,语法如下:

add <reg>,<reg> / sub <reg>,<reg>add <reg>,<mem> / sub <reg>,<mem>add <mem>,<reg> / sub <mem>,<reg>add <reg>,<con> / sub <reg>,<con>add <mem>,<con> / sub <mem>,<con>

举例:

sub eax, 10 ;eax ← eax-10add byte ptr [var], 10  ;10 与 var 值指示的内存地址的一字节值相加,并将结果保存在var 值指示的内存地址的字节中

inc/dec 指令

inc、dec 指令分别表示将操作数自加 1、自减 1,其语法如下:

inc <reg> / dec <reg>inc <mem> / dec <mem>

举例:

dec eax ;eax 值自减 1inc dword ptr [var] ;var 值指示的内存地址的四字节值自加 1

imul 指令
带符号整数乘法指令,它有两种格式:

① 两个操作数,将两个操作数相乘,并将结果保存
在第一个操作数中,第一个操作数必须为寄存器;

② 三个操作数,将第二个和第三个操作数相
乘,并将结果保存在第一个操作数中,第一个操作数必须为寄存器。其语法如下:

imul <reg32>,<reg32>imul <reg32>,<mem>imul <reg32>,<reg32>,<con>imul <reg32>,<mem>,<con>

举例:

imul eax, [var] ;eax ← eax * [var]imul esi, edi, 25 ;esi ← edi * 25

乘法操作结果可能溢出,则编译器值溢出标志 OF=1,以使 CPU 调出溢出异常处理程序。

idiv 指令
带符号整数除法指令,它只有一个操作数,即除数,而被除数则为 edx:eax 中的内容(64 位整数),操作结果有两部分:商和余数,商送到 eax,余数则送到 edx。其语法如下:

idiv <reg32>idiv <mem> 

举例:

idiv ebxidiv dword ptr [var]and/or/xor 指 指令 令

逻辑与、逻辑或、逻辑异或操作指令,用于操作数的位操作,操作结果放在第一个操作数中。
其语法如下:

and <reg>,<reg> / or <reg>,<reg> / xor <reg>,<reg>and <reg>,<mem> / or <reg>,<mem> / xor <reg>,<mem>and <mem>,<reg> / or <mem>,<reg> / xor <mem>,<reg>and <reg>,<con> / or <reg>,<con> / xor <reg>,<con>and <mem>,<con> / or <mem>,<con> / xor <mem>,<con>

举例:

and eax, 0fH ;将 eax 中的前 28 位全部置为 0,最后 4 位保持不变xor edx, edx ;置 edx 中的内容为 0

not 指令
位翻转指令,将操作数中的每一位翻转,即 0→1、1→0。其语法如下:

not <reg>not <mem>

举例:

not byte ptr [var] ;将 var 值的指示内存地址的一个字节的所有位翻转

neg 指令
取负指令。其语法如下:

neg <reg>neg <mem>

举例:

neg eax ;eax ← -eaxshl/shr  指令

逻辑移位指令,shl 为逻辑左移,shr 为逻辑右移,第一个操作数表示被操作数,第二个操作
数指示移位的位数。其语法如下:

shl <reg>,<con8> / shr <reg>,<con8>shl <mem>,<con8> / shr <mem>,<con8>shl <reg>,<cl> / shr <reg>,<cl>shl <mem>,<cl> / shr <mem>,<cl>

举例:

shl eax, 1 ;将 eax 值左移 1 位,相当于乘以 2shr ebx, cl ;将 ebx 值右移 n 位(n 为 cl 中的值),相当于除以 2 n

3)控制流指令
X86 处理器维持着一个指示当前执行指令的指令指针(IP),当一条指令执行后,此指针自动指向下一条指令。IP 寄存器不能直接操作,但可以用控制流指令更新。通常用标签(label)指示程序中的指令地址,在 X86 汇编代码中,可在任何指令前加入标签。如:

mov esi, [ebp+8]begin: xor ecx, ecxmov eax, [esi]

这样就用 begin 指示了第二条指令,控制流指令通过标签就可以实现程序指令的跳转。
jmp 指令
控制 IP 转移到 label 所指示的地址(从 label 中取出指令执行)。其语法如下:

jmp <label>
举例:

jmp begin ;转跳到 begin 标记的指令执行 jcondition  指令

条件转移指令,它依据处理机状态字中的一系列条件状态转移。处理机状态字中包括指示最
后一个算术运算结果是否为 0,运算结果是否为负数等。其语法如下:

je <label> (jump when equal)jne <label> (jump when not equal)jz <label> (jump when last result was zero)jg <label> (jump when greater than)jge <label> (jump when greater than or equal to)jl <label> (jump when less than)jle <label> (jump when less than or equal to)

举例:

cmp eax, ebxjle done ;如果 eax 值小于或等于 ebx 值,跳转到 done 指示的指令执行,否则,执行下一条指令。

cmp 指令
比较两个操作数的值,并根据比较结果设置处理机器状态字中的条件码。其语法如下:

cmp <reg>,<reg>cmp <reg>,<mem>cmp <mem>,<reg>cmp <reg>,<con>

通常和 jcondition 指令搭配使用,举例:

cmp dword ptr [var], 10jne loop  ;如果 var 指示的内存地址的四字节内容等于 10,则继续执行下一条指令。否则,跳转到 loop 指示的指令执行

call/ret 指令
这两条指令实现子程序(过程、函数等)的调用及返回。call 指令首先将当前执行指令地址
入栈,然后无条件转移到由标签指示的指令。与其它简单的跳转指令不同,call 指令保存调用之
前的地址信息(当 call 指令结束后,返回到调用之前的地址)。ret 指令实现子程序的返回机制,
ret 指令弹出栈中保存的指令地址,然后无条件转移到保存的指令地址执行。call 和 ret 是函数调
用中最关键的两条指令,其语法如下:

call <label>
ret
entry:MOV        AX,0            ; 初始化寄存器MOV     SS,AXMOV        SP,0x7c00MOV        DS,AXMOV        ES,AXMOV        SI,msg
putloop:MOV     AL,[SI]ADD      SI,1            ; 给SI加1CMP      AL,0JE      finMOV      AH,0x0e         ; 显示一个文字MOV     BX,15           ; 指定字符颜色INT     0x10            ; 调用显卡BIOSJMP       putloop
fin:HLT                     ; 让CPU停止,等待指令JMP     fin             ; 无限循环

有了这个程序,我们才能够把msg里面写的数据,一个字符一个字符的显示出来,并且数据中可以变成0以后,HLT指令就会让程序进入无限循环. “hello world”

制作启动区

C:\code\day02\helloos4

IPL(Initial Program loader)启动程序装载器

ipl.nas

; hello-os
; TAB=4ORG     0x7c00          ; 指明程序装载地址; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP      entryDB     0x90DB      "HELLOIPL"        ; 启动扇区名称(8字节)DW       512             ; 每个扇区(sector)大小(必须512字节)DB     1               ; 簇(cluster)大小(必须为1个扇区)DW       1               ; FAT起始位置(一般为第一个扇区)DB     2               ; FAT个数(必须为2)DW       224             ; 根目录大小(一般为224项)DW        2880            ; 该磁盘大小(必须为2880扇区1440*1024/512)DB     0xf0            ; 磁盘类型(必须为0xf0)DW     9               ; FAT的长度(必??9扇区)DW        18              ; 一个磁道(track)有几个扇区(必须为18)DW     2               ; 磁头数(必??2)DD     0               ; 不使用分区,必须是0DD       2880            ; 重写一次磁盘大小DB        0,0,0x29        ; 意义不明(固定)DD      0xffffffff      ; (可能是)卷标号码DB     "HELLO-OS   " ; 磁盘的名称(必须为11字?,不足填空格)DB       "FAT12   "        ; 磁盘格式名称(必??8字?,不足填空格)RESB 18              ; 先空出18字节; 程序主体entry:MOV        AX,0            ; 初始化寄存器MOV     SS,AXMOV        SP,0x7c00MOV        DS,AXMOV        ES,AXMOV        SI,msg
putloop:MOV     AL,[SI]ADD      SI,1            ; 给SI加1CMP      AL,0JE      finMOV      AH,0x0e         ; 显示一个文字MOV     BX,15           ; 指定字符颜色INT     0x10            ; 调用显卡BIOSJMP       putloop
fin:HLT                     ; 让CPU停止,等待指令JMP     fin             ; 无限循环msg:DB        0x0a, 0x0a      ; 换行两次DB        "hello, world"DB      0x0a            ; 换行DB      0RESB   0x7dfe-$        ; 填写0x00直到0x001feDB     0x55, 0xaa

启动区只需要521B

文件asm.bat

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

文件makeimg.batipl.bin为基础,制作的镜像文件的helloos.img的批处理文件

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

文件run.bat

copy helloos.img ..\z_tools\qemu\fdimage0.bin
..\z_tools\make.exe -C ../z_tools/qemu

然后双击!cons_nt.bat( 有人会有疑问这个文件是什么,这个文件间就是简单的cmd.exe程序)

asm->makeimg->run

###Makefile

是一种不用带扩展名的批处理的文件


# 默认动作default :../z_tools/make.exe img# 文件生成规则ipl.bin : ipl.nas Makefile../z_tools/nask.exe ipl.nas ipl.bin ipl.lsthelloos.img : ipl.bin Makefile../z_tools/edimg.exe   imgin:../z_tools/fdimg0at.tek \wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img# 命令asm :../z_tools/make.exe -r ipl.binimg :../z_tools/make.exe -r helloos.imgrun :../z_tools/make.exe imgcopy helloos.img ..\z_tools\qemu\fdimage0.bin../z_tools/make.exe -C ../z_tools/qemuinstall :../z_tools/make.exe img../z_tools/imgtol.com w a: helloos.imgclean :-del ipl.bin-del ipl.lstsrc_only :../z_tools/make.exe clean-del helloos.img

ipl.bin : ipl.nas Makefile

表示如果想制作ipl.bin 就需要检查遗爱是否含有 ipl.nas Makefile这个两个文件是否准备好了

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

\表示续行符号

make.bat.文件放在z_new_w文件夹里面

然后直接输入make img 相当于达到了输入make -r helloos.img的效果

第三天-进入32位模式并导入C语言

制作IPL

IPL(Initial Program loader)启动程序装载器

ipl.nas

; haribote-ipl
; TAB=4ORG     0x7c00          ; 声明CYLS=10; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP        entryDB     0x90DB      "HARIBOTE"        ; 启动扇区名称(8字节)DW       512             ; 每个扇区(sector)大小(必须512字节)DB     1               ; 簇(cluster)大小(必须为1个扇区)DW       1               ; FAT起始位置(一般为第一个扇区)DB     2               ; FAT个数(必须为2)DW       224             ; 根目录大小(一般为224项)DW        2880            ; 该磁盘大小(必须为2880扇区1440*1024/512)DB     0xf0            ; 磁盘类型(必须为0xf0)DW     9               ; FAT的长度(必??9扇区)DW        18              ; 一个磁道(track)有几个扇区(必须为18)DW     2               ; 磁头数(必??2)DD     0               ; 不使用分区,必须是0DD       2880            ; 重写一次磁盘大小DB        0,0,0x29        ; 意义不明(固定)DD      0xffffffff      ; (可能是)卷标号码DB     "HARIBOTEOS " ; 磁盘的名称(必须为11字?,不足填空格)DB       "FAT12   "        ; 磁盘格式名称(必??8字?,不足填空格)RESB 18              ; 先空出18字节; 程序主体entry:MOV        AX,0            ; 初始化寄存器MOV     SS,AXMOV        SP,0x7c00MOV        DS,AX; 读取磁盘MOV      AX,0x0820MOV        ES,AXMOV        CH,0            ; 柱面0MOV        DH,0            ; 磁头0MOV        CL,2            ; 扇区2MOV        AH,0x02         ; AH=0x02 : 读入磁盘MOV        AL,1            ; 1个扇区MOV       BX,0MOV     DL,0x00         ; A驱动器INT       0x13            ; 调用磁盘BIOSJC        error; 读取完后执行fin:HLT                        ; 让cpu停止等待JMP       fin             ; 無限循环error:MOV     SI,msg
putloop:MOV     AL,[SI]ADD      SI,1            ; 给SI加1CMP      AL,0JE      finMOV      AH,0x0e         ; 显示一个文字MOV     BX,15           ; 指定字符颜色INT     0x10            ; 调用显卡BIOSJMP       putloop
msg:DB      0x0a, 0x0a      ; 换行2次DB        "load error"DB        0x0a            ; 改行DB      0RESB   0x7dfe-$        ; 填写0x00直到0x7dfeDB      0x55, 0xaa

磁盘

JC指令jump if carry的缩写,如果进位标志(carry flag)是1的话,就跳转

make img->make run

读取18扇区

next:MOV     AX,ES           ; 把内存地址后移0x200(512/16十六进制转换)ADD       AX,0x0020MOV        ES,AX           ; ADD ES,0x020因为没有ADD ES,只能通过AX进行ADD     CL,1            ; 往CL里面加1CMP        CL,18           ; 比较CL与18JBE        readloop        ; CL <= 18 跳转到readloop

JBE指令,条件跳转指令jump if below equal 意思是小于等于则跳转

读取10个柱面

; 读取磁盘MOV        AX,0x0820MOV        ES,AXMOV        CH,0            ; 柱面0MOV        DH,0            ; 磁头0MOV        CL,2            ; 扇区2readloop:MOV       SI,0            ; 记录失败次数寄存器retry:MOV        AH,0x02         ; AH=0x02 : 读入磁盘MOV        AL,1            ; 1个扇区MOV       BX,0MOV     DL,0x00         ; A驱动器INT       0x13            ; 调用磁盘BIOSJNC       next            ; 没出错则跳转到finADD     SI,1            ; 往SI加1CMP      SI,5            ; 比较SI与5JAE     error           ; SI >= 5 跳转到errorMOV       AH,0x00MOV      DL,0x00         ; A驱动器INT       0x13            ; 重置驱动器JMP      retry
next:MOV        AX,ES           ; 把内存地址后移0x200(512/16十六进制转换)ADD       AX,0x0020MOV        ES,AX           ; ADD ES,0x020因为没有ADD ES,只能通过AX进行ADD     CL,1            ; 往CL里面加1CMP        CL,18           ; 比较CL与18JBE        readloop        ; CL <= 18 跳转到readloopMOV       CL,1ADD     DH,1CMP     DH,2JB      readloop        ; DH < 2 跳转到readloopMOV      DH,0ADD     CH,1CMP     CH,CYLSJB       readloop        ; CH < CYLS 跳转到readloop

最简单的操作系统

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

保存在haribote.nas

显示

; haribote-os
; TAB=4ORG     0xc200          ;转载到地方MOV       AL,0x13         ; VGA显卡、320x200x8bit彩色MOV       AH,0x00INT      0x10
fin:HLTJMP      fin

make run

harib00e

显示全黑

导入c语言

bootpack.c

void HariMain(void)
{fin:/* 原本打算写上hlt但是c语言不支持hlt */goto fin;}

makefile

TOOLPATH = ../z_tools/
INCPATH  = ../z_tools/haribote/MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
CC1      = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM  = $(TOOLPATH)obj2bim.exe
BIM2HRB  = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = deldefault :$(MAKE) imgipl10.bin : ipl10.nas Makefile$(NASK) ipl10.nas ipl10.bin ipl10.lstasmhead.bin : asmhead.nas Makefile$(NASK) asmhead.nas asmhead.bin asmhead.lstbootpack.gas : bootpack.c Makefile$(CC1) -o bootpack.gas bootpack.cbootpack.nas : bootpack.gas Makefile$(GAS2NASK) bootpack.gas bootpack.nasbootpack.obj : bootpack.nas Makefile$(NASK) bootpack.nas bootpack.obj bootpack.lstbootpack.bim : bootpack.obj Makefile$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \bootpack.obj
# 3MB+64KB=3136KBbootpack.hrb : bootpack.bim Makefile$(BIM2HRB) bootpack.bim bootpack.hrb 0haribote.sys : asmhead.bin bootpack.hrb Makefilecopy /B asmhead.bin+bootpack.hrb haribote.sysharibote.img : ipl10.bin haribote.sys Makefile$(EDIMG)   imgin:../z_tools/fdimg0at.tek \wbinimg src:ipl10.bin len:512 from:0 to:0 \copy from:haribote.sys to:@: \imgout:haribote.imgimg :$(MAKE) haribote.imgrun :$(MAKE) img$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin$(MAKE) -C ../z_tools/qemuinstall :$(MAKE) img$(IMGTOL) w a: haribote.imgclean :-$(DEL) *.bin-$(DEL) *.lst-$(DEL) *.gas-$(DEL) *.obj-$(DEL) bootpack.nas-$(DEL) bootpack.map-$(DEL) bootpack.bim-$(DEL) bootpack.hrb-$(DEL) haribote.syssrc_only :$(MAKE) clean-$(DEL) haribote.img

make img make run

实现HLT

; naskfunc
; TAB=4[FORMAT "WCOFF"]              ; 制作目标文件的模式
[BITS 32]                       ; 制作32位模式用的机器语言; 制作目标文件的信息[FILE "naskfunc.nas"]           ; 源文件名信息GLOBAL  _io_hlt         ; 程序中包含的函数名; 以下是实际的函数[SECTION .text]        ; 目标文件中写了这些后再写程序_io_hlt:    ; void io_hlt(void);HLTRET
/* 告诉C编译器,有一个函数在别的文件里 */void io_hlt(void);/* 是函数声明却不用{},而用;,这表示的意思是:函数在别的文件中,你自己找一下 */void HariMain(void)
{fin:io_hlt(); /* 执行naskfunc.nas中的_io_hlt函数 */goto fin;}

2020年1月12日 17:51:07

第四天-C语言与画面显示

修改naskfunc.nas中的内容如下

; naskfunc
; TAB=4[FORMAT "WCOFF"]              ; 制作目标文件的模式
[INSTRSET "i486p"]                ; 使用到486为止的指令
[BITS 32]                       ; 3制作32位模式用的机器语言
[FILE "naskfunc.nas"]         ; 文件名GLOBAL _io_hlt,_write_mem8[SECTION .text]_io_hlt:  ; void io_hlt(void);HLTRET_write_mem8:  ; void write_mem8(int addr, int data);MOV       ECX,[ESP+4]        ; [ESP+4]中存放的是地址,将其读入ECX中MOV        AL,[ESP+8]     ; [ESP+8]MOV       [ECX],ALRET

[INSTRSETi486p”] ;告诉nask这个程序是给486用的

白屏

bootpack.c

void io_hlt(void);
void write_mem8(int addr, int data);void HariMain(void)
{int i; /* 変数声明。*/for (i = 0xa0000; i <= 0xaffff; i++) {write_mem8(i, 15); /* MOV BYTE [i],15 */}for (;;) {io_hlt();}
}

make run

线条屏

void io_hlt(void);
void write_mem8(int addr, int data);void HariMain(void)
{int i; /* 変数声明。*/for (i = 0xa0000; i <= 0xaffff; i++) {write_mem8(i, i & 0x0f);}for (;;) {io_hlt();}
}
for (i = 0xa0000; i <= 0xaffff; i++) {write_mem8(i, 15); /* MOV BYTE [i],15 */
}

改成下面

 for (i = 0xa0000; i <= 0xaffff; i++) {write_mem8(i, i & 0x0f);}

void io_hlt(void);void HariMain(void)
{int i; /* 变量申明 */char *p; /* p申明、用于BYTE [...]地址 */for (i = 0xa0000; i <= 0xaffff; i++) {p = i; /*代入地址 */*p = i & 0x0f;/* 这可以代替write_mem8(i,i&0x0f) */}for (;;) {io_hlt();}
}

bootpack.c

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);/* 就算写在一个源文件里,如果想在定义前使用,还是必须先声明一下。 */void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);void HariMain(void)
{int i; /* 声明变量,变量i是32位整形 */char *p; /* p变量的p是byte[...]用的地址 */init_palette(); /* 设定调色板 */p = (char *) 0xa0000; /* 制定地址 */for (i = 0; i <= 0xffff; i++) {p[i] = i & 0x0f;}for (;;) {io_hlt();}
}void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00,   /*  0:黑 */0xff, 0x00, 0x00, /*  1:梁红 */0x00, 0xff, 0x00,    /*  2:亮绿 */0xff, 0xff, 0x00,    /*  3:亮黄 */0x00, 0x00, 0xff,    /*  4:亮蓝 */0xff, 0x00, 0xff,    /*  5:亮紫 */0x00, 0xff, 0xff,    /*  6:浅亮蓝 */0xff, 0xff, 0xff,   /*  7:白 */0xc6, 0xc6, 0xc6, /*  8:亮灰 */0x84, 0x00, 0x00,    /*  9:暗红 */0x00, 0x84, 0x00,    /* 10:暗绿 */0x84, 0x84, 0x00,    /* 11:暗黄 */0x00, 0x00, 0x84,    /* 12:暗青 */0x84, 0x00, 0x84,    /* 13:暗紫 */0x00, 0x84, 0x84,    /* 14:浅暗蓝 */0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);return;/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();  /* 记录中断许可标志的值 */io_cli();                   /* 将中断许可标志置为0,禁止中断 */io_out8(0x03c8, start);for (i = start; i <= end; i++) {io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags); /* 复原中断许可标志 */return;
}

RGB屏幕

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);#define COL8_000000      0
#define COL8_FF0000     1
#define COL8_00FF00     2
#define COL8_FFFF00     3
#define COL8_0000FF     4
#define COL8_FF00FF     5
#define COL8_00FFFF     6
#define COL8_FFFFFF     7
#define COL8_C6C6C6     8
#define COL8_840000     9
#define COL8_008400     10
#define COL8_848400     11
#define COL8_000084     12
#define COL8_840084     13
#define COL8_008484     14
#define COL8_848484     15void HariMain(void)
{char *p; /* 声明变量vram、用于BYTE [...]地址 */init_palette(); /* 设定调色板 */p = (char *) 0xa0000; /* 地址变量赋值*/boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);for (;;) {io_hlt();}
}void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00,   /*  0:黑 */0xff, 0x00, 0x00, /*  1:梁红 */0x00, 0xff, 0x00,    /*  2:亮绿 */0xff, 0xff, 0x00,    /*  3:亮黄 */0x00, 0x00, 0xff,    /*  4:亮蓝 */0xff, 0x00, 0xff,    /*  5:亮紫 */0x00, 0xff, 0xff,    /*  6:浅亮蓝 */0xff, 0xff, 0xff,   /*  7:白 */0xc6, 0xc6, 0xc6, /*  8:亮灰 */0x84, 0x00, 0x00,    /*  9:暗红 */0x00, 0x84, 0x00,    /* 10:暗绿 */0x84, 0x84, 0x00,    /* 11:暗黄 */0x00, 0x00, 0x84,    /* 12:暗青 */0x84, 0x00, 0x84,    /* 13:暗紫 */0x00, 0x84, 0x84,    /* 14:浅暗蓝 */0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);return;/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();  /* 记录中断许可标志的值 */io_cli();                   /* 将中断许可标志置为0,禁止中断 */io_out8(0x03c8, start);for (i = start; i <= end; i++) {io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags); /* 复原中断许可标志 */return;
}void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;}return;
}

桌面雏形

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);#define COL8_000000      0
#define COL8_FF0000     1
#define COL8_00FF00     2
#define COL8_FFFF00     3
#define COL8_0000FF     4
#define COL8_FF00FF     5
#define COL8_00FFFF     6
#define COL8_FFFFFF     7
#define COL8_C6C6C6     8
#define COL8_840000     9
#define COL8_008400     10
#define COL8_848400     11
#define COL8_000084     12
#define COL8_840084     13
#define COL8_008484     14
#define COL8_848484     15void HariMain(void)
{char *vram;int xsize, ysize;init_palette();vram = (char *) 0xa0000;xsize = 320;ysize = 200;boxfill8(vram, xsize, COL8_008484,  0,         0,          xsize -  1, ysize - 29);boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 28, xsize -  1, ysize - 28);boxfill8(vram, xsize, COL8_FFFFFF,  0,         ysize - 27, xsize -  1, ysize - 27);boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 26, xsize -  1, ysize -  1);boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 24);boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  4);boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  3);boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 24);boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize -  3, xsize -  4, ysize -  3);boxfill8(vram, xsize, COL8_FFFFFF, xsize -  3, ysize - 24, xsize -  3, ysize -  3);for (;;) {io_hlt();}
}void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00,   /*  0:黑 */0xff, 0x00, 0x00, /*  1:梁红 */0x00, 0xff, 0x00,    /*  2:亮绿 */0xff, 0xff, 0x00,    /*  3:亮黄 */0x00, 0x00, 0xff,    /*  4:亮蓝 */0xff, 0x00, 0xff,    /*  5:亮紫 */0x00, 0xff, 0xff,    /*  6:浅亮蓝 */0xff, 0xff, 0xff,   /*  7:白 */0xc6, 0xc6, 0xc6, /*  8:亮灰 */0x84, 0x00, 0x00,    /*  9:暗红 */0x00, 0x84, 0x00,    /* 10:暗绿 */0x84, 0x84, 0x00,    /* 11:暗黄 */0x00, 0x00, 0x84,    /* 12:暗青 */0x84, 0x00, 0x84,    /* 13:暗紫 */0x00, 0x84, 0x84,    /* 14:浅暗蓝 */0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);return;/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();  /* 记录中断许可标志的值 */io_cli();                   /* 将中断许可标志置为0,禁止中断 */io_out8(0x03c8, start);for (i = start; i <= end; i++) {io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags); /* 复原中断许可标志 */return;
}void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;}return;
}

第5天:结构体、文字显示与GDT/IDT初始化

接受启动信息

harib02a

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);#define COL8_000000      0
#define COL8_FF0000     1
#define COL8_00FF00     2
#define COL8_FFFF00     3
#define COL8_0000FF     4
#define COL8_FF00FF     5
#define COL8_00FFFF     6
#define COL8_FFFFFF     7
#define COL8_C6C6C6     8
#define COL8_840000     9
#define COL8_008400     10
#define COL8_848400     11
#define COL8_000084     12
#define COL8_840084     13
#define COL8_008484     14
#define COL8_848484     15void HariMain(void)
{char *vram;int xsize, ysize;short *binfo_scrnx, *binfo_scrny;int *binfo_vram;//bingfo是bootinfo的缩写//scrn是screen的缩写init_palette();binfo_scrnx = (short *) 0x0ff4;binfo_scrny = (short *) 0x0ff6;binfo_vram = (int *) 0x0ff8;xsize = *binfo_scrnx;ysize = *binfo_scrny;vram = (char *) *binfo_vram;init_screen(vram, xsize, ysize);for (;;) {io_hlt();}
}void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00,   /*  0:黑 */0xff, 0x00, 0x00, /*  1:梁红 */0x00, 0xff, 0x00,    /*  2:亮绿 */0xff, 0xff, 0x00,    /*  3:亮黄 */0x00, 0x00, 0xff,    /*  4:亮蓝 */0xff, 0x00, 0xff,    /*  5:亮紫 */0x00, 0xff, 0xff,    /*  6:浅亮蓝 */0xff, 0xff, 0xff,   /*  7:白 */0xc6, 0xc6, 0xc6, /*  8:亮灰 */0x84, 0x00, 0x00,    /*  9:暗红 */0x00, 0x84, 0x00,    /* 10:暗绿 */0x84, 0x84, 0x00,    /* 11:暗黄 */0x00, 0x00, 0x84,    /* 12:暗青 */0x84, 0x00, 0x84,    /* 13:暗紫 */0x00, 0x84, 0x84,    /* 14:浅暗蓝 */0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);return;/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();  /* 记录中断许可标志的值 */io_cli();                   /* 将中断许可标志置为0,禁止中断 */io_out8(0x03c8, start);for (i = start; i <= end; i++) {io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags); /* 复原中断许可标志 */return;
}void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;}return;
}void init_screen(char *vram, int x, int y)
{boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);return;
}

将显示的画面的背景单独出来,专门构成一个函数init_screen

试用结构体

harib02b

void HariMain(void)
{char *vram;int xsize, ysize;struct BOOTINFO *binfo;init_palette();binfo = (struct BOOTINFO *) 0x0ff0;xsize = (*binfo).scrnx;ysize = (*binfo).scrny;vram = (*binfo).vram;init_screen(vram, xsize, ysize);for (;;) {io_hlt();}
}

改成下

void HariMain(void)
{char *vram;int xsize, ysize;struct BOOTINFO *binfo;init_palette();binfo = (struct BOOTINFO *) 0x0ff0;xsize = (*binfo).scrnx;ysize = (*binfo).scrny;vram = (*binfo).vram;init_screen(vram, xsize, ysize);for (;;) {io_hlt();}
}

harb02c

显示字符

hari02d

void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;static char font_A[16] = {0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00};init_palette();init_screen(binfo->vram, binfo->scrnx, binfo->scrny);putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);for (;;) {io_hlt();}
}

显示字符串

f

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7lIBImBs-1611924092680)(http://img-blog.szm2019.cn/2020/makeOS/week1/1580540127345.png)]

void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;init_palette();init_screen(binfo->vram, binfo->scrnx, binfo->scrny);putfonts8_asc(binfo->vram, binfo->scrnx,  8,  8, COL8_FFFFFF, "hello world");putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "make OS.");putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "make OS.");for (;;) {io_hlt();}
}

显示变量

sprintf(s, "scrnx = %d", binfo->scrnx);putfonts8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

显示指针

h

void init_mouse_cursor8(char *mouse, char bc)
{static char cursor[16][16] = {"**************..","*OOOOOOOOOOO*...","*OOOOOOOOOO*....","*OOOOOOOOO*.....","*OOOOOOOO*......","*OOOOOOO*.......","*OOOOOOO*.......","*OOOOOOOO*......","*OOOO**OOO*.....","*OOO*..*OOO*....","*OO*....*OOO*...","*O*......*OOO*..","**........*OOO*.","*..........*OOO*","............*OO*",".............***"};int x, y;for (y = 0; y < 16; y++) {for (x = 0; x < 16; x++) {if (cursor[y][x] == '*') {mouse[y * 16 + x] = COL8_000000;}if (cursor[y][x] == 'O') {mouse[y * 16 + x] = COL8_FFFFFF;}if (cursor[y][x] == '.') {mouse[y * 16 + x] = bc;}}}return;
}

第六天-分割编译和中断处理

分割源文件

OBJS_BOOTPACK = bootpack.obj naskfunc.obj hankaku.obj graphic.obj dsctbl.obj \int.objTOOLPATH = ../z_tools/
INCPATH  = ../z_tools/haribote/MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
CC1      = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM  = $(TOOLPATH)obj2bim.exe
MAKEFONT = $(TOOLPATH)makefont.exe
BIN2OBJ  = $(TOOLPATH)bin2obj.exe
BIM2HRB  = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = del# 默认动作default :$(MAKE) img# 镜像文件生成ipl10.bin : ipl10.nas Makefile$(NASK) ipl10.nas ipl10.bin ipl10.lstasmhead.bin : asmhead.nas Makefile$(NASK) asmhead.nas asmhead.bin asmhead.lsthankaku.bin : hankaku.txt Makefile$(MAKEFONT) hankaku.txt hankaku.binhankaku.obj : hankaku.bin Makefile$(BIN2OBJ) hankaku.bin hankaku.obj _hankakubootpack.bim : $(OBJS_BOOTPACK) Makefile$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \$(OBJS_BOOTPACK)
# 3MB+64KB=3136KBbootpack.hrb : bootpack.bim Makefile$(BIM2HRB) bootpack.bim bootpack.hrb 0haribote.sys : asmhead.bin bootpack.hrb Makefilecopy /B asmhead.bin+bootpack.hrb haribote.sysharibote.img : ipl10.bin haribote.sys Makefile$(EDIMG)   imgin:../z_tools/fdimg0at.tek \wbinimg src:ipl10.bin len:512 from:0 to:0 \copy from:haribote.sys to:@: \imgout:haribote.img# 其他指令%.gas : %.c Makefile$(CC1) -o $*.gas $*.c%.nas : %.gas Makefile$(GAS2NASK) $*.gas $*.nas%.obj : %.nas Makefile$(NASK) $*.nas $*.obj $*.lst# 运行程序img :$(MAKE) haribote.imgrun :$(MAKE) img$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin$(MAKE) -C ../z_tools/qemuinstall :$(MAKE) img$(IMGTOL) w a: haribote.imgclean :-$(DEL) *.bin-$(DEL) *.lst-$(DEL) *.obj-$(DEL) bootpack.map-$(DEL) bootpack.bim-$(DEL) bootpack.hrb-$(DEL) haribote.syssrc_only :$(MAKE) clean-$(DEL) haribote.img

###初始化PIC


void init_pic(void)
/* PIC初始化 */
{io_out8(PIC0_IMR,  0xff  ); /* 禁止所有中断 */io_out8(PIC1_IMR,  0xff  ); /* 禁止所有中断 */io_out8(PIC0_ICW1, 0x11  ); /* 边缘触发模式(edge trigger mode) */io_out8(PIC0_ICW2, 0x20  ); /* IRQ0-7由INT20-27接收 */io_out8(PIC0_ICW3, 1 << 2); /* PIC1由IRQ2相连 */io_out8(PIC0_ICW4, 0x01  ); /* 无缓冲区模式 */io_out8(PIC1_ICW1, 0x11  ); /* 边缘触发模式(edge trigger mode) */io_out8(PIC1_ICW2, 0x28  ); /* IRQ8-15由INT28-2f接收 */io_out8(PIC1_ICW3, 2     ); /* PIC1由IRQ2连接 */io_out8(PIC1_ICW4, 0x01  ); /* 无缓冲区模式 */io_out8(PIC0_IMR,  0xfb  ); /* 11111011 PIC1以外全部禁止 */io_out8(PIC1_IMR,  0xff  ); /* 11111111 禁止所有中断 */return;
}

### 中断处理程序

void inthandler21(int *esp)
/* 来自PS/2键盘的中断 */
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");for (;;) {io_hlt();}
}
_asm_inthandler21:PUSH   ESPUSH  DSPUSHADMOV     EAX,ESPPUSH EAXMOV      AX,SSMOV        DS,AXMOV        ES,AXCALL   _inthandler21POP        EAXPOPADPOP     DSPOP       ESIRETD

/* bootpack */#include "bootpack.h"
#include <stdio.h>void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;char s[40], mcursor[256];int mx, my;init_gdtidt();init_pic();io_sti(); /* IDT/PIC的初始化已经完成,于是开放CPU的中断 */init_palette();init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);mx = (binfo->scrnx - 16) / 2; /* 计算画面的中心坐标*/my = (binfo->scrny - 28 - 16) / 2;init_mouse_cursor8(mcursor, COL8_008484);putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);sprintf(s, "(%d, %d)", mx, my);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);io_out8(PIC0_IMR, 0xf9); /* 开放PIC1和键盘中断(11111001) */io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) */for (;;) {zx }
}
/* GDT、IDT、descriptor table 关系处理 */#include "bootpack.h"void init_gdtidt(void)
{struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) ADR_IDT;int i;/* GDT初始化 */for (i = 0; i <= LIMIT_GDT / 8; i++) {set_segmdesc(gdt + i, 0, 0, 0);}set_segmdesc(gdt + 1, 0xffffffff,   0x00000000, AR_DATA32_RW);set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);load_gdtr(LIMIT_GDT, ADR_GDT);/* IDT初始化 */for (i = 0; i <= LIMIT_IDT / 8; i++) {set_gatedesc(idt + i, 0, 0, 0);}load_idtr(LIMIT_IDT, ADR_IDT);/* IDT设置*/set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);return;
}void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{if (limit > 0xfffff) {ar |= 0x8000; /* G_bit = 1 */limit /= 0x1000;}sd->limit_low    = limit & 0xffff;sd->base_low     = base & 0xffff;sd->base_mid     = (base >> 16) & 0xff;sd->access_right = ar & 0xff;sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);sd->base_high    = (base >> 24) & 0xff;return;
}void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{gd->offset_low   = offset & 0xffff;gd->selector     = selector;gd->dw_count     = (ar >> 8) & 0xff;gd->access_right = ar & 0xff;gd->offset_high  = (offset >> 16) & 0xffff;return;
}
/* 关于绘图部分的处理 */#include "bootpack.h"void init_palette(void)
{static unsigned char table_rgb[16 * 3] = {0x00, 0x00, 0x00,   /*  0:黑 */0xff, 0x00, 0x00, /*  1:梁红 */0x00, 0xff, 0x00,    /*  2:亮绿 */0xff, 0xff, 0x00,    /*  3:亮黄 */0x00, 0x00, 0xff,    /*  4:亮蓝 */0xff, 0x00, 0xff,    /*  5:亮紫 */0x00, 0xff, 0xff,    /*  6:浅亮蓝 */0xff, 0xff, 0xff,   /*  7:白 */0xc6, 0xc6, 0xc6, /*  8:亮灰 */0x84, 0x00, 0x00,    /*  9:暗红 */0x00, 0x84, 0x00,    /* 10:暗绿 */0x84, 0x84, 0x00,    /* 11:暗黄 */0x00, 0x00, 0x84,    /* 12:暗青 */0x84, 0x00, 0x84,    /* 13:暗紫 */0x00, 0x84, 0x84,    /* 14:浅暗蓝 */0x84, 0x84, 0x84    /* 15:暗灰 */};set_palette(0, 15, table_rgb);return;/* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}void set_palette(int start, int end, unsigned char *rgb)
{int i, eflags;eflags = io_load_eflags();  /* 记录中断许可标志的值 */io_cli();                   /* 将中断许可标志置为0,禁止中断 */io_out8(0x03c8, start);for (i = start; i <= end; i++) {io_out8(0x03c9, rgb[0] / 4);io_out8(0x03c9, rgb[1] / 4);io_out8(0x03c9, rgb[2] / 4);rgb += 3;}io_store_eflags(eflags); /* 复原中断许可标志 */return;
}void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{int x, y;for (y = y0; y <= y1; y++) {for (x = x0; x <= x1; x++)vram[y * xsize + x] = c;}return;
}void init_screen8(char *vram, int x, int y)
{boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);return;
}void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{int i;char *p, d /* data */;for (i = 0; i < 16; i++) {p = vram + (y + i) * xsize + x;d = font[i];if ((d & 0x80) != 0) { p[0] = c; }if ((d & 0x40) != 0) { p[1] = c; }if ((d & 0x20) != 0) { p[2] = c; }if ((d & 0x10) != 0) { p[3] = c; }if ((d & 0x08) != 0) { p[4] = c; }if ((d & 0x04) != 0) { p[5] = c; }if ((d & 0x02) != 0) { p[6] = c; }if ((d & 0x01) != 0) { p[7] = c; }}return;
}void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{extern char hankaku[4096];/* C语言中,字符串都是以0x00结尾 */for (; *s != 0x00; s++) {putfont8(vram, xsize, x, y, c, hankaku + *s * 16);x += 8;}return;
}void init_mouse_cursor8(char *mouse, char bc)
/* 鼠标的数据准备(16x16) */
{static char cursor[16][16] = {"**************..","*OOOOOOOOOOO*...","*OOOOOOOOOO*....","*OOOOOOOOO*.....","*OOOOOOOO*......","*OOOOOOO*.......","*OOOOOOO*.......","*OOOOOOOO*......","*OOOO**OOO*.....","*OOO*..*OOO*....","*OO*....*OOO*...","*O*......*OOO*..","**........*OOO*.","*..........*OOO*","............*OO*",".............***"};int x, y;for (y = 0; y < 16; y++) {for (x = 0; x < 16; x++) {if (cursor[y][x] == '*') {mouse[y * 16 + x] = COL8_000000;}if (cursor[y][x] == 'O') {mouse[y * 16 + x] = COL8_FFFFFF;}if (cursor[y][x] == '.') {mouse[y * 16 + x] = bc;}}}return;
}void putblock8_8(char *vram, int vxsize, int pxsize,int pysize, int px0, int py0, char *buf, int bxsize)
{int x, y;for (y = 0; y < pysize; y++) {for (x = 0; x < pxsize; x++) {vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];}}return;
}
/*初始化关系 */#include "bootpack.h"void init_pic(void)
/* PIC初始化 */
{io_out8(PIC0_IMR,  0xff  ); /* 禁止所有中断 */io_out8(PIC1_IMR,  0xff  ); /* 禁止所有中断 */io_out8(PIC0_ICW1, 0x11  ); /* 边缘触发模式(edge trigger mode) */io_out8(PIC0_ICW2, 0x20  ); /* IRQ0-7由INT20-27接收 */io_out8(PIC0_ICW3, 1 << 2); /* PIC1由IRQ2相连 */io_out8(PIC0_ICW4, 0x01  ); /* 无缓冲区模式 */io_out8(PIC1_ICW1, 0x11  ); /* 边缘触发模式(edge trigger mode) */io_out8(PIC1_ICW2, 0x28  ); /* IRQ8-15由INT28-2f接收 */io_out8(PIC1_ICW3, 2     ); /* PIC1由IRQ2连接 */io_out8(PIC1_ICW4, 0x01  ); /* 无缓冲区模式 */io_out8(PIC0_IMR,  0xfb  ); /* 11111011 PIC1以外全部禁止 */io_out8(PIC1_IMR,  0xff  ); /* 11111111 禁止所有中断 */return;
}void inthandler21(int *esp)
/* 来自PS/2键盘的中断 */
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");for (;;) {io_hlt();}
}void inthandler2c(int *esp)
/* 来自PS/2鼠标的中断 */
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 2C (IRQ-12) : PS/2 mouse");for (;;) {io_hlt();}
}void inthandler27(int *esp)
/* PIC0中断的不完整策略 */
/* 这个中断在Athlon64X2上通过芯片组提供的便利,只需执行一次 */
/* 这个中断只是接收,不执行任何操作 */
/* 为什么不处理?→  因为这个中断可能是电气噪声引发的、只是处理一些重要的情况。*/
{io_out8(PIC0_OCW2, 0x67); /* 通知PIC的IRQ-07(参考7-1) */return;
}
/* asmhead.nas */
struct BOOTINFO { /* 0x0ff0-0x0fff */char cyls; /* 启动区读磁盘读到此为止 */char leds; /* 启动时键盘的LED的状态 */char vmode; /* 显卡模式为多少位彩色 */char reserve;short scrnx, scrny; /* 画面分辨率 */char *vram;
};
#define ADR_BOOTINFO    0x00000ff0/* naskfunc.nas */
void io_hlt(void);
void io_cli(void);
void io_sti(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);
void asm_inthandler21(void);
void asm_inthandler27(void);
void asm_inthandler2c(void);/* graphic.c */
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen8(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s);
void init_mouse_cursor8(char *mouse, char bc);
void putblock8_8(char *vram, int vxsize, int pxsize,int pysize, int px0, int py0, char *buf, int bxsize);
#define COL8_000000     0
#define COL8_FF0000     1
#define COL8_00FF00     2
#define COL8_FFFF00     3
#define COL8_0000FF     4
#define COL8_FF00FF     5
#define COL8_00FFFF     6
#define COL8_FFFFFF     7
#define COL8_C6C6C6     8
#define COL8_840000     9
#define COL8_008400     10
#define COL8_848400     11
#define COL8_000084     12
#define COL8_840084     13
#define COL8_008484     14
#define COL8_848484     15/* dsctbl.c */
struct SEGMENT_DESCRIPTOR {short limit_low, base_low;char base_mid, access_right;char limit_high, base_high;
};
struct GATE_DESCRIPTOR {short offset_low, selector;char dw_count, access_right;short offset_high;
};
void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
#define ADR_IDT         0x0026f800
#define LIMIT_IDT       0x000007ff
#define ADR_GDT         0x00270000
#define LIMIT_GDT       0x0000ffff
#define ADR_BOTPAK      0x00280000
#define LIMIT_BOTPAK    0x0007ffff
#define AR_DATA32_RW    0x4092
#define AR_CODE32_ER    0x409a
#define AR_INTGATE32    0x008e/* int.c */
void init_pic(void);
void inthandler21(int *esp);
void inthandler27(int *esp);
void inthandler2c(int *esp);
#define PIC0_ICW1       0x0020
#define PIC0_OCW2       0x0020
#define PIC0_IMR        0x0021
#define PIC0_ICW2       0x0021
#define PIC0_ICW3       0x0021
#define PIC0_ICW4       0x0021
#define PIC1_ICW1       0x00a0
#define PIC1_OCW2       0x00a0
#define PIC1_IMR        0x00a1
#define PIC1_ICW2       0x00a1
#define PIC1_ICW3       0x00a1
#define PIC1_ICW4       0x00a1

第七天-FIFO鼠标控制

鼠标解读

void inthandler21(int *esp)
/* 来自PS/2键盘的中断 */
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;unsigned char data, s[4];io_out8(PIC0_OCW2, 0x61);  /* 通知PIC IRQ-01 已经受理完毕 */data = io_in8(PORT_KEYDAT);fifo8_put(&keyfifo, data);return;
}

加快中断

void inthandler2c(int *esp)
/* 来自PS/2鼠标的中断 */
{unsigned char data;io_out8(PIC1_OCW2, 0x64);   /* 通知PIC IRQ-12 已经受理完毕 */io_out8(PIC0_OCW2, 0x62);  /* 通知PIC IRQ-02 已经受理完毕 */data = io_in8(PORT_KEYDAT);fifo8_put(&mousefifo, data);return;
}

制作FIFO缓冲区

int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* 向FIFO传送数据并保存 */
{if (fifo->free == 0) {/* 没有空间了,溢出 */fifo->flags |= FLAGS_OVERRUN;return -1;}fifo->buf[fifo->p] = data;fifo->p++;if (fifo->p == fifo->size) {fifo->p = 0;}fifo->free--;return 0;
}

void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
/* 初始化FIFO缓冲区 */
{fifo->size = size;fifo->buf = buf;fifo->free = size; /* 缓冲区大小 */fifo->flags = 0;fifo->p = 0; /* 下一个数据写入位置 */fifo->q = 0; /* 下一个数据读出位置 */return;
}

自制操作系统——第一周相关推荐

  1. 30天自制操作系统——第一天制作一个Hello word操作系统

    入手一本<30天自制操作系统>,内容诙谐有趣,很适合对操作系统一窍不通的新手. 据作者所言,这本书的最终目标是从零开始编写一个五脏俱全的图形操作系统,不用什么基础,而且只需30天!! 没有 ...

  2. 《30天自制操作系统》---第一天

    <30天自制操作系统>---第一天 二进制编译与文本编译器大家用自己顺手的就可以,今天通过两种方法运行虚拟机,第一种通过作者提供的工具运行在QEMU中,第二种方法运行到VMWare中,直接 ...

  3. 30天自制操作系统:第一天

    30天自制操作系统:第一天 趁着双十一采购了一大堆书,准备消化一下,不然就太浪费了. 书上推荐的二进制编辑器是bz ,界面和版本有点古老,还只有日语和英语版本. 不过直到最近一年还一直在维护,最新的版 ...

  4. 【自制操作系统06】终于开始用 C 语言了,第一行内核代码!

    一.整理下到目前为止的流程图 写到这,终于才把一些苦力活都干完了,也终于到了我们的内核代码部分,也终于开始第一次用 c 语言写代码了!为了这个阶段性的胜利,以及更好地进入内核部分,下图贴一张到目前为止 ...

  5. 操作系统源代码_计算机自制操作系统(八):仿生DOS操作系统源代码

    一.真机运行 我们已经完成了仿生DOS操作系统的制作,并在上一章的末尾给大家在虚拟机上做了演示.今天,我们要将该操作系统在真机上启动运行,是不是非常期待自己做出的第一款比较有意义的操作系统? 在&qu ...

  6. 索骥馆-DIY操作系统之《30天自制操作系统》扫描版[PDF]

    内容简介: <30天自制操作系统>是一本兼具趣味性.实用性与学习性的操作系统图书.作者从计算机的构造.汇编语言.C语言开始解说,让读者在实践中掌握算法.在这本书的指导下,从零编写所有代码, ...

  7. 2013元旦快乐--30自制操作系统之第1天--从计算机结构到汇编程序入门(先熟悉熟悉)

    2012自己以一种悲观者的论调就这样告别,回首这一年,RY有自己的收获和快乐,也有辛酸和愤懑,给自己打个分,大致73分吧. 这一年,自己经历了大二到大三的过渡,在思想上逐渐走向成熟,在技术上慢慢的对自 ...

  8. 《30天自制操作系统》笔记(01)——hello bitzhuwei’s OS!

    <30天自制操作系统>笔记(01)--hello bitzhuwei's OS! 最初的OS代码 1 ; hello-os 2 ; TAB=4 3 4 ORG 0x7c00 ; 指明程序的 ...

  9. 201671010140. 2016-2017-2 《Java程序设计》java学习第一周

       java学习第一周        本周是新学期的开端,也是新的学习进程的开端,第一次接触java这门课程,首先书本的厚度就给我一种无形的压力,这注定了,这门课程不会是轻松的,同时一种全新的学习方 ...

最新文章

  1. LeetCode刷题记录1——717. 1-bit and 2-bit Characters(easy)
  2. 工程路径网站图片路径的问题 绝对路径
  3. Windows API获取系统配置文件的配置参数
  4. 树莓派4b控制机械手臂_Raspberry Pi
  5. Oracle数据库中的SOUNDEX函数
  6. oracle 三层嵌套查询,oracle 三层嵌套分页查询
  7. Linux系统的远程登录
  8. SpringBoot 使用LibreOffice 在线预览 doc,doxc,xls,xlsx,ppt,pptx 文件
  9. http请求pom 客户端_RPC之HttpClient与OkHttp3的实现,服务端知识点
  10. jack编译报错的问题
  11. 1128: mxh道歉记
  12. josn 格式 解析
  13. 关于js的数组方法部分整理
  14. 固态硬盘是什么接口_笔记本固态硬盘的接口有哪些?来学习下笔记本SSD小知识...
  15. Thinking in java生词
  16. python爬取京东商品信息代码_爬取京东商品信息
  17. 微星主板开启安全启动以更新win11教程
  18. Mac 安装 IntelliJ IDEA 以及激活方法
  19. COVID vaccine inequity, species swaps — the week in infographics
  20. 在Ubuntu中配置中文输入法

热门文章

  1. 戴尔计算机主机型号,戴尔电脑在哪看型号_戴尔电脑型号怎么看
  2. 第五十一篇 前端之CSS内容
  3. FFmpeg - Windows下使用MSYS2和VS编译FFmpeg
  4. 固态硬盘和机械硬盘的区别
  5. 信捷PLC中Y0用C语言怎么表示,信捷PLC
  6. 什么是SaaS平台?SaaS软件平台有什么优势
  7. 怎样使用菜单栏中的Apple图标在苹果Mac上强制退出应用程序?
  8. linux键盘错位1格,键盘按键错乱,教您修复键盘按键错乱的方法
  9. 车载应用技术——Android Automotive系统
  10. node-sass sass-loader版本对应问题