8

我正在做一些有趣的复古编程。我想要创建的是一个使用 EGA 图形的 DOS 游戏,但我在网上找到一个好的参考资料时遇到了一些麻烦。每个谈论做 DOS 编程的人都假设程序员将使用模式 13h,虽然有些页面提到了其他图形模式,但我还没有找到讨论它们正确使用的一个。

这是我现在正在尝试的工作:

//------------------------------------------------------------------------------
//  DOS graphics test
//
//  Thanks to the following links:
//    http://gamebub.com/cpp_graphics.php
//
//  Written for Digital Mars C compiler to be compiled as a DOS 16 bit binary.
//------------------------------------------------------------------------------

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

#define SCREEN_WIDTH  320;
#define SCREEN_HEIGHT 200;

unsigned char far *vram = (unsigned char far *)0xA0000000L;

//------------------------------------------------------------------------------
void set_video_mode(unsigned char mode)
{
    union REGS in, out;
    in.h.ah = 0;
    in.h.al = mode;
    int86(0x10, &in, &out);
}

//------------------------------------------------------------------------------
void plot_pixel(unsigned int x, unsigned int y, unsigned char color)
{
    // this is wrong because it's only 4 bpp not 8
    vram[y * 320 + x] = color;
    //vram[((y<<8)+(y<<6))+x] = color;
}

//------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    // EGA 320 x 200 x 16
    set_video_mode(0x0d);

    for (unsigned char i = 0; i < 255; i++)
    {
        vram[i] = i;
    }

    //plot_pixel(10, 10, 1);

    getc(stdin);
    return 0;
}

如果您将 set_video_mode() 更改为 0x13 而不是 0x0d,则此示例代码效果很好,但就像我说的那样,我试图在这里获取 EGA 图形,而不是 VGA。:) 我意识到,为了做到每像素四位,我要么需要假设 plot_pixel 同时写入两个像素,要么进行一些操作以确保我只写入我自己写的四个位其实想要。

我的问题是我没有看到我期望的输出——特别是没有颜色!一切似乎都是单色的,这根本不是我想要的。在这种图形模式中使用调色板与在 13h 中使用调色板有什么不同的过程吗?还是我以某种方式调用了与我想要的完全不同的图形模式?指导将不胜感激。

我不认为我的编译器参数是相关的,但以防万一:

..\dm\bin\dmc test.c -o test -mm
4

3 回答 3

4

模式 0x0d 是基于平面的,而 0x13 不是(没有像模式 X 那样的特殊配置)。您应该查看更多文档以了解如何在平面模式下工作。

于 2010-01-18T10:29:36.140 回答
3

嗯..dunno 如果这是一个有价值的答案,如果我记得,你需要使用inport, inportb, outport,outportb到端口来控制 EGA 寄存器,这是从优秀的 FAQSYSW 中获取的(包括图形演示、数学的大量材料,分形压缩),原始档案在这里这里找到。:

3C0h:属性控制器:地址寄存器
bit 0-4 要写入端口 3C0h 的数据寄存器地址。
      5 如果启用设置屏幕输出且调色板无法修改,
         如果清除屏幕输出被禁用并且可以修改调色板。


端口 3C0h 的特殊之处在于它既是地址寄存器又是数据写寄存器。
内部触发器会记住它当前是作为地址还是
数据寄存器。对属性控制器的访问必须用 at 分隔
至少250ns。读取端口 3dAh 会将触发器重置为地址模式。


3C0h 索引 0-Fh (W):属性:调色板
位 0 原蓝
      1 初级绿色
      2 原红
      3 次蓝色
      4 次绿
      5 次红

3C0h 索引 10h (W):属性:模式控制寄存器
位 0 图形模式(如果设置),否则为字母数字模式。
      1 如果设置为单色模式,则为彩色模式。
      2 9 位宽字符(如果已设置)。
         第 9 位字符 C0h-DFh 将与
         第 8 位。否则它将是背景颜色。
      3 如果设置的属性位 7 闪烁,否则为高强度。

