I have trouble to grasp how to use colors in CGA/EGA/VGA video graphics modes. The video modes I'm particularly interested in are 0Dh (EGA 320x200) and 12h (VGA 640x480). Both of these modes have 4 planes, thus 16 colors.
My (probably incorrect) understanding is that I should activate a set of planes by writing a bitmask to port 03C4h
, then when I write to video memory, the data only gets written to the activated planes. Mostly I used this document to get my information, though I also encountered several other tutorials and discussions:
http://www.techhelpmanual.com/89-video_memory_layouts.html
Now I'm trying to write pixels in all possible colors in the first word in the video memory (top left part of screen). I load 1 for the initial bitmask to AH and 1 bit to BX. Then in a loop, I increment AH and shift (SHL
) the bit in BX to hit a different pixel next time. I OR
BX to A000h:0000h
to add each pixels by leaving the already existing pixels untouched.
What I'm expected to see is a line of pixels in all possible 16 EGA colors on the top left of the screen. What I actually see is 7 white and 1 bright yellow dots with black pixels in between them. What am I doing wrong?
Also, every tutorial says that I must write 0005h
to port 03CEh
before I start to use planes. What is the purpose of that? When I comment those lines out, I can still use planes (I mean, in other programs). Previously I had success using planes when I was writing to different words in video memory (so I didn't need different color pixels in one block of 16 pixels that's represented by a single word in video memory); and when I used BIOS functions (e.g. INT 10h/AH=0Ch) to write pixels, but still I want to understand how to use planar graphics without BIOS, as I believe the BIOS functions are slow.
Here is my code (indentation is optimized for 8-width tabs, so it kind of looks off here):
; ----------------------------------------------------------------------
; 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:
Any ideas why it doesn't work as I expect?