操作系统:内核的基本实现(一)屏幕输出与C语言支持
使用 C 语言
cdecl 约定
HimuOS Kernel 遵守 cdecl 约定,在32位操作系统上,
- 函数实参在线程栈上按照从右至左的顺序依次压栈 (x86 cdecl 的参数总是在栈上)
- 函数结果保存在寄存器EAX/AX/AL中
- 浮点型结果存放在寄存器ST0中
- 编译后的函数名前缀以一个下划线字符
- 调用者负责清栈
- 8比特或者16比特长的整形实参提升为32比特长
- 受到函数调用影响的寄存器:EAX, ECX, EDX, ST0 - ST7, ES, GS
x86_64 的约定
在64位机器上约定有所不同,MSVC和GNU/GCC都有不同的约定。MSVC使用__fastcall,前四个整数或指针参数通过寄存器RCX, RDX, R8, R9传递,其余通过栈传递。GNU/GCC则使用System V ABI,前六个整数或指针参数通过RDI, RSI, RDX, RCX, R8, R9传递,返回值通过RAX
打印函数的实现
在保护模式中,一般使用 in/out 操作显卡有关I/O 端口完成图形操作(更新硬件光标位置等)
- 保存所有通用寄存器的值,以便函数结束时能够恢复原状态。
- 调用
0x03d4/0x03d5
I/O 端口获取/更新硬件光标位置 - 检查是否是回车 (
0x0d
),换行 (0x0a
),或退格 (0x08
):- 回车和换行被视为相同的情况,跳转到
.key_lf
处理。 - 退格字符减少光标位置并清除相应的字符。
- 回车和换行被视为相同的情况,跳转到
- 若是普通字符,将光标位置乘以2(因为视频内存中每个字符占用2字节),把字符写入显存,并设置字符的颜色为
0x07
(通常表示白色字符,黑色背景)。 - 若光标位置超出2000(超出屏幕区域),则跳转到滚动屏幕部分
.roll_screen
- 如果字符是回车或换行,计算新光标位置,换行后光标移动到下一行。
- 屏幕滚动:将前1-24行的缓存区全部向上到0-23行复制,最后一行用空格覆盖,并将光标置于最后一行的起始处。
;
; HIMU OPERATING SYSTEM
;
; File: krnlio.asm
; Kernel I/O functions
; Copyright (C) 2024 HimuOS Project, all rights reserved.
; All builtin functions has __himuos__ prefix to avoid conflicts with other functions
%include "osbase32.inc"
[bits 32]
section .text
; Function: getcrpos
; get current cursor position
; - return ax: cursor position
; ---------------------------------------------------------
global __himuos__getcrpos
__himuos__getcrpos:
push edx
mov dx, 0x03D4
mov al, 0x0E
out dx, al
mov dx, 0x03D5
in al, dx
mov ah, al
mov dx, 0x03D4
mov al, 0x0F
out dx, al
mov dx, 0x03D5
in al, dx
pop edx
ret
; Function: printc
; write a character in stack to the screen
; ---------------------------------------------------------
global __himuos__printc
__himuos__printc:
pushad
mov ax, SELECTOR_VIDEO
mov gs, ax
call __himuos__getcrpos
mov bx, ax
mov ecx, [esp + 36]
cmp cl, 0xd
jz .key_cr
cmp cl, 0xa
jz .key_lf
cmp cl, 0x8
jz .key_backspace
jmp .key_other
.key_backspace:
dec bx
shl bx, 1
mov byte [gs:bx], 0
inc bx
mov byte [gs:bx], 0x07
shr bx, 1
jmp .set_cursor
.key_other:
shl bx, 1
mov byte [gs:bx], cl
inc bx
mov byte [gs:bx], 0x07
shr bx, 1
inc bx
cmp bx, 2000
jl .set_cursor
; we consider carriage returns and line breaks as the same situation (CRLF)
.key_lf:
.key_cr:
xor dx, dx
mov ax, bx
mov si, 80
div si
sub bx, dx
add bx, 80
cmp bx, 2000
jl .set_cursor
.roll_screeen:
cld
mov ecx, 960
mov esi, 0xc00b80a0
mov edi, 0xc00b8000
rep movsd
mov ebx, 3840
mov ecx, 80
.cls:
mov word [gs:ebx], 0x0720
add ebx, 2
loop .cls
mov bx, 1920
.set_cursor:
mov dx, 0x03d4
mov al, 0x0e
out dx, al
mov dx, 0x03d5
mov al, bh
out dx, al
mov dx, 0x03d4
mov al, 0x0f
out dx, al
mov dx, 0x03d5
mov al, bl
out dx, al
.printc_end:
popad
ret
; Function: clscr
; clear the screen and set the cursor to the top left corner
; ---------------------------------------------------------
global __himuos__clscr
__himuos__clscr:
pushad
mov ax, SELECTOR_VIDEO
mov gs, ax
xor ebx, ebx
mov ecx, 2000
.clscr_loop:
mov word [gs:bx], 0x0720
add ebx, 2
loop .clscr_loop
mov bx, 0
mov dx, 0x03d4
mov al, 0x0e
out dx, al
mov dx, 0x03d5
mov al, bh
out dx, al
mov dx, 0x03d4
mov al, 0x0f
out dx, al
mov dx, 0x03d5
mov al, bl
out dx, al
popad
ret
; Function: printstr
; print a string to the screen
; ---------------------------------------------------------
global __himuos__printstr
__himuos__printstr:
push ebx
push ecx
mov ecx, 0
mov ebx, [esp + 12]
.printstr_loop:
mov cl, [ebx]
cmp cl, 0
jz .printstr_end
push ecx
call __himuos__printc
add esp, 4
inc ebx
jmp .printstr_loop
.printstr_end:
pop ecx
pop ebx
ret