内核中的屏幕打印

课程目标

使用C语言设计一组打印函数,为后续进一步开发内核功能做准备!

内核中的屏幕打印模块

接口设计

接口之间的关系

 void PrintChar(char c)

功能定义:在屏幕上打印一个字符

实现原理:直接在显存对应位置写入数据

 这里用内嵌汇编的方式实现这个函数,是因为用汇编实现这个函数比较简单。

C语言中的内嵌汇编

内嵌汇编示例

注意

因为使用了gcc编译器,所以内嵌汇编使用的是 AT&T 汇编格式 (与 nasm 汇编格式不同)

int PrintString(const char* s);

实现方式

  • 循环调用PrintChar知道遇到0结束符

int PrintIntHex(unsigned int n);

实现方式

  • 将参数n转换为对应的16进制数字符串
  • 调用 PrintString() 打印转换得到的字符串

int PrintIntDec(int n);

保护模式下的光标追踪

内核屏幕操作

screen.h


#ifndef SCREEN_H
#define SCREEN_H

#define SCREEN_WIDTH	80
#define SCREEN_HEIGHT   25

typedef enum
{
	SCREEN_GRAY   = 0x07,
    SCREEN_BLUE   = 0x09,
    SCREEN_GREEN  = 0x0A,
    SCREEN_RED    = 0x0C,
    SCREEN_YELLOW = 0x0E,
    SCREEN_WHITE  = 0x0F

}PrintColor;

void ClearScreen();
int SetPrintPos(short w, short h);
void SetPrintColor(PrintColor c);
int PrintChar(char c);
int PrintString(const char* s);
int PrintIntDec(int n);
int PrintIntHex(unsigned int n);

#endif

screen.c

#include "kernel.h"
#include "screen.h"

static int gPosW = 0;
static int gPosH = 0;
static char gColor = SCREEN_WHITE;

void ClearScreen()
{
	int w = 0;
	int h = 0;
	
	SetPrintPos(0, 0);
	
	for(h = 0; h < SCREEN_HEIGHT; h++)
	{
		for(w = 0; w < SCREEN_WIDTH; w++)
		{
			PrintChar(' ');
		}
	}
	
	SetPrintPos(0, 0);
}

int SetPrintPos(short w, short h)
{
	int ret = 0;
	
	if(ret = ((w >= 0) && (w < SCREEN_WIDTH) && (h >= 0) && (h < SCREEN_HEIGHT)))
	{
		short bx = h * SCREEN_WIDTH + w;
		gPosW = w;
		gPosH = h;
		
		asm volatile(
			"movw %0,       %%bx\n"
			"movw $0x03D4,  %%dx\n"
			"movb $0x0E,    %%al\n"
			"outb %%al,     %%dx\n"
			"movw $0x03D5,  %%dx\n"
			"movb %%bh,     %%al\n"
			"outb %%al,     %%dx\n"
			"movw $0x03D4,  %%dx\n"
			"movb $0x0F,    %%al\n"
			"outb %%al,     %%dx\n"
			"movw $0x03D5,  %%dx\n"
			"movb %%bl,     %%al\n"
			"outb %%al,     %%dx\n"
			:
			: "r"(bx)
			: "ax", "bx", "dx"
		);
	}
	
	return ret;
}

void SetPrintColor(PrintColor c)
{
	gColor = c;
}

int PrintChar(char c)
{
	int ret = 0;

	if((c == '\n') || (c == '\r'))
	{
		ret = SetPrintPos(0, gPosH + 1);
	}
	else
	{
		int pw = gPosW;
		int ph = gPosH;
		
		if((pw >= 0) && (pw < SCREEN_WIDTH) && (ph >= 0) && (ph < SCREEN_HEIGHT))
		{
			int edi = (SCREEN_WIDTH * ph + pw) * 2;
			char ah = gColor;
			char al = c;
		
			asm volatile(
				"movl %0, %%edi\n"
				"movb %1, %%ah\n"
				"movb %2, %%al\n"
				"movw %%ax, %%gs:(%%edi)"
				"\n"
				:
				: "r"(edi), "r"(ah), "r"(al)	
				: "ax", "edi"
			);
			
			pw++;
			
			if(pw == SCREEN_WIDTH)
			{
				pw = 0;
				ph += 1;
			}
			
			SetPrintPos(pw, ph);
			
			ret = 1;
		}
	}
	
	return ret;
}

int PrintString(const char* s)
{
	int ret = 0;
	
	if(s != NULL)
	{
		while(*s)
		{
			ret += PrintChar(*s++);
		}
	}
	else
	{
		ret = -1;
	}
	
	return ret;
}

int PrintIntHex(unsigned int n)
{
	int i = 0;
	char hex[11] = {'0', 'x', 0};
	
	for(i = 9; i >= 2; i--)
	{
		int temp = n & 0xF;
		
		if(temp < 10)
		{
			hex[i] = ('0' + temp);
		}
		else
		{
			hex[i] = ('A' + temp - 10);
		}
		
		n >>= 4;
	}
	
	return PrintString(hex);
}

int PrintIntDec(int n)
{
	int ret = 0;
	
	if(n < 0)
	{
		ret += PrintChar('-');
		
		PrintIntDec(-n);
	}
	else
	{
		if(n < 10)
		{
			ret += PrintChar('0' + n);
		}
		else
		{
			ret += PrintIntDec(n / 10);
			ret += PrintIntDec(n % 10);
		}
	}
	
	return ret;
}

需要在跳转到内核程序之前,先设置好相应的段寄存器。

小结

gcc编译器只支持 AT&T 格式的内嵌汇编

通过内嵌汇编的方式可实现 PrintChar() 函数

PrintChar() 是其他屏幕打印函数的基础

通过操作 0x03D4 与 0x03D5 端口对光标位置进行设置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值