【第7章】TTY【一】

     有用过LINUX的同学都知道,在文本模式下,按下shift+ctrl+Fn键,0<n<7,就会切换到对应的console,各个console的输入和输出各自独立,互不干扰。下面我们就在自己的OS中加入这一功能。

     为了尽量简单起见,保持视频适配器最简单的模式80*25模式,此模式下显存的大小为32K,我们计划弄3个console,所以每个console就分得10K,原理在上一篇已经讲过,当发生console的切换时,就向适配器的相应端口写入要显示的console的起始地址。

     首先阐述一下编程思路,原来的程序中只有一个console,所以中断进程就不断的从scan code缓冲区中取得scan code转化成对应的key值,并直接打印出来。而现在有3个console,那么此时key值应该送往当前的console来打印,为了逻辑清晰,为每个console分配1个缓冲区,用来存放key的值。

     创建1个头文件tty.h,建立一个TTY结构:

Code:
  1. /*====================================================================  
  2.                                 tty.h  
  3. ====================================================================*/  
  4.   
  5. #ifndef _TTY_H_   
  6. #define _TTY_H_   
  7.        
  8.     struct s_console;   
  9.        
  10.     typedef struct s_tty   
  11.     {   
  12.         u32 tty_buffer[256];   
  13.         u32 *p_tty_head;   
  14.         u32 *p_tty_tail;   
  15.         int count;   
  16.         struct s_console *console;   
  17.     }TTY;   
  18.        
  19. #endif   

     此结构跟scan code缓冲区的结构很相似,但多了1个s_console结构,这个结构是干嘛的呢,我们知道每个console对应显存不同的位置,占据显存的内存的字节数,console当前的光标位置等信息,需要把这些数据也组织起来,创建1个console.h头文件:

Code:
  1. /*====================================================================  
  2.                               console.h  
  3. ====================================================================*/  
  4.   
  5. #ifndef _CONSOLE_H_   
  6. #define _CONSOLE_H_   
  7.        
  8.     typedef struct s_console   
  9.     {   
  10.         u32 start_addr;   
  11.         u32 current_addr;   
  12.         u32 memory_size;   
  13.         u32 cursor;    
  14.     }CONSOLE;   
  15.        
  16. #endif   

     结构定义好了,接着就是为它们分配内存空间了,由于有3个console,所以分配一个TTY的数组和CONSOLE的数组,数组元素个数都为3。此外,还要记录当前的console是哪一个,所以还要分配一个全部变量来记录这件事儿。在global.h和global.c中添加:

Code:
  1. extern TTY TTY_Table[];   
  2. extern CONSOLE Console_Table[];   
  3. extern int Current_Console;  
Code:
  1. TTY TTY_Table[3];   
  2. CONSOLE Console_Table[3];   
  3. int Current_Console;  

     接下来是初始化这些个数组和变量,由于是跟TTY相关的,那么就在终端进程的执行体中来做:

Code:
  1. TTY *p_tty;     
  2. for(p_tty = TTY_Table ; p_tty < TTY_Table + 3 ; p_tty++)   
  3. {   
  4.     p_tty->p_tty_head = p_tty->p_tty_tail = p_tty->tty_buffer;   
  5.     p_tty->count = 0;   
  6.     p_tty->console = Console_Table + (p_tty - TTY_Table);   
  7.     p_tty->console->start_addr = 1024 * 10 * (p_tty - TTY_Table);   
  8.     p_tty->console->current_addr = p_tty->console->start_addr;   
  9.     p_tty->console->cursor = p_tty->console->start_addr;   
  10.     p_tty->console->memory_size = 1024 * 10;   
  11. }   
  12.   
  13. Current_Console = 0;  

     上述代码把数组中各元素的初始值都设置好,注意console的初始值,每个console占了10K,第1个console的起始地址从0xb8000开始,下一个就是0xb8000+10k。把当前console设置为0。

     进程体接着执行一个死循环,先调用Keyboard_Read韩素来解析扫描码,再调用In_Process函数来把解析后得到的key放到当前console的输出缓冲区中,这两个函数的接口都已经改变,需要在proto.h中修改它们的原型:

Code:
  1. void Keyboard_Read(TTY *p_tty);   
  2. void In_Process(TTY *p_tty,u32 key_value);  

     它们的函数体也有所改变,在Keyboard_Read函数中改变的是:

