打印函数的原理
写操作系统必须要有打印函数,这样才方便调试。对于所有的显示适配器,文本方式下显示字符的原理都是一样的,所不同的是各种适配器的视频显示存储器(又称显存)的起始地址不同;对MDA,显存的起始地址为B000:0000,对CGA、EGA、VGA是B800:0000。每个字符的ASCII码和属性码字节存放于连续的两个字节中。在25*80的文本显示方式下,屏幕有2000个字符位置,因每个字符需要用两个字节来表示,所以显存容量需要4000B。
在显示适配器中,设置光标很重要,下面是VGA模式下设置光标的方法:
写操作系统必须要有打印函数,这样才方便调试。对于所有的显示适配器,文本方式下显示字符的原理都是一样的,所不同的是各种适配器的视频显示存储器(又称显存)的起始地址不同;对MDA,显存的起始地址为B000:0000,对CGA、EGA、VGA是B800:0000。每个字符的ASCII码和属性码字节存放于连续的两个字节中。在25*80的文本显示方式下,屏幕有2000个字符位置,因每个字符需要用两个字节来表示,所以显存容量需要4000B。
在显示适配器中,设置光标很重要,下面是VGA模式下设置光标的方法:
CRT控制器名称 | 端口 |
索引寄存器 | 3D4H |
数据寄存器 | 3D5H |
CRT控制器中有26个寄存器,必须经索引后,才能通过 3D5端口 读写。下面是CRT控制器寄存器列表。
寄存器名称 | 索引号 | 写端口 ( EGA/VGA/TVGA) | 读端口 (EGA) | 读端口 (VGA/TVGA) |
地址寄存器 | -- | 3D5H/3B5H | 3D4H/3B5H | |
光标位置(高位) | 0EH | 3D5H/3B5H | 3D5H/3B5H | 3D5H/3B5H |
光标位置(低位) | 0FH | 3D5H/3B5H | 3D5H/3B5H | 3D5H/3B5H |
获取光标位置的方法
outportb(0x3d4, 0x0e); //索引寄存器,选择光标位置高位寄存器
highbyte= inportb(0x3d5); //取出光标位置高位字节
outportb(0x3d4, 0x0f); //索引寄存器,选择光标位置低位寄存器
lowbyte =inportb(0x3d5); //取出光标位置低位字节
设置光标位置的方法
outportb(0x3d4, 0x0f);
outportb(0x3d5, lowbyte & 0x0ff);
outportb(0x3d4, 0x0e);
outportb(0x3d5, highbyte & 0x0ff);
打印函数的源代码
Intel处理器中IO地址和内存地址不是统一编址,有单独的IO指令。下面是IO读写命令的宏定义。
#ifndef _IO_H_
#define _IO_H_
#define outportb(port,value)\
__asm__ ("outb %%al,%%dx"::"a"(value),"d"(port))
#define inportb(port)({\
unsigned char _v;\
__asm__ volatile ("inb %%dx,%%al":"=a"(_v):"d"(port));\
_v;\
})
#endif
video.h文件的源代码
#ifndef _VIDEO_H_
#define _VIDEO_H_
//字符属性
#define BLACK 0x0
#define BLUE 0x1
#define GREEN 0x2
#define RED 0x4
#define WHITE 0xf
#define YELLOW 0xe
#define char_attr(backgroud,front) (backgroud<<4|front)
void kprint(unsigned char attribute, char *string);
void printnum(unsigned char attribute, unsigned int num);
void cls();
#endif
video.c文件的源代码
#include "video.h"
#include "io.h"
unsigned char *VIDEO_MEMORY = (char *)0xb8000;
void kprint(unsigned char attribute, char *string)
{
unsigned int curchar, vidmem_off, i;
//得到光标位置
outportb(0x3d4, 0x0e); //索引寄存器,选择光标位置高位寄存器
vidmem_off = inportb(0x3d5); //取出光标位置高位字节
vidmem_off <<= 8;
outportb(0x3d4, 0x0f); //索引寄存器,选择光标位置低位寄存器
vidmem_off += inportb(0x3d5); //取出光标位置低位字节
vidmem_off <<= 1;
while((curchar = *string++))
{
switch(curchar)
{
case '\n':
vidmem_off = (vidmem_off/160)*160 + 160;
break;
case '\r':
vidmem_off = (vidmem_off/160)*160;
break;
case '\t':
vidmem_off += 8;
break;
case 8:/*Delete*/
vidmem_off -= 2;
VIDEO_MEMORY[vidmem_off] = 0x20;
break;
default:
VIDEO_MEMORY[vidmem_off++] = curchar;
VIDEO_MEMORY[vidmem_off++] = attribute;
break;
}
if(vidmem_off >= 160*25)
{
for(i = 0; i < 160*24; i++)
{
VIDEO_MEMORY[i] = VIDEO_MEMORY[i+160];
}
for(i = 0; i < 80; i++)
{
VIDEO_MEMORY[(160*24)+(i*2)] = 0x20;
VIDEO_MEMORY[(160*24)+(i*2)+1] = attribute;
}
vidmem_off -= 160;
}
}
//设置光标位置
vidmem_off >>= 1;
outportb(0x3d4, 0x0f);
outportb(0x3d5, vidmem_off & 0x0ff);
outportb(0x3d4, 0x0e);
outportb(0x3d5, vidmem_off >> 8);
}
void cls(void)
{
unsigned int i;
for(i = 0; i < (80*25); i++)
{
VIDEO_MEMORY[i*2] = 0x20;
VIDEO_MEMORY[i*2+1] = 0x07;
}
outportb(0x3d4, 0x0f);
outportb(0x3d5, 0);
outportb(0x3d4, 0x0e);
outportb(0x3d5, 0);
}
void printnum(unsigned char attribute, unsigned int num)
{
unsigned char backstr[11], i = 0, j = 0;
unsigned char str[32];
do
{
backstr[i++] = (num % 10) + '0';
num /= 10;
}
while(num);
do
{
str[j++] = backstr[--i];
}
while(i);
str[j] = '\0';
kprint(attribute, str);
}