[ExpOS]开发经验(2)保护模式中不依赖bios


保护模式中不依赖bios才是重点.

原理: 显示到屏幕上的字母和符号统统存在于一段叫做 framebuffer 的显存中. 至于其出现于内存的物理地址, 要看VGA板的工作模式. VGA

的两种模式是: monochrome (单色?) emulation , 或者color emulation.

emulation---|--framebuffer linear address--|--framebuffer real-mode address--|--I/O address of CRTC 
color-------|--B8000h----------------------|--B800h:0000 --------------------|--3D4h 
monochrome--|--B0000h----------------------|--B000h:0000 --------------------|--3B4h



CRTC 是VGA的一个功能单元, 待会再讨论有关CRTC的东东. 一般来说, 应该是 color emulation, 记得大一的时候我们的实验室倒是有几台386

上有 monochrome 的古董.

备注1 的c代码可以检测VGA是处于那种工作模式.
如果能够看懂, 拿来用用应该不成问题.

不会弄代码的格式, 大家拷贝后自己整理吧.
这里给一个简单的.

/* video card mono/colour detection by Dark Fiber 
    
* returns 0=mono, 1=colour 
    
*/ 
    
int detect_video_type(void) 
    
{ 
    
int rc; 
    
char c=(*(USHORT*)0x410&0x30 
    

/* C can be 0x00 or 0x20 for colour, 0x30 for mono 
    
if(c==0x30) 
    
rc=0; // mono 
    
else 
    
rc=1; // colour 
    

return rc; 
    
}








字符及属性


在framebuffer中的每个字符都占用两个字节: ASCII 码值在地址 N 的话, N+1 就是他的属性字节.
属性字节的各个位的含义如下.

b7 ----- 闪烁
b6:b4 -- 背景色(0-7)
b3:b0 -- 前景色(0-15)


color value -- color -- color value -- color
0 ------------ 黑色------ 8 ---------- 暗灰
1 ------------ 蓝色------ 9 ---------- 亮蓝
2 ------------ green ---- 10 --------- bright green
3 ------------ cyan ----- 11 --------- bright cyan
4 ------------ red ------ 12 --------- pink
5 ------------ magenta -- 13 --------- bright magenta
6 ------------ brown ---- 14 --------- yellow
7 ------------ white ---- 15 --------- bright white


假定使用color模式:

Turbo C 代码的例子(cpu 工作于 16-bit real mode), 把白色的 'H' 以蓝色背景放到屏幕左上角.

#include <dos.h> /* pokeb() */ 
pokeb(0xB800, 0, 'H'); 
pokeb(0xB800, 1, 0x1F);




NASM 汇编中这么写(16-bit real mode):

mov bx,0B800h 
mov es,bx 
mov byte [es:0],'H' 
mov byte [es:1],1Fh




DJGPP 代码(32-bit pmode), 有所不同. 把黄的 'H' 以红色背景放到屏幕右上角.因为处于保护模式, 我们使用far指针.

#include <sys/farptr.h> /* _farpokeb() */ 
#include <go32.h> /* _dos_ds */ 
_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 0, '*'); 
_farpokeb(_dos_ds, 0xB8000 + 79 * 2 + 1, 0x4E);




非得用 near 指针?

#include <sys/nearptr.h> 
#include <crt0.h> 
#include <stdio.h> 

unsigned char *fb; 

if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR)) 
{ if(!__djgpp_nearptr_enable()) 
{ printf("Could not enable nearptr access\n"); 
return -1; } } /* probably Windows NT DOS box */ 
fb = (unsigned char *)0xB8000 + __djgpp_conventional_base; 
fb[79 * 2 + 0] = '*'; 
fb[79 * 2 + 1] = 0x4E;





Scrolling(滚屏)


BIOS 滚屏就算了吧?!

如果使用 movedata() , 也算简单. 与memcpy() 不同的地方在于movedata对于源和目的都使用 far指针.


Turbo C 代码: 在 80x25 的方式下上滚一行(color emulation):

#include <string.h> /* movedata() */ 
movedata(0xB800, 80 * 2, 
0xB800, 0, 
80 * (25 - 1) * 2);




DJGPP 代码

