外中断完成
commit
fecc31e72b
@ -0,0 +1,16 @@
|
||||
|
||||
*.img
|
||||
*.ini
|
||||
*.bin
|
||||
*.map
|
||||
*.o
|
||||
*.out
|
||||
*.zst
|
||||
*.gz
|
||||
*.lock
|
||||
*.vmdk
|
||||
*.log
|
||||
|
||||
build
|
||||
bochs/src
|
||||
bochs/pkg
|
@ -0,0 +1,60 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "onix - Build and debug kernel",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/kernel.bin",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${fileDirname}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerServerAddress": "127.0.0.1:1234",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Set Disassembly Flavor to Intel",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
{
|
||||
"name": "gcc - Build and debug active file",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${fileDirname}/${fileBasenameNoExtension}.out",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${fileDirname}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Set Disassembly Flavor to Intel",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "C/C++: gcc build active file",
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"onix.h": "c",
|
||||
"types.h": "c",
|
||||
"cstring": "c",
|
||||
"string.h": "c",
|
||||
"sstream": "c",
|
||||
"typeinfo": "c",
|
||||
"io.h": "c",
|
||||
"console.h": "c",
|
||||
"system_error": "c",
|
||||
"random": "c",
|
||||
"algorithm": "c",
|
||||
"stdarg.h": "c",
|
||||
"*.tcc": "c",
|
||||
"cstdio": "c",
|
||||
"vprintf.h": "c",
|
||||
"printk.h": "c",
|
||||
"assert.h": "c",
|
||||
"debug.h": "c",
|
||||
"global.h": "c",
|
||||
"task.h": "c",
|
||||
"interrupt.h": "c",
|
||||
"vector": "c",
|
||||
"stdlib.h": "c"
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cppbuild",
|
||||
"label": "C/C++: gcc 生成活动文件",
|
||||
"command": "/usr/bin/gcc",
|
||||
"args": [
|
||||
"-fdiagnostics-color=always",
|
||||
"-m32",
|
||||
"-g",
|
||||
"-I${workspaceFolder}/src/include",
|
||||
"${file}",
|
||||
"-o",
|
||||
"${fileDirname}/${fileBasenameNoExtension}.out"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"detail": "调试器生成的任务。"
|
||||
},
|
||||
{
|
||||
"type": "cppbuild",
|
||||
"label": "C/C++: clang 生成活动文件",
|
||||
"command": "/usr/bin/clang",
|
||||
"args": [
|
||||
"-fcolor-diagnostics",
|
||||
"-fansi-escape-codes",
|
||||
"-g",
|
||||
"${file}",
|
||||
"-o",
|
||||
"${fileDirname}/${fileBasenameNoExtension}"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"group": "build",
|
||||
"detail": "编译器: /usr/bin/clang"
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
# 用于调试 C 程序 以下内容是从 Archlinux AUR 包和 bochs 包抠出来的,专门用于 32 位系统的调试,
|
||||
# 默认的是 64 位操作系统所以会有一些问题
|
||||
# 安装使用 makepkg -si
|
||||
|
||||
pkgname=bochs-gdb
|
||||
pkgver=2.7
|
||||
pkgrel=1
|
||||
pkgdesc="A portable x86 PC emulation software package with gdbstub"
|
||||
arch=('x86_64')
|
||||
url="http://bochs.sourceforge.net/"
|
||||
license=('LGPL')
|
||||
depends=('gcc-libs' 'libxrandr' 'libxpm' 'gtk2')
|
||||
source=("http://downloads.sourceforge.net/sourceforge/bochs/bochs-$pkgver.tar.gz")
|
||||
sha256sums=('a010ab1bfdc72ac5a08d2e2412cd471c0febd66af1d9349bc0d796879de5b17a')
|
||||
|
||||
prepare() {
|
||||
cd "$srcdir/bochs-$pkgver"
|
||||
# 4.X kernel is basically 3.20
|
||||
sed -i 's/2\.6\*|3\.\*)/2.6*|3.*|4.*)/' configure*
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "$srcdir/bochs-$pkgver"
|
||||
|
||||
./configure \
|
||||
--prefix=/usr \
|
||||
--without-wx \
|
||||
--with-x11 \
|
||||
--with-x \
|
||||
--with-term \
|
||||
--disable-docbook \
|
||||
--enable-cpu-level=6 \
|
||||
--enable-fpu \
|
||||
--enable-3dnow \
|
||||
--enable-disasm \
|
||||
--enable-long-phy-address \
|
||||
--enable-disasm \
|
||||
--enable-pcidev \
|
||||
--enable-usb \
|
||||
--enable-all-optimizations \
|
||||
--enable-gdb-stub \
|
||||
--with-nogui \
|
||||
--enable-plugins \
|
||||
# --enable-smp \
|
||||
# --enable-x86-debugger \
|
||||
# --enable-debugger \
|
||||
# --enable-x86-64 \
|
||||
# --enable-avx \
|
||||
# --enable-evex \
|
||||
sed -i 's/^LIBS = /LIBS = -lpthread/g' Makefile
|
||||
make -j 1
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/bochs-$pkgver"
|
||||
make DESTDIR="$pkgdir" install
|
||||
install -Dm644 .bochsrc "$pkgdir/etc/bochsrc-sample.txt"
|
||||
|
||||
cd "$pkgdir/usr/bin/"
|
||||
mv bochs bochs-gdb
|
||||
rm -rf bochs-gdb-a20
|
||||
rm bximage
|
||||
cd "$pkgdir/usr/"
|
||||
rm -rfv share
|
||||
cd "$pkgdir"
|
||||
rm -rfv etc
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
# configuration file generated by Bochs
|
||||
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false
|
||||
config_interface: textconfig
|
||||
# 设置有gui的debug
|
||||
display_library: x, options="gui_debug"
|
||||
memory: host=32, guest=32
|
||||
romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none
|
||||
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
|
||||
# 设置硬盘启动
|
||||
boot: disk
|
||||
floppy_bootsig_check: disabled=0
|
||||
floppya: type=1_44
|
||||
# no floppyb
|
||||
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
# 硬盘启动路径设置
|
||||
ata0-master: type=disk, path="../build/master.img", mode=flat
|
||||
ata0-slave: type=none
|
||||
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
ata1-master: type=none
|
||||
ata1-slave: type=none
|
||||
ata2: enabled=false
|
||||
ata3: enabled=false
|
||||
optromimage1: file=none
|
||||
optromimage2: file=none
|
||||
optromimage3: file=none
|
||||
optromimage4: file=none
|
||||
optramimage1: file=none
|
||||
optramimage2: file=none
|
||||
optramimage3: file=none
|
||||
optramimage4: file=none
|
||||
pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none
|
||||
vga: extension=vbe, update_freq=5, realtime=1, ddc=builtin
|
||||
cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
||||
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor"
|
||||
cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true
|
||||
cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, avx_f16c=false
|
||||
cpuid: avx_fma=false, bmi=0, xop=false, fma4=false, tbm=false, x86_64=true, 1g_pages=false
|
||||
cpuid: pcid=false, fsgsbase=false, smep=false, smap=false, mwait=true
|
||||
print_timestamps: enabled=0
|
||||
debugger_log: -
|
||||
magic_break: enabled=1
|
||||
port_e9_hack: enabled=0
|
||||
private_colormap: enabled=0
|
||||
clock: sync=none, time0=local, rtc_sync=0
|
||||
# no cmosimage
|
||||
log: -
|
||||
logprefix: %t%e%d
|
||||
debug: action=ignore
|
||||
info: action=report
|
||||
error: action=report
|
||||
panic: action=ask
|
||||
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
|
||||
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
|
||||
speaker: enabled=true, mode=system
|
||||
parport1: enabled=true, file=none
|
||||
parport2: enabled=false
|
||||
com1: enabled=true, mode=null
|
||||
com2: enabled=false
|
||||
com3: enabled=false
|
||||
com4: enabled=false
|
@ -0,0 +1,55 @@
|
||||
# configuration file generated by Bochs
|
||||
plugin_ctrl: pcidev=false, speaker=true, parallel=true, biosdev=true, extfpuirq=true, usb_uhci=false, serial=true, unmapped=true
|
||||
config_interface: textconfig
|
||||
display_library: x
|
||||
memory: host=32, guest=32
|
||||
romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none
|
||||
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
|
||||
boot: disk
|
||||
floppy_bootsig_check: disabled=0
|
||||
floppya: type=1_44
|
||||
# no floppyb
|
||||
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
ata0-master: type=disk, path="../build/master.img", mode=flat
|
||||
ata0-slave: type=none
|
||||
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
ata1-master: type=none
|
||||
ata1-slave: type=none
|
||||
ata2: enabled=false
|
||||
ata3: enabled=false
|
||||
optromimage1: file=none
|
||||
optromimage2: file=none
|
||||
optromimage3: file=none
|
||||
optromimage4: file=none
|
||||
optramimage1: file=none
|
||||
optramimage2: file=none
|
||||
optramimage3: file=none
|
||||
optramimage4: file=none
|
||||
pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none
|
||||
vga: extension=vbe, update_freq=5, realtime=1, ddc=builtin
|
||||
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
||||
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor"
|
||||
cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true
|
||||
cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false
|
||||
cpuid: smap=false, mwait=true
|
||||
print_timestamps: enabled=0
|
||||
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
|
||||
port_e9_hack: enabled=0
|
||||
private_colormap: enabled=0
|
||||
clock: sync=none, time0=local, rtc_sync=0
|
||||
# no cmosimage
|
||||
log: -
|
||||
logprefix: %t%e%d
|
||||
debug: action=ignore
|
||||
info: action=report
|
||||
error: action=report
|
||||
panic: action=ask
|
||||
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
|
||||
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
|
||||
speaker: enabled=true, mode=system
|
||||
parport1: enabled=true, file=none
|
||||
parport2: enabled=false
|
||||
com1: enabled=true, mode=null
|
||||
com2: enabled=false
|
||||
com3: enabled=false
|
||||
com4: enabled=false
|
@ -0,0 +1,106 @@
|
||||
BUILD_PATH:=../build
|
||||
SRC:= ./
|
||||
|
||||
# 内核的执行位置
|
||||
KERNEL_ENTRY_POINT := 0x10000
|
||||
|
||||
# 内核编译 .c文件需要传递给gcc的变量
|
||||
CFLAG := -m32 # 32位程序
|
||||
CFLAG += -fno-builtin # 不需要gcc内置函数(比如memcpy, 需要我们自己写)
|
||||
CFLAG += -nostdinc # 不需要标准头文件
|
||||
CFLAG += -fno-pic # 不需要位置无关的代码
|
||||
CFLAG += -fno-pie # 不需要位置无关的可执行程序
|
||||
CFLAG += -nostdlib # 不需要标准库
|
||||
CFLAG += -fno-stack-protector # 不需要栈保护
|
||||
CFLAG += -fno-common
|
||||
CFLAG := $(strip ${CFLAG})
|
||||
|
||||
DEBUG := -g # 需要debug
|
||||
INCLUDE := -I$(SRC)/include # 头文件引入目录
|
||||
|
||||
|
||||
# bootloader的编译
|
||||
$(BUILD_PATH)/boot/%.bin:$(SRC)/boot/%.asm
|
||||
$(shell mkdir -p $(dir $@))
|
||||
nasm -f bin $< -o $@
|
||||
|
||||
# kernel还有其他asm的编译, 把start.asm 编译成elf格式的 start.o
|
||||
$(BUILD_PATH)/%.o:$(SRC)/%.asm
|
||||
$(shell mkdir -p $(dir $@))
|
||||
nasm -f elf32 $(DEBUG) $< -o $@
|
||||
|
||||
# kernel.c还有其他 .c的编译
|
||||
$(BUILD_PATH)/%.o:$(SRC)/%.c
|
||||
$(shell mkdir -p $(dir $@))
|
||||
gcc $(CFLAG) $(DEBUG) $(INCLUDE) -c $< -o $@
|
||||
|
||||
# kernel的编译, 需要依赖上面的 start.o
|
||||
# 使用链接器将所有依赖的($^)的文件链接成一个静态链接的文件
|
||||
# 且重新编排指定程序的入口地址(_start符号)为 KERNEL_ENTRY_POINT 定义的地址,并将生成的可执行文件输出为 $@ 定义的文件名
|
||||
$(BUILD_PATH)/kernel.bin:$(BUILD_PATH)/kernel/start.o \
|
||||
$(BUILD_PATH)/kernel/main.o\
|
||||
$(BUILD_PATH)/kernel/io.o\
|
||||
$(BUILD_PATH)/lib/string.o\
|
||||
$(BUILD_PATH)/lib/console.o\
|
||||
$(BUILD_PATH)/lib/vprintf.o\
|
||||
$(BUILD_PATH)/kernel/printk.o\
|
||||
$(BUILD_PATH)/lib/assert.o\
|
||||
$(BUILD_PATH)/kernel/debug.o\
|
||||
$(BUILD_PATH)/kernel/global.o\
|
||||
$(BUILD_PATH)/kernel/task.o\
|
||||
$(BUILD_PATH)/kernel/schdule.o\
|
||||
$(BUILD_PATH)/kernel/handle.o\
|
||||
$(BUILD_PATH)/kernel/interrupt.o\
|
||||
$(BUILD_PATH)/lib/stdlib.o\
|
||||
|
||||
$(shell mkdir -p $(dir $@))
|
||||
ld -m elf_i386 -static $^ -o $@ -Ttext $(KERNEL_ENTRY_POINT)
|
||||
|
||||
# elf文件从磁盘进入内存还需要特殊处理, 使用这种方式, 直接读入内存
|
||||
# objcopy -O binary 输出文件中只包含了输入文件的纯二进制数据,没有任何 ELF 文件格式的头部信息
|
||||
# 如数据段, 代码段等
|
||||
$(BUILD_PATH)/system.bin: $(BUILD_PATH)/kernel.bin
|
||||
objcopy -O binary $< $@
|
||||
|
||||
# 导出kernel的符号表
|
||||
$(BUILD_PATH)/system.map: $(BUILD_PATH)/kernel.bin
|
||||
nm $< | sort > $@
|
||||
|
||||
|
||||
# 创建镜像文件
|
||||
$(BUILD_PATH)/master.img: $(BUILD_PATH)/boot/boot.bin \
|
||||
$(BUILD_PATH)/boot/loader.bin \
|
||||
$(BUILD_PATH)/system.bin \
|
||||
$(BUILD_PATH)/system.map
|
||||
|
||||
yes | bximage -q -hd=16 -func=create -sectsize=512 -imgmode=flat $@
|
||||
dd if=$(BUILD_PATH)/boot/boot.bin of=$@ bs=512 count=1 conv=notrunc
|
||||
dd if=$(BUILD_PATH)/boot/loader.bin of=$@ bs=512 seek=2 count=4 conv=notrunc
|
||||
dd if=$(BUILD_PATH)/system.bin of=$@ bs=512 seek=10 count=200 conv=notrunc
|
||||
|
||||
# 清理
|
||||
.PHONY:clean
|
||||
clean:
|
||||
@rm -rf $(BUILD_PATH)
|
||||
|
||||
test: $(BUILD_PATH)/master.img
|
||||
|
||||
# 启动系统
|
||||
.PHONY:bochs
|
||||
bochs: $(BUILD_PATH)/master.img
|
||||
@rm -rf $(BUILD_PATH)/*lock
|
||||
bochs -q -f ../bochs/bochsrc
|
||||
|
||||
# 启动系统
|
||||
.PHONY:bochsg
|
||||
bochsg: $(BUILD_PATH)/master.img
|
||||
@rm -rf $(BUILD_PATH)/*lock
|
||||
bochs-gdb -q -f ../bochs/bochsrc.gdb
|
||||
|
||||
.PHONY: qemu
|
||||
qemu: $(BUILD_PATH)/master.img
|
||||
qemu-system-i386 \
|
||||
-s -S \
|
||||
-m 64M \
|
||||
-boot c \
|
||||
-hda $<
|
@ -0,0 +1,280 @@
|
||||
; 声明这段代码的位置运行时会在0x7c00, 直接取址会加上0x7c00
|
||||
; xchg bx, bx ; bochs 的魔数, 代码执行到这里会停下
|
||||
|
||||
[org 0x7c00]
|
||||
|
||||
start:
|
||||
init:
|
||||
; 设置屏幕模式微文本模式, 清除屏幕
|
||||
mov ax, 3
|
||||
int 0x10
|
||||
|
||||
; 初始化段寄存器
|
||||
mov ax, 0
|
||||
mov bx, ax
|
||||
mov cx, ax
|
||||
mov dx, ax
|
||||
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
|
||||
mov si, ax
|
||||
mov di, ax
|
||||
|
||||
; 修改栈顶为0x7c00, 使其向下增长
|
||||
mov sp, 0x7c00
|
||||
|
||||
; 读取loader.bin
|
||||
mov edi, 0x1000 ; 读取到目标内存地址(32位地址空间)
|
||||
mov ebx, 2 ; 从第 n 个扇区开始读(32位 扇区最大有 2的27次方个)
|
||||
mov cl, 4 ; 读 1个 扇区(8位 每次最多读取256个扇区)
|
||||
call read_disk
|
||||
|
||||
; 校验上方读入的数据
|
||||
cmp word es:[0x1000], 0xa0a0
|
||||
jnz error
|
||||
|
||||
; mov edi, 0x1000 ; 把內存中什么地方的数据写出来
|
||||
; mov ebx, 1 ; 从第 n 个扇区开始写(32位 扇区最大有 2的27次方个)
|
||||
; mov cl, 1 ; 写 1个 扇区(8位 每次最多读取256个扇区)
|
||||
; call write_disk
|
||||
|
||||
mov si, booting
|
||||
call print
|
||||
|
||||
|
||||
jmp 0x1002
|
||||
|
||||
|
||||
; 阻塞, 一直跳转到当前行
|
||||
jmp $
|
||||
ret
|
||||
|
||||
|
||||
read_disk:
|
||||
push edx
|
||||
push eax
|
||||
|
||||
; ecx寄存器校验, 使其最大值为256
|
||||
and ecx,0b1111_1111
|
||||
|
||||
; 设置读取扇区的数量 0x1f2
|
||||
mov dx, 0x1f2
|
||||
mov al, cl ; cl:1, al:1
|
||||
out dx, al ; out 0x1f2, 1
|
||||
|
||||
mov eax, ebx ; eax: 0
|
||||
|
||||
; 起始扇区的 0~7位 设置 0x1f3
|
||||
inc dx
|
||||
out dx, al ; al: 0
|
||||
|
||||
; 起始扇区的 8~15位 设置 0x1f4
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 16~23位 设置 0x1f5
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 24~27位 设置 0x1f6
|
||||
inc dx
|
||||
shr eax, 8 ; al: 0
|
||||
|
||||
; 高4位设为0, 低四位保持不变
|
||||
and al, 0b0000_1111 ; al: 0000_0000
|
||||
|
||||
; 读取模式为 LBA模式
|
||||
or al, 0b1110_0000 ; al: 1110_0000
|
||||
out dx, al
|
||||
|
||||
; 从硬盘读 0x1f7
|
||||
inc dx
|
||||
mov al, 0x20 ; al: 0x20
|
||||
|
||||
out dx, al
|
||||
|
||||
|
||||
; 等待读取完毕
|
||||
call .waits
|
||||
; 读取到指定为止
|
||||
call .reads
|
||||
jmp .end
|
||||
|
||||
|
||||
.reads:
|
||||
push cx
|
||||
; 读取每个扇区的512字节, 每次读2字节读256次
|
||||
mov cx, 256
|
||||
mov dx, 0x1f0
|
||||
|
||||
.readw:
|
||||
in ax, dx
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
mov es:[edi], ax
|
||||
add edi, 2
|
||||
loop .readw
|
||||
pop cx
|
||||
; 如果读取了多个扇区 继续循环
|
||||
loop .reads
|
||||
ret
|
||||
|
||||
; 循环检查磁盘状态
|
||||
.waits:
|
||||
mov dx, 0x1f7
|
||||
in al,dx
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 如果 第三位是1, 说明准备好了
|
||||
and al, 0b1000_1000
|
||||
cmp al, 0b0000_1000
|
||||
jnz .waits
|
||||
ret
|
||||
|
||||
.end:
|
||||
pop eax
|
||||
pop edx
|
||||
ret
|
||||
|
||||
|
||||
|
||||
write_disk:
|
||||
push edx
|
||||
push eax
|
||||
|
||||
; ecx寄存器校验, 使其最大值为256
|
||||
and ecx,0b1111_1111
|
||||
|
||||
; 设置写入扇区的数量 0x1f2
|
||||
mov dx, 0x1f2
|
||||
mov al, cl ; cl:1, al:1
|
||||
out dx, al ; out 0x1f2, 1
|
||||
|
||||
mov eax, ebx ; eax: 0
|
||||
|
||||
; 起始扇区的 0~7位 设置 0x1f3
|
||||
inc dx
|
||||
out dx, al ; al: 0
|
||||
|
||||
; 起始扇区的 8~15位 设置 0x1f4
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 16~23位 设置 0x1f5
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 24~27位 设置 0x1f6
|
||||
inc dx
|
||||
shr eax, 8 ; al: 0
|
||||
|
||||
; 高4位设为0, 低四位保持不变
|
||||
and al, 0b0000_1111 ; al: 0000_0000
|
||||
|
||||
; 模式为 LBA模式
|
||||
or al, 0b1110_0000 ; al: 1110_0000
|
||||
out dx, al
|
||||
|
||||
; 从硬盘设置写 0x1f7
|
||||
inc dx
|
||||
mov al, 0x30 ; al: 0x20
|
||||
|
||||
out dx, al
|
||||
|
||||
|
||||
; 写入到指定为止
|
||||
call .writes
|
||||
jmp .end
|
||||
|
||||
|
||||
.writes:
|
||||
; 等待硬盘空闲
|
||||
call .waits
|
||||
|
||||
push cx
|
||||
; 写入每个扇区的512字节, 每次写2字节读256次
|
||||
mov cx, 256
|
||||
mov dx, 0x1f0
|
||||
|
||||
.writew:
|
||||
mov ax, es:[edi]
|
||||
out dx, ax
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
mov es:[edi], ax
|
||||
add edi, 2
|
||||
loop .writew
|
||||
pop cx
|
||||
; 如果写了多个扇区 继续循环
|
||||
loop .writes
|
||||
ret
|
||||
|
||||
; 循环检查磁盘状态
|
||||
.waits:
|
||||
mov dx, 0x1f7
|
||||
in al,dx
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 如果 第三位是1, 说明准备好了
|
||||
and al, 0b1000_0000 ; 写入,不需要校验第三位了, 秩序要校验第七位
|
||||
cmp al, 0b0000_0000
|
||||
jnz .waits
|
||||
ret
|
||||
|
||||
.end:
|
||||
pop eax
|
||||
pop edx
|
||||
ret
|
||||
|
||||
|
||||
|
||||
; 一个print函数, 调用时 把字符串的地址 mov到si寄存器即可
|
||||
print:
|
||||
push ax
|
||||
mov ah, 0x0e
|
||||
.show:
|
||||
mov al,[si]
|
||||
cmp al, 0
|
||||
jz .end
|
||||
int 0x10
|
||||
inc si
|
||||
jmp .show
|
||||
.end:
|
||||
pop ax
|
||||
ret
|
||||
|
||||
|
||||
error:
|
||||
mov si, .error_msg
|
||||
call print
|
||||
hlt ; 让 CPU 停止
|
||||
jmp $
|
||||
ret
|
||||
.error_msg db "Booting Error!!!", 10, 13, 0
|
||||
|
||||
|
||||
|
||||
|
||||
booting:
|
||||
db "Booting Start ...", 10, 13, 0 ; 结尾 \n \r 0
|
||||
|
||||
|
||||
padding:
|
||||
; 填充数据, 引导扇区必须为512字节, 最后两个字节是魔数, 除了代码之外必须用0填充
|
||||
times 510 - ($ - $$) db 0 ; 最后俩字节是0xaa55, 所以一共需要填充 510 - (当前行位置 - 开始的的位置) = 510 - 代码段的大小
|
||||
|
||||
|
||||
; 魔数
|
||||
dw 0xaa55
|
@ -0,0 +1,288 @@
|
||||
[org 0x1000]
|
||||
|
||||
; 校验用的, 这里会被读到 0x1000处
|
||||
dw 0xa0a0
|
||||
|
||||
mov si, loading_log
|
||||
call print
|
||||
call detect_memory
|
||||
call prepare_protect_mode
|
||||
|
||||
jmp $
|
||||
|
||||
|
||||
|
||||
detect_memory:
|
||||
; 设置检测内存的buffer位置
|
||||
mov ax, 0
|
||||
mov es, ax
|
||||
mov edi, mem_task_buffer
|
||||
|
||||
; 固定签名
|
||||
mov edx, 0x534d4150
|
||||
|
||||
; 置为0, 每次系统调用会修改这个寄存器
|
||||
xor ebx, ebx
|
||||
|
||||
; 保存的目的地
|
||||
mov di, mem_task_buffer
|
||||
|
||||
.next:
|
||||
; 子功能号
|
||||
mov eax, 0xe820
|
||||
; ards 结构的大小 (字节)
|
||||
mov cx, 20
|
||||
; 调用中断
|
||||
int 0x15
|
||||
; 如果cf置位, 表示出错了
|
||||
jc error
|
||||
|
||||
; 计算下一个内存结构体保存的首地址
|
||||
add di, cx
|
||||
|
||||
inc word [mem_task_count]
|
||||
|
||||
; 不为0 说明检查未完成
|
||||
cmp ebx, 0
|
||||
jnz .next
|
||||
|
||||
|
||||
mov si, detect_memory_log
|
||||
call print
|
||||
ret
|
||||
|
||||
; ; 循环结构体内的值(我们只读取低32位相关的信息, 高32位的暂时不需要)
|
||||
; mov cx, [mem_task_count]
|
||||
; ; 初始偏移量
|
||||
; mov si, 0
|
||||
; .show
|
||||
; mov eax, [mem_task_buffer + si] ; 基地址 低32位
|
||||
; mov ebx, [mem_task_buffer + si + 8] ; 内存长度的低32位
|
||||
; mov edx, [mem_task_buffer + si + 16] ; 本段内存类型 1: 可以使用, 2: 内存使用或者被保留中, 其他: 未定义
|
||||
|
||||
; add si, 20
|
||||
; ; xchg bx, bx ; bochs 的魔数, 代码执行到这里会停下
|
||||
; loop .show
|
||||
|
||||
|
||||
print:
|
||||
push ax
|
||||
mov ah, 0x0e
|
||||
.show:
|
||||
mov al,[si]
|
||||
cmp al, 0
|
||||
jz .end
|
||||
int 0x10
|
||||
inc si
|
||||
jmp .show
|
||||
.end:
|
||||
pop ax
|
||||
ret
|
||||
|
||||
prepare_protect_mode:
|
||||
|
||||
cli ; 关闭中断
|
||||
|
||||
; ; 打开 A20线
|
||||
; mov al, 0xdd
|
||||
; out 0x64, al
|
||||
|
||||
; 打开 A20 线
|
||||
in al, 0x92
|
||||
or al, 0b10
|
||||
out 0x92, al
|
||||
|
||||
; 进入保护模式
|
||||
mov eax, cr0
|
||||
or eax, 0b1
|
||||
mov cr0, eax
|
||||
|
||||
; 加载 gdt
|
||||
lgdt [gdt_ptr]
|
||||
|
||||
; 长跳转, 刷新缓存, 跳进保护模式
|
||||
jmp dword code_selecter:protect_mode
|
||||
|
||||
ret
|
||||
|
||||
[bits 32]
|
||||
protect_mode:
|
||||
; 这里已经进入了保护模式
|
||||
|
||||
; 初始化段寄存器(将代码段之外的寄存器都设置为 数据段)
|
||||
mov ax, data_selecter
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
mov gs, ax
|
||||
mov fs, ax
|
||||
|
||||
; 修改自己设置的栈顶
|
||||
mov esp, 0x10000
|
||||
|
||||
; 进入保护模式之后, 实模式的print就不能用了
|
||||
mov byte [0xb8000], 'P'
|
||||
|
||||
; 加载kernel, 把从10扇区 读取200个扇区的数据, 读到0x10000
|
||||
mov edi, 0x10000 ;
|
||||
mov ebx, 10
|
||||
mov cl, 200
|
||||
call read_disk
|
||||
|
||||
jmp dword code_selecter:0x10000
|
||||
ud2 ; 执行到这里直接出错(不可能执行到这里)
|
||||
jmp $
|
||||
|
||||
read_disk:
|
||||
push edx
|
||||
push eax
|
||||
|
||||
; ecx寄存器校验, 使其最大值为256
|
||||
and ecx,0b1111_1111
|
||||
|
||||
; 设置读取扇区的数量 0x1f2
|
||||
mov dx, 0x1f2
|
||||
mov al, cl ; cl:1, al:1
|
||||
out dx, al ; out 0x1f2, 1
|
||||
|
||||
mov eax, ebx ; eax: 0
|
||||
|
||||
; 起始扇区的 0~7位 设置 0x1f3
|
||||
inc dx
|
||||
out dx, al ; al: 0
|
||||
|
||||
; 起始扇区的 8~15位 设置 0x1f4
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 16~23位 设置 0x1f5
|
||||
inc dx
|
||||
shr eax, 8
|
||||
out dx, al ; al:0
|
||||
|
||||
; 起始扇区的 24~27位 设置 0x1f6
|
||||
inc dx
|
||||
shr eax, 8 ; al: 0
|
||||
|
||||
; 高4位设为0, 低四位保持不变
|
||||
and al, 0b0000_1111 ; al: 0000_0000
|
||||
|
||||
; 读取模式为 LBA模式
|
||||
or al, 0b1110_0000 ; al: 1110_0000
|
||||
out dx, al
|
||||
|
||||
; 从硬盘读 0x1f7
|
||||
inc dx
|
||||
mov al, 0x20 ; al: 0x20
|
||||
|
||||
out dx, al
|
||||
|
||||
|
||||
; 等待读取完毕
|
||||
call .waits
|
||||
; 读取到指定为止
|
||||
call .reads
|
||||
jmp .end
|
||||
|
||||
|
||||
.reads:
|
||||
push cx
|
||||
; 读取每个扇区的512字节, 每次读2字节读256次
|
||||
mov cx, 256
|
||||
mov dx, 0x1f0
|
||||
|
||||
.readw:
|
||||
in ax, dx
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
mov es:[edi], ax
|
||||
add edi, 2
|
||||
loop .readw
|
||||
pop cx
|
||||
; 如果读取了多个扇区 继续循环
|
||||
loop .reads
|
||||
ret
|
||||
|
||||
; 循环检查磁盘状态
|
||||
.waits:
|
||||
mov dx, 0x1f7
|
||||
in al,dx
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 如果 第三位是1, 说明准备好了
|
||||
and al, 0b1000_1000
|
||||
cmp al, 0b0000_1000
|
||||
jnz .waits
|
||||
ret
|
||||
|
||||
.end:
|
||||
pop eax
|
||||
pop edx
|
||||
ret
|
||||
|
||||
error:
|
||||
mov si, .error_msg
|
||||
call print
|
||||
hlt ; 让 CPU 停止
|
||||
jmp $
|
||||
ret
|
||||
.error_msg db "Loader Error!!!", 10, 13, 0
|
||||
|
||||
mem_task_count:
|
||||
dw 0
|
||||
|
||||
; 用来存放检测内存结果的结构体
|
||||
mem_task_buffer:
|
||||
times 20*10 db 0
|
||||
|
||||
|
||||
; 定义gdt
|
||||
base equ 0 ; 段地址
|
||||
limit equ 0xfffff ; 段界限数量, 和粒度搭配 如果粒度是4k, 那么 0xfffff*4096 最大的大小
|
||||
|
||||
; 段选择子
|
||||
code_selecter equ 1 << 3 ; 选择子 前13位 代表索引, 后面3位暂时不需要
|
||||
data_selecter equ 2 << 3 ;
|
||||
|
||||
; gdt表的指针
|
||||
gdt_ptr:
|
||||
dw gdt_end - gdt_start - 1 ; 16位表示gdt的总大小, 每个段描述符8字节, 2**16/8=8192刚好可以表示8292; (2的13次方个刚好是段选择子的最大索引)
|
||||
dd gdt_start ; gdt表起始位置
|
||||
|
||||
|
||||
; gdt 表的详情
|
||||
gdt_start:
|
||||
gdt_base:
|
||||
times 8 db 0 ; 第0个段描述符 不能被选择也不可使用和访问, 否则会cpu发出异常
|
||||
gdt_code:
|
||||
dw limit ; 段界限 dw 0~15
|
||||
dw base ; 段基址 dw 0~15
|
||||
db base >> 16 ; 段基址16~23
|
||||
db 0b_1_00_1_1010 ; P_DPL_S_TYPE(代码段, 非依从, 可读, 没有被cpu访问过)
|
||||
db 0b_1_1_0_0_0000 | limit >> 16 ; G_D/B_L_AVL | 段界限16~19
|
||||
db base >> 24 ; 段基址24~31
|
||||
gdt_data:
|
||||
dw limit ; 段界限 dw 0~15
|
||||
dw base ; 段基址 dw 0~15
|
||||
db base >> 16 ; 段基址16~23
|
||||
db 0b_1_00_1_0010 ; P_DPL_S_TYPE(0010 表示"数据段,数据可写, 数据向上拓展")
|
||||
db 0b_1_1_0_0_0000 | limit >> 16 ; G_D/B_L_AVL | 段界限16~19
|
||||
db base >> 24 ; 段基址24~31
|
||||
gdt_padding:
|
||||
times 2<<16-($-gdt_start) db 0
|
||||
gdt_end:
|
||||
|
||||
|
||||
|
||||
|
||||
loading_log:
|
||||
db 'Loader Start', 13, 10, 0
|
||||
|
||||
detect_memory_log:
|
||||
db 'detect_memory end', 13, 10, 0
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
#ifndef ONIX_ASSERT_H
|
||||
#define ONIX_ASSERT_H
|
||||
|
||||
void assertion_failure(char *exp, char *file, char *base, int line);
|
||||
|
||||
#define assert(exp) \
|
||||
if (exp) \
|
||||
; \
|
||||
else \
|
||||
assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)
|
||||
|
||||
void panic(const char *fmt, ...);
|
||||
|
||||
#endif
|
@ -0,0 +1,48 @@
|
||||
#ifndef ONIX_CONSOLE_H
|
||||
#define ONIX_CONSOLE_H
|
||||
|
||||
#include <onix/types.h>
|
||||
#include <onix/io.h>
|
||||
#include <onix/string.h>
|
||||
|
||||
|
||||
|
||||
#define CRT_ADDR_PORT 0x3d4 // crt位置寄存器 port
|
||||
#define CRT_DATA_PORT 0x3d5 // crt数据寄存器 port
|
||||
|
||||
#define CRT_CURSOR_H_VALUE 0xe // 当前光标所在字符距离0xb800 高位 value
|
||||
#define CRT_CURSOR_L_VALUE 0xf // 当前光标所在字符距离0xb800 低位 value
|
||||
|
||||
#define CRT_MEM_H_VALUE 0xC // 当前屏幕字符起始位置距离0xb800 - 高位
|
||||
#define CRT_MEM_L_VALUE 0xD // 当前屏幕显示字符起始位置距离0xb800 - 低位
|
||||
|
||||
#define CRT_MEM_START 0xB8000 // 显卡内存起始位置
|
||||
#define CRT_MEM_SIZE 0x4000 // 显卡内存大小
|
||||
#define CRT_MEM_END (CRT_MEM_START + CRT_MEM_SIZE) // 显卡内存结束位置
|
||||
|
||||
#define WIDTH 80 // 屏幕文本列数
|
||||
#define HEIGHT 25 // 屏幕文本行数
|
||||
|
||||
#define ROW_SIZE (WIDTH * 2) // 每行字节数
|
||||
#define SCR_SIZE (ROW_SIZE * HEIGHT) // 一个屏幕容纳的字节数
|
||||
|
||||
|
||||
// 特殊字符
|
||||
#define NUL 0x00
|
||||
#define ENQ 0x05
|
||||
#define ESC 0x1B // ESC
|
||||
#define BEL 0x07 // \a
|
||||
#define BS 0x08 // \b 退格键
|
||||
#define HT 0x09 // \t
|
||||
#define LF 0x0A // \n 换行
|
||||
#define VT 0x0B // \v
|
||||
#define FF 0x0C // \f
|
||||
#define CR 0x0D // \r 回到开头的位置
|
||||
#define DEL 0x7F // 删除当前位置的字符, 但是不退格
|
||||
|
||||
|
||||
console_init();
|
||||
console_clear();
|
||||
console_write(u8* buf, usize count);
|
||||
|
||||
#endif
|
@ -0,0 +1,11 @@
|
||||
#ifndef ONIX_DEBUG_H
|
||||
#define ONIX_DEBUG_H
|
||||
|
||||
void debugk(char *file, int line, const char *fmt, ...);
|
||||
|
||||
#define BMB asm volatile("xchgw %bx, %bx") // bochs magic breakpoint
|
||||
#define DEBUGK(fmt, args...) debugk(__BASE_FILE__, __LINE__, fmt, ##args)
|
||||
|
||||
// #define LOGK(fmt, args...) DEBUGK(fmt, ##args)
|
||||
|
||||
#endif
|
@ -0,0 +1,63 @@
|
||||
#ifndef ONIX_INTERRUPT_H
|
||||
#define ONIX_INTERRUPT_H
|
||||
|
||||
#include <onix/types.h>
|
||||
#include <onix/debug.h>
|
||||
|
||||
// 在实模式中0x000~0x3ff是中断向量表, 一共1024字节, 每个中断描述符占4个字节 段地址2个字节, 偏移地址2个字节 一共可以存放256个
|
||||
// 保护模式下, 一个中断描述符 gate_t占 8个字节, 除了函数的段内偏移地址, 还有一些属性
|
||||
|
||||
#define IDT_SIZE 256
|
||||
#define ENTRY_SIZE 0x30 // 我们实现的中断数量
|
||||
#define LOGK(fmt, args...) DEBUGK(fmt, ##args)
|
||||
|
||||
#define PIC_M_CTRL 0x20 // 主片的控制端口
|
||||
#define PIC_M_DATA 0x21 // 主片的数据端口
|
||||
#define PIC_S_CTRL 0xa0 // 从片的控制端口
|
||||
#define PIC_S_DATA 0xa1 // 从片的数据端口
|
||||
#define PIC_EOI 0x20 // 通知中断控制器中断结束
|
||||
|
||||
|
||||
typedef struct gate_t{
|
||||
u16 offset0; // 段内偏移 0 ~ 15 位
|
||||
u16 selector; // 代码段选择子
|
||||
u8 reserved; // 保留不用
|
||||
u8 type : 4; // 任务门/中断门/陷阱门
|
||||
u8 segment : 1; // segment = 0 表示系统段
|
||||
u8 DPL : 2; // 使用 int 指令访问的最低权限
|
||||
u8 present : 1; // 是否有效
|
||||
u16 offset1; // 段内偏移 16 ~ 31 位
|
||||
} _packed gate_t;
|
||||
|
||||
|
||||
// 初始化保护模式的中断向量表
|
||||
void interrupt_init();
|
||||
|
||||
|
||||
static char *messages[] = {
|
||||
"#DE Divide Error\0",
|
||||
"#DB RESERVED\0",
|
||||
"-- NMI Interrupt\0",
|
||||
"#BP Breakpoint\0",
|
||||
"#OF Overflow\0",
|
||||
"#BR BOUND Range Exceeded\0",
|
||||
"#UD Invalid Opcode (Undefined Opcode)\0",
|
||||
"#NM Device Not Available (No Math Coprocessor)\0",
|
||||
"#DF Double Fault\0",
|
||||
" Coprocessor Segment Overrun (reserved)\0",
|
||||
"#TS Invalid TSS\0",
|
||||
"#NP Segment Not Present\0",
|
||||
"#SS Stack-Segment Fault\0",
|
||||
"#GP General Protection\0",
|
||||
"#PF Page Fault\0",
|
||||
"-- (Intel reserved. Do not use.)\0",
|
||||
"#MF x87 FPU Floating-Point Error (Math Fault)\0",
|
||||
"#AC Alignment Check\0",
|
||||
"#MC Machine Check\0",
|
||||
"#XF SIMD Floating-Point Exception\0",
|
||||
"#VE Virtualization Exception\0",
|
||||
"#CP Control Protection Exception\0",
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,51 @@
|
||||
#ifndef ONIX_IO_H
|
||||
#define ONIX_IO_H
|
||||
|
||||
#include <onix/types.h>
|
||||
|
||||
// 这个文件只是定义了头文件, 真正实现的是 io.asm
|
||||
|
||||
extern u8 in_8(u16 port); // 从端口读出一个字节
|
||||
extern u16 in_16(u16 port); // 从端口读出一个字
|
||||
|
||||
|
||||
extern void out_8(u16 port, u8 value); // 写入到 端口内 一个字节
|
||||
extern void out_16(u16 port, u8 value); // 写入到 端口内 一个字
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// #define CRT_ADDR_PORT 0x3d4 // 位置寄存器 port
|
||||
// #define CRT_DATA_PORT 0x3d5 // 数据寄存器 port
|
||||
|
||||
// #define CRT_CURSOR_H_VALUE 0xe // 光标高位 value
|
||||
// #define CRT_CURSOR_L_VALUE 0xf // 光标低位 value
|
||||
// {
|
||||
// // 读取光标位置的 高8位
|
||||
// out_8(CRT_ADDR_PORT, CRT_CURSOR_H_VALUE);
|
||||
// u8 pos_h = in_8(CRT_DATA_PORT);
|
||||
|
||||
// // 读取光标位置的 低8位
|
||||
// out_8(CRT_ADDR_PORT, CRT_CURSOR_L_VALUE);
|
||||
// u8 pos_l = in_8(CRT_DATA_PORT);
|
||||
|
||||
// // 获得光标位置
|
||||
// u16 pos = (pos_h << 8) | pos_l;
|
||||
// }
|
||||
|
||||
|
||||
// {
|
||||
// // 设置光标位置到666处
|
||||
// u16 pos = 444;
|
||||
|
||||
// // 设置高地址
|
||||
// out_8(CRT_ADDR_PORT, CRT_CURSOR_H_VALUE);
|
||||
// u8 pos_h = pos >> 8;
|
||||
// out_8(CRT_DATA_PORT, pos_h);
|
||||
|
||||
// // 设置低地址
|
||||
// u8 pos_l = pos & 0xff;
|
||||
// out_8(CRT_ADDR_PORT, CRT_CURSOR_L_VALUE);
|
||||
// out_8(CRT_DATA_PORT, pos_l);
|
||||
// }
|
@ -0,0 +1,18 @@
|
||||
#ifndef ONIX_H
|
||||
#define ONIX_H
|
||||
#include <onix/types.h>
|
||||
#include <onix/io.h>
|
||||
#include <onix/string.h>
|
||||
#include <onix/console.h>
|
||||
#include <onix/assert.h>
|
||||
#include <onix/printk.h>
|
||||
#include <onix/debug.h>
|
||||
#include <onix/global.h>
|
||||
#include <onix/task.h>>
|
||||
#include <onix/interrupt.h>>
|
||||
#include <onix/stdlib.h>>
|
||||
|
||||
|
||||
void kernel_init();
|
||||
|
||||
#endif
|
@ -0,0 +1,7 @@
|
||||
#ifndef ONIX_PRINTK_H
|
||||
#define ONIX_PRINTK_H
|
||||
#include <onix/types.h>
|
||||
|
||||
u32 printk(const u8 *fmt, ...);
|
||||
|
||||
#endif
|
@ -0,0 +1,10 @@
|
||||
#ifndef ONIX_STDARG_H
|
||||
#define ONIX_STDARG_H
|
||||
|
||||
typedef char *va_list;
|
||||
|
||||
#define va_start(ap, v) (ap = (va_list)&v + sizeof(char *))
|
||||
#define va_arg(ap, t) (*(t *)((ap += sizeof(char *)) - sizeof(char *)))
|
||||
#define va_end(ap) (ap = (va_list)0)
|
||||
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
#ifndef ONIX_STDLIB_H
|
||||
#define ONIX_STDLIB_H
|
||||
#include <onix/types.h>
|
||||
void delay (u32 count);
|
||||
void hang();
|
||||
#endif
|
@ -0,0 +1,37 @@
|
||||
#ifndef ONIX_STRING_H
|
||||
#define ONIX_STRING_H
|
||||
|
||||
#include <onix/types.h>
|
||||
|
||||
// 字符串 src copy 到 dest
|
||||
strcpy(u8* dest, const u8* src);
|
||||
// 字符串 src copy count 个字符 到 dest
|
||||
strncpy(u8* dest, const u8* src, usize count);
|
||||
// 字符串 dest += src
|
||||
strcat(u8* dest, const u8* src);
|
||||
// 统计字符串长度
|
||||
usize strlen(const u8* str);
|
||||
// 比较字符的大小, 返回值 -1/0/1 ("acd", "abd") 返回 -1, 因为 acd < abd
|
||||
i8 strcmp(const u8* lhs, const u8* rhs);
|
||||
// 左边第一个 指定字符串中找都指定字符所在的指针地址, 如果没有找到, 返回 nullptr
|
||||
u8* strchr_l(const u8* str, u8 ch);
|
||||
// 右边第一个 指定字符串中找都指定字符所在的指针地址, 如果没有找到, 返回 nullptr
|
||||
u8* strchr_r(const u8* str, u8 ch);
|
||||
// u8* strsep(const u8* str);
|
||||
// u8* strrsep(const u8* str);
|
||||
|
||||
// 比较指定字节内存
|
||||
i8 memcmp(const u8* lhs, const u8* rhs, usize count);
|
||||
// 将指定区域赋值为 ch
|
||||
memset(u8* src, u8 ch, usize count);
|
||||
// 从src copy指定字节的数据 到dest
|
||||
memcpy(u8* dest, const u8* src, usize count);
|
||||
// 从内存总找到, 第一个 ch字符 从左到右
|
||||
u8* memchr(const u8* src, u8 ch, usize count);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
#ifndef ONIX_TASK_H
|
||||
#define ONIX_TASK_H
|
||||
|
||||
#include <onix/types.h>
|
||||
|
||||
#define PAGE_SIZE 0x1000 // 每页的内存大小 4k
|
||||
|
||||
typedef u32 target_t(); // 函数的入口地址
|
||||
|
||||
|
||||
typedef struct task_t{
|
||||
u8* stack; // 内核线程的栈顶
|
||||
} task_t;
|
||||
|
||||
// ABI 需要保存的寄存器(字段顺序 不要变!)
|
||||
typedef struct task_frame_t{
|
||||
u32 edi;
|
||||
u32 esi;
|
||||
u32 ebx;
|
||||
u32 ebp;
|
||||
void (*eip)(void); // 一个函数指针
|
||||
} task_frame_t;
|
||||
|
||||
// 初始化任务的函数
|
||||
void task_init();
|
||||
|
||||
#endif
|
@ -0,0 +1,36 @@
|
||||
#ifndef ONIX_TYPES_H
|
||||
#define ONIX_TYPES_H
|
||||
|
||||
typedef char i8;
|
||||
typedef unsigned char u8;
|
||||
typedef short i16;
|
||||
typedef unsigned short u16;
|
||||
typedef int i32;
|
||||
typedef unsigned int u32;
|
||||
typedef long long i64;
|
||||
typedef unsigned long long u64;
|
||||
typedef i32 isize;
|
||||
typedef u32 usize;
|
||||
|
||||
|
||||
#define bool _Bool
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
#define EOF -1
|
||||
#define EOS '\0'
|
||||
|
||||
#define nullptr ((void*) 0)
|
||||
|
||||
#define _packed __attribute__((packed)) // 用于定义特殊的结构体, 使用该属性可以使得变量或者结构体成员使用最小的对齐方式
|
||||
// 即对变量是一字节对齐, 对域(field)是位对齐
|
||||
|
||||
// 用于省略函数的栈帧
|
||||
#define _ofp __attribute__((optimize("omit-frame-pointer")))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,9 @@
|
||||
#ifndef ONIX_VPRINTF_H
|
||||
#define ONIX_VPRINTF_H
|
||||
|
||||
#include <onix/stdarg.h>
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args);
|
||||
int sprintf(char *buf, const char *fmt, ...);
|
||||
|
||||
#endif
|
@ -0,0 +1,16 @@
|
||||
#include <onix/debug.h>
|
||||
#include <onix/stdarg.h>
|
||||
#include <onix/vprintf.h>
|
||||
#include <onix/printk.h>
|
||||
|
||||
static char buf[1024];
|
||||
|
||||
void debugk(char *file, int line, const char *fmt, ...){
|
||||
// 先格式化自定义的一些文本信息
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
printk("[%s] [%d] %s \n", file, line, buf);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
#include <onix/debug.h>
|
||||
#include <onix/string.h>
|
||||
#include <onix/global.h>
|
||||
|
||||
|
||||
descriptor_t gdt[GDT_SIZE]; // 内核全部的全局描述符表
|
||||
pointer_t gdt_ptr; // 内核全局描述符表指针
|
||||
|
||||
// 初始化全局描述符表 为我们 c语言 的数组
|
||||
// 把loader中的全局描述符表 copy 到内核中
|
||||
void gdt_init(){
|
||||
// 把loader内的的全局描述符表指针加载出来
|
||||
asm volatile("sgdt gdt_ptr");
|
||||
// 把全部的全局描述符表 copy出来
|
||||
memcpy(&gdt, (u8*)gdt_ptr.base, gdt_ptr.limit + 1);
|
||||
|
||||
//
|
||||
gdt_ptr.base = (usize)&gdt;
|
||||
gdt_ptr.limit = sizeof(gdt) - 1;
|
||||
|
||||
// 把内核的gdt表加载进去
|
||||
asm volatile("lgdt gdt_ptr");
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
[bits 32]
|
||||
; 这里调用关系复杂, 解释一下, INTERRUPT_HANDLER这个宏生成和handler_entry_table表里对应的函数, handler_entry_table表里存放呃是宏生成的每个函数的位置的指针
|
||||
; 然后interrupt.c语言初始化中断向量表的时候, 把handler_entry_table里面函数的指针, 加载到idt里面,
|
||||
; 在异常或者中断发生的时候, 有的中断会push进去一个eflag 有的不会, 这里 通过宏整理判断 没有push的都会push进去一点东西, 保证参数数量 统一一点
|
||||
; 然后跳入interrupt_entry中执行, 根据中断号, 去调用interrupt.c定义的 handler_table, 然后iret
|
||||
|
||||
; 导入c语言 整理过的中断向量表
|
||||
; c语言整理的其实也是下面的 handler_entry_table
|
||||
extern handler_table
|
||||
|
||||
; 中断发生时栈的布局为: 低地址(栈顶) %1(中断向量号) gp错误码(如果有) ip cs eflags 低高地址(栈底)
|
||||
section .text
|
||||
|
||||
; ## gp错误码
|
||||
; !!注意, 只有硬中断才会压入gp错误码, 而我们手动调用int 不会触发, (ps: int 一个我们没有定义中断处理函数的中断, 会调用0x0d,由我们软中断->硬中断)
|
||||
; 这个宏主要是为 没有gp错误码的异常, 随便压入一个参数到栈中为了保持中断时的一致,后面容易进行弹栈操作, 然后push到终端处理函数的入口
|
||||
; gp错误码的格式 0b0000000000000_01_0, 第1位是判断有内部还是由外部触发, 第2~3位表示是 IDT(01或者11) 还是GDT(00)还是LDT(10) 4~16位表示选择子索引
|
||||
|
||||
; int 0x80 的异常码 就是 0x00000402 换成2进制就是 10000000_01_0, 其中二进制的10000000 换算16进制就是0x80,
|
||||
; 由于0x80 我们没有中断处理函数, 手动软中断出发之后, 又会立即硬中断所以会又触发0x0d一般性保护异常, 把异常压入栈
|
||||
; 在80286中 gp错误码是16个bit, 现在为了兼容 扩充到了32位
|
||||
|
||||
; ## eflag ; sti打开中断这个指令会把 if位 置为1,
|
||||
; 如果使用sti指令 后压入栈的 eflags就是 0x212, 如果关闭中断则压入栈的eflags就变成了0x012, 0x212或者0x012 这俩数, 他们的bit位的第九位存在差别
|
||||
; 此时压入栈的是0x212, 而eflag寄存器则是0x12, 因为我们初始化中断描述符的时候, 设置的是中断门 所以第九位被置为了0保存在了eflag寄存器中
|
||||
|
||||
; 时钟中断时 0x20 这个也没有错误码
|
||||
%macro INTERRUPT_HANDLER 2
|
||||
interrupt_handler_%1:
|
||||
xchg bx, bx ; 这时候观察栈, 如果有状态码
|
||||
%ifn %2
|
||||
push 0x20222202 ; 这个是给没有错误码的调用添加一个自己定义的魔数
|
||||
%endif
|
||||
push %1; 压入宏的中断向量,跳转到中断入口
|
||||
jmp interrupt_entry
|
||||
%endmacro
|
||||
|
||||
|
||||
interrupt_entry:
|
||||
mov eax, [esp]
|
||||
; 调用中断处理函数 handler_table 中存储了中断处理函数的指针, 并把 上方push %1 中断号传入中断处理函数的指针(所以这里 *4)
|
||||
call [handler_table + eax * 4]
|
||||
; 弹栈, 弹出 %1, 弹出0x20222202, 后面调用iret
|
||||
|
||||
add esp, 8
|
||||
iret
|
||||
|
||||
; 下面的功能, 主要是用 用宏生成代码
|
||||
; 异常处理
|
||||
INTERRUPT_HANDLER 0x00, 0; divide by zero
|
||||
INTERRUPT_HANDLER 0x01, 0; debug
|
||||
INTERRUPT_HANDLER 0x02, 0; non maskable interrupt
|
||||
INTERRUPT_HANDLER 0x03, 0; breakpoint
|
||||
|
||||
INTERRUPT_HANDLER 0x04, 0; overflow
|
||||
INTERRUPT_HANDLER 0x05, 0; bound range exceeded
|
||||
INTERRUPT_HANDLER 0x06, 0; invalid opcode
|
||||
INTERRUPT_HANDLER 0x07, 0; device not avilable
|
||||
|
||||
INTERRUPT_HANDLER 0x08, 1; double fault
|
||||
INTERRUPT_HANDLER 0x09, 0; coprocessor segment overrun
|
||||
INTERRUPT_HANDLER 0x0a, 1; invalid TSS
|
||||
INTERRUPT_HANDLER 0x0b, 1; segment not present
|
||||
|
||||
INTERRUPT_HANDLER 0x0c, 1; stack segment fault
|
||||
INTERRUPT_HANDLER 0x0d, 1; general protection fault
|
||||
INTERRUPT_HANDLER 0x0e, 1; page fault
|
||||
INTERRUPT_HANDLER 0x0f, 0; reserved
|
||||
|
||||
INTERRUPT_HANDLER 0x10, 0; x87 floating point exception
|
||||
INTERRUPT_HANDLER 0x11, 1; alignment check
|
||||
INTERRUPT_HANDLER 0x12, 0; machine check
|
||||
INTERRUPT_HANDLER 0x13, 0; SIMD Floating - Point Exception
|
||||
|
||||
INTERRUPT_HANDLER 0x14, 0; Virtualization Exception
|
||||
INTERRUPT_HANDLER 0x15, 1; Control Protection Exception
|
||||
INTERRUPT_HANDLER 0x16, 0; reserved
|
||||
INTERRUPT_HANDLER 0x17, 0; reserved
|
||||
|
||||
INTERRUPT_HANDLER 0x18, 0; reserved
|
||||
INTERRUPT_HANDLER 0x19, 0; reserved
|
||||
INTERRUPT_HANDLER 0x1a, 0; reserved
|
||||
INTERRUPT_HANDLER 0x1b, 0; reserved
|
||||
|
||||
INTERRUPT_HANDLER 0x1c, 0; reserved
|
||||
INTERRUPT_HANDLER 0x1d, 0; reserved
|
||||
INTERRUPT_HANDLER 0x1e, 0; reserved
|
||||
INTERRUPT_HANDLER 0x1f, 0; reserved
|
||||
|
||||
; 外中断
|
||||
INTERRUPT_HANDLER 0x20, 0; clock 时钟中断
|
||||
INTERRUPT_HANDLER 0x21, 0; keyboard 键盘中断
|
||||
INTERRUPT_HANDLER 0x22, 0
|
||||
INTERRUPT_HANDLER 0x23, 0; com2 串口2
|
||||
INTERRUPT_HANDLER 0x24, 0; com1 串口1
|
||||
INTERRUPT_HANDLER 0x25, 0
|
||||
INTERRUPT_HANDLER 0x26, 0
|
||||
INTERRUPT_HANDLER 0x27, 0
|
||||
INTERRUPT_HANDLER 0x28, 0; rtc 实时时钟
|
||||
INTERRUPT_HANDLER 0x29, 0
|
||||
INTERRUPT_HANDLER 0x2a, 0
|
||||
INTERRUPT_HANDLER 0x2b, 0
|
||||
INTERRUPT_HANDLER 0x2c, 0
|
||||
INTERRUPT_HANDLER 0x2d, 0
|
||||
INTERRUPT_HANDLER 0x2e, 0; harddisk1 硬盘主通道
|
||||
INTERRUPT_HANDLER 0x2f, 0; harddisk2 硬盘从通道
|
||||
|
||||
|
||||
; 下面的数组记录了每个中断入口函数的指针, 每个4个字节 dd
|
||||
; 这里每个指针就是 INTERRUPT_HANDLER 宏生成的 函数的入口地址
|
||||
; 后面还需要再导出handler_entry_table 到c语言
|
||||
section .data
|
||||
global handler_entry_table
|
||||
handler_entry_table:
|
||||
dd interrupt_handler_0x00
|
||||
dd interrupt_handler_0x01
|
||||
dd interrupt_handler_0x02
|
||||
dd interrupt_handler_0x03
|
||||
dd interrupt_handler_0x04
|
||||
dd interrupt_handler_0x05
|
||||
dd interrupt_handler_0x06
|
||||
dd interrupt_handler_0x07
|
||||
dd interrupt_handler_0x08
|
||||
dd interrupt_handler_0x09
|
||||
dd interrupt_handler_0x0a
|
||||
dd interrupt_handler_0x0b
|
||||
dd interrupt_handler_0x0c
|
||||
dd interrupt_handler_0x0d
|
||||
dd interrupt_handler_0x0e
|
||||
dd interrupt_handler_0x0f
|
||||
dd interrupt_handler_0x10
|
||||
dd interrupt_handler_0x11
|
||||
dd interrupt_handler_0x12
|
||||
dd interrupt_handler_0x13
|
||||
dd interrupt_handler_0x14
|
||||
dd interrupt_handler_0x15
|
||||
dd interrupt_handler_0x16
|
||||
dd interrupt_handler_0x17
|
||||
dd interrupt_handler_0x18
|
||||
dd interrupt_handler_0x19
|
||||
dd interrupt_handler_0x1a
|
||||
dd interrupt_handler_0x1b
|
||||
dd interrupt_handler_0x1c
|
||||
dd interrupt_handler_0x1d
|
||||
dd interrupt_handler_0x1e
|
||||
dd interrupt_handler_0x1f
|
||||
|
||||
; 外中断
|
||||
dd interrupt_handler_0x20
|
||||
dd interrupt_handler_0x21
|
||||
dd interrupt_handler_0x22
|
||||
dd interrupt_handler_0x23
|
||||
dd interrupt_handler_0x24
|
||||
dd interrupt_handler_0x25
|
||||
dd interrupt_handler_0x26
|
||||
dd interrupt_handler_0x27
|
||||
dd interrupt_handler_0x28
|
||||
dd interrupt_handler_0x29
|
||||
dd interrupt_handler_0x2a
|
||||
dd interrupt_handler_0x2b
|
||||
dd interrupt_handler_0x2c
|
||||
dd interrupt_handler_0x2d
|
||||
dd interrupt_handler_0x2e
|
||||
dd interrupt_handler_0x2f
|
||||
|
||||
|
||||
; [bits 32]
|
||||
; ; 中断处理函数入口
|
||||
|
||||
; section .text
|
||||
|
||||
; extern printk ; 导入一个外部符号进来
|
||||
|
||||
; global interrupt_handler ; 导出一个符号
|
||||
|
||||
; interrupt_handler:
|
||||
|
||||
; ; 传入参数
|
||||
; push message
|
||||
; call printk
|
||||
|
||||
; ; 恢复上面的 push message之前的栈
|
||||
; add esp, 4
|
||||
|
||||
; iret
|
||||
|
||||
|
||||
; section .data
|
||||
; message:
|
||||
; db "default interrupt", 10, 13, 0
|
@ -0,0 +1,107 @@
|
||||
#include <onix/interrupt.h>
|
||||
#include <onix/onix.h>
|
||||
#include <onix/global.h> // // 全局描述符表 指针
|
||||
|
||||
// 全局中断向量表 一共IDT_SIZE个中断符号
|
||||
gate_t idt[IDT_SIZE];
|
||||
pointer_t idt_ptr;
|
||||
|
||||
// 这个会被handle.asm导入, 里面放的是我们定义的中断处理函数
|
||||
u8* handler_table[IDT_SIZE];
|
||||
|
||||
// 从handle.asm 导入一共 ENTRY_SIZE个 存放了统一的中断描述符处理函数的地址
|
||||
// 为了让我们从c语言加载 idt表, 中断时 会先进入这个指针所在函数内, 然后继续跳入到 handler_table中
|
||||
extern handler_entry_table[ENTRY_SIZE];
|
||||
|
||||
// 异常 中断处理函数默认函数
|
||||
void exception_handle(u8 handle_num){
|
||||
char *msg;
|
||||
if (handle_num < 22) {
|
||||
msg = messages[handle_num];
|
||||
} else{
|
||||
msg = messages[15];
|
||||
}
|
||||
printk("Exception as [0x%02X] %s\n", handle_num, msg);
|
||||
hang();
|
||||
}
|
||||
|
||||
// 通知中断控制器,中断处理结束
|
||||
void send_eoi(u8 handle_num)
|
||||
{
|
||||
if (handle_num >= 0x20 && handle_num < 0x28)
|
||||
{
|
||||
out_8(PIC_M_CTRL, PIC_EOI);
|
||||
}
|
||||
if (handle_num >= 0x28 && handle_num < 0x30)
|
||||
{
|
||||
out_8(PIC_M_CTRL, PIC_EOI);
|
||||
out_8(PIC_S_CTRL, PIC_EOI);
|
||||
}
|
||||
}
|
||||
|
||||
u32 counter = 0;
|
||||
// 外中断 中断处理函数默认函数
|
||||
void default_handler(u8 handle_num)
|
||||
{
|
||||
send_eoi(handle_num);
|
||||
counter += 1;
|
||||
DEBUGK("[%x] default interrupt called... %d", handle_num, counter);
|
||||
}
|
||||
|
||||
|
||||
void idt_init(){
|
||||
for (usize i = 0; i < ENTRY_SIZE; i++){
|
||||
// 得到中断处理函数
|
||||
void* handle = handler_entry_table[i];
|
||||
|
||||
idt[i].offset0 = (usize)handle;
|
||||
idt[i].offset1 = ((usize)handle) >> 16;
|
||||
idt[i].selector = CODE_SELECT; // 代码段
|
||||
idt[i].reserved = 0; // 保留不用
|
||||
idt[i].type = 0b1110; // 任务门, 中断门, 陷阱门, 这里使用中断门, 他比陷阱门多了一个eflag, 任务门最复杂性能也不咋地
|
||||
idt[i].segment = 0; // 系统级别的中断
|
||||
idt[i].DPL = 0; // 内核态权限才能调用
|
||||
idt[i].present = 1; // 在内存中是有效的
|
||||
}
|
||||
|
||||
// 加载idt表
|
||||
idt_ptr.base = (usize)idt;
|
||||
idt_ptr.limit = sizeof(idt)-1;
|
||||
asm volatile("lidt idt_ptr\n");
|
||||
|
||||
// 修改 handler_table, 使其调用int中断执行handle.asm统一处理函数之后, 执行我们自己的这里的c处理程序
|
||||
// 异常处理函数设置
|
||||
for (usize i = 0; i < 0x20; i++){
|
||||
handler_table[i] = exception_handle;
|
||||
}
|
||||
|
||||
// 外中断处理函数设置
|
||||
for (usize i = 0x20; i < ENTRY_SIZE; i++){
|
||||
handler_table[i] = default_handler;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化中断控制器
|
||||
void pic_init(){
|
||||
out_8(PIC_M_CTRL, 0b00010001); // ICW1: 边沿触发, 级联 8259, 需要ICW4.
|
||||
out_8(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号 0x20
|
||||
out_8(PIC_M_DATA, 0b00000100); // ICW3: IR2接从片.
|
||||
out_8(PIC_M_DATA, 0b00000001); // ICW4: 8086模式, 正常EOI
|
||||
|
||||
out_8(PIC_S_CTRL, 0b00010001); // ICW1: 边沿触发, 级联 8259, 需要ICW4.
|
||||
out_8(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号 0x28
|
||||
out_8(PIC_S_DATA, 2); // ICW3: 设置从片连接到主片的 IR2 引脚
|
||||
out_8(PIC_S_DATA, 0b00000001); // ICW4: 8086模式, 正常EOI
|
||||
|
||||
out_8(PIC_M_DATA, 0b11111110); // 只打开主片的第0个中断也就是 的主片的0x20时钟中断
|
||||
out_8(PIC_S_DATA, 0b11111111); // 关闭从片所有pic中断
|
||||
}
|
||||
|
||||
|
||||
// 初始化保护模式的中断向量表
|
||||
void interrupt_init(){
|
||||
pic_init();
|
||||
idt_init();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
[bits 32]
|
||||
|
||||
|
||||
|
||||
section .text ; 代码段
|
||||
|
||||
global in_8, in_16, out_8 ; 将符号公开导入, 外部可以使用
|
||||
|
||||
in_8:
|
||||
; 栈帧保存
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
|
||||
xor eax, eax
|
||||
mov edx, [ebp + 8] ; +0 是本身ebp的值, +4 是call in_8所在代码的下一行, +8 是第一个参数port
|
||||
|
||||
in al, dx ; 将 port 的数据 读取1个字节 (8个比特位 al) 到al, 根据调用约定, eax作为返回值
|
||||
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 恢复栈帧
|
||||
leave
|
||||
ret
|
||||
|
||||
|
||||
in_16:
|
||||
; 栈帧保存
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
|
||||
xor eax, eax
|
||||
mov edx, [ebp + 8]
|
||||
|
||||
in ax, dx
|
||||
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 恢复栈帧
|
||||
leave
|
||||
ret
|
||||
|
||||
|
||||
|
||||
out_8:
|
||||
; 栈帧保存
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
|
||||
mov edx, [ebp + 8] ; port
|
||||
mov eax, [ebp + 12] ; value
|
||||
|
||||
out dx, al ; 将 value 的8比特写入到 port
|
||||
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 恢复栈帧
|
||||
leave
|
||||
ret
|
||||
|
||||
out_16:
|
||||
; 栈帧保存
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
|
||||
mov edx, [ebp + 8] ; port
|
||||
mov eax, [ebp + 12] ; value
|
||||
|
||||
out dx, ax ; 将 value 的 16比特写入到 port
|
||||
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
jmp $+2
|
||||
|
||||
; 恢复栈帧
|
||||
leave
|
||||
ret
|
@ -0,0 +1,34 @@
|
||||
|
||||
#include <onix/onix.h>
|
||||
|
||||
|
||||
|
||||
|
||||
void kernel_init(){
|
||||
// 初始化控制台
|
||||
console_init();
|
||||
|
||||
// 初始化全局描述符
|
||||
gdt_init();
|
||||
|
||||
// 初始化任务
|
||||
// task_init();
|
||||
|
||||
// 初始化中断
|
||||
interrupt_init();
|
||||
|
||||
asm volatile(
|
||||
"sti\n" // 打开cpu的中断中断
|
||||
"movl %eax, %eax\n"
|
||||
);
|
||||
|
||||
// u32 counter = 0;
|
||||
// while(true){
|
||||
// counter += 1;
|
||||
// DEBUGK("looping in kernel init %d", counter);
|
||||
// delay(10000000);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
|
||||
#include <onix/console.h>
|
||||
#include <onix/stdarg.h>
|
||||
#include <onix/vprintf.h>
|
||||
|
||||
static u8* buf[1024];
|
||||
|
||||
u32 printk(const u8 *fmt, ...){
|
||||
va_list args;
|
||||
u32 i;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
i = vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
console_write(buf, i);
|
||||
return i;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
[bits 32]
|
||||
|
||||
global task_switch
|
||||
|
||||
task_switch:
|
||||
; 保存栈帧
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
; 调用ABI约定, 保存寄存器
|
||||
push ebx
|
||||
push esi
|
||||
push edi
|
||||
|
||||
mov eax, esp ;先得到当前调用栈
|
||||
and eax, 0xfffff000 ; 当前任务的最开始的地方, 也就是线程栈的开始这里保存了4字节的当前线程的栈顶指针
|
||||
|
||||
; 保存当前调用栈的的 esp 栈顶 到 task_t->stack 也就是0x1000 或者0x2000处的地方
|
||||
mov [eax], esp
|
||||
|
||||
; 得到传进来的参数 next, ebp保存的是current在进入函数之后最开始的栈顶, ebp+0保存的本来的ebp, ebp+4保存的call task_switch之后下一行地址
|
||||
; ebp +8 得到是传进来的参数 即 next线程栈的最开始的地址(把栈顶,0x1000或者0x2000保存的4字节栈顶 传入到 esp中)
|
||||
mov eax, [ebp + 8]
|
||||
|
||||
; next的 的栈顶指针
|
||||
mov esp, [eax]
|
||||
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebx
|
||||
pop ebp
|
||||
|
||||
; 此时 栈顶是 call task_switch 的下一行代码的位置, ret即可
|
||||
ret
|
@ -0,0 +1,19 @@
|
||||
[bits 32]
|
||||
|
||||
; 导入外部符号
|
||||
extern kernel_init
|
||||
|
||||
; 导出符号
|
||||
global _start
|
||||
|
||||
|
||||
_start:
|
||||
call kernel_init
|
||||
; int 0x0d
|
||||
; mov bx, 0
|
||||
; div bx
|
||||
|
||||
jmp $
|
||||
|
||||
|
||||
|
@ -0,0 +1,98 @@
|
||||
#include <onix/task.h>
|
||||
#include <onix/printk.h>
|
||||
|
||||
extern void task_switch(task_t* next);
|
||||
|
||||
task_t* task_a_stack = (task_t*) (PAGE_SIZE * 1); // A线程栈开始的位置
|
||||
task_t* task_b_stack = (task_t*) (PAGE_SIZE * 2); // B线程栈开始的位置
|
||||
|
||||
|
||||
|
||||
static void task_create(task_t* task_struct, usize target_handle){
|
||||
usize stack = (usize)task_struct + PAGE_SIZE; // 得到线程栈的栈底
|
||||
stack -= sizeof(task_frame_t); // 线程本身的信息留出来空间
|
||||
task_frame_t* frame = (task_frame_t*) stack; // 上面留出来的空间, 创建frame
|
||||
|
||||
frame->edi = 0x11111111;
|
||||
frame->esi = 0x22222222;
|
||||
frame->ebx = 0x33333333;
|
||||
frame->ebp = 0x44444444;
|
||||
frame->eip = (u8*) target_handle; // ip指向指定的 函数
|
||||
|
||||
task_struct->stack = (u8*)stack; // 把减去了 frame大小的栈的栈底, 赋值给 task结构 的首地址, 后面会被 schdule 间接取址
|
||||
int a = 123;
|
||||
}
|
||||
|
||||
task_t* running_task(){
|
||||
asm volatile(
|
||||
"movl %esp, %eax\n" // 当前 栈顶
|
||||
"andl $0xfffff000, %eax\n" // 得到栈底; 得到当前线程开始的位置, 即task_create中创建的 frame,
|
||||
// 由于我们每个任务 占用 1个页, 1个页的大小是0x1000, 所以我们每个线程开始的地方
|
||||
// 刚好是 0x1000的整倍数, 直接把低3位置位0 即可得到每个任务开始的地方也就是保存了task_frame_t和后续栈的地方
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void schdule(){
|
||||
task_t* current = running_task();
|
||||
// 这里实验的, 如果当前是任务a 就切换到任务b, 如果是任务b 就切换到任务a
|
||||
task_t* next;
|
||||
if (current == task_a_stack) {
|
||||
next = task_b_stack;
|
||||
} else {
|
||||
next = task_a_stack;
|
||||
}
|
||||
task_switch(next); // 调用汇编实现的切换函数
|
||||
}
|
||||
|
||||
|
||||
void thread_a(){
|
||||
while(true){
|
||||
printk("A");
|
||||
schdule();
|
||||
}
|
||||
}
|
||||
void thread_b(){
|
||||
while(true){
|
||||
printk("B");
|
||||
schdule();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void task_init(){
|
||||
// test();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void test(){
|
||||
task_create(task_a_stack, thread_a);
|
||||
task_create(task_b_stack, thread_b);
|
||||
schdule();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
线程栈的内存分布
|
||||
|
||||
|
||||
|
||||
| | | |
|
||||
| ------ | ------ | ------------ |
|
||||
| eip | 0x1fff | Function_ptr |
|
||||
| ebp | | 0x44444444 |
|
||||
| ebx | | 0x33333333 |
|
||||
| esi | | 0x22222222 |
|
||||
| edi | | 0x11111111 |
|
||||
| 栈底 | 0x1f00 | |
|
||||
| | | ... |
|
||||
| | | ... |
|
||||
| 栈顶 | 0x1100 | |
|
||||
| | | |
|
||||
| ... | | |
|
||||
| 保存栈顶 | 0x1000 | 0x1100 |
|
||||
|
||||
*/
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
#include <onix/assert.h>
|
||||
#include <onix/stdarg.h>
|
||||
#include <onix/types.h>
|
||||
#include <onix/printk.h>
|
||||
#include <onix/vprintf.h>
|
||||
|
||||
static u8 buf[1024];
|
||||
|
||||
// 强制阻塞
|
||||
static void spin(char *name)
|
||||
{
|
||||
printk("spinning in %s ...\n", name);
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
|
||||
void assertion_failure(char *exp, char *file, char *base, int line)
|
||||
{
|
||||
printk(
|
||||
"\n--> assert(%s) failed!!!\n"
|
||||
"--> file: %s \n"
|
||||
"--> base: %s \n"
|
||||
"--> line: %d \n",
|
||||
exp, file, base, line);
|
||||
|
||||
spin("assertion_failure()");
|
||||
|
||||
// 不可能走到这里,否则出错;
|
||||
asm volatile("ud2");
|
||||
}
|
||||
|
||||
void panic(const char *fmt, ...)
|
||||
{
|
||||
// 先格式化用户的自定义的输出信息
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int i = vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
printk("!!! panic !!!\n--> %s \n", buf);
|
||||
spin("panic()");
|
||||
|
||||
// 不可能走到这里,否则出错;
|
||||
asm volatile("ud2");
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
#include <onix/console.h>
|
||||
|
||||
static u8 char_attr = 7; // 字符默认样式
|
||||
static u16 space = 0x0720; // 带有样式的空格
|
||||
struct CURSOR_REL {u8 x; u8 y} ; // 光标距离 当前屏幕的 x和y 单位为字符
|
||||
|
||||
|
||||
// 得到当前显示器这一屏的开始的内存位置
|
||||
static usize get_current_screen_mem_status(){
|
||||
// 首先 获得当前显示器 距离 0xb800 多少个字符
|
||||
// 高八位
|
||||
out_8(CRT_ADDR_PORT, CRT_MEM_H_VALUE);
|
||||
u8 h = in_8(CRT_DATA_PORT);
|
||||
|
||||
// 低8位
|
||||
out_8(CRT_ADDR_PORT, CRT_MEM_L_VALUE);
|
||||
u8 l = in_8(CRT_DATA_PORT);
|
||||
|
||||
u16 char_pos = (h << 8) | l;
|
||||
|
||||
// 累加 得到当前显示器的内存地址
|
||||
usize mem_pos = char_pos << 1; // 一个字符 2字节
|
||||
return CRT_MEM_START + mem_pos;
|
||||
}
|
||||
|
||||
// 设置显示器当前屏需要展示的字符的内存位置 为新的一屏
|
||||
static set_current_screen_mem_status(usize screen){
|
||||
|
||||
// 得到距离 crt开始的内存的距离
|
||||
usize screen_rel = screen-CRT_MEM_START;
|
||||
|
||||
// 高八位字符设置
|
||||
out_8(CRT_ADDR_PORT, CRT_MEM_H_VALUE);
|
||||
out_8(CRT_DATA_PORT, (screen_rel >> 1) >> 8);
|
||||
|
||||
// 低8位
|
||||
out_8(CRT_ADDR_PORT, CRT_MEM_L_VALUE);
|
||||
out_8(CRT_DATA_PORT, (screen_rel >> 1));
|
||||
}
|
||||
|
||||
// 得到当前光标在内存中的位置
|
||||
static usize get_current_cursor_mem_status(){
|
||||
// 首先 获得当前当前光标 距离 0xb800 多少个字符
|
||||
// 高八位
|
||||
out_8(CRT_ADDR_PORT, CRT_CURSOR_H_VALUE);
|
||||
u8 h = in_8(CRT_DATA_PORT);
|
||||
|
||||
// 低8位
|
||||
out_8(CRT_ADDR_PORT, CRT_CURSOR_L_VALUE);
|
||||
u8 l = in_8(CRT_DATA_PORT);
|
||||
|
||||
u16 char_pos = (h << 8) | l;
|
||||
|
||||
// 累加 得到当前光标的内存地址
|
||||
usize mem_pos = char_pos << 1; // 一个字符 2字节
|
||||
return CRT_MEM_START + mem_pos;
|
||||
}
|
||||
|
||||
// 设置光标的新的内存位置
|
||||
static set_current_cursor_mem_status(usize cursor){
|
||||
// 得到距离 crt开始的内存的距离
|
||||
usize cursor_rel = cursor-CRT_MEM_START;
|
||||
|
||||
// 高八位字符设置
|
||||
out_8(CRT_ADDR_PORT, CRT_CURSOR_H_VALUE);
|
||||
out_8(CRT_DATA_PORT, (cursor_rel >> 1) >> 8);
|
||||
|
||||
// 低8位
|
||||
out_8(CRT_ADDR_PORT, CRT_CURSOR_L_VALUE);
|
||||
out_8(CRT_DATA_PORT, (cursor_rel >> 1));
|
||||
}
|
||||
|
||||
// 得当当前距离屏幕的相对坐标
|
||||
static struct CURSOR_REL get_current_cursor_x_and_y(){
|
||||
// 获得当前 光标内存位置
|
||||
usize cursor_mem_abs = get_current_cursor_mem_status();
|
||||
// 获得当前显示器内存位置
|
||||
usize screen_mem_abs = get_current_screen_mem_status();
|
||||
|
||||
// 当前光标距离当前屏幕首地址几个字符
|
||||
usize char_count = (cursor_mem_abs - screen_mem_abs) >> 1;
|
||||
|
||||
u8 x = char_count % WIDTH;
|
||||
u8 y = char_count / WIDTH;
|
||||
struct CURSOR_REL tmp = {x,y};
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// 向上滚动 count行
|
||||
static scroll_up(usize* cursor_mem_abs_ptr){
|
||||
usize count_bytes = (1 * 2 * 80 * 1);
|
||||
|
||||
// 当前屏幕的内存位置
|
||||
usize screen_mem_abs = get_current_screen_mem_status();
|
||||
|
||||
// 新的内存屏幕的位置
|
||||
usize new_screen_mem_abs_start = screen_mem_abs + count_bytes;
|
||||
|
||||
// 显存检测, 如果写入超过显存
|
||||
if (*cursor_mem_abs_ptr >= CRT_MEM_END) {
|
||||
|
||||
// 把当前屏幕数据copy到 起始 位置
|
||||
memcpy((u8*)CRT_MEM_START, (u8*)screen_mem_abs, (1 * 2 * WIDTH * HEIGHT));
|
||||
|
||||
// 清空现在当前屏幕, 内存之下所有的数据(!有问题, 用下面的循环)
|
||||
// memset((u8*)CRT_MEM_START+(1 * 2 * WIDTH * HEIGHT), 0, CRT_MEM_END-(CRT_MEM_START+(1 * 2 * WIDTH * HEIGHT)));
|
||||
|
||||
// 结束位置
|
||||
u8* e = (u8*)CRT_MEM_START + (1 * 2 * WIDTH * HEIGHT);
|
||||
while (true){
|
||||
if (e >= CRT_MEM_END) {
|
||||
break;
|
||||
}
|
||||
*e = space;
|
||||
e += 2;
|
||||
}
|
||||
|
||||
|
||||
// 设置 new_screen_mem_abs_start 为 新地址
|
||||
new_screen_mem_abs_start = CRT_MEM_START + count_bytes;
|
||||
|
||||
// 光标也挪过去(cursor_mem_abs_ptr 是指针类型指针)
|
||||
(*cursor_mem_abs_ptr) -= (screen_mem_abs - CRT_MEM_START);
|
||||
}
|
||||
|
||||
// 设置屏幕
|
||||
set_current_screen_mem_status(new_screen_mem_abs_start);
|
||||
}
|
||||
|
||||
|
||||
console_write(u8* buf, usize count){
|
||||
// 当前光标内存位置
|
||||
u8* cursor_mem_abs_ptr = (u8*)get_current_cursor_mem_status();
|
||||
|
||||
// 当前屏幕的内存位置
|
||||
u8* screen_mem_abs_ptr = (u8*)get_current_screen_mem_status();
|
||||
|
||||
// 当前光标相对于屏幕坐标系
|
||||
struct CURSOR_REL cursor_rel_xy = get_current_cursor_x_and_y();
|
||||
|
||||
for (usize i = 0; i < count; i++){
|
||||
|
||||
u8 ch = buf[i];
|
||||
if (ch == '~'){
|
||||
int f = 123;
|
||||
}
|
||||
|
||||
switch (ch){
|
||||
case EOS:
|
||||
break;
|
||||
case BEL:
|
||||
break;
|
||||
case BS:
|
||||
// 如果光标内存位置==屏幕内存位置, 说明在开头
|
||||
if (cursor_mem_abs_ptr == screen_mem_abs_ptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 前一个字符的 内存位置
|
||||
cursor_mem_abs_ptr -= 2;
|
||||
// 修改为空字符
|
||||
*cursor_mem_abs_ptr = space;
|
||||
break;
|
||||
case HT:
|
||||
break;
|
||||
case LF:
|
||||
// 光标换行
|
||||
cursor_mem_abs_ptr += (WIDTH << 1);
|
||||
cursor_rel_xy.y += 1;
|
||||
// 换行是否需要滚动
|
||||
if(cursor_rel_xy.y >= HEIGHT) {
|
||||
scroll_up(&cursor_mem_abs_ptr);
|
||||
cursor_rel_xy.y -= 1;
|
||||
}
|
||||
goto in_cr;
|
||||
break;
|
||||
case VT:
|
||||
break;
|
||||
case FF:
|
||||
goto in_cr;
|
||||
break;
|
||||
case CR:
|
||||
// 使光标回到开始的位置
|
||||
in_cr:
|
||||
cursor_mem_abs_ptr -= (cursor_rel_xy.x << 1);
|
||||
cursor_rel_xy.x = 0;
|
||||
break;
|
||||
case DEL:
|
||||
// 直接替换当前字符为空白
|
||||
*cursor_mem_abs_ptr = space;
|
||||
break;
|
||||
default:
|
||||
cursor_rel_xy.x += 1;
|
||||
|
||||
// 如果需要换行
|
||||
if(cursor_rel_xy.x >= WIDTH) {
|
||||
cursor_rel_xy.x =0;
|
||||
cursor_rel_xy.y += 1;
|
||||
|
||||
// 即将在下一行写入字符, 要保证下一行在 最大高度HEIGHT 之内
|
||||
// cursor_rel_xy 内保存的是idx, 需要+1得到当前屏幕的行数 和HEIGHT 比较如果相等, 说明当前屏幕已经写满了
|
||||
if(cursor_rel_xy.y >= HEIGHT) {
|
||||
scroll_up(&cursor_mem_abs_ptr);
|
||||
cursor_rel_xy.y -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 替换字符和样式
|
||||
*cursor_mem_abs_ptr = ch;
|
||||
|
||||
cursor_mem_abs_ptr += 1;
|
||||
*cursor_mem_abs_ptr = char_attr;
|
||||
cursor_mem_abs_ptr += 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// // 如果最后一个字符显示完毕后刚好是屏幕的最后一个字符
|
||||
// if(cursor_rel_xy.x >= WIDTH) {
|
||||
// cursor_rel_xy.x =0;
|
||||
// cursor_rel_xy.y += 1;
|
||||
|
||||
// // 即将在下一行写入字符, 要保证下一行在 最大高度HEIGHT 之内
|
||||
// // cursor_rel_xy 内保存的是idx, 需要+1得到当前屏幕的行数 和HEIGHT 比较如果相等, 说明当前屏幕已经写满了
|
||||
// if(cursor_rel_xy.y >= HEIGHT) {
|
||||
// scroll_up(&cursor_mem_abs_ptr);
|
||||
// cursor_rel_xy.y -= 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 重新设置光标位置
|
||||
set_current_cursor_mem_status((usize)cursor_mem_abs_ptr);
|
||||
}
|
||||
|
||||
console_clear(){
|
||||
// 整个显存内存区域改成 有样式的空格
|
||||
usize crt_mem_start = CRT_MEM_START;
|
||||
u8* crt_mem_start_ptr = (u8*)crt_mem_start;
|
||||
for (usize i = 0; i < CRT_MEM_SIZE; i++){
|
||||
*crt_mem_start_ptr = space;
|
||||
crt_mem_start_ptr += 2;
|
||||
}
|
||||
|
||||
// 光标重置到当前屏幕的第一个字符
|
||||
set_current_cursor_mem_status(get_current_screen_mem_status());
|
||||
}
|
||||
|
||||
console_init(){
|
||||
// test_console();
|
||||
console_clear();
|
||||
}
|
||||
|
||||
|
||||
test_console(){
|
||||
struct CURSOR_REL cursor_rel_xy = get_current_cursor_x_and_y();
|
||||
int a = 0;
|
||||
|
||||
// 当前位置
|
||||
usize screen_mem_abs = get_current_screen_mem_status();
|
||||
|
||||
// 设置从第二行开始显示 (2*80)表示一行的字节数量
|
||||
usize screen = CRT_MEM_START + (2*80)*1;
|
||||
set_current_screen_mem_status(screen);
|
||||
// 当前位置
|
||||
screen_mem_abs = get_current_screen_mem_status();
|
||||
|
||||
// 当前光标位置
|
||||
usize cursor_mem_abs = get_current_cursor_mem_status();
|
||||
// 设置新的光位置视在 第一行的 下标为3的字符, 当然, 这是不可见的, 因为 上面第一行已经不显示了
|
||||
cursor_mem_abs = CRT_MEM_START + (1 * 2) * 3;
|
||||
set_current_cursor_mem_status(cursor_mem_abs);
|
||||
|
||||
// 设置光标在第二行 下标为4的 字符处(这里屏幕中的第一行, 就是内存中的第二行)
|
||||
cursor_mem_abs = CRT_MEM_START + (1 * 2) * 80 + (1 * 2) * 4;
|
||||
set_current_cursor_mem_status(cursor_mem_abs);
|
||||
|
||||
// 得到当前光标的相对位置
|
||||
struct CURSOR_REL cur = get_current_cursor_x_and_y();
|
||||
|
||||
|
||||
|
||||
console_clear();
|
||||
char* message = "123456781234567812345672812345678123456781234567812345678123456781234567812345678\n";
|
||||
for (usize i = 0; i < 49; i++){
|
||||
console_write(message, strlen(message));
|
||||
}
|
||||
char* message1= "123456\n";
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
|
||||
cursor_rel_xy = get_current_cursor_x_and_y();
|
||||
a = 0;
|
||||
|
||||
console_write(message1, strlen(message1));
|
||||
|
||||
cursor_rel_xy = get_current_cursor_x_and_y();
|
||||
a = 0;
|
||||
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
console_write(message1, strlen(message1));
|
||||
|
||||
char* message2= "111~\n";
|
||||
console_write(message2, strlen(message2));
|
||||
|
||||
|
||||
|
||||
// screen_mem_abs = get_current_screen_mem_status();
|
||||
// cursor_rel_xy = get_current_cursor_x_and_y();
|
||||
|
||||
|
||||
// usize tmp = (6 * 2) + (24 * 80 * 2);
|
||||
// set_current_cursor_mem_status(screen_mem_abs + tmp);
|
||||
|
||||
// char* message2= "1234567\n";
|
||||
// console_write(message2, strlen(message2));
|
||||
// console_write(message2, strlen(message2));
|
||||
// console_write(message2, strlen(message2));
|
||||
// console_write(message2, strlen(message2));
|
||||
// console_write(message2, strlen(message2));
|
||||
// console_write(message2, strlen(message2));
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#include <onix/stdlib.h>
|
||||
|
||||
void delay(usize count){
|
||||
for (usize i = 0; i < count; i++);
|
||||
}
|
||||
|
||||
void hang(){
|
||||
while (true){
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
#include <onix/string.h>
|
||||
|
||||
strcpy(u8* dest, const u8* src){
|
||||
while (true){
|
||||
*dest = *src;
|
||||
if (*src == EOS){
|
||||
break;
|
||||
}
|
||||
dest += 1;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(u8* dest, const u8* src, usize count){
|
||||
for (usize i = 0; i < count; i++){
|
||||
if (*src == EOS){
|
||||
break;
|
||||
}
|
||||
*dest = *src;
|
||||
dest += 1;
|
||||
src += 1;
|
||||
}
|
||||
*dest = EOS;
|
||||
}
|
||||
|
||||
strcat(u8* dest, const u8* src){
|
||||
// 找到dest 的结尾
|
||||
while (true) {
|
||||
if (*dest == EOS) {
|
||||
break;
|
||||
}
|
||||
dest += 1;
|
||||
}
|
||||
|
||||
// 从结尾开始拼接
|
||||
while (true){
|
||||
*dest = *src;
|
||||
if (*src == EOS){
|
||||
break;
|
||||
}
|
||||
dest += 1;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
usize strlen(const u8* src){
|
||||
u8* start = src;
|
||||
while (*src != EOS){
|
||||
src += 1;
|
||||
}
|
||||
return src - start;
|
||||
}
|
||||
|
||||
i8 strcmp(const u8* lhs, const u8* rhs){
|
||||
while (*lhs == *rhs && *lhs != EOS && *rhs != EOS){
|
||||
lhs += 1;
|
||||
rhs += 1;
|
||||
}
|
||||
|
||||
// 比较不相同的一位
|
||||
if (*lhs < *rhs) {
|
||||
return -1;
|
||||
} else if (*lhs == *rhs){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8* strchr_l(const u8* src, u8 ch) {
|
||||
while (true){
|
||||
if (*src == EOS) {
|
||||
return nullptr;
|
||||
} else if (*src == ch){
|
||||
return src;
|
||||
}
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
u8* strchr_r(const u8* src, u8 ch) {
|
||||
u8* last_ptr = nullptr;
|
||||
while (true){
|
||||
if (*src == EOS) {
|
||||
return last_ptr;
|
||||
} else if (*src == ch){
|
||||
last_ptr = src;
|
||||
}
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
i8 memcmp(const u8* lhs, const u8* rhs, usize count){
|
||||
if (count == 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (usize i = 0; i < count; i++){
|
||||
if (*lhs != *rhs) {
|
||||
break;
|
||||
}
|
||||
lhs += 1;
|
||||
rhs += 1;
|
||||
}
|
||||
if (*lhs < *rhs) {
|
||||
return -1;
|
||||
} else if (*lhs == *rhs){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(u8* src, u8 ch, usize count){
|
||||
for (usize i = 0; i < count; i++){
|
||||
*src = ch;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(u8* dest, const u8* src, usize count){
|
||||
for (usize i = 0; i < count; i++){
|
||||
*dest = *src;
|
||||
dest += 1;
|
||||
src += 1;
|
||||
}
|
||||
}
|
||||
|
||||
u8* memchr(const u8* src, u8 ch, usize count){
|
||||
for (usize i = 0; i < count; i++){
|
||||
if (*src == ch){
|
||||
return src;
|
||||
}
|
||||
src += 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void test_string(){
|
||||
u8 message[] = "hello~";
|
||||
u8 buffer[1024];
|
||||
|
||||
u8* video = (u8*)0xb8000;
|
||||
for (usize i = 0; i < sizeof(message); i++){
|
||||
video[i * 2] = message[i];
|
||||
}
|
||||
|
||||
int res;
|
||||
res = strcmp(message, buffer); // 1
|
||||
strcpy(buffer, message);
|
||||
res = strcmp(message, buffer); // 0
|
||||
strcat(buffer, message); // 拼接一下
|
||||
res = strcmp(message, buffer); // -1
|
||||
res = strlen(buffer); // 12
|
||||
res = strlen(message); // 6
|
||||
res = sizeof(message); // 7 sizeof 把 0 也统计了
|
||||
|
||||
|
||||
usize l_pos = strchr_l(message, 'l') - message; // 2
|
||||
l_pos = strchr_r(message, 'l') - message; // 3 这是最后一个l 他在指针开始下标为 3 的位置
|
||||
|
||||
memset(buffer, 0, sizeof(buffer)); // 重置为0
|
||||
|
||||
res = memcmp(message, buffer, sizeof(message)); // 比较内存的大小, buff重置了之后, 这里结果应该是 1
|
||||
|
||||
memcpy(buffer, message, sizeof(message));
|
||||
res = memcmp(message, buffer, sizeof(message)); // copy完 再比较一下 应该是 0
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#include <onix/types.h>
|
||||
|
||||
void main(){
|
||||
printf("size of u8 %d\n", sizeof(u8));
|
||||
printf("size of u16 %d\n", sizeof(u16));
|
||||
printf("size of u32 %d\n", sizeof(u32));
|
||||
}
|
Loading…
Reference in New Issue