⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 console.c

📁 linux完全注释的源代码。内核版本为0.11的linux kernel。
💻 C
📖 第 1 页 / 共 3 页
字号:
/* passed* linux/kernel/console.c** (C) 1991 Linus Torvalds*/#include <set_seg.h>
/** console.c** 该模块实现控制台输入输出功能* 'void con_init(void)'* 'void con_write(struct tty_queue * queue)'* 希望这是一个非常完整的VT102 实现。** 感谢John T Kohl 实现了蜂鸣指示。*//** 注意!!! 我们有时短暂地禁止和允许中断(在将一个字(word)放到视频IO),但即使* 对于键盘中断这也是可以工作的。因为我们使用陷阱门,所以我们知道在获得一个* 键盘中断时中断是不允许的。希望一切均正常。*//** 检测不同显示卡的代码大多数是Galen Hunt 编写的,* <g-hunt@ee.utah.edu>*/#include <linux/sched.h>// 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,						// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。#include <linux/tty.h>	// tty 头文件,定义了有关tty_io,串行通信方面的参数、常数。#include <asm/io.h>		// io 头文件。定义硬件端口输入/输出宏汇编语句。#include <asm/system.h>	// 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。/** 这些是设置子程序setup 在引导启动系统时设置的参数:*/// 参见对boot/setup.s 的注释,和setup 程序读取并保留的参数表。#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 /* 单色文本 */#define VIDEO_TYPE_CGA 0x11 /* CGA 显示器 */#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA 单色 */#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA 彩色 */#define NPAR 16extern void keyboard_interrupt (void);	// 键盘中断处理程序(keyboard.S)。static unsigned char video_type;	/* 使用的显示类型 */static unsigned long video_num_columns;	/* 屏幕文本列数 */static unsigned long video_size_row;	/* 每行使用的字节数 */static unsigned long video_num_lines;	/* 屏幕文本行数 */static unsigned char video_page;	/* 初始显示页面 */static unsigned long video_mem_start;	/* 显示内存起始地址 */static unsigned long video_mem_end;	/* 显示内存结束(末端)地址 */static unsigned short video_port_reg;	/* 显示控制索引寄存器端口 */static unsigned short video_port_val;	/* 显示控制数据寄存器端口 */static unsigned short video_erase_char;	/* 擦除字符属性与字符(0x0720) */// 以下这些变量用于屏幕卷屏操作。static unsigned long origin;	/* 用于EGA/VGA 快速滚屏 */// 滚屏起始内存地址。static unsigned long scr_end;	/* 用于EGA/VGA 快速滚屏 */// 滚屏末端内存地址。static unsigned long pos;	// 当前光标对应的显示内存位置。static unsigned long x, y;	// 当前光标位置。static unsigned long top, bottom;	// 滚动时顶行行号;底行行号。// state 用于标明处理ESC 转义序列时的当前步骤。npar,par[]用于存放ESC 序列的中间处理参数。static unsigned long state = 0;	// ANSI 转义字符序列处理状态。static unsigned long npar, par[NPAR];	// ANSI 转义字符序列参数个数和参数数组。static unsigned long ques = 0;static unsigned char attr = 0x07;	// 字符属性(黑底白字)。static void sysbeep (void);	// 系统蜂鸣函数。/** 下面是终端回应ESC-Z 或csi0c 请求的应答(=vt100 响应)。*/// csi - 控制序列引导码(Control Sequence Introducer)。#define RESPONSE "\033[?1;2c"/* 注意!gotoxy 函数认为x==video_num_columns,这是正确的 *///// 跟踪光标当前位置。// 参数:new_x - 光标所在列号;new_y - 光标所在行号。// 更新当前光标位置变量x,y,并修正pos 指向光标在显示内存中的对应位置。static _inline voidgotoxy (unsigned int new_x, unsigned int new_y){// 如果输入的光标行号超出显示器列数,或者光标行号超出显示的最大行数,则退出。	if (new_x > video_num_columns || new_y >= video_num_lines)		return;// 更新当前光标变量;更新光标位置对应的在显示内存中位置变量pos。	x = new_x;	y = new_y;	pos = origin + y * video_size_row + (x << 1);}//// 设置滚屏起始显示内存地址。static _inline voidset_origin (void){	cli ();// 首先选择显示控制数据寄存器r12,然后写入卷屏起始地址高字节。向右移动9 位,表示向右移动// 8 位,再除以2(2 字节代表屏幕上1 字符)。是相对于默认显示内存操作的。	outb_p (12, video_port_reg);	outb_p ((unsigned char)(0xff & ((origin - video_mem_start) >> 9)), video_port_val);// 再选择显示控制数据寄存器r13,然后写入卷屏起始地址底字节。向右移动1 位表示除以2。	outb_p (13, video_port_reg);	outb_p ((unsigned char)(0xff & ((origin - video_mem_start) >> 1)), video_port_val);	sti ();}//// 向上卷动一行(屏幕窗口向下移动)。// 将屏幕窗口向下移动一行。参见程序列表后说明。static voidscrup (void){
	unsigned long t1,t2,t3;
