linux-0.11分析:init文件 main.c的第五个初始化函数tty_init()第七篇随笔

5、第五个初始化函数,tty_init()

参考 [github这个博主的 厉害][ https://github.com/sunym1993/flash-linux0.11-talk ]

先看看这个tty_init()的这段代码:

kernel文件 -> chr_drv文件 ->tty_io.c

void tty_init(void)
{
rs_init();
con_init();
}

可以看到这两个函数调用了rs_init()和con_init()这两个函数:

第一个方法:rs_init()

kernel文件 -> chr_drv文件 ->serial.c

void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);
set_intr_gate(0x23,rs2_interrupt);
init(tty_table[1].read_q.data);
init(tty_table[2].read_q.data);
outb(inb_p(0x21)&0xE7,0x21);
}

可以看出它里面又调用了三个函数:set_intr_gate、init、outb

可以看到第一个set_intr_gate很熟悉,就是在注册中断信息,注册了24(rs1_interrupt),23(rs2_interrupt)中断信息

set_intr_gate()

#define set_intr_gate(n,addr) \
_set_gate(&idt[n],14,0,addr)

init(),传入的就是tty_table这个结构体传入的端口

static void init(int port)
{
outb_p(0x80,port+3); /* 设置线路控制寄存器的DLAB */
outb_p(0x30,port); /* 除数的倒数(48->2400 bps) */
outb_p(0x00,port+1); /* 除数的/*MS */
outb_p(0x03,port+3); /* 重置DLAB */
outb_p(0x0b,port+4); /* 设置DTR、RTS和OUT_ 2 */
outb_p(0x0d,port+1); /* 启用除写入之外的所有Intr */
(void)inb(port); /* 读取数据端口以重置内容(?) */
}

这里就是通过outb_p()函数设置了一些东西,具体我也不是很了解

第二个方法:con_init()这个方法比较长,我们一部分一部分来看

kernel文件 -> chr_drv文件 ->console.c

void con_init(void)
{
register unsigned char a;
char *display_desc = "????";
char *display_ptr;
video_num_columns = ORIG_VIDEO_COLS; //(((*(unsigned short *)0x90006) & 0xff00) >> 8)
video_size_row = video_num_columns * 2;
video_num_lines = ORIG_VIDEO_LINES;
video_page = ORIG_VIDEO_PAGE;
video_erase_char = 0x0720;
if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
{
video_mem_start = 0xb0000;
video_port_reg = 0x3b4;
video_port_val = 0x3b5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAM;
video_mem_end = 0xb8000;
display_desc = "EGAm";
}
else
{
video_type = VIDEO_TYPE_MDA;
video_mem_end = 0xb2000;
display_desc = "*MDA";
}
}
else /* If not, it is color. */
{
video_mem_start = 0xb8000;
video_port_reg = 0x3d4;
video_port_val = 0x3d5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAC;
video_mem_end = 0xbc000;
display_desc = "EGAc";
}
else
{
video_type = VIDEO_TYPE_CGA;
video_mem_end = 0xba000;
display_desc = "*CGA";
}
}
/* Let the user known what kind of display driver we are using */
display_ptr = ((char *)video_mem_start) + video_size_row - 8;
while (*display_desc)
{
*display_ptr++ = *display_desc++;
display_ptr++;
}
/* Initialize the variables used for scrolling (mostly EGA/VGA) */
origin = video_mem_start;
scr_end = video_mem_start + video_num_lines * video_size_row;
top = 0;
bottom = video_num_lines;
gotoxy(ORIG_X,ORIG_Y);
set_trap_gate(0x21,&keyboard_interrupt);
outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61);
outb_p(a|0x80,0x61);
outb(a,0x61);
}

第一部分:

首先先看看这个文件上面的一些define:提取对下面有用的

#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 */
#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */
#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */
#define NPAR 16
......
register unsigned char a;
char *display_desc = "????";
char *display_ptr;
video_num_columns = ORIG_VIDEO_COLS; //这个是在设置为,显示模式
// video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8)
video_size_row = video_num_columns * 2;
video_num_lines = ORIG_VIDEO_LINES; //是在设置行数
// video_num_lines = (25)
video_page = ORIG_VIDEO_PAGE; //在设置显示页面
// video_page = (*(unsigned short *)0x90004)
video_erase_char = 0x0720;
......

这是就是在初始化一些东西,video_xxx_xx都是一些全局变量,后面的都是一些定义好的数据在这个文件的上面

还记得在setup.s阶段有一个存储设备相关信息表

