中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 搜索 | 上传 | 帮助 »
English/Chinese Fix Translation
作者:
标题: DJGPP教程2 -- VGA编程 上一主题 | 下一主题
Wengier
系统支持

“新DOS时代”站长


积分 27736
发帖 10521
注册 2002-10-9
状态 离线
『楼 主』:  DJGPP教程2 -- VGA编程 [已使用 LLM 解释]

In this page I'll explain how to programming the VGA at a resolution of 320x200x256. I will also include a demo program (see it later).

Structure of the video memory at 320x200x256
The memory structure at this resolution is very simple to use: is only a sequence of bytes mapped in the central memory. First, remember this: a pixel on the screen is a byte in the memory. So at a resolution of 320x200 the screen use 64000 bytes of memory. This memory start at the address A000:0000 (A0000 in protected mode) of the main memory. Under a real mode system is simple to access at this memory by using a far pointer, like this:

char far *vgamemory = (char *)0xA0000000L;

or (on old compilators)

char far *vgamemory = (char *)MK_FP(0xA000, 0);

But under DJGPP isn't so simple because the memory is protected, so you can't access directly to all the memory of the system. The best solution is to disable the protection of the DOS memory by calling the procedure __djgpp_nearptr_enable(). There are a simple code that shows how to access at the DOS memory:

#include

__djgpp_nearptr_enable();
vgamemory=(char *)(0xa0000 + __djgpp_conventional_base);

__djgpp_nearptr_disable();

The header nearptr.h is necessary for the prototipes of the functions. We should use __djgpp_conventional_base to get the right address of the DOS memory (the protected mode is very intricate). At the end of the program, use __djgpp_nearptr_disable() to get the program back to regular protected mode. But you must remember that with this code you disable the protection of the memory, so you can damage the DOS area and block the system. Use the program info (you must download the file txi390b.zip) for more informations.
For clearing the screen with a specified color simply call the function memset() with a pointer to the video memory.

Setting the VGA mode 320x200x256
The BIOS supplies a procedure that provide to set the VGA mode 320x200x256 via interrupt 10h. We must only set a register (ax) and call this interrupt. To return to the text mode (usually mode 03h) we can use the same procedure. There are the simple code to swap to the wanted mode:

#define TEXT_MODE 0x03
#define VGA_MODE 0x13

void setmode(short mode)
{
union REGS regs;

regs.h.ah = 0x00; // Procedure 0x00: Set Video Mode
regs.h.al = mode; // The requested video mode
int86(0x10,&regs,&regs) // Call the BIOS
}

If you set the most significant bit of the register al (use 0x93 instead of 0x13) the video memory is not cleared. If you don't like to use the BIOS you can download the file modes.zip that explain how to set the video mode without the BIOS.

Put the first pixel
In the paragraph Structure of the video memory I've introduced how to access to the video memory. Now is very simple to draw a pixel: is enough to write at the right offset in the video memory. So I've draw a simple image to explain how the video memory is used at this resolution:
If you write a color byte at the start of the video memory (A000:0000) you'll show a pixel at the upper-left corner of the screen (0, 0). When you write at the offset 319 (A000:013F) you'll show a pixel at the upper right corner. Writing at the next byte (A000:0140) and the pixel will appear at the position (0, 1). So we can understand that if we want to show a pixel at the position (x, y) we must write at the address A000:(y*320+x) of the video memory. This is a fast method for write a pixel. But we can optimize a bit the speed by using shifting instead of multiplications. So we divide the number 320 into the powers of two 256 and 64 and we have 320y = 256y + 64y and next offset = (y<<8) + (y<<6) + x .

The basical algorithms
Lines
For drawing a line we must calculate where an immaginary line intersect an immaginary rect. For each square of the rect correspond a pixel on the screen. So for each square intersected by a immaginary line we have a pixel of the real line to draw. We must calculate what squares is intersected by the line. A simple method is by using the equation x-x1=m(y-y1) , but is a bit slow.
Instead we can use the Bresenham's line-drawing algorithm that use only additions and subtractions. It is based on the relation:

sizex/sizey=countx/county

sizex and sizey are the sizes of the rectangle that contain the line
countx and county are the counters impiegated by the algorithm

so we have:

county * sizex
calculatex= --------------
sizey

countx * sizey
calculatey= --------------
sizex

But the Bresenham's algorithm transform the equations for drawing the lines without the multiplications and the divisions. See the demo program for the code of the algorithm.

Rectangles
Drawing rectangles is very simple: simply drawing 2 horizontals and 2 verticals lines. But we can make an optimized code that not use the drawline() function and draw a rectangle more faster. We know the structure of the video memory, so is simple to design an algorithm for a rectangle.


