保护模式中不依赖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]
推荐网站
pc-hardware
VGADOC
一个vag包
execpc的控制台代码
备注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;
}