scroll 80x25 display up one line (color emulation): 
    
 
#include <string.h> /* movedata() */ 
#include <go32.h> /* _dos_ds */ 
movedata(_dos_ds, 0xB8000L + 80 * 2, 
_dos_ds, 0xB8000L, 
80 * (25 - 1) * 2);




使用 movedata() 的的话,如果 src < dst, 比如下滚, 可能不正确. 使用near 指针最好了, memmove() 保证任何滚动都能正确的工

作.


hardware scrolling

硬件来做滚动就比较快了. 把VGA配置成使用不同地址的framebuffer 就可以实现快速滚屏. CRTC 寄存器 12 号13号 分别包含framebuffer

相对于B0000h, B8000h, or A0000h 之偏移(offset) 的MSB 与 LSB .

/* scroll up one line */ 
#include <dos.h> /* outportb() */ 
unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */ 
unsigned short offset = 80; 

/* the CRTC index is at crtc_adr + 0 
select register 12 */ 
outportb(crtc_adr + 0, 12); 
/* the selected CRTC register appears at crtc_adr + 1 */ 
outportb(crtc_adr + 1, offset >> 8); 
outportb(crtc_adr + 0, 13); 
outportb(crtc_adr + 1, offset & 0xFF);





硬件滚屏的缺陷在于不能够持续无限的滚动. 因为最终 framebuffer 会超过 video memory 的上(下)限.
可以用作 framebuffer 的那段内存可以分成几个虚拟控制台(virtual consoles (VCs)). 32K 的 video memory 可以被分成8 个80x25的VCs.

console 译作控制台我认为不妥, 这里的console无非就是虚拟的几个屏幕.上面的代码就可以选择把那个虚拟屏呈现给用户. (Linux 的 VCs

使用了不同的管理方法, 我不知道.)


Moving the cursor


CRTC 寄存器14号和 15 号, 包含光标位置的 MSB LSB . 光标的位置用相对B8000h 或 B0000h的偏移来表示.

#include <dos.h> /* outportb() */ 
unsigned short crtc_adr = 0x3D4; /* 0x3B4 for monochrome */ 
unsigned short offset; 
unsigned short x = 20, y = 3; 

offset = x + y * 80; /* 80 characters per line */ 
outportb(crtc_adr + 0, 14); /* MSB of offset to CRTC reg 14 */ 
outportb(crtc_adr + 1, offset >> 8); 
outportb(crtc_adr + 0, 15); /* LSB of offset to CRTC reg 15 */ 
outportb(crtc_adr + 1, offset);





[i] 不要告诉我, 你不知道outportb 那里去找! [/i]








备注1




/***************************************************************************** 
    
Determines if VGA board is set for monochrome or color emulation. 
    
Uses 3 different algorithms. 
    

This code is public domain (no copyright). 
    
You can do whatever you want with it. 
    
*****************************************************************************/ 
    
#include <stdlib.h> /* atoi() */ 
    
#include <stdio.h> /* printf() */ 
    
//#include "../port.c" /* inportb(), peekw() */ 
    

/********************************* TURBO C **********************************/ 
    
#if defined(__TURBOC__) 
    
#include <dos.h> /* inportb(), peek() */ 
    

#define peekw(S,O) peek(S,O) 
    

/********************************* DJGPP ************************************/ 
    
#elif defined(__DJGPP__) 
    
#include <crt0.h> /* _CRT0_FLAG_LOCK_MEMORY */ 
    
#include <dos.h> /* inportb() */ 
    

//#define NEARPTR 1 
    

/* near pointers; not supported in Windows NT/2k/XP DOS box 
    
Must call __djgpp_nearptr_enable() before using these functions */ 
    
#if defined(NEARPTR) 
    
#include <sys/nearptr.h> /* __djgpp_conventional_base, __djgpp_nearptr_enable() */ 
    
#include <stdio.h> /* printf() */ 
    
#include <crt0.h> /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */ 
    

#define peekw(S,O) *(unsigned short *)(16uL * (S) + (O) + \ 
    
__djgpp_conventional_base) 
    
/* far pointers */ 
    
#else 
    
#include <sys/farptr.h> /* _farpeekw() */ 
    
