参考《linux内核完全注释》和网上相关文章
/*
* 控制台显示操作
*/
/*
* linux/kernel/console.c
*
* (C) 1991 Linus Torvalds
*/
/*
* console.c
*
* This module implements the console io functions
* 'void con_init(void)'
* 'void con_write(struct tty_queue * queue)'
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
*/
/**********************
* con_init con_write *
**********************/
/*
* NOTE!!! We sometimes disable and enable interrupts for a short while
* (to put a word in video IO), but this will work even for keyboard
* interrupts. We know interrupts aren't enabled when getting a keyboard
* interrupt, as we use trap-gates. Hopefully all is well.
*/
/*
* Code to check for different video-cards mostly by Galen Hunt,
* <g-hunt@ee.utah.edu>
*/
#include <linux/sched.h>
#include <linux/tty.h> // 定义tty_struct结构,串行通信方面的参数和常量
#include <asm/io.h> // 硬件对应的汇编io指令
#include <asm/system.h>
/*
* These are set up by the setup-routine at boot-time:
*/
#define ORIG_X (*(unsigned char *)0x90000) // 光标列号
#define ORIG_Y (*(unsigned char *)0x90001) // 光标行号
#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) // 显示页面
#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) // 显示模式
#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) // 字符列数
#define ORIG_VIDEO_LINES (25) // 字符行数
#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) // ??
#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) // 显示内存大小和色彩模式
#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) // 显示卡特性参数
#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display,单色文本 */
#define VIDEO_TYPE_CGA 0x11 /* CGA Display,CGA 显示器 */
#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode,EGA/VGA 单色 */
#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode,EGA/VGA 彩色 */
#define NPAR 16
extern void keyboard_interrupt(void); // 键盘中断处理程序
/* 全局变量,指示显示器的相关信息 */
static unsigned char video_type; /* Type of display being used */
static unsigned long video_num_columns; /* Number of text columns */
static unsigned long video_size_row; /* Bytes per row,每行使用的字节数 */
static unsigned long video_num_lines; /* Number of test lines,屏幕文本行数 */
static unsigned char video_page; /* Initial video page,初始显示页面 */
static unsigned long video_mem_start; /* Start of video RAM */
static unsigned long video_mem_end; /* End of video RAM (sort of) */
static unsigned short video_port_reg; /* Video register select port */
/* 显示控制索引寄存器端口 */
static unsigned short video_port_val; /* Video register value port */
static unsigned short video_erase_char; /* Char+Attrib to erase with,擦除字符属性与字符 */
/* 下面的全局变量是屏幕卷屏操作所使用 */
static unsigned long origin; /* Used for EGA/VGA fast scroll */ // 滚屏起始内存地址
static unsigned long scr_end; /* Used for EGA/VGA fast scroll */ // 滚屏末端内存地址
static unsigned long pos; // 当前光标位置相对于显存的位置
static unsigned long x,y; // 当前光标
static unsigned long top,bottom; // 滚动时顶行行号和底行行号
/**************************************************************/
static unsigned long state=0; // 处理ESC 转义序列时的当前进行到哪
static unsigned long npar,par[NPAR]; // 放ESC 序列的中间处理参数
/************************************************************/
static unsigned long ques=0;
static unsigned char attr=0x07; // char
static void sysbeep(void);
/*
* this is what the terminal answers to a ESC-Z or csi0c
* query (= vt100 response).
*/
#define RESPONSE "/033[?1;2c"
/* NOTE! gotoxy thinks x==video_num_columns is ok */
/* 更新当前光标位置 */
/*
* |----------------------------------------------------------> x
* | video_size_row
* |--------------------------------------| -|------> top顶行行号
* |--------------------------------------| |
* | | |
* | * * * | | --> video_num_row
* P(x, y) |
* |--------------------------------------| -|------> button底行行号
* |--------------------------------------|
* | |
* | video_num_columns
* |
* | y
*
* all video memory = video_size_row * video_num_row
* the screen point P(x, y) is mapping the main memory
* video_mem_start + y * video_size_row + (x << 1)
*/
static inline void gotoxy(unsigned int new_x,unsigned int new_y)
{
if (new_x > video_num_columns || new_y >= video_num_lines)
return; // 错误检查
x=new_x; // 更新x,y
y=new_y;
pos=origin + y*video_size_row + (x<<1); // 重新计算全局变量pos值
}
/* 设置滚屏起始显示内存地址 */
static inline void set_origin(void)
{
cli();
outb_p(12, video_port_reg); // 向择显示控制数据寄存器r12写入卷屏起始地址高字节
outb_p(0xff&((origin-video_mem_start)>>9), video_port_val);
outb_p(13, video_port_reg); // 向显示控制数据寄存器r13写入卷屏起始地址底字节
outb_p(0xff&((origin-video_mem_start)>>1), video_port_val);
sti();
}
/* 向上卷动一行 */
static void scrup(void)
{
/*
* 我们可以认为的是显存就是一片内存区域,显示器在决定显示部分时,根据的是
* 显示控制器的的内存