地址 字节 存储的东西
0x90000 1 光标列号
0x90001 1 光标行号
0x90002 2 内存大小
0x90004 2 显示页面
0x90006 1 显示模式
0x90007 1 窗口宽度
0x90008 2 配置参数
0x9000A 2 配置参数
0x9000C 2 配置参数
0x90080 16 hd0硬盘信息
0x90010 16 hd1硬盘信息

第二部分:

if (ORIG_VIDEO_MODE == 7) /* 这是单色显示器吗 */
{
video_mem_start = 0xb0000;
video_port_reg = 0x3b4;
video_port_val = 0x3b5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAM; 0x20
video_mem_end = 0xb8000;
display_desc = "EGAm";
}
else
{
video_type = VIDEO_TYPE_MDA; 0x10
video_mem_end = 0xb2000;
display_desc = "*MDA";
}
}
else /* 它配置了颜色. */
{
video_mem_start = 0xb8000;
video_port_reg = 0x3d4;
video_port_val = 0x3d5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAC; 0x21
video_mem_end = 0xbc000;
display_desc = "EGAc";
}
else
{
video_type = VIDEO_TYPE_CGA; 0x11
video_mem_end = 0xba000;
display_desc = "*CGA";
}
}

基本上可以看出就是一些不同if else判断设置一些东西;设置:video_mem_start、video_port_reg、video_port_val、video_type、video_mem_end、display_desc这些参数

总共有四个分支

第一个分支结果 第二个分支结果
video_mem_start = 0xb0000; video_mem_start = 0xb0000;
video_port_reg = 0x3b4; video_port_reg = 0x3b4;
video_port_val = 0x3d5; video_port_val = 0x3d5;
video_type = 0x20; video_type = 0x10;
video_mem_end = 0xb8000; video_mem_end = 0xb2000;
display_desc = "EGAm"; display_desc = "*MDA";
第三个分支结果 第四个分支结果
video_mem_start = 0xb8000; video_mem_start = 0xb8000;
video_port_reg = 0x3d4; video_port_reg = 0x3d4;
video_port_val = 0x3b5; video_port_val = 0x3b5;
video_type = 0x21; video_type = 0x11;
video_mem_end = 0xbc000; video_mem_end = 0xba000;
display_desc = "EGAc"; display_desc = "*CGA";

第三部分:

/*让用户知道我们使用的是哪种显示驱动程序 */
display_ptr = ((char *)video_mem_start) + video_size_row - 8;
while (*display_desc)
{
*display_ptr++ = *display_desc++;
display_ptr++;
}

这是通过上面的结果去判断是那种显示的驱动

第四部分:

/* 初始化用于滚动的变量(主要是EGA/VGA) */
origin = video_mem_start;
scr_end = video_mem_start + video_num_lines * video_size_row;
top = 0;
bottom = video_num_lines;
gotoxy(ORIG_X,ORIG_Y);
// ORIG_X (*(unsigned char *)0x90000)光标列号
// ORIG_Y (*(unsigned char *)0x90001)光标行号
set_trap_gate(0x21,&keyboard_interrupt);
outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61);
outb_p(a|0x80,0x61);
outb(a,0x61);

这是在设置光标位置,开启0x21键盘中断

这里学过汇编就应该知道,一般开起int 0x21中断后,就会根据不同寄存器的值跳转到中断位置进行操作

gotoxy(ORIG_X,ORIG_Y); 就在进行操了,看看这个函数

static unsigned long pos;
static unsigned long x,y;
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;
y=new_y;
pos=origin + y*video_size_row + (x<<1);
}

这里先判断是否超过行或者列

在用x,y,pos来保存,x:列号,y:行号,pos:对应的内存地址

然后就是开起0x21中断:

具体过程如下:

这个中断在这个文件下的keyboard.S中,比较长就不一一列举了

_keyboard_interrupt:
......
call _do_tty_interrupt
......

在kernel文件 -> chr_drv文件 ->tty_io.c

void do_tty_interrupt(int tty)
{
copy_to_cooked(tty_table+tty);
}
void copy_to_cooked(struct tty_struct * tty)
{
signed char c;
......
tty->write(tty);
.....
wake_up(&tty->secondary.proc_list);
}

在kernel文件 -> chr_drv文件 ->console.c

void con_write(struct tty_struct * tty)
{
int nr;
char c;
......
__asm__("movb _attr,%%ah\n\t"
"movw %%ax,%1\n\t"
::"a" (c),"m" (*(short *)pos)
:"ax");
pos += 2;
x++;
......

可以看到这段内联汇编,在进行光标的移动操作;省略的一部分就是一边界操作:都在console.c的函数

static void lf(void) //换行
static void ri(void) //退行
......

就不一一列举了

到这里就结束了这个tty_init()初始化;这个时候键盘的功能解决大致完成了,就看通过键盘输入来进行中断了

posted @   水三丫  阅读(204)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示