draw sizex pixels from (x1, y1)
onerow:
jump 320-sizex pixels
draw a pixel
jump sizex-2 (intsizex) pixels
draw a pixel
while row drawed < sizey-2
jump 320-sizex pixel
draw sizex pixels

See the demo program for the code of the algorithm.

Circles
Now I've no time for this.

Using the palette
In mode 13h we can use 256 colors for display graphics. Each color is selected from a palette of 262144 colors, so if we must use a special color we can modify the default selection of a color for setting the one that we want. For make this is necessary to tell to the VGA card the number of the color that we want to change and next the RGB value. This value is a triple of byte in the range 0..63, that specify the intensity of Red, Green and Blue. Whit the mixing of these colors we can make the wanted color. The VGA card have 3 register for change the palette: RGB_READ for telling the index of the color to read, RGB_WRITE for telling the index of the color to write and RGB_DATA for read/write the RGB value. RGB_RESET is used for prepare the VGA card (isn't necessary). The following code shows how to setting the RGB value of the color 15 (WHITE):

#define RGB_RESET 0x03C6
#define RGB_READ 0x03C7
#define RGB_WRITE 0x03C8
#define RGB_DATA 0x03C9

void setwhite(void)
{
outp(RGB_RESET, 0xFF); // Prepare the VGA card
outp(RGB_WRITE, WHITE); // Tell that we want to write the color 15 (WHITE)
outp(RGB_DATA, 64); // Red value
outp(RGB_DATA, 64); // Green value
outp(RGB_DATA, 64); // Blue value
}

For setting all the 256 colors simply tell to the VGA card that we want to write the color 0, and next write the RGB of all the colors:

void setpalette(char *palette)
{
register int i;

outp(RGB_RESET, 0xFF); // Prepare the VGA card
outp(RGB_WRITE, 0); // Tell that we want to write the entire palette
for(i=0;i<256;i++) {
outp(RGB_DATA, palette[i*3]); // Red value
outp(RGB_DATA, palette[i*3+1]); // Green value
outp(RGB_DATA, palette[i*3+2]); // Blue value
}
}

For reading the RGB value of a color or of all the colors simply write the index to RGB_READ and read the values from RGB_DATA.See the demo program for the code.

How about snow or noise?
Is possible that a noise will appear on the screen when the palette is changed continuously. This effect is done because we change the palette in the same time that the VGA card update the screen. For prevent this effect is necessary to wait the retrace signal of the card via the bit 3 of the register 0x03DA. There are the code:

void waitretrace(void)
{
// Wait vertical retrace
while(inportb(0x03DA)&0x08);
// Wait refresh
while(!(inportb(0x03DA)&0x08));
}
LLM 解释

1. 视频内存结构解释

  • 320x200x256分辨率下的内存结构
    • 在320x200x256分辨率下,屏幕由连续的字节组成,一个像素对应内存中的一个字节。该分辨率下屏幕占用64000字节内存,内存起始地址在实模式下为A000:0000(保护模式下为A0000)。
    • 在DJGPP环境下,由于内存受保护,不能直接访问系统内存。需要调用__djgpp_nearptr_enable()来启用对DOS内存的访问,访问视频内存的地址计算为(char *)(0xa0000 + __djgpp_conventional_base),使用完后调用__djgpp_nearptr_disable()恢复保护模式。例如:
#include <dpmi.h>
#include <go32.h>

__djgpp_nearptr_enable();
char far *vgamemory = (char *)(0xa0000 + __djgpp_conventional_base);
__djgpp_nearptr_disable();
  • 清空屏幕可以使用memset()函数,传入视频内存指针和指定颜色来实现。
  • 设置VGA模式
    • 通过BIOS中断10h设置VGA模式,例如设置为320x200x256模式(模式0x13)和文本模式(模式0x03)的代码如下:
#include <dos.h>

#define TEXT_MODE 0x03
#define VGA_MODE  0x13

void setmode(short mode) {
    union REGS regs;
    regs.h.ah = 0x00;
    regs.h.al = mode;
    int86(0x10, &regs, &regs);
}

2. 像素绘制

  • 像素位置与内存偏移:屏幕上位置(x, y)的像素在视频内存中的偏移地址为A000:(y*320 + x)。可以优化计算,利用位移操作:offset = (y << 8) + (y << 6) + x,因为320y = 256y + 64y,即y<<8是256y,y<<6是64y。

3. 基本算法

  • 直线绘制(Bresenham算法)
    • Bresenham算法基于sizex/sizey = countx/county的关系,通过加法和减法来计算直线经过的像素点。具体代码可参考示例程序,其避免了乘法和除法操作,提高了绘制效率。
  • 矩形绘制
    • 可以通过绘制水平和垂直线来实现矩形,也可以优化代码直接利用视频内存结构来快速绘制矩形。例如,先绘制指定长度的一行像素,然后处理多行来形成矩形。
  • 调色板使用
    • 在VGA模式13h下,可通过VGA卡的寄存器来修改调色板。使用RGB_RESETRGB_WRITERGB_DATA等寄存器来设置颜色的RGB值。例如设置颜色15(白色)的代码:
#define RGB_RESET 0x03C6
#define RGB_WRITE 0x03C8
#define RGB_DATA  0x03C9

void setwhite(void) {
    outp(RGB_RESET, 0xFF);
    outp(RGB_WRITE, 15);
    outp(RGB_DATA, 64);
    outp(RGB_DATA, 64);
    outp(RGB_DATA, 64);
}
  • 设置整个调色板的代码:
void setpalette(char *palette) {
    int i;
    outp(RGB_RESET, 0xFF);
    outp(RGB_WRITE, 0);
    for (i = 0; i < 256; i++) {
        outp(RGB_DATA, palette[i * 3]);
        outp(RGB_DATA, palette[i * 3 + 1]);
        outp(RGB_DATA, palette[i * 3 + 2]);
    }
}
  • 等待垂直回扫
    • 为了防止改变调色板时出现屏幕噪声,需要等待VGA卡的垂直回扫信号。通过读取寄存器0x03DA的位3来判断回扫状态,代码如下:
void waitretrace(void) {
    while (inportb(0x03DA) & 0x08);
    while (!(inportb(0x03DA) & 0x08));
}

以上就是在DJGPP环境下进行VGA 320x200x256分辨率编程的相关内容,包括内存访问、模式设置、像素绘制、基本图形算法、调色板操作以及回扫等待等方面的知识。





Wengier - 新DOS时代

欢迎大家来到我的“新DOS时代”网站,里面有各类DOS软件和资料,地址:
http://wendos.mycool.net/

E-Mail & MSN: wengierwu AT hotmail.com (最近比较忙,有事请联系DOSroot和雨露,谢谢!)

2003-5-14 00:00
查看资料  发送邮件  访问主页  发短消息  网志  OICQ (29206679)  编辑帖子  回复  引用回复
如是大师
元老会员

步行的人


积分 9654
发帖 3351
注册 2003-3-11
来自 湖北
状态 离线
『第 2 楼』:   使用 LLM 解释/回答一下

翻一下嘛,。。这可难为我们这些E盲了。。。




弄花香满衣,掬水月在手。
明月鹭鸟飞, 芦花白马走。
我自一过后,野渡现横舟。
青云碧空在,净瓶水不流。
http://dos.e-stone.cn/guestbook/index.asp
======中國DOS聯盟=====
我的新网页http://rsds.7i24.com欢迎光顾
2003-5-17 00:00
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
Wengier
系统支持

“新DOS时代”站长


积分 27736
发帖 10521
注册 2002-10-9
状态 离线
『第 3 楼』:   使用 LLM 解释/回答一下

请anyhow、Sandy和Roy来翻译一下吧,谢了。




Wengier - 新DOS时代

欢迎大家来到我的“新DOS时代”网站,里面有各类DOS软件和资料,地址:
http://wendos.mycool.net/

E-Mail & MSN: wengierwu AT hotmail.com (最近比较忙,有事请联系DOSroot和雨露,谢谢!)

2003-5-17 00:00
查看资料  发送邮件  访问主页  发短消息  网志  OICQ (29206679)  编辑帖子  回复  引用回复
LanE
银牌会员




积分 1833
发帖 648
注册 2002-11-8
状态 离线
『第 4 楼』:   [已使用 LLM 解释]

#define TEXT_MODE 0x03
#define VGA_MODE 0x13

void setmode(short mode)
{
union REGS regs;

regs.h.ah = 0x00; // Procedure 0x00: Set Video Mode
regs.h.al = mode; // The requested video mode
int86(0x10,&reg;s,&reg;s) // Call the BIOS
}

看这个和TURBO的用法和相似啊
LLM 解释

代码分析与相关背景

1. 代码功能与相关BIOS中断

  • 代码功能: 这段C语言代码定义了两个宏TEXT_MODEVGA_MODE来表示不同的显示模式,然后定义了一个函数setmode用于设置视频模式。函数内部通过使用BIOS中断0x10来实现设置视频模式的功能。
  • BIOS中断0x10: 在IBM PC兼容机中,BIOS中断0x10是视频相关的中断。其中功能号0x00用于设置视频模式。regs.h.ah = 0x00表示使用功能号0x00来设置视频模式,regs.h.al = mode则将传入的模式值设置到寄存器中,然后通过int86函数来调用BIOS中断0x10来执行设置视频模式的操作。int86函数是DOS系统中用于调用BIOS或DOS中断的函数,它需要传入中断号以及保存寄存器值的REGS结构体。

2. 与Turbo相关的相似性

  • Turbo中的视频模式设置: 在Turbo C等开发环境中,也常常会使用类似的方式来调用BIOS中断进行视频模式设置。Turbo C提供了一些库函数或者允许直接通过内联汇编或者像这里用C语言封装BIOS中断调用的方式来操作硬件相关功能。这里通过自定义函数setmode来封装对BIOS中断0x10功能号0x00的调用,和Turbo中为了方便使用而进行的硬件相关操作封装在逻辑上是相似的,都是为了更方便地实现视频模式的设置功能,只是具体的实现细节和所使用的函数调用方式在不同的开发环境中有不同的呈现,但核心目的都是通过调用BIOS中断来实现对硬件(视频显示)的控制。

3. DOS平台兼容性

  • 对于老旧处理器: 这段代码在DOS平台上,由于使用的是标准的BIOS中断调用方式,在16位实模式下是可以运行的,对于老旧的x86处理器(如8086等)也是兼容的,因为BIOS中断0x10的功能号0x00设置视频模式的操作是在实模式下就可以支持的基本功能。
  • 语言标准适配: 这里使用的C语言代码是符合早期C语言标准的,在DOS下使用DJGPP等编译器进行编译时,能够正确地进行汇编代码与C代码的混合操作(通过union REGS等结构体来操作寄存器),从而实现对BIOS中断的调用。

以下是对代码中一些细节的详细解释:

#include <dos.h>  // 包含dos.h头文件,其中定义了union REGS等结构体以及int86函数等

#define TEXT_MODE 0x03  // 定义文本模式的代码
#define VGA_MODE  0x13  // 定义VGA图形模式的代码

void setmode(short mode)  // 定义设置视频模式的函数,参数mode是要设置的模式代码
{
    union REGS regs;  // 定义一个union REGS类型的变量regs,用于保存寄存器值

    regs.h.ah = 0x00;  // 将寄存器ah设置为0x00,指定BIOS中断0x10的功能号为0x00(设置视频模式)
    regs.h.al = mode;  // 将传入的mode值设置到寄存器al中,al用于传递要设置的视频模式
    int86(0x10, &regs, &regs);  // 调用int86函数,传入中断号0x10,以及regs的地址,用于调用BIOS中断0x10来设置视频模式
}

如果要使用这个函数来设置模式,例如:

#include <dos.h>
#include <stdio.h>

#define TEXT_MODE 0x03
#define VGA_MODE  0x13

void setmode(short mode);

int main()
{
    setmode(VGA_MODE);  // 设置为VGA图形模式
    // 这里可以添加在VGA模式下的绘图等操作
    setmode(TEXT_MODE);  // 切换回文本模式
    return 0;
}

void setmode(short mode)
{
    union REGS regs;
    regs.h.ah = 0x00;
    regs.h.al = mode;
    int86(0x10, &regs, &regs);
}

这段示例代码展示了如何调用setmode函数来切换视频模式,首先切换到VGA图形模式,然后又切换回文本模式。



2003-5-23 00:00
查看资料  发送邮件  发短消息  网志  OICQ (9367907)  编辑帖子  回复  引用回复
Wengier
系统支持

“新DOS时代”站长


积分 27736
发帖 10521
注册 2002-10-9
状态 离线
『第 5 楼』:   使用 LLM 解释/回答一下

DJGPP本来就是一种新的高级的C/C++语言嘛。。




Wengier - 新DOS时代

欢迎大家来到我的“新DOS时代”网站,里面有各类DOS软件和资料,地址:
http://wendos.mycool.net/

E-Mail & MSN: wengierwu AT hotmail.com (最近比较忙,有事请联系DOSroot和雨露,谢谢!)

2003-5-23 00:00
查看资料  发送邮件  访问主页  发短消息  网志  OICQ (29206679)  编辑帖子  回复  引用回复
cir2633
初级用户





积分 52
发帖 23
注册 2009-12-25
状态 离线
『第 6 楼』:   使用 LLM 解释/回答一下

问下, 保护模式下还用指定指针的模式吗 ? 比如far,near
我觉得没必要i


2010-1-13 10:14
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复

请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: