无法在 C 中写入屏幕内存

Cannot write to screen memory in C

我是 C 的新手,它是我继 Java 之后的第二门高级编程语言。我已经掌握了大部分基础知识,但无论出于何种原因,我无法将单个字符写入屏幕内存。

此程序是使用 Turbo C for DOS 在 Am486-DX4-100 运行 上以 120mhz 编译的。显卡是非常标准的VLB Diamond Multimedia Stealth SE,使用Trio32芯片。

对于 OS 我是 运行 PC-DOS 2000,加载了 ISO 代码页。我是 运行 标准 MDA/CGA/EGA/VGA 样式的 80 列文本模式,带颜色。

这是我写的程序:

#include <stdio.h>

int main(void) {
    unsigned short int *Video = (unsigned short int *)0xB8000;
    *Video = 0x0402;
    getchar();
    return 0;
}

正如我所说,我是 C 的新手,所以如果我的错误看起来很明显,我深表歉意,我无法找到我可以理解的有关如何执行此操作的可靠来源。

据我所知,在 x86 平台的实模式下,文本模式的屏幕内存从 0xB8000 开始。每个字符存储在两个字节中,一个用于字符,一个用于background/foreground。思路是将值0x0402(应该是红色笑脸)写入0xB8000。这应该放在屏幕的左上角。

我已经考虑到屏幕可能会滚动的可能性,因此在执行时立即删除我的角色有两种方式。为了解决这个问题,我试过:

我可以读取和打印我写入内存的值,因此它显然仍在内存中的某个位置,但出于某种原因我没有在屏幕上显示任何内容。我显然做错了什么,但我不知道可能是什么问题。如果需要任何其他详细信息,请询问。感谢您提供的任何可能的帮助。

内存段地址取决于使用的视频模式:

0xA0000 for EGA/VGA graphics modes (64 KB)
0xB0000 for monochrome text mode (32 KB)
0xB8000 for color text mode and CGA-compatible graphics modes (32 KB)

要直接访问 vram,您需要一个 32 位指针来保存段和偏移地址,否则您会弄乱您的堆。这通常会导致未定义的行为。

char far *Video = (char far *)0xb8000000;

另请参阅:What are near, far and huge pointers?

在实模式下,要寻址第一个完整的 1MiB 内存,一种称为 20-bit segment:offset addressing is used. 0xb8000 is a physical memory address. You need to use something called a far pointer that allows you to address memory with real mode segmentation. The different types of pointers are described in this Whosebug Answer

的机制

0xb8000可以表示为0xb800的段和0x0000的偏移量。得到物理地址的计算方式是segment*16+offset。 0xb800*16+0x0000=0xb8000。考虑到这一点,您可以包含 dos.h 并使用 MK_FP C 宏来初始化一个 far 指针,指向给定段和偏移量的地址。

documentationMK_FP定义为:

MK_FP() Make a Far Pointer

#include   <dos.h>

void       far *MK_FP(seg,off);
unsigned   seg;                         Segment
unsigned   off;                         Offset

MK_FP() is a macro that makes a far pointer from its component segment 'seg' and offset 'off' parts.

Returns: A far pointer.

你的代码可以这样写:

#include <stdio.h>
#include <dos.h>
int main(void) {
    unsigned short int far *Video = (unsigned short int far *)MK_FP(0xB800,0x0000);
    *Video = 0x0402;
    getchar();
    return 0;
}

正如@stacker 指出的那样,在 16 位环境中,您需要仔细分配指针。据我所知,您需要输入 FAR 关键字(我的天哪,多么怀旧)。

还要确保您没有在所谓的 "Huge" 内存模型中编译。它与远寻址不兼容,因为每个 32 位指针自动 "normalized" 到 20 位。尝试选择 "Large" 内存模型。