3C0h 索引 11h (W):属性:过扫描颜色寄存器。
位 0-5 屏幕边框的颜色。颜色在调色板寄存器中定义。
注意:EGA 要求在高分辨率模式下过扫描颜色为 0。

3C0h index 12h (W): 属性: Color Plane Enable Register
bit 0 如果设置,则启用位平面 0。
      1 如果设置,则启用位平面 1。
      2 如果设置,则启用位平面 2。
      3 如果设置,则启用位平面 3。
    4-5 视频状态 MUX。仅供诊断使用。
         两个属性位出现在输入状态的位 4 和 5
         寄存器 1 (3dAh)。0:红/蓝,1:蓝(I)/绿,2:红(I)/绿(I)

3C0h 索引 13h (W):属性:水平 PEL 平移寄存器
位 0-3 指示要向左移动显示的像素数
         值 9bit textmode 256color mode 其他模式
           0 1 0 0
           1 2 不适用 1
           2 3 1 2
           3 4 不适用 3
           4 5 2 4
           5 6 不适用 5
           6 7 3 6
           7 8 不适用 7
           8 0 不适用/不适用

3C2h (R):输入状态 #0 寄存器
bit 4 由杂项输出选择的开关状态
         寄存器 3C2h 位 2-3。如果设置,则切换为高电平。
      5 如果设置,功能连接器 (FEAT0) 的引脚 19 为高电平
      6 功能连接器 (FEAT1) 的引脚 17 如果设置为高电平
      7 如果设置 IRQ 2 由于垂直回扫而发生。应该清除
         由 IRQ 2 中断例程通过清除端口 3d4h 索引 11h 位 4。

3C2h (W):杂项输出寄存器
位 0 如果设置颜色仿真。基地址 = 3Dxh 否则单声道仿真。根据
         地址=3Bxh。
      1 启用 CPU 访问视频内存(如果设置)
    2-3 时钟选择。0:14MHz,1:16MHz,2:外部
      4 禁用内部视频驱动程序(如果已设置)
      5 在奇数/偶数模式下选择高 64k 库(如果已设置)
      6 水平同步极性。如果设置为负
      7 垂直同步极性。如果设置为负
         位 6-7 表示显示屏上的行数:
           0:200 行,2:350 行
注意:在硬件复位时设置为全零。

3C4h 索引 0 (W):定序器:复位
位 0 异步复位(如果清零)
      1 同步复位(如果清除)

3C4h 索引 1 (W):定序器:时钟模式
位 0 如果设置的字符时钟为 8 点宽,否则为 9。
      1 如果设置 CRTC 使用 2/5 的时钟周期,否则使用 4/5。
      2 如果设置每隔一个字符加载视频序列化器
         时钟周期,否则每一个。
      3 如果设置点时钟是主时钟/2,否则与主时钟相同
          (见 3C2h 位 2-3)。(双倍像素)。

3C4h 索引 2 (W):定序器:映射掩码寄存器
位 0 如果设置,则启用对平面 0 的写入
      1 如果设置,则启用对平面 1 的写入
      2 如果设置,则启用对平面 2 的写入
      3 如果设置,则启用对平面 3 的写入

3C4h 索引 3 (W):定序器:字符映射选择寄存器
位 0-1 如果字符的位 3 选择 EGA 字符映射 (0..3)
         属性明确。
    2-3 如果字符的第 3 位,则选择 EGA 字符映射 (0..3)
         属性已设置。
注意:字符映射放置如下:
      在 0k 时映射 0,在 16k 时映射 1,在 32k 时映射 2,在 48k 时映射 3

3C4h 索引 4 (W):定序器:内存模式寄存器
位 0 如果在字母数字模式下设置,在图形模式下清除。
      1 如果适配器上超过 64kbytes,则设置。
      2 如果设置,则启用奇/偶寻址模式。奇/偶模式放置所有奇数
         平面 1&3 中的字节,以及平面 0&2 中的所有偶数字节。

3CAh (W):图形 2 位置
bit 0-1 选择应该由图形控制器控制的位平面
         #2。始终设置为 1。