// 如果显示类型是EGA,则执行以下操作。	if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)	{// 如果移动起始行top=0,移动最底行bottom=video_num_lines=25,则表示整屏窗口向下移动。		if (!top && bottom == video_num_lines)		{// 调整屏幕显示对应内存的起始位置指针origin 为向下移一行屏幕字符对应的内存位置,同时也调整// 当前光标对应的内存位置以及屏幕末行末端字符指针scr_end 的位置。			origin += video_size_row;			pos += video_size_row;			scr_end += video_size_row;// 如果屏幕末端最后一个显示字符所对应的显示内存指针scr_end 超出了实际显示内存的末端,则将// 屏幕内容内存数据移动到显示内存的起始位置video_mem_start 处,并在出现的新行上填入空格字符。			if (scr_end > video_mem_end)			{// %0 - eax(擦除字符+属性);%1 - ecx((显示器字符行数-1)所对应的字符数/2,是以长字移动);// %2 - edi(显示内存起始位置video_mem_start);%3 - esi(屏幕内容对应的内存起始位置origin)。// 移动方向:[edi]->[esi],移动ecx 个长字。
				t1 = (video_num_lines - 1) * video_num_columns >> 1;
				_asm {					pushf
					mov ecx,t1;
//			  mov ecx,((video_num_lines - 1) * video_num_columns >> 1);
					mov ax,video_erase_char;
					mov edi,video_mem_start;
					mov esi,origin;
					cld;	// 清方向位。
					rep movsd;	// 重复操作,将当前屏幕内存数据移动到显示内存起始处。
					mov ecx,video_num_columns;	// ecx=1 行字符数。
					rep stosw;	// 在新行上填入空格字符。					popf
				}/*	      __asm__ ("cld\n\t"		       "rep\n\t"		       "movsl\n\t" 		       "movl _video_num_columns,%1\n\t"		       "rep\n\t"			   "stosw"
		::"a" (video_erase_char), "c" ((video_num_lines - 1) * video_num_columns >> 1), 		       "D" (video_mem_start), "S" (origin):"cx", "di","si"); */// 根据屏幕内存数据移动后的情况,重新调整当前屏幕对应内存的起始指针、光标位置指针和屏幕末端// 对应内存指针scr_end。				scr_end -= origin - video_mem_start;				pos -= origin - video_mem_start;				origin = video_mem_start;			}			else			{// 如果调整后的屏幕末端对应的内存指针scr_end 没有超出显示内存的末端video_mem_end,则只需在// 新行上填入擦除字符(空格字符)。// %0 - eax(擦除字符+属性);%1 - ecx(显示器字符行数);%2 - edi(屏幕对应内存最后一行开始处);				t1 = scr_end - video_size_row;
				_asm {					pushf
					mov ax,video_erase_char;
					mov ecx,video_num_columns;
					mov edi,t1;
//			  mov edi,(scr_end - video_size_row);
					cld;	// 清方向位。
					rep stosw;	// 重复操作,在新出现行上填入擦除字符(空格字符)。					popf
				}
