• ORANGE'S操作系统:编写OS层次的IO程序(实现TAB、搜索模式、撤销)


    github:https://github.com/Cpaulyz/OSLAB/tree/master/lab3

    base:原书代码

    hw:作业代码

    1 一些笔记

    在当前目录(.)下创建一个新的软盘镜像a.img

    mkfs.fat -F 12 -C a.img 1440

    bootloader使用

    image-20201117212608456

    加入内核

    image-20201117212714573

    image-20201117220539784

    改用

    ld -m elf_i386 -s -o kernel.bin kernel.o

    Makefile

    #########################
    # Makefile for Orange'S #
    #########################
    
    # Entry point of Orange'S
    # It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
    ENTRYPOINT	= 0x30400
    
    # Offset of entry point in kernel file
    # It depends on ENTRYPOINT
    ENTRYOFFSET	=   0x400
    
    # Programs, flags, etc.
    ASM		= nasm
    DASM		= ndisasm
    CC		= gcc
    LD		= ld
    ASMBFLAGS	= -I boot/include/
    ASMKFLAGS	= -I include/ -f elf
    CFLAGS		= -I include/ -c -fno-builtin
    LDFLAGS		= -s -Ttext $(ENTRYPOINT)
    DASMFLAGS	= -u -o $(ENTRYPOINT) -e $(ENTRYOFFSET)
    
    # This Program
    ORANGESBOOT	= boot/boot.bin boot/loader.bin
    ORANGESKERNEL	= kernel.bin
    OBJS		= kernel/kernel.o kernel/start.o lib/kliba.o lib/string.o
    DASMOUTPUT	= kernel.bin.asm
    
    # All Phony Targets
    .PHONY : everything final image clean realclean disasm all buildimg
    
    # Default starting position
    everything : $(ORANGESBOOT) $(ORANGESKERNEL)
    
    all : realclean everything
    
    final : all clean
    
    image : final buildimg
    
    clean :
    	rm -f $(OBJS)
    
    realclean :
    	rm -f $(OBJS) $(ORANGESBOOT) $(ORANGESKERNEL)
    
    disasm :
    	$(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)
    
    # We assume that "a.img" exists in current folder
    buildimg :
    	dd if=boot/boot.bin of=a.img bs=512 count=1 conv=notrunc
    	sudo mount -o loop a.img /mnt/floppy/
    	sudo cp -fv boot/loader.bin /mnt/floppy/
    	sudo cp -fv kernel.bin /mnt/floppy
    	sudo umount /mnt/floppy
    
    boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
    	$(ASM) $(ASMBFLAGS) -o $@ $<
    
    boot/loader.bin : boot/loader.asm boot/include/load.inc 
    			boot/include/fat12hdr.inc boot/include/pm.inc
    	$(ASM) $(ASMBFLAGS) -o $@ $<
    
    $(ORANGESKERNEL) : $(OBJS)
    	$(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $(OBJS)
    
    kernel/kernel.o : kernel/kernel.asm
    	$(ASM) $(ASMKFLAGS) -o $@ $<
    
    kernel/start.o : kernel/start.c include/type.h include/const.h include/protect.h
    	$(CC) $(CFLAGS) -o $@ $<
    
    lib/kliba.o : lib/kliba.asm
    	$(ASM) $(ASMKFLAGS) -o $@ $<
    
    lib/string.o : lib/string.asm
    	$(ASM) $(ASMKFLAGS) -o $@ $<
    
    • =定义变量

    • ${XXX}使用变量

    • 标准语法

      target: prerequsites
      		command
      

      image-20201117222459928

    • $@代表target

    • $<代表prerequisites的第一个名字

    踩坑

    • ld参数要加上 -m elf_i386
    • gcc参数要加上-m32

    code

    image-20201118102113478

    image-20201118102127879

    • break code是make code与0x80进行or操作得到的结果

    2 实现

    2.1 base

    将orange书中的chapter7/n复制过来,跑一下

    注意修改makefile里的坑

    发现已经实现了以下的基本功能

    • ⽀持回车键换⾏。
    • 支持空格键,不支持tab。
    • ⽀持⽤退格键删除输⼊内容。
    • 可以输⼊并显⽰ a-z,A-Z 和 0-9 字符。

    基本框架有了,就在其基础上做实现就可以,一个个来

    2.2 清屏

    目的:从从屏幕左上⻆开始,以白色显示键盘输入的字符。

    实现:

    • 在初始化之前将整个屏幕打印满空格,然后把指针移到最前面

    main.c中加入以下方法

    // 清屏,将显存指针disp_pos指向第一个位置
    void cleanScreen(){
    	disp_pos = 0;
    	int i;
    	for (i = 0 ; i < SCREEN_SIZE; ++i){
    		disp_str(" ");
    	}
    	disp_pos = 0;
    }
    

    2.3 tab支持

    2.3.1 支持输入

    • tty.c的``in_process`方法中加入TAB判断

      ...
      		case TAB:
      			put_key(p_tty, '	');
      			break;
      ...
      
    • console.h中定义TAB的宽度

      #define TAB_WIDTH 			4
      
    • 在输出的时候判断 ,对console.cout_char()方法进行修改

      	case '	': // TAB输出,将cursor往后移动TAB_WIDTH
      		if(p_con->cursor < p_con->original_addr +
      		    p_con->v_mem_limit - TAB_WIDTH){
      			int i;
      			for(i=0;i<TAB_WIDTH;++i){ // 用空格填充
      				*p_vmem++ = ' ';
      				*p_vmem++ = DEFAULT_CHAR_COLOR;
      			}
      			push_pos(p_con,p_con->cursor);
      			p_con->cursor += TAB_WIDTH; // 调整光标
      		}
      		break;
      

    至此已经可以实现TAB的输入了,但是关于删除还没解决,这时候删除一次还只能删除一个空格,TAB要删除四次

    2.3.2 支持删除

    在用书上代码的时候,发现除了TAB退格有问题,换行的退格也有问题

    按照源代码,换行后退格,会退到上一行的最后位置

    image-20201120214713096

    这显然不是之前的位置,所以很直观的想法就是在打印之前,拿一个变量来存储前一个位置

    但是存一个位置显然是不够的,得存每个走过的位置,所以需要拿一个栈来存

    • console.h

      /*新增,记录光标曾在位置*/
      typedef struct cursor_pos_stack 
      {
      	int *ptr;
      	int pos[SCREEN_SIZE];
      }POSSTACK;
      
      /* CONSOLE */
      typedef struct s_console
      {
      	unsigned int	current_start_addr;	/* 当前显示到了什么位置	  */
      	unsigned int	original_addr;		/* 当前控制台对应显存位置 */
      	unsigned int	v_mem_limit;		/* 当前控制台占的显存大小 */
      	unsigned int	cursor;			/* 当前光标位置 */
      	POSSTACK pos_stack;	/*新增*/
      }CONSOLE;
      
    • 初始化时候,在console.c/init_screen中新增

      	// 初始化pos_stack的ptr指针
      	p_tty->p_console->pos_stack.ptr = p_tty->p_console->pos_stack.ptr;
      
    • console.h新增两个方法

      PRIVATE void push_pos(CONSOLE* p_con,int pos);
      PRIVATE int pop_pos(CONSOLE* p_con);
      /*======================================================================*
      		新增方法,用于记录/获取指针所处位置
       *======================================================================*/
      PRIVATE void push_pos(CONSOLE* p_con,int pos){
      	p_con->pos_stack.pos[p_con->pos_stack.ptr++] = pos;
      }
      PRIVATE int pop_pos(CONSOLE* p_con){
      	if(p_con->pos_stack.ptr==0){
      		return 0; // 不会发生这种情况
      	}else{
      		--p_con->pos_stack.ptr;
      		return p_con->pos_stack.pos[p_con->pos_stack.ptr];
      	}
      }
      
    • 修改输出方法

      主要做的事情是:在cursor移动前push_pos,在退格前pop_pos来获取之前的位置

      case '
      ':
      		if (p_con->cursor < p_con->original_addr +
      		    p_con->v_mem_limit - SCREEN_WIDTH) {
      			push_pos(p_con,p_con->cursor);
      			p_con->cursor = p_con->original_addr + SCREEN_WIDTH * 
      				((p_con->cursor - p_con->original_addr) /
      				 SCREEN_WIDTH + 1);
      		}
      		break;
      	case '':
      		if (p_con->cursor > p_con->original_addr) {
      			// p_con->cursor--;
      			int temp = p_con->cursor; // 原先位置
      			p_con->cursor = pop_pos(p_con);
      			int i;
      			for(i=0;i<temp-p_con->cursor;++i){ // 用空格填充
      				*(p_vmem-2-2*i) = ' ';
      				*(p_vmem-1-2*i) = DEFAULT_CHAR_COLOR;
      			}
      			// *(p_vmem-2) = ' ';
      			// *(p_vmem-1) = DEFAULT_CHAR_COLOR;
      		}
      		break;
      	case '	': // TAB输出,将cursor往后移动TAB_WIDTH
      		if(p_con->cursor < p_con->original_addr +
      		    p_con->v_mem_limit - TAB_WIDTH){
      			push_pos(p_con,p_con->cursor);
      			p_con->cursor += TAB_WIDTH; // 直接调整光标即可
      		}
      		break;
      	default:
      		if (p_con->cursor <
      		    p_con->original_addr + p_con->v_mem_limit - 1) {
      			*p_vmem++ = ch;
      			*p_vmem++ = DEFAULT_CHAR_COLOR;
      			push_pos(p_con,p_con->cursor);
      			p_con->cursor++;
      		}
      		break;
      

    2.4 shift组合键

    发现在keyboard.c里,orange帮我们实现好了,那没事了。

    2.5 清屏

    目的:每隔20s清屏

    实现:很直接的想法,在定时进程TestA里进行修改

    void TestA()
    {
    	int i = 0;
    	while (1) {
    		// disp_str("A."); 
    		cleanScreen();
    		milli_delay(100000); //经过尝试发现设置为 100000 差不多是20秒左右
    	}
    }
    

    发现是可以的,但是光标不会复位,所以需要重新初始化screen

    tty.c中添加方法

    // 新增,初始化所有tty的screen
    PUBLIC void init_all_screen(){
    	TTY *p_tty;
    	for (p_tty = TTY_FIRST; p_tty < TTY_END; p_tty++)
    	{
    		init_screen(p_tty);
    	}
    	select_console(0);
    }
    

    修改后的TestA为:

    void TestA()
    {
    	int i = 0;
    	while (1) {
    		// disp_str("A."); 
    		cleanScreen();
    		init_all_screen();
    		milli_delay(100000); //经过尝试发现设置为 100000 差不多是20秒左右
    	}
    }
    

    运行完发现报错了

    image-20201122002323760

    查了各种资料,发现大概是因为用户进程不能使用tty里的方法?

    尝试将TestA的进程从PROCS转为TASKS

    // global.c
    PUBLIC	TASK	task_table[NR_TASKS] = {
    	{task_tty, STACK_SIZE_TTY, "tty"},
    	{TestA, STACK_SIZE_TESTA, "TestA"}}; // 新增,改为TASKS
    
    PUBLIC  TASK    user_proc_table[NR_PROCS] = {
    	// {TestA, STACK_SIZE_TESTA, "TestA"},
    	{TestB, STACK_SIZE_TESTB, "TestB"},
    	{TestC, STACK_SIZE_TESTC, "TestC"}};
    
    //proc.h
    /* Number of tasks & procs */
    // #define NR_TASKS	1
    // #define NR_PROCS	3
    #define NR_TASKS	2
    #define NR_PROCS	2
    

    至此,完成清屏,但是后面还需要进行修改。

    2.6 ESC

    2.6.1 切换模式/输出红色

    需要记录模式状态,加入代码

    //global.c
    PUBLIC int mode;  
    /*
    0:正常模式
    1:搜索模式
    */
    
    //global.h
    extern int mode;//标识模式
    

    响应的,只有mode==0的时候可以清屏,所以需要修改

    void TestA()
    {
    	int i = 0;
    	while (1) {
    		if(mode==0){
    			// disp_str("A."); 
    			cleanScreen();
    			init_all_screen();
    			milli_delay(100000); //经过尝试发现设置为 100000 差不多是20秒左右
    		}else{
    			milli_delay(10);
    		}
    	}
    }
    

    tty.c中加入代码

    PUBLIC void in_process(TTY *p_tty, u32 key)
    {		...
        	case ESC:
    		// ESC 切换模式
    			if(mode==0){
    				mode = 1;
    			}else if(mode==1){
    				mode = 0;
    				// TODO:清除内容
    			}
     			break;
     		...
    }
    

    修改console.c

    PUBLIC void out_char(CONSOLE* p_con, char ch)
    {
    	...
    	default:
    		if (p_con->cursor <
    		    p_con->original_addr + p_con->v_mem_limit - 1) {
    			*p_vmem++ = ch;
    			if(mode==0){
    				*p_vmem++ = DEFAULT_CHAR_COLOR;
    			}else{
    				*p_vmem++ = RED; //输出红色
    			}
    			push_pos(p_con,p_con->cursor);
    			p_con->cursor++;
    		}
    		break;
    	}
    	...
    }
    

    image-20201122134918046

    完成

    2.6.2 搜索输入栈

    搜索模式下输入需要做的事情有:

    • 记录输入的字符(其实不用,因为从显存里就可以拿到)
    • 记录输入开始的位置(以便退出ESC的时候清空输入)

    修改console.h,加入搜索输入栈、并在CONSOLE和POSSTACK中开辟变量,记录ESC开始位置

    /*新增,记录光标曾在位置*/
    typedef struct cursor_pos_stack 
    {
    	int ptr; //offset
    	int pos[SCREEN_SIZE];
    	int search_start_ptr;/*新增:ESC模式开始时候的ptr位置*/
    }POSSTACK;
    
    /* CONSOLE */
    typedef struct s_console
    {
    	unsigned int	current_start_addr;	/* 当前显示到了什么位置	  */
    	unsigned int	original_addr;		/* 当前控制台对应显存位置 */
    	unsigned int	v_mem_limit;		/* 当前控制台占的显存大小 */
    	unsigned int	cursor;				/* 当前光标位置 */
    	unsigned int 	search_start_pos;	/*新增:ESC模式开始时候的cursor位置*/
    	POSSTACK pos_stack;					/*新增*/	
    }CONSOLE;
    

    (1)实现退出时光标复位

    console.c中增加方法

    PUBLIC void exit_esc(CONSOLE* p_con);
    
    /*======================================================================*
    		新增方法,退出ESC模式
     *======================================================================*/
    PUBLIC void exit_esc(CONSOLE* p_con){
    	u8* p_vmem = (u8*)(V_MEM_BASE + p_con->cursor * 2);
    	// 清屏,用空格填充
    	int i;
    	for(i=0;i<p_con->cursor-p_con->search_start_pos;++i){ 
    		*(p_vmem-2-2*i) = ' ';
    		*(p_vmem-1-2*i) = DEFAULT_CHAR_COLOR;
    	}
    	// 复位指针
    	p_con->cursor = p_con->search_start_pos;
    	p_con->pos_stack.ptr = p_con->pos_stack.search_start_ptr;
    	flush(p_con); // 更新p_con,这个不能漏
    }
    

    (2)搜索

    console.c中,新增一个方法,直接暴力匹配了

    PUBLIC void search(CONSOLE *p_con);
    /*======================================================================*
    		新增方法,搜索,并将搜索结果标为红色
     *======================================================================*/
    PUBLIC void search(CONSOLE *p_con){
    	int i,j;
    	int begin,end; // 滑动窗口
    	for(i = 0; i < p_con->search_start_pos*2;i+=2){ // 遍历原始白色输入
    		begin = end = i; // 初始化窗口为0
    		int found = 1; // 是否匹配
    		// 遍历匹配
    		for(j = p_con->search_start_pos*2;j<p_con->cursor*2;j+=2){
    			if(*((u8*)(V_MEM_BASE+end))==*((u8*)(V_MEM_BASE+j))){
    				end+=2;
    			}else{
    				found = 0;
    				break;
    			}
    		}
    		// 如果找到,标红
    		if(found == 1){
    			for(j = begin;j<end;j+=2){
    				*(u8*)(V_MEM_BASE + j + 1) = RED;
    			}
    		}
    	}
    }
    

    tty.cin_process()中调用

    		case ENTER:
    			if(mode==0){
    				put_key(p_tty, '
    ');
    			}else if(mode==1){
    				search(p_tty->p_console);
    			}
    			break;
    

    image-20201122150357777

    完成了,但是还有以下问题没有解决:

    • 退出的时候颜色变回白色
    • 按回车后,屏蔽除了ESC外的输出

    (3)退出的时候颜色变回白色

    在(1)中的exit_esc代码中加上以下循环即可

    	for(i=0;i<p_con->search_start_pos*2;i+=2){ 
    		*(u8*)(V_MEM_BASE + i + 1) = DEFAULT_CHAR_COLOR;
    	}
    

    (4)按回车后,屏蔽除了ESC外的输出

    给mode加一个定义

    // global.c
    PUBLIC int mode;  
    /*
    0:正常模式
    1:搜索模式
    2:ESC+ENTER
    */
    

    在mode1下输入enter时切换到mode2

    		case ENTER:
    			if(mode==0){
    				put_key(p_tty, '
    ');
    			}else if(mode==1){
    				search(p_tty->p_console);
    				mode = 2; //切换模式,禁止输入
    			}
    			break;
    

    tty.c中的in_process进入处理前进行判断

    	// ESC+ENTER下不允许输入
    	if (mode == 2)
    	{
    		if ((key & MASK_RAW) == ESC)
    		{
    			mode = 0;
    			// 清除内容
    			exit_esc(p_tty->p_console);
    		}
    		return; // 无论如何都返回,不进行后续处理了
    	}
    

    2.7 撤销

    按下 control + z 组合键可以撤回操作(包含回车和 Tab 和删除),直到初始状态。

    2.7.1 识别control+z

    先不管撤销,先识别出来,然后打印一个*

    // global.h
    extern int control; // 是否按下了control
    
    // global.c
    PUBLIC int control;
    /*
    0:没有按下
    1:按下了,make
    */
    

    keyboard.ckeyboard_read中添加

    // 新增,识别是否按下了control,如果按下了则把control设为1,否则为0
    control = ctrl_l||ctrl_r;
    

    console.h中,在out_char()方法中添加

    	case 'z':
    	case 'Z':
    		if(control){
    			ch = '*';
    		}
    	default:
    		if (p_con->cursor <
    		    p_con->original_addr + p_con->v_mem_limit - 1) {
    			*p_vmem++ = ch;
    			if(mode==0){
    				*p_vmem++ = DEFAULT_CHAR_COLOR;
    			}else{
    				*p_vmem++ = RED;//输出红色
    			}
    			push_pos(p_con,p_con->cursor);
    			// push_search(p_con,ch);
    			p_con->cursor++;
    		}
    		break;
    

    image-20201122160523586

    ok,control+z已经能够识别了,接下来做撤销操作。

    2.7.2 撤销操作

    可以撤回操作(包含回车和 Tab 和删除),直到初始状态。

    说白了,就是要记录每一步操作

    (1)mode0

    console.h中新增数据结构

    typedef struct out_char_stack 
    {
    	int ptr; //offset
    	char ch[SCREEN_SIZE];
    }OUTCHARSTACK;
    
    /* CONSOLE */
    typedef struct s_console
    {
    	unsigned int	current_start_addr;	/* 当前显示到了什么位置	  */
    	unsigned int	original_addr;		/* 当前控制台对应显存位置 */
    	unsigned int	v_mem_limit;		/* 当前控制台占的显存大小 */
    	unsigned int	cursor;				/* 当前光标位置 */
    	unsigned int 	search_start_pos;	/*新增:ESC模式开始时候的cursor位置*/
    	POSSTACK pos_stack;					/*新增*/	
    	OUTCHARSTACK out_char_stack;		/*新增*/
    }CONSOLE;
    

    tty.c中,write的时候将out_char的ch压入操作记录栈

    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--;
    		push_out_char(p_tty->p_console,ch); // 新增,压入操作记录栈
    		out_char(p_tty->p_console, ch);
    	}
    }
    
    //console.c
    PUBLIC void push_out_char(CONSOLE* p_con,char ch){
    	// 新增,压入操作队列
    	p_con->out_char_stack.ch[p_con->out_char_stack.ptr++]=ch;
    }
    

    判断control+z,进行撤销

    	case 'z':
    	case 'Z':
    		if(control){
    			if(mode==0){
    			// clean and init screen
    			disp_pos = 0;
    			int i;
    			for (i = 0 ; i < SCREEN_SIZE; ++i){
    				disp_str(" ");
    			}
    			disp_pos = 0;
    			// 初始化pos_stack的ptr指针
    			p_con->pos_stack.ptr = 0;
    			p_con->cursor = disp_pos / 2;
    
    			flush(p_con);
    			redo(p_con);
    			return; // 撤销操作后直接返回
    			}
    		}
    
    		//TODO:现在只有在mode0下正常,mode1下不正常
    

    具体撤销方法使用redo实现

    /*======================================================================*
    		新增方法,redo以实现撤销操作,少做1步的操作
     *======================================================================*/
    PRIVATE void redo(CONSOLE *p_con){
    	p_con->out_char_stack.ptr-=2; // z也被压栈了,所以要-=2而不是1
    	if(p_con->out_char_stack.ptr<=0){
    		p_con->out_char_stack.ptr=0;
    		return;		// 如果已经清空了,直接返回,不操作
    	} 
    	int i;
    	for(i=0;i<p_con->out_char_stack.ptr;++i){
    		out_char(p_con,p_con->out_char_stack.ch[i]);
    		// out_char(p_con,'*');
    	}
    }
    

    (2)mode1

    加入数据结构search_start_ptr

    typedef struct out_char_stack 
    {
    	int ptr; //offset
    	char ch[SCREEN_SIZE];
    	int search_start_ptr;/*新增:ESC模式开始时候的ptr位置*/
    }OUTCHARSTACK;
    

    修改mode切换的出入口相应地方

    //tty.c in_process
    // 记录ESC开始前的位置
    p_tty->p_console->search_start_pos = p_tty->p_console->cursor;
    p_tty->p_console->pos_stack.search_start_ptr = p_tty->p_console->pos_stack.ptr;
    p_tty->p_console->out_char_stack.search_start_ptr = p_tty->p_console->out_char_stack.ptr;
    		
    //console.c exit_esc
    // 复位指针
    p_con->cursor = p_con->search_start_pos;
    p_con->pos_stack.ptr = p_con->pos_stack.search_start_ptr;
    p_con->out_char_stack.ptr = p_con->out_char_stack.search_start_ptr;
    

    control+Z操作

    	case 'Z':
    		if(control&&(mode==0||mode==1)){ // mode=0或1下才可以进行撤销,逻辑不一样,只能撤销当前模式下的输入 
    			int temp; // 清屏开始的位置
    			if(mode==0){
    				temp = 0;	
    				// 初始化pos_stack的ptr指针
    				p_con->pos_stack.ptr = 0;
    			}else if(mode==1){
    				temp = p_con->search_start_pos*2;
    				// 还原
    				p_con->pos_stack.ptr = p_con->pos_stack.search_start_ptr;
    			}
    			// clean and init screen
    			disp_pos = temp;
    			int i;
    			for (i = 0 ; i < SCREEN_SIZE; ++i){
    				disp_str(" ");
    			}
    			disp_pos = temp;
    			p_con->cursor = disp_pos / 2;
    			flush(p_con);
    			redo(p_con);
    			return; // 撤销操作后直接返回
    		}
    

    其中的redo方法也有修改

    /*======================================================================*
    		新增方法,redo以实现撤销操作,少做1步的操作
     *======================================================================*/
    PRIVATE void redo(CONSOLE *p_con){
    	int start;
    	if(mode==0){
    		start = 0;
    	}else if(mode==1){
    		start = p_con->out_char_stack.search_start_ptr;
    	}
    	p_con->out_char_stack.ptr-=2; // z也被压栈了,所以要-=2而不是1
    	if(p_con->out_char_stack.ptr<=start){
    		p_con->out_char_stack.ptr=start;
    		return;		// 如果已经清空了,直接返回,不操作
    	} 
    	int i;
    	for(i=start;i<p_con->out_char_stack.ptr;++i){
    		out_char(p_con,p_con->out_char_stack.ch[i]);
    	}
    }
    

    2.8 TAB识别

    空格和Tab识别要区分开

    console.h中定义一个特殊的颜色,其实是啥都行,这里用的是绿色

    #define TAB_CHAR_COLOR 0x2 /*绿色,随便拿个用不到的颜色*/
    

    输出TAB的时候,改用TAB_CHAR_COLOR,反正也看不见颜色

    	case '	': // TAB输出,将cursor往后移动TAB_WIDTH
    		if(p_con->cursor < p_con->original_addr +
    		    p_con->v_mem_limit - TAB_WIDTH){
    			int i;
    			for(i=0;i<TAB_WIDTH;++i){ // 用空格填充
    				*p_vmem++ = ' ';
    				*p_vmem++ = TAB_CHAR_COLOR; // tab空格的颜色是特殊的,在search的时候要进行区分
    			}
    			push_pos(p_con,p_con->cursor);
    			p_con->cursor += TAB_WIDTH; // 调整光标
    		}
    		break;
    

    修改搜索逻辑,添加一个if分支,对空格进行特殊判断即可,遇到空格的时候往后看一步颜色

    			if(*((u8*)(V_MEM_BASE+end))==' '){ // 如果是空格,特殊处理
    				if(*((u8*)(V_MEM_BASE+j))!=' '){ // 如果压根不是空格,直接不做了,break
    					found = 0 ;
    					break;
    				}
    				if(*((u8*)(V_MEM_BASE+end+1))==TAB_CHAR_COLOR){ // 如果是TAB
    					if(*((u8*)(V_MEM_BASE+j+1))==TAB_CHAR_COLOR){
    						end+=2;
    					}else{
    						found = 0;
    						break;
    					}
    				}else{ // 普通空格
    					end+=2;
    				}
    			}
    			else if(*((u8*)(V_MEM_BASE+end))==*((u8*)(V_MEM_BASE+j))){
    				end+=2;
    			}else{
    				found = 0;
    				break;
    			}
    
  • 相关阅读:
    apply 无循环拼接数组
    匿名函数自调的三种方法
    移动元素时,translate要比margin好
    Tesseract库原理分析
    [转]基于OCR的图片字符识别可行性研究
    [转]C&C++图形图像处理开源库
    [转]OCR识别的开源分析
    [转]众多OCR软件
    [转]常用OCR软件介绍
    [转]浅谈OCR之Tesseract
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14136087.html
Copyright © 2020-2023  润新知