3CCh (W):图形 1 位置
bit 0-1 选择应该由图形控制器控制的位平面
         #1。始终设置为 0。

3CEh 索引 0 (W):图形:设置/复位寄存器
位 0 如果在写入模式 0 和 3CEh 索引 1 的位 0 设置为写入
         显示内存会将字节的平面 0 中的所有位设置为此
         位,如果在映射掩码寄存器中设置了相应的位(3CEh
         索引 8)。
      1 平面 1 和 3CEh 索引 1 的位 1 相同。
      2 平面 2 和 3CEh 索引 1 的位 2 相同。
      3 平面 3 和 3CEh 索引 1 的位 3 相同。

3CEh 索引 1 (W):图形:启用设置/复位寄存器
位 0 如果置位,则在写入模式 0 中启用平面 ​​0 的置位/复位。
      1 与平面 1 相同。
      2 与飞机 2 相同。
      3 与飞机 3 相同。

3CEh 索引 2 (W):图形:颜色比较寄存器
位 0-3 在读取模式 1 中,比较读取字节地址处的每个像素
         将此颜色和输出中的相应位设置为 1 如果
         它们匹配,如果不匹配,则为 0。颜色无关登记(3CEh 索引 7)
         可以从比较中排除位平面。

3CEh 索引 3 (W):图形:数据循环
位 0-2 数据写入前轮换的位置数
         显示内存。仅在写入模式 0 中有效。
    3-4 在写入模式 2 中,此字段控制数据之间的关系
         从 CPU 写入,从先前读取锁存的数据和
         写入显示内存的数据:
           0:CPU 数据未修改写入
           1:CPU 数据与锁存数据进行“与”运算
           2:CPU 数据与锁存数据进行或运算。
           3:CPU 数据与锁存数据进行异或运算。

3CEh 索引 4 (W):图形:读取映射选择寄存器
位 0-1 将读取的平面读取模式 0 的编号。

3CEh 索引 5 (W):图形:模式寄存器
bit 0-1 写模式:控制来自 CPU 的数据在之前如何转换
         被写入显示内存:
           0:模式 0 用作读取-修改-写入操作。
              首先,读访问将 EGA 的数据锁存器与
              在寻址位置的视频内存中的值。然后写
              访问将提供目标地址和 CPU 数据
              字节。写入的数据由函数代码修改
              数据循环寄存器(3CEh 索引 3)作为 CPU 的函数
              数据和锁存器,然后数据是由指定的旋转
              同一个寄存器。
           1:模式 1 用于视频到视频的传输。
              读访问将加载数据锁存器的内容
              显存的寻址字节。写访问将写入
              锁存器的内容到寻址字节。因此单
              MOVSB 指令可以复制源地址字节中的所有像素
              到目的地址。
           2:模式 2 将颜色写入地址字节中的所有像素
              视频内存。CPU 数据的位 0 写入平面 0 et
              等等。个别位可以通过启用或禁用
              位掩码寄存器(3CEh 索引 8)。
      2 如果设置,则强制所有输出为高阻抗状态。
      3 阅读模式
           0:根据读取映射从 4 个位平面之一读取数据
              选择寄存器(3CEh 索引 4)。
           1:返回的数据是占用8个像素的比较
              读取字节和颜色比较寄存器中的颜色(3CEh
              索引 2)。如果相应像素的颜色,则设置一个位
              匹配寄存器。
      4 如果设置,则启用奇/偶模式(参见 3C4h 索引 4 位 2)。
      5 如果设置,则使用偶数/奇数位对启用 CGA 样式 4 色像素。

3CEh 索引 6 (W):图形:杂项寄存器
位 0 如果设置则表示图形模式,否则表示字母数字模式。
      1 如果设置,则启用奇/偶模式。
    2-3 内存映射:
           0:使用 A000h-BFFFh
           1:使用 A000h-AFFFh EGA 图形模式
           2:使用 B000h-B7FFh 单色模式
           3:使用 B800h-BFFFh CGA 模式