/*		  __asm__ ("cld\n\t"		       "rep\n\t"		       "stosw" 			::"a" (video_erase_char), "c" (video_num_columns), 		      "D" (scr_end - video_size_row):"cx","di");*/			} // 向显示控制器中写入新的屏幕内容对应的内存起始位置值。			set_origin ();// 否则表示不是整屏移动。也即表示从指定行top 开始的所有行向上移动1 行(删除1 行)。此时直接// 将屏幕从指定行top 到屏幕末端所有行对应的显示内存数据向上移动1 行,并在新出现的行上填入擦// 除字符。// %0-eax(擦除字符+属性);%1-ecx(top 行下1 行开始到屏幕末行的行数所对应的内存长字数);// %2-edi(top 行所处的内存位置);%3-esi(top+1 行所处的内存位置)。		}		else		{
			t1 = (bottom - top - 1) * video_num_columns >> 1;
			t2 = origin + video_size_row * top;
			t3 = origin + video_size_row * (top + 1);
			_asm {				pushf
//		  mov ecx,((bottom - top - 1) * video_num_columns >> 1);
				mov ecx,t1;
//		  mov edi,(origin + video_size_row * top);
				mov edi,t2;
//		  mov esi,(origin + video_size_row * (top + 1));
				mov esi,t3;
				mov ax,video_erase_char;
				cld;	// 清方向位。
				rep movsd;// 循环操作,将top+1 到bottom 行 所对应的内存块移到top 行开始处。
				mov ecx,video_num_columns;	// ecx = 1 行字符数。
				rep stosw;// 在新行上填入擦除字符。				popf
			}