Code:
  1. In_Process(p_tty,key);     

     In_Process的函数体变化比较大:

Code:
  1. void In_Process(TTY *p_tty,u32 key_value)   
  2. {/*  
  3.     char disp[2];  
  4.     Memory_Set(disp,2,0);  
  5.     */  
  6.     if(!(key_value & FLAG_EXT))   
  7.     {/*  
  8.         disp[0] = key_value & 0xff;  
  9.         Disp_Color_Str(disp,0xa);  
  10.         Disable_Int();  
  11.         Out_Byte(0x3d4,0xf);  
  12.         Out_Byte(0x3d5,(Disp_Pos / 2) & 0xff);  
  13.         Out_Byte(0x3d4,0xe);  
  14.         Out_Byte(0x3d5,((Disp_Pos /2) >> 8) & 0xff);  
  15.         Enable_Int();   
  16.         */  
  17.         if(p_tty->count < 256)   
  18.         {   
  19.             Disable_Int();   
  20.             *p_tty->p_tty_tail = key_value;   
  21.             p_tty->p_tty_tail++;   
  22.             if(p_tty->p_tty_tail == p_tty->tty_buffer + 256)   
  23.             {   
  24.                 p_tty->p_tty_tail = p_tty->tty_buffer;   
  25.             }   
  26.             p_tty->count++;   
  27.             Enable_Int();   
  28.         }   
  29.     }   
  30.     else  
  31.     {   
  32.         /* 略 */        
  33.     }   
  34. }   

     代码逻辑已经改变,变成把解析得到的key值放到当前console的输出缓冲区中。

     OK,下面的工作就该是把各个console的输出缓冲区的东东打印到各自对应的地方了。先把死循环的代码贴上来:

Code:
  1. char c_disp;
    u8 *disp_addr;
  2. while(1)   
  3.     {   
  4.         for(p_tty = TTY_Table ; p_tty < TTY_Table + 3 ; p_tty++)   
  5.         {   
  6.             if(p_tty->console == &Console_Table[Current_Console])   
  7.             {   
  8.                 Keyboard_Read(p_tty);   
  9.             }   
  10.             if(p_tty->count > 0)   
  11.             {   
  12.                 Disable_Int();   
  13.                 c_disp = *p_tty->p_tty_head;   
  14.                 p_tty->p_tty_head++;   
  15.                 if(p_tty->p_tty_head == p_tty->tty_buffer + 256)   
  16.                 {   
  17.                     p_tty->p_tty_head = p_tty->tty_buffer;   
  18.                 }   
  19.                 p_tty->count--;   
  20.                 Enable_Int();   
  21.                 disp_addr = (u8*)(0xb8000 + p_tty->console->cursor);   
  22.                 *disp_addr++ = c_disp;   
  23.                 *disp_addr++ = 0xa;   
  24.                 p_tty->console->cursor += 2;    
  25.                 Disable_Int();   
  26.                 Out_Byte(0x3d4,0xf);   
  27.                 Out_Byte(0x3d5,(p_tty->console->cursor / 2) & 0xff);   
  28.                 Out_Byte(0x3d4,0xe);   
  29.                 Out_Byte(0x3d5,((p_tty->console->cursor /2) >> 8) & 0xff);   
  30.                 Enable_Int();    
  31.             }   
  32.         }   
  33.     }  

     代码是实现了功能,但没像作者那么分成好几个小的功能函数,来显得结构清晰,这是后话。

     首先是TTY的一个遍历,如果遍历到的TTY包含的console是当前的console,则调用Keyboard_Read函数来把解析后的key填充到当前的console的输出缓冲区中。然后判断当前遍历的TTY的console的输出缓冲区有没有什么要输出的,有就取出来,打印到相应的位置。注意到,在打印的时候没有使用Disp_Color_Str函数,因为此函数是根据0xB8000来打印的,如果当前console的start_addr不是0xB8000,那就有点麻烦了。既然在CONSOLE结构中已经有了打印的相关信息了,那么就直接根据这些信息来打印了。利用指针来直接把输出缓冲区中的值写到内存去。最后再把光标定位到相应的位置。

     我们还没有写切换console的代码,这个待会儿再实现。添加了两个头文件,要修改MAKEFILE,而且包含proto.h的源文件中要先包含这两个新添加的头文件。

     make,运行,结果跟上次没什么两样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值