3CEh 索引 7 (W):图形:颜色无关寄存器
位 0 如果清除,则在读取模式 1 中忽略位平面 0。
      1 如果清除,则在读取模式 1 中忽略位平面 1。
      2 如果清除,则在读取模式 1 中忽略位平面 2。
      3 如果清除,则在读取模式 1 中忽略位平面 3。

3CEh 索引 8 (W):图形:位掩码寄存器
位 0-7 如果设置了每个位,则可以写入一个字节的相应位
         显示内存。

3d4h 索引 0(W):CRTC:水平总寄存器
bit 0-7 水平总字符时钟数-2

3d4h索引1(W):CRTC:水平显示结束寄存器
bit 0-7 显示的字符时钟数 -1

3d4h 索引 2 (W):CRTC:开始水平消隐寄存器
bit 0-7 水平消隐开始的计数

3d4h 索引 3 (W):CRTC:结束水平消隐寄存器
bit 0-4 水平消隐在字符的最后 5 位时结束
         计数器等于该字段。
    5-6 水平后延迟显示开始的字符时钟数
         已达到总数。

3d4h 索引 4 (W):CRTC:开始水平回扫寄存器
bit 0-7 水平回扫在字符计数器达到此值时开始
         价值。

3d4h 索引 5 (W):CRTC:结束水平回扫寄存器
bit 0-4 水平回扫在字符计数器的最后 5 位时结束
         等于这个值。
    5-6 水平后延迟显示开始的字符时钟数
         回溯。
      7 在奇数/偶数模式下提供平滑滚动。设置显示开始时
         从一个奇数字节。

3d4h索引6(W):CRTC:垂直总寄存器
位 0-7 垂直总计的低 8 位。位 8 在 3d4h 索引 7 中找到
         位 0。

3d4h 索引 7 (W):CRTC:溢出寄存器
垂直总计的位 0 位 8(3d4h 索引 6)
      1 垂直显示结束的第 8 位(3d4h 索引 12h)
      2 垂直回扫开始的第 8 位(3d4h 索引 10h)
      3 开始垂直消隐的第 8 位(3d4h 索引 15h)
      4 行比较寄存器的第 8 位(3d4h 索引 18h)

3d4h 索引 8 (W):CRTC:预设行扫描寄存器
bit 0-4 我们在第一个字符行中向下滚动的行数。
         提供平滑的垂直滚动。

3d4h 索引 9 (W):CRTC:最大扫描线寄存器
位 0-4 字符行中的扫描行数 -1。在图形模式下,这是
         该行在传递到之前显示的次数 (-1)
         下一行(0:正常,1:双,2:三...)。
         这与第 7 位无关,但在 CGA 模式中似乎
         要求该字段为 1 和位 7 才能工作。

3d4h 索引 0Ah (W):CRTC:光标起始寄存器
位 0-4 字符内光标的第一个扫描行。

3d4h 索引 0Bh (W):CRTC:光标结束寄存器
位 0-4 字符内光标的最后一个扫描行
    5-6 字符时钟中光标数据的延迟。

3d4h 索引 0Ch (W):CRTC:起始地址高位寄存器
bit 0-7 显示缓冲区起始地址的高 8 位

3d4h 索引 0Dh (W):CRTC:起始地址低位寄存器
bit 0-7 显示缓冲区起始地址的低 8 位

3d4h 索引 0Eh (W):CRTC:光标位置高位寄存器
bit 0-7 光标地址的高 8 位

3d4h 索引 0Fh (W):CRTC:光标位置低位寄存器
bit 0-7 光标地址的低 8 位

3d4h 索引 10h (R): CRTC: Light Pen High Register
位 0-7 灯笔位置地址的高 8 位。

3d4h 索引 10h (W):CRTC:垂直回扫开始寄存器
bit 0-7 垂直回扫开始的低 8 位。垂直回扫开始时
         行计数器达到此值。位 8 在 3d4h 索引 7 中找到
         位 2。

3d4h 索引 11h (R): CRTC: Light Pen Low Register
位 0-7 灯笔位置地址的低 8 位。

