--------------------------------------------------------
TTY 框架搭建
--------------------------------------------------------
从这一篇章开始,开始讨论 TTY ,而 TTY 涉及到 "视频" 的操作,因此先要了解 VGA ...
一,让光标出现在显示字符的后面
运行:
可以看到光标出现在打印字符的后面!
二,搭建 TTY 框架
这次的框架搭建虽然有点麻烦,但是机制并不复杂,let us do it!
// ----------------------------
// <console.c>
// Jack Zheng 12.1
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
/* 本文件内函数声明 */
PRIVATE void set_cursor(unsigned int position);
PUBLIC void out_char(CONSOLE* p_con, char ch)
{
t_8* p_vmem = (t_8*)(V_MEM_BASE + disp_pos);
*p_vmem++ = ch;
*p_vmem++ = DEFAULT_CHAR_COLOR;
disp_pos += 2;
set_cursor(disp_pos/2);
}
PUBLIC t_bool is_current_console(CONSOLE* p_con)
{
return (p_con == &console_table[nr_current_console]);
}
PRIVATE void set_cursor(unsigned int position)
{
disable_int();
out_byte(CRTC_ADDR_REG, CRTC_DATA_IDX_CURSOR_H);
out_byte(CRTC_DATA_REG, (position >> 8) & 0xFF);
out_byte(CRTC_ADDR_REG, CRTC_DATA_IDX_CURSOR_L);
out_byte(CRTC_DATA_REG, position & 0xFF);
enable_int();
}
// ----------------------------
// <tty.c>
// Jack Zheng 11.30
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#define TTY_FIRST (tty_table)
#define TTY_END (tty_table + NR_CONSOLES)
/* 本文件内函数声明 */
PRIVATE void init_tty(TTY* p_tty);
PRIVATE void tty_do_read(TTY* p_tty);
PRIVATE void tty_do_write(TTY* p_tty);
PUBLIC void task_tty()
{
TTY* p_tty;
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
init_tty(p_tty);
}
nr_current_console = 0;
while (1) {
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
tty_do_read(p_tty);
tty_do_write(p_tty);
}
}
}
PRIVATE void init_tty(TTY* p_tty)
{
p_tty->inbuf_count = 0;
p_tty->p_inbuf_head = p_tty->p_inbuf_tail = p_tty->in_buf;
int nr_tty = p_tty - tty_table;
p_tty->p_console = console_table + nr_tty;
}
PUBLIC void in_process(TTY* p_tty, t_32 key)
{
if (!(key & FLAG_EXT)) {
if (p_tty->inbuf_count < TTY_IN_BYTES) {
*(p_tty->p_inbuf_head) = key;
p_tty->p_inbuf_head++;
if (p_tty->p_inbuf_head == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_head = p_tty->in_buf;
}
p_tty->inbuf_count++;
}
}
else {
int raw_code = key & MASK_RAW;
switch(raw_code) {
case UP:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) { /* Shift + Up */
disable_int();
out_byte(CRTC_ADDR_REG, CRTC_DATA_IDX_START_ADDR_H);
out_byte(CRTC_DATA_REG, ((80*15) >> 8) & 0xFF);
out_byte(CRTC_ADDR_REG, CRTC_DATA_IDX_START_ADDR_L);
out_byte(CRTC_DATA_REG, (80*15) & 0xFF);
enable_int();
}
break;
case DOWN:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) { /* Shift + Down */
}
break;
default:
break;
}
}
}
PRIVATE void tty_do_read(TTY* p_tty)
{
if (is_current_console(p_tty->p_console)) {
keyboard_read(p_tty);
}
}
PRIVATE void tty_do_write(TTY* p_tty)
{
if (p_tty->inbuf_count) {
char ch = *(p_tty->p_inbuf_tail);
p_tty->p_inbuf_tail++;
if (p_tty->p_inbuf_tail == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_tail = p_tty->in_buf;
}
p_tty->inbuf_count--;
out_char(p_tty->p_console, ch);
}
}
// ----------------------------
// <gloabl.h>
// Jack Zheng 11.27
// ----------------------------
#ifndef _TINIX_GLOBAL_H_
#define _TINIX_GLOBAL_H_
// these variables will real define in global.c!
#ifdef GLOBAL_VARIABLES_HERE
#undef EXTERN
#define EXTERN
#endif
EXTERN int disp_pos;
EXTERN t_32 k_reenter;
EXTERN int ticks;
EXTERN PROCESS* p_proc_ready;
EXTERN TSS tss;
/* gdt & idt */
EXTERN t_8 gdt_ptr[6];
EXTERN DESCRIPTOR gdt[GDT_SIZE];
EXTERN t_8 idt_ptr[6];
EXTERN GATE idt[IDT_SIZE];
/* task... */
extern PROCESS proc_table[NR_TASKS];
extern char task_stack[STACK_SIZE_TOTAL];
extern TASK task_table[NR_TASKS];
/* tty and console*/
#ifndef _TINIX_TTY_H_
#define TTY_IN_BYTES 256 /* tty input queue size */
typedef struct s_tty
{
t_32 in_buf[TTY_IN_BYTES]; /* TTY 输入缓冲区 */
t_32* p_inbuf_head; /* 指向缓冲区中下一个空闲位置 */
t_32* p_inbuf_tail; /* 指向键盘任务应处理的键值 */
int inbuf_count; /* 缓冲区中已经填充了多少 */
struct s_console * p_console;
}TTY;
#endif
#ifndef _TINIX_CONSOLE_H_
typedef struct s_console
{
//struct s_tty* p_tty;
unsigned int current_start_addr; /* 当前显示到了什么位置 */
unsigned int original_addr; /* 当前控制台对应显存位置 */
unsigned int v_mem_limit; /* 当前控制台占的显存大小 */
unsigned int cursor; /* 当前光标位置 */
}CONSOLE;
#endif
EXTERN int nr_current_console;
extern TTY tty_table[];
extern CONSOLE console_table[];
/* hard interrupt handler table */
extern t_pf_irq_handler irq_table[NR_IRQ];
/* syscall function table */
extern t_sys_call sys_call_table[NR_SYS_CALL];
#endif
运行:
接下来我们捋一下键盘和 TTY 的配合:
1,首先键盘中断函数 keyboard_handler 从键盘端口里取得扫描码放入 kb_in 中存储:
typedef struct s_kb {
char* p_head; /* 指向缓冲区中下一个空闲位置 */
char* p_tail; /* 指向键盘任务应处理的字节 */
int count; /* 缓冲区中共有多少字节 */
char buf[KB_IN_BYTES]; /* 缓冲区 */
}KB_INPUT;
2,然后 TTY_TASK 循环调用函数 keyboard_read 读取 kb_in ,并将 kb_in 中扫描码解析成字符交给 in_process!
3,in_process 将解析到的字符交给传递给它的 TTY,TTY 有个数据结构 struct TTY 用来保存交给自己的字符:
typedef struct s_tty
{
t_32 in_buf[TTY_IN_BYTES]; /* TTY 输入缓冲区 */
t_32* p_inbuf_head; /* 指向缓冲区中下一个空闲位置 */
t_32* p_inbuf_tail; /* 指向键盘任务应处理的键值 */
int inbuf_count; /* 缓冲区中已经填充了多少 */
struct s_console * p_console;
}TTY;
4,最后 tty_do_write 函数将 TTY 缓冲区的字符写到 TTY 对应的 Console 上!
这里有两个数结构 kb_in 和 in_buf ,kb_in 保存从键盘缓冲区接收到的扫描码,然后被 keyboard_read 解析成字符送给 in_buf !
OK,TTY 的框架已经搭建起来了,下一节我们就实现一个多 TTY 的内核!