• 41 内核中的屏幕打印


    参考

    https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程

    一.屏幕的打印-使用C语言设计一组打印函数,为后续进一步开发内核功能做准备

    内核中的屏幕打印模块
    操作系统-内核中的屏幕打印(上)
    这里有三个不同的入口,在screen.模块中是提供保护模式下在屏幕下打印文本的功能,该模块的具体实现是
    操作系统-内核中的屏幕打印(上)
    如图所示有7个接口需要设计,最重要的一个函数为void PrintChar(char c)-打印字符串的函数,由此总结了以上七个函数接口的关系,如下图所示
    操作系统-内核中的屏幕打印(上)
    如图所示中心函数就为上文所说的函数,由此可以得出上图的关系

    二.void PrintChar(char c)函数的实现

    可以得出最重要的函数为void PrintChar(char c),所以来实现该函数
    1.功能定义:在屏幕上打印一个字符
    2.实现原理:直接在显存对应的位置写入数据
    操作系统-内核中的屏幕打印(上)
    由上图可知,写入数据的部分可以用四行汇编的语言来实现,但是通过之前的学习,应该是C语言来实现内核开发的,原因是在屏幕打印字符串使用汇编语言较简单,同时汇编的指令有时是必须由汇编语言来实现,之后在C语言调用。
    三.语言中的内嵌汇编-内嵌汇编的语法格式
    操作系统-内核中的屏幕打印(上)
    代码示例
    操作系统-内核中的屏幕打印(上)
    从代码示例可以看到该函数是C语言来编写的,但是通过asm volatile关键字来实现汇编语言的内嵌,在这里需要注意的是因为使用GCC编译器,所以在内嵌汇编时使用的时AT&T汇编格式(与nasm汇编格式不同)

    三.屏幕打印的实现

    1.首先创建screen.c与screen.h的文件,同时在makefile中进行改动,同时在编译后需要将编译之后的文件保存在后面,所以改代的地方如下
    操作系统-内核中的屏幕打印(上)操作系统-内核中的屏幕打印(上)

    2.screen.h头文件的实现

    #ifndef SCREEN_H
    #define SCREEN_H
    
    #define SCREEN_WIDTH    80//定义的宽与高-横坐标与纵坐标
    #define SCREEN_HEIGHT   25
    
    typedef enum//颜色枚举类型的定义
    {
        SCREEN_GRAY   = 0x07,
        SCREEN_BLUE   = 0x09,
        SCREEN_GREEN  = 0x0A,
        SCREEN_RED    = 0x0C,
        SCREEN_YELLOW = 0x0E,
        SCREEN_WHITE  = 0x0F
    } PrintColor;
    //接口的声明
    void ClearScreen();
    int  SetPrintPos(short w, short h);
    void SetPrintColor(PrintColor c);
    int PrintChar(char c);
    int PrintString(const char* s);
    int PrintIntDec(int n);
    int PrintIntHex(unsigned int n);
    
    #endif
    

    3.screen.c的实现-int SetPrintPos与void SetPrintColor

    static int  gPosW = 0;
    static int  gPosH = 0;//全局变量的定义
    static char gColor = SCREEN_WHITE;
    
    int SetPrintPos(short w, short h)
    {
        int ret = 0;
    
        if( ret = ((0 <= w) && (w <= SCREEN_WIDTH) && (0 <= h) && (h <= SCREEN_HEIGHT)) )
        {
            unsigned short bx = SCREEN_WIDTH * h + w;
    
            gPosW = w;
            gPosH = h;
    
            asm volatile(
                "movw %0,      %%bx
    "
                "movw $0x03D4, %%dx
    "
                "movb $0x0E,   %%al
    "
                "outb %%al,    %%dx
    "
                "movw $0x03D5, %%dx
    "
                "movb %%bh,    %%al
    "
                "outb %%al,    %%dx
    "
                "movw $0x03D4, %%dx
    "
                "movb $0x0F,   %%al
    "
                "outb %%al,    %%dx
    "
                "movw $0x03D5, %%dx
    "
                "movb %%bl,    %%al
    "
                "outb %%al,    %%dx
    "
                :
                : "r"(bx)
                : "ax", "bx", "dx"
            );
        }
    
        return ret;
    }
    
    void SetPrintColor(PrintColor c)
    {
        gColor = c;
    }
    

    4.PrintChar函数的实现

    int PrintChar(char c)
    {
        int ret = 0;
    
        if( (c == '
    ') || (c == '
    ') )//需要注意这两个字符,当出现这俩个时换行,不进行打印
        {
            ret = SetPrintPos(0, gPosH + 1);//重新换行
        } 
        else
        {
            int pw = gPosW;
            int ph = gPosH;
    
            if( (0 <= pw) && (pw <= SCREEN_WIDTH) && (0 <= ph) && (ph <= SCREEN_HEIGHT) )
            {
                int edi = (SCREEN_WIDTH * ph + pw) * 2;//位置的确定
                char ah = gColor;//颜色的确定
                char al = c;//具体打印的字符
                //汇编的嵌入 l四个字节,b为一个字节,w为俩个字节
                asm volatile(
                   "movl %0,   %%edi
    "
                   "movb %1,   %%ah
    "
                   "movb %2,   %%al
    "
                   "movw %%ax, %%gs:(%%edi)"
                   "
    "
                   :
                   : "r"(edi), "r"(ah), "r"(al)
                   : "ax", "edi"
                );
    
                pw++;
    
                if( pw == SCREEN_WIDTH )
                {
                    pw = 0;
                    ph = ph + 1;
                }
    
                ret = 1;
            }
    
            SetPrintPos(pw, ph);
        }
    
        return ret;
    }
    

    5.需要将loader.asm进行设置,因为是在32位的保护模式下运行的,选择子也需要改动
    操作系统-内核中的屏幕打印(上)操作系统-内核中的屏幕打印(上)
    6.PrintString的实现
    实现方式-循环调用PrintChar直到遇到0结束符
    int PrintIntHex的实现
    实现方式-将参数n转换为对应的16进制数字字符串,调用PrintString打印转换得到的字符串

    int PrintString(const char* s)//需f要对参数进行判断,如果参数为空,返回-1
    {
        int ret = 0;
    
        if( s != NULL )
        {
            while( *s )
            {
                ret += PrintChar(*s++);//不为空时进行循环,直到遇见结束符结束
            }
        }
        else
        {
            ret = -1;
        }
    
        return ret;
    }
    
    //在这里需要单独对NULL进行定义   kernel.h
    
    #ifndef KERNEL_H
    #define KERNEL_H
    
    #define NULL ((void*)0)
    
    #endif
    

    屏幕清空的操作如下

    void ClearScreen()//二重循环对屏幕每一个位置打印空字符
    {
        int h = 0;
        int w = 0;
    
        SetPrintPos(0, 0);
    
        for(h=0; h<SCREEN_HEIGHT; h++)
        {
            for(w=0; w<SCREEN_WIDTH; w++)
            {
                PrintChar(' ');
            }
        }
    
        SetPrintPos(0, 0);
    }
    

    PrintIntHex()的实现

    int PrintIntHex(unsigned int n)
    {
        char hex[11] = {'0', 'x', 0};//将整数进行转换
        int i = 0;
    
        for(i=9; i>=2; i--)
        {
            int p = n & 0xF;//位与判断
    
            if( p < 10 )
            {
                hex[i] = ('0' + p);//将0-9数字转换为字符
            }
            else
            {
                hex[i] = ('A' + p - 10);//当低四位大于10,A B C
            }
    
            n = n >> 4;//将N右移四位
        }
    
        return PrintString(hex);//16进制的字符串打印
    }
    

    在之前实现了其中的函数接口,接下来对剩下的函数接口进行实现
    int PrintIntDec(int c)--递归推导
    代码实现-十进制

    int PrintIntDec(int n)
    {
        int ret = 0;
    
        if( n < 0 )
        {
            ret += PrintChar('-');//多打印一个字符
    
            n = -n;
    
            ret += PrintIntDec(n);
        }
        else
        {
            if( n < 10 )
            {
                ret += PrintChar('0' + n);
            }
            else
            {
                ret += PrintIntDec(n/10);
                ret += PrintIntDec(n%10);
            }
        } 
    
        return ret;
    }
    

    操作系统-操作系统-内核中的屏幕打印(下)
    在保护模式下的光标跟踪-两个不同指定端口的操作
    操作系统-操作系统-内核中的屏幕打印(下)
    上图所示的是高八位与低八位的光标设置,代码实现已经在int SetPrintPos(short w, short h)函数中进行实现,将光标设置高8位与低8位的汇编代码内嵌到C语言代码中

    实验与结果-符合预期的结果

    操作系统-操作系统-内核中的屏幕打印(下)操作系统-操作系统-内核中的屏幕打印(下)
    与该实验相关的代码在该地址进行保存,需要可以进行下载https://down.51cto.com/13465106/up

    小结

    1.GCC编译器只支持AT&T格式的内嵌汇编
    2.通过汇编的方式可实现PrintCha()函数
    3.PrintCha()是其它屏幕打印函数的基础
    4.通过操作0x03D4与0x03D5端口对光标位置进行设置

      

      

      

     

      

      

      

      

  • 相关阅读:
    P3图片导致iOS9.3以下崩溃问题
    [ios] 如何调用其他app h5界面调用打开app
    Swift学习中遇到的小坑
    代码行数统计(mac)
    路径专题 绝对路径 根路径 相对路径
    java.lang.StackOverflowError 解决办法
    Myeclipse运行提示错误: 找不到或无法加载主类 test.test1 终极解决办法
    myeclipse的最有用的设置
    关闭myeclipse可视化视图
    数据三大范式
  • 原文地址:https://www.cnblogs.com/lh03061238/p/14630622.html
Copyright © 2020-2023  润新知