32位装载程序 - 基础视频驱动

1. 首先面临的问题

从16位装载程序切换到32位装载程序后,就没有BIOS可用了,原因就不多解释了,如果不服,你可以试试直接在32位保护模式下调用BIOS,让事实教育教育你。你会发现,想在屏幕输出字符都不行了。因为没有现成的API给你用,所以,进入32位保护模式后,首先面临的问题就是:一切从零开始

2. 最先需要实现的模块 - 基础视频驱动

无论你想先开始干什么,你都需要看到结果,难道你认为你可以在什么都看不到的情况下,就能确认程序运行正确了?

所以,无论你想要干什么,你都要先想办法让自己能看到运行的信息,要看到信息,最直接的当然是在显示器上输出信息了,所以最先要实现的是视频驱动,当然这个视频驱动不需要很强大的功能,只需要具备几个输出文本信息的基础功能就可以了,所以我把它叫做:基础视频驱动(Basic Video Driver, BVD),BVD让自己能看到程序运行的信息。

如果你非要抬杠说Bochs能看到寄存器内容之类的调试信息,好吧,那你就先不要实现视频输出功能,就去看Bochs那个调试功能吧。

3. 最急需的功能 - 输出字符串

注意啊,最亟需的功能是输出字符串,并不是格式化输出字符串,虽然格式化输出字符串很方便,但却是不是最基础的,也不应该划入BVD的范围。

在程序运行过程中,给出符号信息是最简单的,随便在屏幕上给出一个符号信息就行了。但这在程序较复杂后,要通过少数的几个符号或字母来识别信息,就不合适了。要给出比较容易识别,实现还比较简单的方法,就是输出文字了。

4. 基础视频驱动(Basic Video Driver, BVD)

虽然BVD在正式的系统中也要用,但是正式的中需要更完整一些,所以就不考虑代码共享的事情了,这里就按最简单的功能来写。

5. CRT基础

视频基础知识就约等于VGA的知识了,引导阶段,不用搞图形,只需要处理字符界面即可,所以关注点就变成关注CRT了,CRT又是VGA的一个子集。

在现阶段,只要知道几个CRT寄存器和字符视频内存基址就差不多了,其他的,可以当作用不到。具体的内容就放到书里了。

控制显存起始位置

显存的物理起始地址是0x000B8000,但是实际的显示起始地址并不一定是从0x000B8000开始的,实际的显示起始地址=显存基址 + 显示起始偏移,所以需要提供一个操作显存起始偏移的功能。

其实,为了简单,我并不使用显示起始偏移,也就是把显示起始偏移当作0来处理。

#define CRT_REG_ADDR                ((void *)0x03D4)
#define CRT_REG_DATA                ((void *)0x03D5)
//
/*  CRT Controller Register, CCR */
#define CCR_IDX_START_ADDR_H        (0x0C)
#define CCR_IDX_START_ADDR_L        (0x0D)
#define CCR_IDX_CURSOR_LOC_H        (0x0E)
#define CCR_IDX_CURSOR_LOC_L        (0x0F)

void IO_OutB(void * pPort, byte_t nData)
{
    __asm{
        mov     edx, pPort;
        mov     al, nData
        out     dx, al
    }
}
//
byte_t IO_InB(void * pPort)
{
    byte_t          nData;
    __asm{
        mov     edx, pPort;
        in      al, dx
        mov     nData, al
    }
    return nData;
}
//
uint_t CRT_GetVdoBufStart(void)
{
    byte_t      nHigh;
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_START_ADDR_H);
    nHigh = IO_InB(CRT_REG_DATA);
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_START_ADDR_L);
    return ((uint_t)nHigh << 8) | IO_InB(CRT_REG_DATA);
}
//
void CRT_SetVdoBufStart(uint_t nStartAddr)
{
    IO_OutB(CRT_REG_ADDR, CCR_IDX_START_ADDR_H);
    IO_OutB(CRT_REG_DATA, (byte_t)(nStartAddr >> 8));
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_START_ADDR_L);
    IO_OutB(CRT_REG_DATA, (byte_t)nStartAddr);
}

控制光标及显示属性

在CRT中,光标的位置并不是以横纵坐标来表示的,而是使用一维坐标来表示的,对应的换算方法是:一维坐标(cursor) = 二维横坐标(x) + 二维纵坐标(y) * 水平分辨率

对应于一般的80*25的字符界面,cursor = x + y * 80

实际能看到的光标显示位置是光标位置 - 显示起始偏移,如果显示起始偏移是0,那么光标位置就是实际的光标显示位置

uint_t              nCursorLoc = 0;
byte_t              nAttr;
byte_t CRT_GetAttr(void)
{
    return nAttr;
}
//
byte_t CRT_SetAttr(byte_t nNewAttr)
{
    byte_t nOldAttr = nAttr;
//
    nAttr = nNewAttr;
    return nOldAttr;
}
//
uint_t CRT_GetCursorLoc(void)
{
    byte_t          nHigh;
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_CURSOR_LOC_H);
    nHigh = IO_InB(CRT_REG_DATA);
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_CURSOR_LOC_L);
    return  ((uint_t)(nHigh) << 8) + IO_InB(CRT_REG_DATA);
}
//
void CRT_SetCursorLoc(uint_t nCursorLocation)
{
    IO_OutB(CRT_REG_ADDR, CCR_IDX_CURSOR_LOC_H);
    IO_OutB(CRT_REG_DATA, (byte_t)(nCursorLocation >> 8));
//
    IO_OutB(CRT_REG_ADDR, CCR_IDX_CURSOR_LOC_L);
    IO_OutB(CRT_REG_DATA, (byte_t)nCursorLocation);
}
//
void CRT_GetCursorXY(uint8_t * pX, uint8_t * pY)
{
    *pX = nCursorLoc % 80;
    *pY = nCursorLoc / 80;
}
//
void CRT_SetCursorXY(uint8_t nX, uint8_t nY)
{
    if( nX >= 80 || nY >= 25 )
        return ;
    nCursorLoc = nY * 80 + nX;
    CRT_SetCursorLoc(nCursorLoc);
}

6. 我的BVD实现的几个功能

我的BVD和核心功能就是输出字符串,在指定位置输出字符串和在光标位置输出字符串

定义视频对象

我是做了一个前提条件,那就是在16位初始化的时候,已将视频系统设置为文本模式,分辨率是80*25,BVD就以这个为基础开发。如果不是这个模式?哦,16位的装载程序会设置的。如果非要说设置失败了?那就显示错乱了。

在指定位置输出字符串 BVD_PrintAt

在BVD中,仅处理两个回车和换行控制字符,其他的控制字符当作普通字符处理。

#define VBUF ((word_t *)0x000B8000)
void BVD_PrintAt(uint8_t nX, uint8_t nY, byte_t nAttr, const char * str,
    int nLen)
{
    char            c;
    int             i;
    word_t        * pVBuf = VBUF;
    uint_t          nLoc = 80 * nY + nX;
    word_t          nChar = nAttr << 8;
//
    if( nLen == 0 )
        return ;
    if( nLen < 0 )
        goto c_string;
//
    for( i = 0; i < nLen; i++ ){
        c = *str++;
        switch( c ){
        case '\n':
            nLoc += 80;
        case '\r':
            nLoc -= nLoc % 80;
            break;
        default:
            pVBuf[nLoc++] = nChar | c;
            break;
        }
        if( nLoc >= 80*25 ){
            BVD_Scroll( (nLoc - 80 * 25) / 80 + 1);
            nLoc -= ((nLoc - 80 * 25) / 80 + 1) * 80;
        }
    }
    pVBuf[nLoc] = nChar | ' ';
    nCursorLoc = nLoc;
    CRT_SetCursorLoc(nCursorLoc);
    return ;
//    
c_string:
    while( *str ){
        c = *str++;
        switch( c ){
        case '\n':
            nLoc += 80;
        case '\r':
            nLoc -= nLoc % 80;
            break;
        default:
            pVBuf[nLoc++] = nChar | c;
            break;
        }
        if( nLoc >= 80*25 ){
            BVD_Scroll( (nLoc - 80 * 25) / 80 + 1);
            nLoc -= ((nLoc - 80 * 25) / 80 + 1) * 80;
        }
    }
    pVBuf[nLoc] = nChar | ' ';
    nCursorLoc = nLoc;
    CRT_SetCursorLoc(nCursorLoc);
}

在光标位置输出字符串 BVD_Print

方法就是,先获得光标位置,然后在光标位置输出字符串。

void BVD_Print(const char * str, byte_t nAttr)
{
    uint8_t         nX, nY;
    CRT_GetCursorXY(&nX, &nY);
    BVD_PrintAt(nX, nY, nAttr, str, -1);
}

6. 格式化输出字符串

这个并不是BVD的功能,只是因为它会被大量用到,所以就和BVD放在一些写,

格式化字符串 LDR_NVSPrintf

功能和vsprintf一样的,只是没有提供浮点数支持。一个函数就有200多行,就不贴了,只贴一个函数原型

int LDR_NVSPrintf(char * buffer, const char * fmt, va_list al, int bufsize);

格式化输出 LDR_Printf

函数提供的缓冲区只有256个字节,也就是说,每次最多输出255个字符。也许会有人局的缓冲区太少了,其实,在引导程序中,用得着一次输出那么长的信息吗?如果真的需要,好吧,那你就分开几次来输出。

int LDR_Printf(const char * fmt,...)
{
    static byte_t   buf[256];
    va_list         arglist;
    int             len;    
//
    va_start(arglist,fmt);
    len = LDR_NVSPrintf(buf, fmt, arglist, 256);
    va_end(arglist);
    BVD_Print(buf, 0x07);
//
    return len;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值