使用 CGA/EGA/VGA 平面图形模式

Using the CGA/EGA/VGA planar graphics modes

我很难掌握如何在 CGA/EGA/VGA 视频图形模式中使用颜色。我特别感兴趣的视频模式是 0Dh (EGA 320x200) 和 12h (VGA 640x480)。这两种模式都有 4 个平面,因此有 16 种颜色。

我(可能不正确)的理解是我应该通过将位掩码写入端口 03C4h 来激活一组平面,然后当我写入视频内存时,数据只会写入激活的平面。大多数情况下,我使用此文档来获取我的信息,尽管我也遇到了其他几个教程和讨论: http://www.techhelpmanual.com/89-video_memory_layouts.html

现在我正在尝试在视频存储器(屏幕左上部分)的第一个字中写入所有可能颜色的像素。我将初始位掩码的 1 加载到 AH,将 1 位加载到 BX。然后在一个循环中,我递增 AH 并移动 (SHL) BX 中的位以在下次点击不同的像素。我 OR BX 到 A000h:0000h 通过保持现有像素不变来添加每个像素。

我希望看到的是屏幕左上角所有可能的 16 种 EGA 颜色的像素线。我实际看到的是 7 个白色和 1 个亮黄色点,它们之间有黑色像素。我做错了什么?

另外,每个教程都说我必须在开始使用飞机之前将0005h写入端口03CEh。这样做的目的是什么?当我注释掉这些线条时,我仍然可以使用平面(我的意思是,在其他程序中)。以前,当我在视频内存中写入不同的字时,我成功地使用了平面(所以我不需要在一个由视频内存中的单个字表示的 16 像素块中使用不同的颜色像素);当我使用 BIOS 功能(例如 INT 10h/AH=0Ch)写入像素时,但我仍然想了解如何在没有 BIOS 的情况下使用平面图形,因为我认为 BIOS 功能很慢。

这是我的代码(缩进针对 8 宽制表符进行了优化,所以这里看起来有点不对):

; ----------------------------------------------------------------------
; PLANE - TEST PLANAR VIDEO MODE
; nasm -o plane.com plane.asm
; ----------------------------------------------------------------------

        ORG 100h            ; Code starts at CS:0100h

        MOV AX, 0F00h       ; Get current video mode
        INT 10h
        PUSH    AX          ; Save it to restore later

        MOV AX, 000Dh       ; Set video mode 0Dh (EGA 320x200, 16 color)
        INT 10h

        MOV DX, 03CEh       ; Set up for plane masking
        MOV AX, 0005h
        OUT DX, AX

        MOV AX, 0A000h      ; Load video segment to ES
        MOV ES, AX          ; and set Destination Index
        XOR DI, DI          ; to write data to A000h:0000h

        MOV CX, 14          ; Iterate through all plane masks
        MOV AX, 0100h       ; First plane mask is 01h
        MOV BX, 1           ; Initial pixel to write
LOOP_PIXEL:
        CALL    SET_PLANES      ; Set planes according to AH
        PUSH    BX          ; Save current pixels to [DATA+CX*2]
        MOV BX, CX          ; for debugging purposes
        SHL BX, 1
        MOV DX, ES:[DI]
        MOV [DATA + BX], DX
        POP BX
        OR  ES:[DI], BX     ; Add new pixel to video memory from BX
        SHL BX, 1           ; Shift BX so we'll activate a different pixel next time
        INC AH          ; Increment plane mask
        LOOP    LOOP_PIXEL

        XOR AX, AX          ; Wait for keypress
        INT 16h

        POP AX          ; Restore previous video mode
        XOR AH, AH
        INT 10h

        INT 20h         ; Exit program


; ----------------------------------------------------------------------
; SET_PLANES: Set video color plane mask.
;
; Inputs:
;   AH  - plane mask
;
; Outputs:
;   None.
; ----------------------------------------------------------------------
SET_PLANES:
        PUSH    AX
        PUSH    DX
        MOV DX, 03C4h
        MOV AL, 02h
        OUT DX, AX
        POP DX
        POP AX
        RET

DATA:

知道为什么它没有像我预期的那样工作吗?

将字 0005h 写入端口 03CEh 和 03CFh 将 select 写入模式 0。这是一个复杂的模式,涉及 VGA 的许多功能,但对我们来说幸运的是,当视频模式为设置。
但是您的代码仍然需要执行以下操作:

  • 为了填充 VGA 的内部 32 位锁存器,必须执行先读后写操作
  • 使用 BitMask 寄存器将输出限制为单个或几个像素。

下一个片段显示了 16 条 1 像素宽的垂直线的彩虹:

  xor   di, di
  mov   ax, 0F02h            ; First plane mask is 15
  mov   cx, 8008h            ; First bitmask is 10000000b, Width=1
LOOP1:
  mov   dx, 03C4h
  out   dx, ax               ; MapMask register
  xchg  ax, cx
  mov   dx, 03CEh
  out   dx, ax               ; BitMask register
  xchg  ax, cx

  xor   bx, bx
.a:
  mov   dl, [es:di+bx]
  mov   byte [es:di+bx], 255
  add   bx, 40
  cmp   bx, 1600              ; Height = 40
  jb    .a

  ror   ch, 1                 ; Width = 1
  adc   di, 0
  dec   ah
  jns   LOOP1

此代码段显示了 16 条 2 像素宽的垂直线的彩虹:

  mov   di, 13
  mov   ax, 0F02h            ; First plane mask is 15
  mov   cx, 0C008h           ; First bitmask is 11000000b, Width=2
LOOP2:
  mov   dx, 03C4h
  out   dx, ax               ; MapMask register
  xchg  ax, cx
  mov   dx, 03CEh
  out   dx, ax               ; BitMask register
  xchg  ax, cx

  xor   bx, bx
.a:
  mov   dl, [es:di+bx]
  mov   byte [es:di+bx], 255
  add   bx, 40
  cmp   bx, 800               ; Height = 20
  jb    .a

  ror   ch, 2                 ; Width = 2
  adc   di, 0
  dec   ah
  jns   LOOP2

第三个片段显示了 16 条 4 像素宽的垂直线的彩虹:

  mov   di, 26
  mov   ax, 0F02h            ; First plane mask is 15
  mov   cx, 0F008h           ; First bitmask is 11110000b, Width=4
LOOP3:
  mov   dx, 03C4h
  out   dx, ax               ; MapMask register
  xchg  ax, cx
  mov   dx, 03CEh
  out   dx, ax               ; BitMask register
  xchg  ax, cx

  xor   bx, bx
.a:
  mov   dl, [es:di+bx]
  mov   byte [es:di+bx], 255
  add   bx, 40
  cmp   bx, 400               ; Height = 10
  jb    .a

  ror   ch, 4                 ; Width = 4
  adc   di, 0
  dec   ah
  jns   LOOP3

已采取预防措施,因此您可以在同一个程序中同时尝试所有片段!