gpt4 book ai didi

c - 为什么我的显存偏移量计算偏差了一个?

转载 作者:太空狗 更新时间:2023-10-29 17:24:47 26 4
gpt4 key购买 nike

我一直在阅读并遵循 Nick Blundell 撰写的关于从头编写操作系统的教程,该教程可在 https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf 找到。

我已经成功地编写了一个可以调用 C 代码的引导加载程序,所以我开始用 C 编写我的内核。我现在正在尝试编写可以在屏幕上打印字符和字符串的函数。当我开始执行 C 代码时,我处于 ​​32 位保护模式,因此我试图从视频内存地址 0xb8000 正确计算内存偏移量。

当我尝试使用计算出的偏移量访问视频内存的特定区域时,我的问题就开始了。由于文本区域是 25 行 x 80 列,我使用公式 ((row * 80) + column) * 2 因为我必须有一个字符字节和一个属性字节。当我设置 row = 0 和 column = 0 时,我尝试打印的 X 不存在。设置 row = 0 和 column = 1,X 出现在左上角。

从 char* video_memory = 0xb8000 开始并重复发出 video_memory++ 允许我正确访问每个字节并在黑色背景上打印一个空格。

这是我的主要代码:

#include "../drivers/screen.h"

void main() {

//clear_screen();
//print_character('X', 0, 0, 0);

// Helper variables.
int row;
int column;

// We need to point at 0xB8000, where video memory resides.
unsigned char* video_memory = (unsigned char*)0xB8000;
for(row = 0; row < 25; row++) {
for(column = 0; column < 80; column++) {
// Clear the screen by printing a space on a black background.
*video_memory = ' ';
video_memory += 1;
*video_memory = 0x0F;
video_memory += 1;
}
}

// Test the offset calculation by printing at row 0, column 0 (the upper
// left corner of the screen).
row = 0;
column = 0;

// For an 80 by 25 grid. Multiply by 2 to account for the need of two bytes
// to display a character with given attributes.
int offset = ((row * 80) + column) * 2;

// Reset memory location after the loop.
video_memory = (unsigned char*)0xB8000;

// Add the offset to get the desired cell.
// THIS IS WHERE THE PROBLEM IS! Setting column = 1 prints in the first cell
// of video memory instead of the second.
video_memory += offset;

// Set character and its attributes.
*video_memory = 'X';
video_memory++;
*video_memory = 0x0F;
}

这是当 row = 0 和 column = 0 时显示的控制台: The console when row and column are 0. No X appears.

这是行 = 0 和列 = 1 时的控制台: The console when row is 0 and column is 1. An X appears.

这是我上面的 kernel.c 文件的 objdump:

kernel.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
#include "../drivers/screen.h"

void main() {
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
// Helper variables.
int row;
int column;

// We need to point at 0xB8000, where video memory resides.
unsigned char* video_memory = (unsigned char*)0xB8000;
4: 48 c7 45 f8 00 80 0b mov QWORD PTR [rbp-0x8],0xb8000
b: 00
for(row = 0; row < 25; row++) {
c: c7 45 ec 00 00 00 00 mov DWORD PTR [rbp-0x14],0x0
13: eb 2f jmp 44 <main+0x44>
for(column = 0; column < 80; column++) {
15: c7 45 f0 00 00 00 00 mov DWORD PTR [rbp-0x10],0x0
1c: eb 1c jmp 3a <main+0x3a>
// Clear the screen by printing a space on a black background.
*video_memory = ' ';
1e: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
22: c6 00 20 mov BYTE PTR [rax],0x20
video_memory += 1;
25: 48 83 45 f8 01 add QWORD PTR [rbp-0x8],0x1
*video_memory = 0x0F;
2a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
2e: c6 00 0f mov BYTE PTR [rax],0xf
video_memory += 1;
31: 48 83 45 f8 01 add QWORD PTR [rbp-0x8],0x1
int column;

// We need to point at 0xB8000, where video memory resides.
unsigned char* video_memory = (unsigned char*)0xB8000;
for(row = 0; row < 25; row++) {
for(column = 0; column < 80; column++) {
36: 83 45 f0 01 add DWORD PTR [rbp-0x10],0x1
3a: 83 7d f0 4f cmp DWORD PTR [rbp-0x10],0x4f
3e: 7e de jle 1e <main+0x1e>
int row;
int column;

// We need to point at 0xB8000, where video memory resides.
unsigned char* video_memory = (unsigned char*)0xB8000;
for(row = 0; row < 25; row++) {
40: 83 45 ec 01 add DWORD PTR [rbp-0x14],0x1
44: 83 7d ec 18 cmp DWORD PTR [rbp-0x14],0x18
48: 7e cb jle 15 <main+0x15>
}
}

// Test the offset calculation by printing at row 0, column 0 (the upper
// left corner of the screen).
row = 0;
4a: c7 45 ec 00 00 00 00 mov DWORD PTR [rbp-0x14],0x0
column = 0;
51: c7 45 f0 00 00 00 00 mov DWORD PTR [rbp-0x10],0x0

// For an 80 by 25 grid. Multiply by 2 to account for the need of two bytes
// to display a character with given attributes.
int offset = ((row * 80) + column) * 2;
58: 8b 55 ec mov edx,DWORD PTR [rbp-0x14]
5b: 89 d0 mov eax,edx
5d: c1 e0 02 shl eax,0x2
60: 01 d0 add eax,edx
62: c1 e0 04 shl eax,0x4
65: 89 c2 mov edx,eax
67: 8b 45 f0 mov eax,DWORD PTR [rbp-0x10]
6a: 01 d0 add eax,edx
6c: 01 c0 add eax,eax
6e: 89 45 f4 mov DWORD PTR [rbp-0xc],eax

// Reset memory location after the loop.
video_memory = (unsigned char*)0xB8000;
71: 48 c7 45 f8 00 80 0b mov QWORD PTR [rbp-0x8],0xb8000
78: 00

// Add the offset to get the desired cell.
// THIS IS WHERE THE PROBLEM IS! Setting column = 1 prints in the first cell
// of video memory instead of the second.
video_memory += offset;
79: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
7c: 48 98 cdqe
7e: 48 01 45 f8 add QWORD PTR [rbp-0x8],rax

// Set character and its attributes.
*video_memory = 'X';
82: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
86: c6 00 58 mov BYTE PTR [rax],0x58
video_memory++;
89: 48 83 45 f8 01 add QWORD PTR [rbp-0x8],0x1
*video_memory = 0x0F;
8e: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
92: c6 00 0f mov BYTE PTR [rax],0xf
}
95: 90 nop
96: 5d pop rbp
97: c3 ret

我已经追踪并手工检查了我的偏移量计算的实际装配说明,它们似乎是正确的。我怀疑当我尝试将我的偏移量(类型 int)添加到我的视频内存地址(类型 unsigned char*)时出现问题,但我还是不完全确定。

此外,我尝试对偏移量的特定数字进行硬编码。使用 video_memory += 0 而不是 video_memory += offset 可以正常工作。

最佳答案

经过多方查找,我在ARM Information Center上找到了一篇文章描述使用 C 指针访问内存映射 I/O 设备的特定地址。

将我的 video_memory 指针变量声明为“volatile”可确保“编译器始终执行内存访问,而不是优化它们……”。显然,根据 this answer on Quora ,编译器可以生成指令,在数据刷新到内存之前覆盖写入缓冲区中的数据,这就是我的问题所在。

因此,将我的变量声明为 volatile unsigned char* video_memory = 0xB8000; 会产生预期的结果。

关于c - 为什么我的显存偏移量计算偏差了一个?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50459574/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com