#include <go32.h> /* _dos_ds */ 
    

#define peekw(S,O) _farpeekw(_dos_ds, 16uL * (S) + (O)) 
    
#endif 
    

/******************************** WATCOM C **********************************/ 
    
#elif defined(__WATCOMC__) 
    
#include <conio.h> /* inp() */ 
    

#if defined(__386__) 
    
/* CauseWay DOS extender only */ 
    
#define peekw(S,O) *(unsigned short *)(16uL * (S) + (O)) 
    
#else 
    
#include <dos.h> /* MK_FP() */ 
    

#define peekw(S,O) *(unsigned short far *)MK_FP(S,O) 
    
#endif 
    

#define inportb(P) inp(P) 
    

#else 
    
#error Not Turbo C, not DJGPP, not Watcom C. Sorry. 
    
#endif 
    


static unsigned short g_crtc_base_adr; 
    
/***************************************************************************** 
    
Pentium 486 Bochs 
    
method color color (color) mono 
    
------ ------- ----- ------- ------- 
    
1 pass pass pass UNTESTED 
    
2 pass pass pass UNTESTED 
    
3 pass pass pass UNTESTED 
    
*****************************************************************************/ 
    
int main(int arg_c, char *arg_v[]) 
    
{ 
    
int method; 
    

#if defined(__DJGPP__)&&defined(NEARPTR) 
    
if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR)) 
    
{ 
    
if(!__djgpp_nearptr_enable()) 
    
{ 
    
printf("Could not enable nearptr access " 
    
"(Windows NT/2k/XP?)\nUn-define NEARPTR " 
    
"in source code and re-compile\n"); 
    
return 1; 
    
} 
    
} 
    
#endif 
    
if(arg_c < 2) 
    
{ 
    
printf("attempt to detect monochrome/color VGA emulation " 
    
"using one of three methods\n" 
    
"specify 1, 2, or 3 on the command line\n"); 
    
return 1; 
    
} 
    
method = atoi(arg_v[1]); 
    
switch(method) 
    
{ 
    
case 1: 
    
/* this method cobbled from info in Finn Thoegersen's VGADOC4 */ 
    
#define VGA_MISC_READ 0x3CC 
    

if((inportb(VGA_MISC_READ) & 0x01) == 0) 
    
g_crtc_base_adr = 0x3B4; /* mono */ 
    
else 
    
g_crtc_base_adr = 0x3D4; /* color */ 
    
break; 
    
case 2: 
    
/* I forgot where this came from: 
    
"The word at low memory address 0040:0063 (or 0000:0463) contains the 
    
I/O address of the CRTC which can be used to determine whether the video 
    
system is colour or monochrome. A value of 3B4 hex indicates monochrome." 
    
(I presume 3D4 hex means color; my Pentium system has that value at 0463.) */ 
    
g_crtc_base_adr = peekw(0x40, 0x63); 
    
break; 
    
case 3: 
    
/* Dark Fiber's method, from the OS FAQ 
    
[url=www.mega-tokyo.com/os]http://www.mega-tokyo.com/os[/url] 
    

from MEMORY.LST of Ralf Brown's Interrupt List 
    
0040:0010 is Installed Hardware word, b5:b4 indicate video hardware: 
    
00 EGA,VGA,PGA, or other with on-board video BIOS 
    
01 40x25 CGA color 
    
10 80x25 CGA color 
    
11 80x25 mono text 
    

whoa, this won't work with DJGPP -- OK, I will make a slight change here 
    
if((*(unsigned short *)0x410 & 30) == 0x30) */ 
    
if((peekw(0x40, 0x10) & 30) == 0x30) 
    
g_crtc_base_adr = 0x3B4; /* mono */ 
    
else 
    
g_crtc_base_adr = 0x3D4; /* color */ 
    
break; 
    
default: 
    
printf("didn't find 1, 2, or 3 on the command line, sorry\n"); 
    
return 1; 
    
} 
    
/* what've we got? */ 
    
if(g_crtc_base_adr < 0x3C0) 
    
printf("MONOCHROME emulation detected\n"); 
    
else 
    
printf("color emulation detected\n"); 
    
return 0; 
    
}