第6章 打印函数

打印函数的原理

写操作系统必须要有打印函数,这样才方便调试。对于所有的显示适配器,文本方式下显示字符的原理都是一样的,所不同的是各种适配器的视频显示存储器(又称显存)的起始地址不同;对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);
}

Leave a Reply

Your email address will not be published. Required fields are marked *