如何使用C在8086中读取和存储视频内存



我正在为我的家庭作业编写一个小型操作系统,我想保留shell上显示的内容(因为当我使用shell打开另一个应用程序时,屏幕会被它的新内容覆盖,我想恢复shell,就像返回后调用应用程序之前一样(。

我想用C语言来完成它,但我不知道如何正确使用指针来处理它。我的想法是读取视频内存中的所有数据,并将其保存在数据结构中,然后通过将其重写到视频内存中来恢复它,但是我尝试了很多方法,但都没有成功。

我试着写这样的函数:

void storeContents()
{
uint16_t * ptr =0xB800;
int cnt = 0;
while(cnt < 80 * 25)
{
store[cnt++] = * (ptr++);
}
}

我不知道这个函数是否正确,但当我使用Bochs进行调试时,我发现在输入这个函数时,它被卡在了这个循环中,我确信指针一定有问题(但我不知道这意味着什么,我找不到调用哪个中断(

(0) [0x0000000fe9e6] f000:e9e6 (unk. ctxt): push ax ; 50
<bochs:26> s
Next at t=439640331
(0) [0x0000000fe9e7] f000:e9e7 (unk. ctxt): call .-22398 (0x000f926c) ; e882a8
<bochs:27> s
Next at t=439640332
(0) [0x0000000f926c] f000:926c (unk. ctxt): mov al, 0x20 ; b020
<bochs:28> s
Next at t=439640333
(0) [0x0000000f926e] f000:926e (unk. ctxt): out 0x20, al ; e620
<bochs:29> s
Next at t=439640334
(0) [0x0000000f9270] f000:9270 (unk. ctxt): ret ; c3
<bochs:30> s
Next at t=439640335
(0) [0x0000000fe9ea] f000:e9ea (unk. ctxt): pop ax ; 58
<bochs:31> s
Next at t=439640336
(0) [0x0000000fe9eb] f000:e9eb (unk. ctxt): iret ; cf

你能帮我吗?非常感谢。(顺便说一句,我使用的是图形卡的文本模式,我的操作系统上还没有文件系统。操作系统目前处于真实模式(

在8086(和8088(上,内存访问使用两个寄存器完成,一个是16位段寄存器,另一个是16bit偏移值/寄存器。实际地址是通过取段寄存器,将其向左移位4(乘以16(并添加偏移量来计算的。因此,如果使用DS作为段寄存器,使用AX作为偏移寄存器,则实际内存地址将为(DS * 16) + AX

8086提供了4个寄存器来保存内存访问的段值:DS(数据段(、SS(堆栈段(、CS(代码段(和ES(额外段(。使用哪一个取决于操作代码。指令提取将始终相对于CS

请注意,分段可以重叠,因此不同的分段/偏移组合可以引用相同的内存。例如,[aaaa:0000][aaa9:0010][aaa8:0020][aaa7:0030]都引用地址0xaaaa0

8086的地址总线是20位的,所以如果地址计算溢出,它就会循环。所以[ffff:0010]将是一种模糊的写作方式CCD_ 11。80286增加了更宽的地址总线,在8086模式下运行时不会溢出,因此为了使IBM PC-AT与原始PC保持兼容,IBM决定实现一些奇怪的功能,包括名为A20门的键盘控制器芯片。(但这偏离了主题。(

因此,如果您想访问从0xB8000开始的内存块,您可以设置DS(或一些其他段寄存器(到0xB800,并使用适当的偏移量访问您的数据。

自然地,用C编程不必直接处理寄存器,但您仍然需要理解8086的分段内存模型。

通常,8086的C编译器允许您使用3种不同的寻址模式编译代码:

  1. 小型内存模型-此处所有数据和代码都在同一段(DS = SS = CS = ES(中。指针是16位的,包含段中的偏移量。代码和数据的总大小可能只有64k。

  2. 大内存模型-类似于";"小存储器模型";,而是一个用于数据的段和一个用于代码的段(DS = SS = ES != CS(。指针仍然是16位。代码和数据的大小每个最大可以是64k。

  3. 巨大的内存模型-指针是32位的,同时包含内存地址的分段和偏移部分。指针运算是通过只修改指针的偏移部分来完成的。因此,即使程序可以访问整个1Mb的地址空间,c中的本地数据对象(包括结构和数组(也只能是最大64k,并且只有当它们以16字节对齐开始时。

如果是IRC,原始PC上的视频内存在0xb8000启动。这将是片段0xb800。为了能够访问,我们必须使用";巨大的存储器模型";(32位指针(。在文本模式中,字符存储为两个字节,一个用于属性,另一个用于字符代码。

把所有这些放在一起,假设视频控制器设置为80x25字母数字模式,我们可以这样写到屏幕上:

struct char_cell
{
unsigned char attribute;
unsigned char char_code;
};
/*
How to handle pointers is compiler dependent, but we assume here that 
the segment value is stored in the high 16 bits. So memory address
0xb8000 or [b800:0000] can be written as 0xb8000000
*/
#define video_buffer ((struct char_cell *)0xb8000000ul) 
void put_char(int line, int column, int ch)
{
video_buffer[line * 80 + column].char_code = ch;
}
int get_char(int line, int column, int ch)
{
return video_buffer[line * 80 + column].char_code;
}
/*
Saving/restoring buffer doesn't handle saving/restoring cursor position.
This must be done separately.
*/ 
void save_video_buffer(struct char_cell buffer[80 * 25])
{
memcpy(save_buffer, video_buffer, sizeof(*buffer) * 80 * 25); 
}
void restore_video_buffer(struct char_cell buffer[80 * 25])
{
memcpy(video_buffer, save_buffer, sizeof(*buffer) * 80 * 25); 
}

我已经很久没有做MS-DOS编程了,所以细节可能有点错误。

相关内容

  • 没有找到相关文章

最新更新