19

For those of you retro developers out there, I am having a hard time figuring out how exactly to select the "darker" color palette in 320x200 CGA mode. Here's what I have so far:

setup_cga_graphics:
    mov     ah, 0           ; int 10,0
    mov     al, 4           ; mode 4 (cga 320x200 4 color)
    int     10h             ; bios int

    mov     ah, 0Bh         ; interrupt 10B
    mov     bh, 1           ; 1 = 4 pallette mode
    mov     bl, 0           ; 0 = warm colors (r,g,b) 1 = cool (c,m,w)
    int     10h             ; bios int

I assemble this with NASM and fire it up. It brings up a C:\ prompt in bright yellow. This tells me that somehow the "intensity bit" is set high and thus the brighter color scheme is selected by default. I realize this is somewhat arcane/esoteric and absolutely obsolescent. I guess I'm hoping that a few of you oldschool x86 coders are still out there.

I tried looking at other interrupts to set the intensity bit like service 10H, however it appears to only support EGA. Any pointers would be great.

EDIT: To avoid confusion i've attached two images, one from Defender of the Crown using the lower intensity that I am aiming for:

What I'm not looking for is this:

JohnnyStarr
  • 313
  • 1
  • 8

3 Answers3

15

Your code is correct; a yellow prompt means that you’re using the red/green/brown palette. However, to get the low intensity variant, you also need to call interrupt 10h service 0Bh with BX set to 0 (black background, low intensity; strictly speaking, you can have any background — the bottom four bits, 3–0 — and the fifth bit, bit 4, controls the intensity; this maps to register 03D9h, the colour control register). You can draw the various colours and flip palettes to see the difference:

    org 100h
section .text

start: mov ax, 4 ; set CGA 320x200 int 10h

mov ah, 0Bh         ; set the cool palette
mov bx, 0101h       ; (cyan, magenta, white)
int 10h

xor bx, bx          ; low intensity, black background
int 10h

mov ax, 0B800h      ; draw some pixels in each colour
mov es, ax
mov di, 0
mov bx, 320
xor ax, ax
mov cx, bx
rep stosw
inc ax
mov cx, bx
rep stosw
inc ax
mov cx, bx
rep stosw
inc ax
mov cx, bx
rep stosw

mov ah, 1           ; wait for a character
int 21h

mov ah, 0Bh
mov bx, 0010h       ; high intensity
int 10h

mov ah, 1           ; wait for a character
int 21h

mov ah, 0Bh         ; set the warm palette
mov bx, 0100h       ; (red, green, brown)
int 10h

mov ah, 1           ; wait for a character
int 21h

mov ah, 0Bh
xor bx, bx          ; low intensity
int 10h

mov ah, 1           ; wait for a character
int 21h

mov ax, 4C00h       ; exit
int 21h

This will produce the following output:

  • low intensity, cyan/magenta/white

    rows of dots, top third in cyan, middle third magenta, bottom third white

  • high intensity, cyan/magenta/white

    rows of dots, top third in cyan, middle third magenta, bottom third white, brighter than the previous screenshot

  • high intensity, red/green/yellow

    rows of dots, top third bright green, middle third bright red, bottom third yellow

  • low intensity, red/green/brown

    rows of dots, top third green, middle third red, bottom third brown

(Also tested on actual hardware.)

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
  • 3
    perhaps I wasn't 100% clear on my original question, but what I'm getting at is the darker set of colors. Your example works just fine, but its with red, green and yellow (not brown). – JohnnyStarr Jan 04 '19 at 20:04
8

On the original IBM PC BIOS and true compatibles, the foreground intensity can be set using INT 10h with AH = 0Bh and BH = 00h. The low four bits of BL give the background colour, and bit 4 gives the foreground intensity. This is documented in (for example) the PC XT technical reference, page A-53.

So after selecting your palette, this should switch to low intensity (brown rather than yellow text):

mov ah, 10h
mov bh, 0   ; Set background / intensity
mov bl, 0   ; Black background, low intensity 
int 10h

Alternatively, you can avoid possible BIOS compatibility issues by writing directly to the CGA colour select register at 03D9h. This won't work on non-CGA hardware:

mov ax,40h
mov es,ax       ; ES -> BIOS data segment
mov al,es:[66h] ; Last value written to 03D9h
mov dx,03D9h
and al,0EFh     ; Reset intensity bit
mov es:[66h],al ; Update last value written
out dx,al       ; And write to the CGA
john_e
  • 7,263
  • 20
  • 44
  • 1
    But that doesn’t change the palette, which is what the question is about (switching between the red/green/blue and cyan/magenta/white palettes). – Stephen Kitt Jan 04 '19 at 13:44
  • 1
    My reading of "I tried looking at other interrupts to set the intensity bit" is that the the questioner wants to know how to control the foreground intensity (bit 4 of the colour select register) rather than select the palette (bit 5). – john_e Jan 04 '19 at 19:49
  • I have combined both @john_e and Stephen Kitt's source code and I am still only getting red, green and yellow. I was comfortable switching palettes just fine, but what I can't figure it is how to switch intensity. – JohnnyStarr Jan 04 '19 at 20:06
  • Please see my edit at the top, I've included some images to clarify what I meant to originally ask. – JohnnyStarr Jan 04 '19 at 20:15
  • 1
    It works for me in PCEM on an emulated IBM XT with CGA, but this behaviour may not be implemented by clone BIOSes or EGA/VGA cards. – john_e Jan 04 '19 at 20:51
  • 1
    @john_e I'm using DOSBox on Windows 10. The weird thing is I've tried out Defender of the Crown in CGA mode with the same DOSBox configuration and sure enough, the colors work perfectly. I've tried both your BIOS and direct color select register examples and i'm still getting the light colors. I'm assembling with NASM to make a COM file with these flags: nasm -f bin -o test.o test.asm – JohnnyStarr Jan 04 '19 at 22:05
  • 1
    @JohnnyStarr I don't know what you're doing differently. You can find my test code at http://www.seasip.info/Misc/brown.asm - it works for me under dosbox -machine cga – john_e Jan 04 '19 at 23:54
  • Many CGA clone cards use I/O address 0x3D9 for the same purpose as the CGA. – supercat Feb 12 '23 at 16:44
2

for those like me who read this 5 times and still didn't get it. the explanation is here:

https://www.chibialiens.com/8086/platform.php?noui=1

; 1 set video mode

mov ah, 0 ; video command mov al, 4 ; cga mode, 320x200, 4 colors int 10h

; 2. set up palette

mov ah, 0bh ; palette command mov bh, 1 ; bh is subcommand, 1 = choose which palette mov bl, 0 ; this sets the palette to red green yellow black int 10h

; 3. set up palette brightness

mov ah, 0bh ; palette command mov bh, 0 ; bh is subcommand, 0 = control brightness mov bl, 0 ; low intensity. this makes yellow into brown int 10h

don bright
  • 465
  • 2
  • 12