/*	  __asm__ ("cld\n\t"		   "rep\n\t"		   "movsl\n\t"	//		   "movl _video_num_columns,%%ecx\n\t"		   "rep\n\t"	"stosw"::"a" (video_erase_char), "c" ((bottom - top - 1) * video_num_columns >> 1), 	"D" (origin + video_size_row * top), "S" (origin + video_size_row * (top + 1))
			:"cx", "di","si");*/		}	}// 如果显示类型不是EGA(是MDA),则执行下面移动操作。因为MDA 显示控制卡会自动调整超出显示范围// 的情况,也即会自动翻卷指针,所以这里不对屏幕内容对应内存超出显示内存的情况单独处理。处理// 方法与EGA 非整屏移动情况完全一样。	else				/* Not EGA/VGA */	{
		t1 = (bottom - top - 1) * video_num_columns >> 1;
		t2 = origin + video_size_row * top;
		t3 = origin + video_size_row * (top + 1);
		_asm {			pushf
			mov ecx,t1;
//		  mov ecx,((bottom - top - 1) * video_num_columns >> 1);
			mov edi,t2;
//		  mov edi,(origin + video_size_row * top);
			mov esi,t3;
//		  mov esi,(origin + video_size_row * (top + 1));
			mov ax,video_erase_char;
			cld;
			rep movsd;
			mov ecx,video_num_columns;
			rep stosw;			popf
		}/*    __asm__ ("cld\n\t" "rep\n\t" "movsl\n\t" \
		"movl _video_num_columns,%%ecx\n\t" "rep\n\t" "stosw"\
		::"a" (video_erase_char), \
		"c" ((bottom - top - 1) * video_num_columns >> 1), \
		"D" (origin + video_size_row * top), \
		"S" (origin + video_size_row * (top + 1)):"cx", "di","si");*/
	}}//// 向下卷动一行(屏幕窗口向上移动)。// 将屏幕窗口向上移动一行,屏幕显示的内容向下移动1 行,在被移动开始行的上方出现一新行。参见// 程序列表后说明。处理方法与scrup()相似,只是为了在移动显示内存数据时不出现数据覆盖错误情// 况,复制是以反方向进行的,也即从屏幕倒数第2 行的最后一个字符开始复制static voidscrdown (void){
	unsigned long t1,t2,t3;// 如果显示类型是EGA,则执行下列操作。// [??好象if 和else 的操作完全一样啊!为什么还要分别处理呢?难道与任务切换有关?]	if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)	{// %0-eax(擦除字符+属性);%1-ecx(top 行开始到屏幕末行-1 行的行数所对应的内存长字数);// %2-edi(屏幕右下角最后一个长字位置);%3-esi(屏幕倒数第2 行最后一个长字位置)。// 移动方向:[esi]??[edi],移动ecx 个长字。
		t1 = (bottom - top - 1) * video_num_columns >> 1;
		t2 = origin + video_size_row * bottom - 4;
		t3 = origin + video_size_row * (bottom - 1) - 4;
		_asm {			pushf
			mov ecx,t1;
//		  mov ecx,((bottom - top - 1) * video_num_columns >> 1);
			mov edi,t2;
//		  mov edi,(origin + video_size_row * bottom - 4);
			mov esi,t3;
//		  mov esi,(origin + video_size_row * (bottom - 1) - 4);
			mov ax,video_erase_char;
			std;	// 置方向位。
			rep movsd;	// 重复操作,向下移动从top 行到bottom-1 行对应的内存数据。
			add edi,2;	/* %edi 已经减4,因为也是方向填擦除字符 */
			mov ecx,video_num_columns;	// 置ecx=1 行字符数。
			rep stosw;	// 将擦除字符填入上方新行中。			popf
		}/*      __asm__ ("std\n\t"	       "rep\n\t"	       "movsl\n\t"	// 	       "addl $2,%%edi\n\t"	       "movl _video_num_columns,%%ecx\n\t"	       "rep\n\t"		   "stosw":
	:"a" (video_erase_char), \
	"c" ((bottom - top - 1) * video_num_columns >> 1), \
	"D" (origin + video_size_row * bottom - 4), \
	"S" (origin + video_size_row * (bottom - 1) - 4)
	:"ax", "cx", "di", "si");*/	}// 如果不是EGA 显示类型,则执行以下操作(目前与上面完全一样)。	else				/* Not EGA/VGA */	{
		t1 = (bottom - top - 1) * video_num_columns >> 1;
		t2 = origin + video_size_row * bottom - 4;
		t3 = origin + video_size_row * (bottom - 1) - 4;
		_asm {			pushf
			mov ecx,t1;
//		  mov ecx,((bottom - top - 1) * video_num_columns >> 1);
			mov edi,t2;
//		  mov edi,(origin + video_size_row * bottom - 4);
			mov esi,t3;
//		  mov esi,(origin + video_size_row * (bottom - 1) - 4);
			mov ax,video_erase_char;
			std;
			rep movsd;
			add edi,2;/* %edi has been decremented by 4 */
			mov ecx,video_num_columns;
			rep stosw;			popf
		} /*     __asm__ ("std\n\t" "rep\n\t" "movsl\n\t" "addl $2,%%edi\n\t"				"movl _video_num_columns,%%ecx\n\t" "rep\n\t" "stosw":
			:"a" (video_erase_char), 
			"c" ((bottom - top - 1) * video_num_columns >> 1), 
			"D" (origin + video_size_row * bottom - 4), 
			"S" (origin + video_size_row * (bottom - 1) - 4)	       :"ax", "cx", "di","si");*/	}}//// 光标位置下移一行(lf - line feed 换行)。static voidlf (void){// 如果光标没有处在倒数第2 行之后,则直接修改光标当前行变量y++,并调整光标对应显示内存位置// pos(加上屏幕一行字符所对应的内存长度)。	if (y + 1 < bottom)	{		y++;		pos += video_size_row;		return;	}// 否则需要将屏幕内容上移一行。	scrup ();}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -