博客网址:www.shicoder.top
微信:18223081347
欢迎加群聊天 :452380935
这一次我们正式进入内核,编写相关的内核代码,也就是kernel代码
数据类型定义
因为我们在内核中会使用一些数据,因此先提前定义一些数据类型
#define EOF -1
#define NULL ((void *)0) // 空指针
#define EOS '\0' // 字符串结尾
#define bool _Bool
#define true 1
#define false 0
#define _packed __attribute__((packed)) // 用于定义特殊的结构体 不对齐
typedef unsigned int size_t;
typedef char int8;
typedef short int16;
typedef int int32;
typedef long long int64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef u32 time_t;
typedef u32 idx_t;
输入输出
我们知道,在操作系统启动的时候,刚开始都是黑乎乎的界面,然后光标闪烁等,那么这个是怎么实现的呢,一般这种都是通过向一些寄存器写入一些值和和获取一些值实现,因此就需要用一些输入输出函数
首先是四个函数
extern u8 inb(u16 port); // 输入1个字节 从port端口中读一个字节
extern u16 inw(u16 port); // 输入2个字节 从port端口中读2个字节
extern void outb(u16 port,u8 value); // 输出1个字节 将value值输入到port端口中
extern void outw(u16 port,u16 value); // 输出2个字节 将value值输入到port端口中
我们采用汇编实现
global inb ; 将inb导出
inb:
; 栈帧保存
push ebp
mov ebp, esp
xor eax, eax ;清空
mov edx, [ebp + 8] ;port [ebp + 8]就是传入进来的port
in al, dx ;将dx所指向的端口,读取一个字放在al,也就是从port端口读一个字节
jmp $+2 ;延迟
jmp $+2 ;延迟
jmp $+2 ;延迟
leave ; 恢复栈帧
ret
global outb ; 将outb导出
outb:
; 栈帧保存
push ebp
mov ebp, esp
mov edx, [ebp + 8] ;port [ebp + 8]就是传入进来的port
mov eax, [ebp + 12] ; value 参数入栈是从右往左 所以value地址更高
out dx, al ;将al的8比特输出到dx的端口号
jmp $+2 ;延迟
jmp $+2 ;延迟
jmp $+2 ;延迟
leave ; 恢复栈帧
ret
global inw ; 将inw导出
inw:
; 栈帧保存
push ebp
mov ebp, esp
xor eax, eax ;清空
mov edx, [ebp + 8] ;port [ebp + 8]就是传入进来的port
in ax, dx ;将dx所指向的端口,读取2个字放在ax
jmp $+2 ;延迟
jmp $+2 ;延迟
jmp $+2 ;延迟
leave ; 恢复栈帧
ret
global outw ; 将outw导出
outw:
; 栈帧保存
push ebp
mov ebp, esp
mov edx, [ebp + 8] ;port [ebp + 8]就是传入进来的port
mov eax, [ebp + 12] ; value 参数入栈是从右往左 所以value地址更高
out dx, ax ;将ax的2个字输出到dx的端口号
jmp $+2 ;延迟
jmp $+2 ;延迟
jmp $+2 ;延迟
leave ; 恢复栈帧
ret
我们在kernel中测试下获取光标的位置,相关的寄存器有以下几个
- CRT 地址寄存器 0x3D4
- CRT 数据寄存器 0x3D5
- CRT 光标位置 - 高位 0xE
- CRT 光标位置 - 低位 0xF
比如我们把光标高位位置给地址寄存器,那么就可以通过数据寄存器得到和设置光标位置的高位值
// - CRT 地址寄存器 0x3D4
// - CRT 数据寄存器 0x3D5
// - CRT 光标位置 - 高位 0xE
// - CRT 光标位置 - 低位 0xF
#define CRT_ADDR_REG 0x3d4
#define CRT_DATA_REG 0x3d5
#define CRT_CURSOR_H 0xe
#define CRT_CURSOR_L 0xf
void kernel_init()
{
outb(CRT_ADDR_REG,CRT_CURSOR_H);
u16 pos = inb(CRT_DATA_REG) << 8;
outb(CRT_ADDR_REG,CRT_CURSOR_L);
pos |= inb(CRT_DATA_REG); // 到这里,pos值为240,通过qemu也可以看到,光标在第4行,每行80字符
u8 data = inb(CRT_DATA_REG);
// 比如想把光标位置改为160
outb(CRT_ADDR_REG,CRT_CURSOR_H);
outb(CRT_DATA_REG,0);
outb(CRT_ADDR_REG,CRT_CURSOR_L);
outb(CRT_DATA_REG,160); // 到这里,就可以看到光标在第3行开始处
}
字符串函数实现
我们在C语言中,使用过很多字符串函数,比如
char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
size_t strlen(const char *str);
int strcmp(const char *lhs, const char *rhs);
char *strchr(const char *str, int ch);
char *strrchr(const char *str, int ch);
int memcmp