3d4h 索引 11h (W):CRTC:垂直回扫结束寄存器
当行计数器的最后 4 位等于时,位 0-3 垂直回扫结束
         这个值。
      4 如果清除 清除挂起的垂直中断。
      如果设置,则禁用 5 个垂直中断 (IRQ 2)。通常可以留下
         禁用,但某些系统(包括 PS/2)要求启用它。

3d4h 索引 12h (W): CRTC: 垂直显示结束寄存器
bit 0-7 垂直显示结束的低 8 位。显示结束时行
         计数器达到这个值。位 8 在 3d4h 索引 7 位 1 中找到。

3d4h 索引 13h (W):CRTC:偏移寄存器
bit 0-7 扫描线中的字节数/K。其中 K 为 2 表示字节模式,4 表示
         字模式和 8 双字模式。

3d4h 索引 14h (W):CRTC:下划线位置寄存器
bit 0-4 字符单元格中下划线的位置。

3d4h 索引 15h (W):CRTC:开始垂直空白寄存器
bit 0-7 垂直空白起始的低 8 位。垂直消隐开始时
         行计数器达到此值。位 8 在 3d4h 索引 7 中找到
         位 3。

3d4h 索引 16h (W):CRTC:结束垂直空白寄存器
bit 0-4 当行计数器的低 5 位时,垂直消隐停止
         等于这个字段。

3d4h 索引 17h (W):CRTC:模式控制寄存器
bit 0 如果清除使用 CGA 兼容的内存寻址系统
         通过用字符行扫描计数器位 0 代替地址位 13,
         从而为偶数和奇数扫描线创建 2 个存储库。
      1 如果清除使用 Hercules 兼容的内存寻址系统
         用字符行扫描计数器位 1 代替地址位 14,
         从而创建了 4 个银行。
      2 如果设置仅每隔一行增加一次扫描行计数器。
      3 如果设置仅每隔一个字符增加内存地址计数器
         钟。
      4 如果设置禁用 EGA 输出驱动程序。
      5 当处于字模式时,如果该位被设置,则第 15 位循环到第 0 位
         位 13 旋转到位 0。
      6 如果清除系统处于字模式。地址向上旋转 1 个位置
         将第 13 位或第 15 位带入第 0 位。
      7 清除该位将重置显示系统,直到该位被设置
         再次。

3d4h 索引 18h (W):CRTC:行比较寄存器
bit 0-7 行比较的低 8 位。当线路计数器达到这个
         值,显示地址换成 0。提供分屏
         设施。位 8 在 3d4h 索引 7 位 4 中找到。

3dAh (R):输入状态 #1 寄存器
位 0 如果设置,则垂直或水平回扫处于活动状态
      1 光笔已触发(如果已设置)
      2 灯笔开关打开(如果设置)
      3 垂直回扫正在进行(如果已设置)
    4-5 显示 6 个颜色输出中的两个,取决于 3C0h 索引 12h。
          Attr:位 4-5:输出位 4 输出位 5
                   0 蓝色 红色
                   1 我蓝绿
                   2 我红我绿

3dAh (W):特征控制寄存器
位 0 输出到功能连接器的引脚 21。
      1 输出到功能连接器的引脚 20。
于 2010-01-25T23:00:31.513 回答
1

它是单色的,因为它同时写入所有四个颜色平面。模式 D 是平面的,正如其他答案所表明的那样,并且一个寄存器控制其中哪些是通过循环写入的。默认情况下,它选择同时写入所有平面,因此是单声道。

例如,要选择“红色”平面和“强烈”,您可以执行以下操作:

mov dx, 3C4h       ; address of sequencer address register
mov al, 2h         ; index of map mask register
out dx, al

mov dx, 3C5h       ; address of sequencer data register
mov al, 00001100b  ; turn on the 'red' and 'intense' planes
out dx, al

对 VRAM 的后续写入将为每个写入的位生成一个亮红色像素(1 位 = 1 个像素)。颜色当然假设调色板没有从默认值更改,您也可以这样做。

于 2018-06-24T19:43:38.493 回答