📄 console.c
字号:
// 光标回到第1 列(0 列)左端(cr - carriage return 回车)。static void cr(void){// 光标所在的列号*2 即0 列到光标所在列对应的内存字节长度。pos -= x<<1;x=0;}// 擦除光标前一字符(用空格替代)(del - delete 删除)。static void del(void){// 如果光标没有处在0 列,则将光标对应内存位置指针pos 后退2 字节(对应屏幕上一个字符),然后// 将当前光标变量列值减1,并将光标所在位置字符擦除。if (x) {pos -= 2;x--;*(unsigned short *)pos = video_erase_char;}}//// 删除屏幕上与光标位置相关的部分,以屏幕为单位。csi - 控制序列引导码(Control Sequence// Introducer)。// ANSI 转义序列:'ESC [sJ'(s = 0 删除光标到屏幕底端;1 删除屏幕开始到光标处;2 整屏删除)。// 参数:par - 对应上面s。static void csi_J(int par){long count __asm__( "cx"); // 设为寄存器变量。long start __asm__( "di");// 首先根据三种情况分别设置需要删除的字符数和删除开始的显示内存位置。switch (par) {case 0: /* erase from cursor to end of display */ /* 擦除光标到屏幕底端 */count = (scr_end-pos)>>1;start = pos;break;case 1: /* erase from start to cursor */ /* 删除从屏幕开始到光标处的字符 */count = (pos-origin)>>1;start = origin;break;case 2: /* erase whole display */ /* 删除整个屏幕上的字符 */count = video_num_columns * video_num_lines;start = origin;break;default:return;}// 然后使用擦除字符填写删除字符的地方。// %0 - ecx(要删除的字符数count);%1 - edi(删除操作开始地址);%2 - eax(填入的擦除字符)。__asm__( "cld\n\t""rep\n\t""stosw\n\t":: "c" (count),"D" (start), "a" (video_erase_char): "cx", "di");}//// 删除行内与光标位置相关的部分,以一行为单位。// ANSI 转义字符序列:'ESC [sK'(s = 0 删除到行尾;1 从开始删除;2 整行都删除)。static void csi_K(int par){long count __asm__( "cx"); // 设置寄存器变量。long start __asm__( "di");// 首先根据三种情况分别设置需要删除的字符数和删除开始的显示内存位置。switch (par) {case 0: /* erase from cursor to end of line */ /* 删除光标到行尾字符 */if (x>=video_num_columns)return;count = video_num_columns-x;start = pos;break;case 1: /* erase from start of line to cursor */ /* 删除从行开始到光标处 */start = pos - (x<<1);count = (x<video_num_columns)?x:video_num_columns;break;case 2: /* erase whole line */ /* 将整行字符全删除 */start = pos - (x<<1);count = video_num_columns;break;default:return;}// 然后使用擦除字符填写删除字符的地方。// %0 - ecx(要删除的字符数count);%1 - edi(删除操作开始地址);%2 - eax(填入的擦除字符)。__asm__( "cld\n\t""rep\n\t""stosw\n\t":: "c" (count),"D" (start), "a" (video_erase_char): "cx", "di");}//// 允许翻译(重显)(允许重新设置字符显示方式,比如加粗、加下划线、闪烁、反显等)。// ANSI 转义字符序列:'ESC [nm'。n = 0 正常显示;1 加粗;4 加下划线;7 反显;27 正常显示。void csi_m(void){int i;for (i=0;i<=npar;i++)switch (par[i]) {case 0:attr=0x07;break;case 1:attr=0x0f;break;case 4:attr=0x0f;break;case 7:attr=0x70;break;case 27:attr=0x07;break;}}//// 根据设置显示光标。// 根据显示内存光标对应位置pos,设置显示控制器光标的显示位置。static inline void set_cursor(void){cli();// 首先使用索引寄存器端口选择显示控制数据寄存器r14(光标当前显示位置高字节),然后写入光标// 当前位置高字节(向右移动9 位表示高字节移到低字节再除以2)。是相对于默认显示内存操作的。outb_p(14, video_port_reg);outb_p(0xff&((pos-video_mem_start)>>9), video_port_val);// 再使用索引寄存器选择r15,并将光标当前位置低字节写入其中。outb_p(15, video_port_reg);outb_p(0xff&((pos-video_mem_start)>>1), video_port_val);sti();}//// 发送对终端VT100 的响应序列。// 将响应序列放入读缓冲队列中。static void respond(struct tty_struct * tty){char * p = RESPONSE;cli(); // 关中断。while (*p) { // 将字符序列放入写队列。PUTCH(*p,tty->read_q);p++;}sti(); // 开中断。copy_to_cooked(tty); // 转换成规范模式(放入辅助队列中)。}//// 在光标处插入一空格字符。static void insert_char(void){int i=x;unsigned short tmp, old = video_erase_char;unsigned short * p = (unsigned short *) pos;// 光标开始的所有字符右移一格,并将擦除字符插入在光标所在处。// 若一行上都有字符的话,则行最后一个字符将不会更动??while (i++<video_num_columns) {tmp=*p;*p=old;old=tmp;p++;}}//// 在光标处插入一行(则光标将处在新的空行上)。// 将屏幕从光标所在行到屏幕底向下卷动一行。static void insert_line(void){int oldtop,oldbottom;oldtop=top; // 保存原top,bottom 值。oldbottom=bottom;top=y; // 设置屏幕卷动开始行。bottom = video_num_lines; // 设置屏幕卷动最后行。scrdown(); // 从光标开始处,屏幕内容向下滚动一行。top=oldtop; // 恢复原top,bottom 值。bottom=oldbottom;}//// 删除光标处的一个字符。static void delete_char(void){int i;unsigned short * p = (unsigned short *) pos;// 如果光标超出屏幕最右列,则返回。if (x>=video_num_columns)return;// 从光标右一个字符开始到行末所有字符左移一格。i = x;while (++i < video_num_columns) {*p = *(p+1);p++;}// 最后一个字符处填入擦除字符(空格字符)。*p = video_erase_char;}//// 删除光标所在行。// 从光标所在行开始屏幕内容上卷一行。static void delete_line(void){int oldtop,oldbottom;oldtop=top; // 保存原top,bottom 值。oldbottom=bottom;top=y; // 设置屏幕卷动开始行。bottom = video_num_lines; // 设置屏幕卷动最后行。scrup(); // 从光标开始处,屏幕内容向上滚动一行。top=oldtop; // 恢复原top,bottom 值。bottom=oldbottom;}//// 在光标处插入nr 个字符。// ANSI 转义字符序列:'ESC [n@ '。// 参数 nr = 上面n。static void csi_at(unsigned int nr){// 如果插入的字符数大于一行字符数,则截为一行字符数;若插入字符数nr 为0,则插入1 个字符。if (nr > video_num_columns)nr = video_num_columns;else if (!nr)nr = 1;// 循环插入指定的字符数。while (nr--)insert_char();}//// 在光标位置处插入nr 行。// ANSI 转义字符序列'ESC [nL'。static void csi_L(unsigned int nr){// 如果插入的行数大于屏幕最多行数,则截为屏幕显示行数;若插入行数nr 为0,则插入1 行。if (nr > video_num_lines)nr = video_num_lines;else if (!nr)nr = 1;// 循环插入指定行数nr。while (nr--)insert_line();}//// 删除光标处的nr 个字符。// ANSI 转义序列:'ESC [nP'。static void csi_P(unsigned int nr){// 如果删除的字符数大于一行字符数,则截为一行字符数;若删除字符数nr 为0,则删除1 个字符。if (nr > video_num_columns)nr = video_num_columns;else if (!nr)nr = 1;// 循环删除指定字符数nr。while (nr--)delete_char();}//// 删除光标处的nr 行。// ANSI 转义序列:'ESC [nM'。static void csi_M(unsigned int nr){// 如果删除的行数大于屏幕最多行数,则截为屏幕显示行数;若删除的行数nr 为0,则删除1 行。if (nr > video_num_lines)nr = video_num_lines;else if (!nr)nr=1;// 循环删除指定行数nr。while (nr--)delete_line();}static int saved_x=0; // 保存的光标列号。static int saved_y=0; // 保存的光标行号。//// 保存当前光标位置。static void save_cur(void){saved_x=x;saved_y=y;}//// 恢复保存的光标位置。static void restore_cur(void){gotoxy(saved_x, saved_y);}//// 控制台写函数。// 从终端对应的tty 写缓冲队列中取字符,并显示在屏幕上。void con_write(struct tty_struct * tty){int nr;char c;// 首先取得写缓冲队列中现有字符数nr,然后针对每个字符进行处理。nr = CHARS(tty->write_q);while (nr--) {// 从写队列中取一字符c,根据前面所处理字符的状态state 分别处理。状态之间的转换关系为:// state = 0:初始状态;或者原是状态4;或者原是状态1,但字符不是'[';// 1:原是状态0,并且字符是转义字符ESC(0x1b = 033 = 27);// 2:原是状态1,并且字符是'[';// 3:原是状态2;或者原是状态3,并且字符是';'或数字。// 4:原是状态3,并且字符不是';'或数字;GETCH(tty->write_q,c);switch(state) {case 0:// 如果字符不是控制字符(c>31),并且也不是扩展字符(c<127),则if (c>31 && c<127) {// 若当前光标处在行末端或末端以外,则将光标移到下行头列。并调整光标位置对应的内存指针pos。if (x>=video_num_columns) {x -= video_num_columns;pos -= video_size_row;lf();}// 将字符c 写到显示内存中pos 处,并将光标右移1 列,同时也将pos 对应地移动2 个字节。__asm__( "movb _attr,%%ah\n\t""movw %%ax,%1\n\t":: "a" (c), "m" (*(short *)pos): "ax");pos += 2;x++;// 如果字符c 是转义字符ESC,则转换状态state 到1。} else if (c==27)state=1;// 如果字符c 是换行符(10),或是垂直制表符VT(11),或者是换页符FF(12),则移动光标到下一行。else if (c==10 || c==11 || c==12)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -