显示文本的视频内存0xb8000不使用C库 [英] Displaying text video memory at 0xb8000 without using the C library

查看:1198
本文介绍了显示文本的视频内存0xb8000不使用C库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C.被编写内核我一直在使用GCC交叉编译器,写在Windows系统上和指定16位实模式。我没有可用的C库写的内核。我已经开始与一些code这是假设直接打印字符在屏幕上。下面是一个函数 kernel.c

  INT的main()
{
  字符* SRC =(字符*)0xB8000000L;
  * SRC ='M';
  SRC + = 2;
  * SRC ='D';
  返回0;
}

我使用GCC的参数 -m16 来产生code将在实模式下运行编译我的code。我使用这些命令生成我 kernel.bin

  GCC -ffreestanding -c -m16 kernel.c -o kernel.o
LD -Ttext 0x10000的-o kernel.pe kernel.o
objcopy把-O二进制kernel.pe kernel.bin

堆栈溢出用户迈克尔·佩奇解决了我的连接问题但评论的code本身是不正确。他做此评论:


  

除了连接问题,你想转换旧TURBOC / MSVC 16位code到GCC?我觉得(字符*)0xB8000000L可疑。如果这是一个真正的16位C编译器,如果它是(CHAR FAR *)0xB8000000L这可能是正确的。 GCC是不是一个真正的16位C编译器,没有旧式远指针的概念。所以,即使你得到这个code编译这可能不是做你认为是这样,我从GCC的-m16选项假设你想要创建一个实模式的16位内核(而不是保护模式的)?


我一直在努力实现自己的的printf 像C函数为我自己的操作系统。我上面提供的code只是我的理解一个小提取物。我已经创造了一个组装引导程序(8086)。

是迈克尔是否正确?如果是这样,我怎么能解决这个问题,并在 0xb8000

直接写显存
解决方案

如果你是使用意向的 GCC 的有 -m16 然后,将自动假定你将在一个80386+运行。我不能强调这一点,使用的 GCC 的创建16位code是充满了陷阱。它是由糟糕你选择把你的内核在0x10000在内存中。 0x10000的不能重新psented为16位偏移$ P $,这可能会导致的 GCC 的发出code,可能无法正常工作,特别是如果你想用<开启优化code> -O1 , -O2 -O3 等甚至访问全局变量可能会导致问题!

强烈推荐(几乎需要避免麻烦最多):您可能需要较少的问题,如果你把你的内核在内存64KB第一数据。在内存地址0x00520原点只是BIOS数据区和更低的内存的保留区域之上。

预先警告: GCC 的有 -m16 目标实模式是使用AT YOUR风险即可。你可能会失去你的理智为好。把处理器进入32位保护模式与平面内存模式从0延伸至4GB理想为的 GCC


这code使你是不是在虚幻模式,虽然你系统可能处于该模式。

GCC 的假定的 CS = DS = ES 的,并且该内存模型是平的。它一般不会改变的 ES 的一个好主意。这是可以使用的 ES 的,如果你保存它,做的工作,并在两者之间恢复它所有的没有中间的 C 的code。由于的 GCC 的要求80386,我们可以用其他的段寄存器中的一项: FS 的和的 GS 的。在这个例子中,我们将使用的 FS

另外一个prerequisite是您了解实模式分割 。我认为你这样做,因为你已经创建了一个引导程序。对于物理内存地址的计算方法是:

 物理内存地址=(段&LT;&LT; 4)+偏移

文本模式(彩色)视频内存的物理地址0xb8000。该存储器的基座可以被重新psented作为段$ P $:抵消对0xb800:0×0000自:

 (0xb800&LT;&4;)+ 0×0000 = 0xb8000

在可视屏幕上的每个单元是字(16位)。的的的高8位的属性和较低的是在链路详细的字符。调色板在此维基页面描述。

如果我们使用的 FS 的为我们的领域,我们可以将其设置为0xb800与它引用显存。由于您的code可能最终使用的 FS 的为各种东西,我们会使用一些内联汇编code保存,在显存做的工作,和恢复的 FS 的什么是previously。

由于我使用的内联汇编你不妨看看有用的链接彼得协和客机的列表上的主题。

的code经由<青霉> FS 我们设定为0xb800段寄存器<取到上述情况,并提供了一​​个机构,用于在一个行更新屏幕,列与属性/ p>

有更多code比你可能会喜欢,但我想表现出比输出一个性格比较。在code注释可以帮助你在你的方式。

 的#include&LT; stdint.h&GT;/ *使用regparm(3)使用约定,其中前三
 *整数大小的参数寄存器(EAX,EDX,ECX)传递,而
 *比堆栈。 regparm(0)是默认的CDECL基于堆栈的
 *参数的传递。 regparm(3)普遍较快全面,比较
 *路过栈上的所有参数。在内部,Linux内核
 *采用了这种约定,以减少开销堆栈功能时
 *被称为在不同的内核模块。
 * /
#定义FASTCALL __attribute __((regparm(3)))
的#define asmlinkage __attribute __((regparm(0)))/ *将导出全局函数* /
FASTCALL的extern无效dispchar(uint16_t celldata,uint16_t偏移);
FASTCALL的extern无效dispstring(为const char * outstring,uint8_t有ATTR,
                                uint16_t偏移);
FASTCALL的extern无效dispchar_nofsupd(uint16_t celldata,uint16_t偏移);
FASTCALL的extern无效dispstring_nofsupd(为const char * outstring,uint8_t有ATTR,
                                        uint16_t偏移);
FASTCALL的extern uint32_t的getset_fs(uint32_t的段);
FASTCALL的extern无效set_fs(uint32_t的段);
FASTCALL的extern uint32_t的set_videomode_fs(无效);
静态内联uint16_t
tm_rowcol_to_vidoffset(uint16_t排,uint16_t关口,uint16_t数numCols);
静态内联uint16_t
tm_charattr_to_celldata(uint8_t有ochar,uint8_t有attr)使用;/ * ------------------------------------------------ ---------- * /#定义COLSPERROW 80
#定义ROW 3
#定义COL 40
#定义RED_ON_BLACK 4 / *属性=红色性格的黑色背景* /
#定义MAGENTA_ON_BLACK 5 / *属性=黑色背景上的*洋红色字符// *彩色文本模式内存段* /
#定义VIDEO_SEG 0xb800所有其他code *前/ *将主/
INT
_主要()
{
    FS *的/ *设置FS到视频模式段和节省previous值/
    uint32_t的oldfs = set_videomode_fs();
    dispchar_nofsupd(tm_charattr_to_celldata('A',RED_ON_BLACK)
                     tm_rowcol_to_vidoffset(ROW,COL,COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata(B,RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW,COL + 1,COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata('',RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW,COL + 2,COLSPERROW));
    dispstring_nofsupd(Hello World的,RED_ON_BLACK,
                       tm_rowcol_to_vidoffset(ROW,COL + 3,COLSPERROW));    / *做完视频模式工作时,* FS恢复到初始值/
    set_fs(oldfs);    / *显示的Hello World使用版本dispstring
     *该保存/恢复FS自动* /
    dispstring(Hello World的,MAGENTA_ON_BLACK,
               tm_rowcol_to_vidoffset(ROW + 1,COL + 3,COLSPERROW));    返回0;
}
/ *转换文本模式(TM)行,列,数numCols
 *到视频偏移。数numCols是列数
 *每行。返回值是一个字节偏移量(不是WORD)
 * /
静态内联uint16_t
tm_rowcol_to_vidoffset(uint16_t排,uint16_t关口,uint16_t数numCols)
{
    收益率((行*数numCols + COL)* 2);
}静态内联uint16_t
tm_charattr_to_celldata(uint8_t有ochar,uint8_t有ATTR)
{
    返回(uint16_t)(ATTR&LT;&LT; 8)| (uint8_t有)ochar;
}/ *与FS变化*显示字符/
快速调用无效
dispchar(uint16_t celldata,uint16_t偏移)
{
    uint32_t的oldfs = set_videomode_fs();
    dispchar_nofsupd(celldata,偏移量);
    set_fs(oldfs);
}/ *没有FS变化*显示字符/
快速调用无效
dispchar_nofsupd(uint16_t celldata,uint16_t偏移)
{
    __asm​​__(MOVW%W [wordval] %% FS:%[memloc]的\\ n \\ t的
             :
             :[wordval]里(celldata)
              [memloc]的m(*(uint32_t的*)(uint32_t的)偏移)
              :记忆);
}/ *设置FS段和返回previous值* /
快速调用uint32_t的
getset_fs(uint32_t的段)
{
    uint32_t的origfs;
    __asm​​__ __volatile __(MOV %% FS,%W [origfs]的\\ n \\ t的
                         MOV%W [片段] %% FS \\ n \\ t的
                         :[origfs]=安培; RM(origfs)
                         :[段]RM(段));
    返回origfs;
}/ *设置FS段* /
快速调用无效
set_fs(uint32_t的段)
{
    __asm​​ __(MOV%W [片段] %% FS \\ n \\ t的
            :
            :[段]RM(段));
}/ *设置FS到视频模式段0xb800 * /
快速调用uint32_t的
set_videomode_fs(无效)
{
    返回getset_fs(VIDEO_SEG);
}/ *显示字符串FS变化* /
快速调用无效
dispstring(为const char * outstring,uint8_t有ATTR,uint16_t偏移)
{
    uint32_t的oldfs = set_videomode_fs();
    dispstring_nofsupd(outstring,ATTR,偏移量);
    set_fs(oldfs);
}/ *显示字符串FS变化* /
快速调用无效
dispstring_nofsupd(为const char * outstring,uint8_t有ATTR,uint16_t偏移)
{
    为const char * curchar = outstring;
    INT I = 0;    对于(* curchar; curchar ++,我++)
        dispchar_nofsupd(tm_charattr_to_celldata(* curchar,ATTR)
                         胶印+ I * 2);
}


链接器脚本GCC在Windows

kernel.bin 使用的 GCC 的窗户下时可能会比预期的要大。这是因为该 GCC 的使用默认对齐规则。以下链接脚本可能有助于减少尺寸:

  ENTRY(__主);
OUTPUT(i386pe);截面
{
    __kernelbase =量0x520;
    。 = __kernelbase;    的.text:SUBALIGN(4){
        *(text.st);
        *(。文本);
    }    。数据:
        SUBALIGN(4){
        __data_start =。
        *(RDATA *);
        *(。数据);
        __data_end =。
        __bss_start =。
        *(BSS);
        __bss_end =。
    }
}

该脚本设置为量0x520(不0x10000处)的ORG。正如前面提到的previously它强烈建议不要使用0x10000的原点,你已经与16位的 GCC 的产生code。命名链接脚本 linker.ld 然后就可以使用这些命令汇编器和链接内核:

  GCC -ffreestanding -c -m16 kernel.c -o kernel.o -O3
LD -o kernel.pe kernel.o -Tlinker.ld
objcopy把-O二进制kernel.pe kernel.bin

您将不得不修改您的引导程序来读取内核部门到内存起始地址量0x520。

通过一个简单的引导程序和这个内核使用提供code /链接脚本建,这是什么的Bochs 的显示了它的运行:

Bochs的输出


看看某些生成$ C $的C

函数的前几行保存当前的 FS 的注册,设置的 FS 应用于视频片段0xb800并打印出3个字符:

  INT
_主要()
{
    FS *的/ *设置FS到视频模式段和节省previous值/
    uint32_t的oldfs = set_videomode_fs();
    dispchar_nofsupd(tm_charattr_to_celldata('A',RED_ON_BLACK)
                     tm_rowcol_to_vidoffset(ROW,COL,COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata(B,RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW,COL + 1,COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata('',RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW,COL + 2,COLSPERROW));
    dispstring_nofsupd(Hello World的,RED_ON_BLACK,
                       tm_rowcol_to_vidoffset(ROW,COL + 3,COLSPERROW));
    [code,打印字符串已经被剪断为了简洁]
    set_fs(oldfs);

生成可以看出,code。通过使用该 objdump的命令:

  objdump的-Dx kernel.pe --no秀,原始的insn -mi8086 -Mintel

英特尔语法输出结果(使用 -O3 优化)与我的编译器如下:

  00000520&LT; __主要计算值:
 520:推ESI;保存寄存器内容
 522:MOV EAX,0xb800
 528:推EBX;保存寄存器内容
 52A:MOV SI,FS;保存旧的FS到SI
 52D:MOV FS,斧头;更新FS与0xb800(视频段)
 52F:MOV WORD PTR FS:0x230,0x441; 0x441 =红黑色字母A
                                       ;写偏移0x230((80 * 3 + 40)* 2)行= 3,列= 40
 536:MOV WORD PTR FS:0x232,0x442; 0x442 =红黑色字母B
                                       ;写偏移0x232((80 * 3 + 41)* 2)行= 3,列= 41
 53D:MOV WORD PTR FS:0x234,0x420;量0x420 =红灯亮黑色空间CHAR
                                       ;写偏移0x234((80 * 3 + 42)* 2)行= 3,列= 42

这行的 C 的code那恢复的 FS 的:

  set_fs(oldfs);

有了这个指令后面:

  571:MOV FS,SI;恢复原始价值previously保存在SI

我已经标注了意见拆装展示如何每个的的值在视频显示内存更新。很多行的 C 的code,但输出非常简单。

I have been writing kernel in C. I've been using the GCC cross-compiler, writing on a Windows system and targeting 16bit Real Mode. I don't have the C library available to write the kernel. I have started with some code which is suppose to print a character directly to the screen. Here is a function from kernel.c:

int main()
{
  char *src = (char *)0xB8000000L;
  *src = 'M';
  src += 2;
  *src = 'D';
  return 0;
}

I compiled my code using GCC with the parameter -m16 to generate code that will run in real mode. I use these commands to generate my kernel.bin:

gcc -ffreestanding -c -m16 kernel.c -o kernel.o
ld -Ttext 0x10000 -o kernel.pe kernel.o
objcopy -O binary kernel.pe kernel.bin

Stack Overflow user Michael Petch solved my linker problem but commented the code itself is incorrect. He made this comment:

Besides the linker problem, are you trying to convert old TurboC/MSVC 16-bit code to GCC? I find (char *)0xB8000000L suspicious. If it was a true 16-bit C compiler it might be okay if it was (char far *)0xB8000000L. GCC is not a true 16-bit C compiler and doesn't have a notion of old style far pointers. So even if you get this code to compile this may not do what you think it does, I'm assuming from the -m16 option with GCC you are trying to create a real-mode 16-bit kernel (rather than a protected mode one)?

I have been trying to implement my own printf like function in C for my own operating system. The code I provided above is only a small extract of what I understand. I have created a bootloader in assembly(8086).

Is Michael correct? If so, how can I resolve this problem and write directly to the video memory at 0xb8000?

解决方案

If you are intent on using GCC with -m16 then it's automatically assumed that you will be running on an 80386+. I can't stress this enough, using GCC to create 16-bit code is fraught with pitfalls. It's made worse by your choice to put your kernel at 0x10000 in memory. 0x10000 can't be represented as a 16-bit offset and this can cause GCC to emit code that may not work, especially if you ever want to turn on optimizations with -O1, -O2, -O3 etc. Even accessing global variables may cause issues!

HIGHLY RECOMMEND (almost required to avoid most hassles): You may have fewer issues if you put your kernel and its data in the first 64kb of memory. An origin at memory address 0x00520 is just above the BIOS data area and reserved area of lower memory.

Be forewarned: GCC with -m16 targeting real mode is USE AT YOUR ON RISK. You may lose your sanity as well. Putting the processor into 32-bit protected mode with a flat memory model extending from 0 to 4gb is ideal for GCC


This code makes the assumption that you are not in unreal mode, although your system likely is in that mode.

GCC assumes that CS=DS=ES, and that the memory model is flat. It's generally not a good idea to change ES. It's possible to use ES if you save it, do work, and restore it all without intervening C code in between. Since GCC requires 80386, we can use one of the other segment registers: FS and GS. In this example we'll use FS.

One other prerequisite is that you understand Real Mode Segmentation. I assume you do since you have created a bootloader. The calculation for physical memory address is:

Physical memory address = (segment << 4) + offset

Text mode (color) video memory is at physical address 0xb8000. The base of that memory can be represented as a segment:offset pair of 0xb800:0x0000 since:

(0xb800 << 4) + 0x0000 = 0xb8000

Each cell on the visible screen is a WORD (16-bit). The upper 8 bits of the WORD are an attribute and the lower are the character as detailed at the link. The color palette is described in this Wiki page.

If we use FS as our segment, we can set it to 0xb800 and reference the video memory with it. Since your code may eventually use FS for a variety of things, we'll save it using some inline assembler code, do work on the video memory, and restore FS to what it was previously.

Since I am using inline assembler you may wish to look at Peter Corde's list of useful links on the subject.

The code that takes the above into account, and provides a mechanism for updating the screen at a row, col with an attribute via the FS segment register that we set to 0xb800.

There is more code than you may have liked but I wanted to show more than outputting a single character. The code comments may help you get on your way.

#include <stdint.h>

/* use regparm(3) to use convention where first three
 * integer sized parameters are passed in registers (EAX, EDX, ECX) rather
 * than the stack. regparm(0) is default CDECL stack based
 * parameter passing. regparm(3) is generally faster overall, compared
 * to passing all parameters on the stack. Internally, the Linux kernel 
 * uses this convention to reduce stack overhead when functions
 * are called across different kernel modules.
 */
#define fastcall __attribute__((regparm(3)))
#define asmlinkage __attribute__((regparm(0)))

/* Global functions that will be exported */
extern fastcall void dispchar(uint16_t celldata, uint16_t offset);
extern fastcall void dispstring(const char *outstring, uint8_t attr,
                                uint16_t offset);
extern fastcall void dispchar_nofsupd(uint16_t celldata, uint16_t offset);
extern fastcall void dispstring_nofsupd(const char *outstring, uint8_t attr,
                                        uint16_t offset);
extern fastcall uint32_t getset_fs(uint32_t segment);
extern fastcall void set_fs(uint32_t segment);
extern fastcall uint32_t set_videomode_fs(void);
static inline uint16_t
tm_rowcol_to_vidoffset(uint16_t row, uint16_t col, uint16_t numcols);
static inline uint16_t
tm_charattr_to_celldata(uint8_t ochar, uint8_t attr);

/*----------------------------------------------------------*/

#define COLSPERROW 80
#define ROW  3
#define COL  40
#define RED_ON_BLACK     4 /* attribute= Red character on black background */
#define MAGENTA_ON_BLACK 5 /* attribute= Magenta character on black background */

/* Color text mode memory segment */
#define VIDEO_SEG 0xb800

/* Place main before all other code */
int
_main()
{
    /* Set FS to video mode segment and save previous value of FS */
    uint32_t oldfs = set_videomode_fs();
    dispchar_nofsupd(tm_charattr_to_celldata('A', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL, COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata('B', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL + 1, COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata(' ', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL + 2, COLSPERROW));
    dispstring_nofsupd("Hello World", RED_ON_BLACK,
                       tm_rowcol_to_vidoffset(ROW, COL + 3, COLSPERROW));

    /* Restore FS to original value when finished doing video mode work */
    set_fs(oldfs);

    /* Display Hello World using version dispstring
     * that saves/restores FS automatically */
    dispstring("Hello World", MAGENTA_ON_BLACK,
               tm_rowcol_to_vidoffset(ROW+1, COL + 3, COLSPERROW));

    return 0;
}


/* Convert Text Mode(TM) row, col, numcols
 * to a video offset. numcols is the number of columns
 * per row. Return value is a BYTE offset (not WORD)
 */
static inline uint16_t
tm_rowcol_to_vidoffset(uint16_t row, uint16_t col, uint16_t numcols)
{
    return ((row * numcols + col) * 2);
}

static inline uint16_t
tm_charattr_to_celldata(uint8_t ochar, uint8_t attr)
{
    return (uint16_t) (attr << 8) | (uint8_t) ochar;
}

/* Display character with FS change */
fastcall void
dispchar(uint16_t celldata, uint16_t offset)
{
    uint32_t oldfs = set_videomode_fs();
    dispchar_nofsupd(celldata, offset);
    set_fs(oldfs);
}

/* Display character with no FS change */
fastcall void
dispchar_nofsupd(uint16_t celldata, uint16_t offset)
{
    __asm__ ("movw %w[wordval], %%fs:%[memloc]\n\t"
             :
             :[wordval]"ri"(celldata),
              [memloc] "m"(*(uint32_t *)(uint32_t)offset)
              :"memory");
}

/* Set FS segment and return previous value */
fastcall uint32_t
getset_fs(uint32_t segment)
{
    uint32_t origfs;
    __asm__ __volatile__("mov %%fs, %w[origfs]\n\t"
                         "mov %w[segment], %%fs\n\t"
                         :[origfs] "=&rm"(origfs)
                         :[segment] "rm"(segment));
    return origfs;
}

/* Set FS segment */
fastcall void
set_fs(uint32_t segment)
{
    __asm__("mov %w[segment], %%fs\n\t"
            :
            :[segment]"rm"(segment));
}

/* Set FS to video mode segment 0xb800 */
fastcall uint32_t
set_videomode_fs(void)
{
    return getset_fs(VIDEO_SEG);
}

/* Display string with FS change */
fastcall void
dispstring(const char *outstring, uint8_t attr, uint16_t offset)
{
    uint32_t oldfs = set_videomode_fs();
    dispstring_nofsupd(outstring, attr, offset);
    set_fs(oldfs);
}

/* Display string with FS change */
fastcall void
dispstring_nofsupd(const char *outstring, uint8_t attr, uint16_t offset)
{
    const char *curchar = outstring;
    int i = 0;

    for (; *curchar; curchar++, i++)
        dispchar_nofsupd(tm_charattr_to_celldata(*curchar, attr),
                         offset + i * 2);
}


Linker script for GCC on Windows

Your kernel.bin may become larger than you expect when using GCC under windows. This is because of the default alignment rules that GCC is using. The following linker script may help reduce the size:

ENTRY(__main);
OUTPUT(i386pe);

SECTIONS
{
    __kernelbase = 0x520;
    . = __kernelbase;

    .text : SUBALIGN(4) {
        *(.text.st);
        *(.text);
    }

    .data : 
        SUBALIGN(4) {
        __data_start = .;
        *(.rdata*);
        *(.data);
        __data_end = .;
        __bss_start = .;
        *(.bss);
        __bss_end = .;
    }
}

This script is set to an ORG of 0x520 (not 0x10000). As mentioned previously it's highly recommended to not use an origin of 0x10000 as you have been with 16-bit GCC generated code. Name the linker script linker.ld and then you can use these commands to assembler and link the kernel:

gcc -ffreestanding -c -m16 kernel.c -o kernel.o -O3
ld -o kernel.pe kernel.o -Tlinker.ld
objcopy -O binary kernel.pe kernel.bin

You would have to modify your bootloader to read kernel sectors into memory starting at address 0x520.

With a simple bootloader and this kernel built using the provided code/linker script, this is what Bochs shows when it's run:


A Look at some of the Generated Code

The first few lines of function main save the current FS register, set FS to the video segment 0xb800 and prints out 3 characters:

int
_main()
{
    /* Set FS to video mode segment and save previous value of FS */
    uint32_t oldfs = set_videomode_fs();
    dispchar_nofsupd(tm_charattr_to_celldata('A', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL, COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata('B', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL + 1, COLSPERROW));
    dispchar_nofsupd(tm_charattr_to_celldata(' ', RED_ON_BLACK),
                     tm_rowcol_to_vidoffset(ROW, COL + 2, COLSPERROW));
    dispstring_nofsupd("Hello World", RED_ON_BLACK,
                       tm_rowcol_to_vidoffset(ROW, COL + 3, COLSPERROW));
    [code that prints strings has been snipped for brevity]
    set_fs(oldfs);

The code generated can be seen by using this objdump command:

objdump -Dx kernel.pe --no-show-raw-insn -mi8086 -Mintel

The Intel syntax output is as follows with my compiler (using -O3 optimizations):

00000520 <__main>:
 520:   push   esi                     ; Save register contents
 522:   mov    eax,0xb800
 528:   push   ebx                     ; Save register contents
 52a:   mov    si,fs                   ; Save old FS to SI                  
 52d:   mov    fs,ax                   ; Update FS with 0xb800 (segment of video) 
 52f:   mov    WORD PTR fs:0x230,0x441 ; 0x441 = Red on black Letter 'A'
                                       ; Write to offset 0x230 ((80*3+40)*2) row=3,col=40
 536:   mov    WORD PTR fs:0x232,0x442 ; 0x442 = Red on black Letter 'B'
                                       ; Write to offset 0x232 ((80*3+41)*2) row=3,col=41
 53d:   mov    WORD PTR fs:0x234,0x420 ; 0x420 = Red on black space char
                                       ; Write to offset 0x234 ((80*3+42)*2) row=3,col=42

This line of C code that restored FS:

 set_fs(oldfs);

With this instructions later on:

 571:   mov    fs,si                   ; Restore original value previously saved in SI

I've annotated the disassembly with comments to show how each of the WORD values were updated in video display memory. A lot of lines of C code, but the output is very simple.

这篇关于显示文本的视频内存0xb8000不使用C库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