视频内存偏移计算为何差1?自制OS字符显示异常排查
Hey there! I’ve run into this exact quirk when messing with VGA text mode during my own OS dev projects—let’s break down what’s likely going wrong here and how to debug it:
1. You’re probably mismatching pointer type and offset math
This is the #1 mistake with VGA text mode:
- If
video_memoryis declared as anunsigned short*(each element holds a full 2-byte character+attribute pair), your offset should just be(row * 80) + col—no need to multiply by 2. - But you said you’re using
unsigned char*, which means each index is a single byte. So your formula((row * 80) + col) * 2is technically correct… unless you accidentally mixed up the pointer type in code, or forgot to multiply (or multiplied twice) somewhere.
For example, if you had a leftover unsigned short* declaration but kept the *2 in your offset, you’d be addressing 0xb8000 + ((row*80+col)*2)*2—that’s 4x the intended offset, which would dump your character in a totally random spot.
2. Variable type truncation is sneaking in
If row and col are defined as 8-bit types (like char or unsigned char), you might be hitting unexpected truncation during calculations:
row * 80will get promoted tointautomatically, but if you store the final offset in an 8-bit variable (e.g.,char offsetinstead ofint offset), it’ll get chopped down to 8 bits. Even withrow=0, this could cause weird behavior if other parts of the calculation overflow 8 bits.- Fix this by explicitly declaring
offsetas anint(orunsigned int) to keep the full 32-bit value during calculations. You can also castrowandcoltointin the formula to avoid implicit promotions:int offset = (((int)row * 80) + (int)col) * 2;
3. Let’s dig into that disassembly
Your kernel’s disassembly holds the smoking gun—focus on these steps:
- Parameter passing: Double-check that
rowandcolare being loaded correctly. Did the calling convention mix up their order? (C uses right-to-left stack pushing, so if your function expects(char c, int row, int col), the stack should havecol,row,con top.) - Offset calculation: Look for instructions like
mul(forrow*80),add(for+col), andshl(for*2). Is theshlmissing? Did it use an arithmetic shift (sar) instead of logical shift? Any sign extension weirdness (e.g., 16-bit registers being sign-extended to 32-bit instead of zero-extended)? - Addressing: Is the final offset correctly added to
0xb8000? If you see something likeadd eax, 0xb8000whereeaxholds your offset, that’s good—but ifeaxhas a truncated or wrong value, that’s the issue.
4. Don’t forget the attribute byte
Even though your clear-screen loop works, double-check that you’re setting the attribute byte when printing 'X':
- If you only do
video_memory[offset] = 'X'without settingvideo_memory[offset + 1] = 0x07(black background, white text), the attribute byte will be whatever random value was in memory. Forrow=0 col=0, that might be0x00(black on black), making the character invisible. Forrow=0 col=1, maybe the clear-screen loop already set that attribute byte to0x07, so the character shows up—but in the wrong spot because your offset is off.
Quick sanity checks to narrow it down
- Hardcode the offset: Try
video_memory[0] = 'X'; video_memory[1] = 0x07;—if the top-left corner shows 'X', your pointer and memory access are working, so the problem is definitely in your offset calculation. - Print the offset value: Debug by writing the offset number to a fixed screen position (e.g.,
row=2, col=0). See what value comes out forrow=0 col=0vsrow=0 col=1—does it match0and2like you expect? - Simplify the formula: Replace the dynamic calculation with hardcoded values one step at a time to isolate where it breaks.
内容的提问来源于stack exchange,提问作者Jakeman582

