Mode X Madness

After learning about Mode 13h last time, I’ve been digging about in the ancient tomes of wisdom and discovered Mode-X. Let’s make a PC’s VGA card do odd things it was never designed for.


When IBM created the original VGA adaptor, they never intended it to produce high colour real time graphics. However by cleverly altering the card it is possible to unlock an undocumented video mode known simply as Mode X.

Created by IBM in 1987 for their PS/2 line of computers, the VGA standard quickly became the minimum video standard adopted by… well… pretty much everyone. Your fancy 50-series video card that cost more than my first car still pretends to be one - unless it’s set on fire.

Designed by boring people in suits to go inside a boring business computer, the VGA card was only really intended to offer a bunch of colours and a higher resolution than the vomit coloured CGA and 16 colour EGA that came before it. I think you were supposed to at most run Windows 3.1 on it and play Solitaire.

However, maybe the boring people in suits weren’t as boring as we thought. Instead of making the card out of discrete logic, it features a bunch of registers that control some fairly fundamental parts of the card.

Things like the horizontal and vertical screen timings.

And since at the time RAM was expensive and slow, it had planar memory because it made sense to put different R, G and B values into separate physical memory chips - you could pull all three colours out into several special latch registers and dump that on the screen far quicker.

I came across the Graphics Programming Black Book by Michael Abrash and printed out chapter 49 to read, which describes how this mysterious Mode-X actually works, including source code to play with.

You can find the source over on my Mode X Example GitHub repo, but here’s the basic gist of the thing…

VGA cards come with 256k of video memory, but you can’t touch all of it for historical reasons that weren’t ever documented - this is IBM, the answer is probably quite beige and requires a tie.

This is one of the advantages Mode X has, it lets you use all the video ram by doing a pretty cunning trick:

  • First you put the card into Mode 13H to get it into 256 colour mode.
  • I think then you set the card up to go into 640x480 16 colour mode which uses bitplanes, originally intended for R,G and B pixels. Except by cleverly altering the timings it creates a 320x480 display instead, and the card isn’t correctly set up and is still really in 256 colour mode.
  • And then some shenanigans with the screen timings are done to stretch that 320x480 display so that only the top 320x240 fits on the screen. This now gives the programmer several screen sized off-screen areas of video ram to use.

It’s very odd and I barely understand it, but it sort of makes sense.

The end result is some pretty quick pixel manipulation techniques can be done to shuffle data around video memory without needing the CPU to do much or drag data up and down the ISA BUS into system RAM.

Here’s the code to do it

; Mode X (320x240, 256 colors) mode set routine. Works on all VGAs.
; ****************************************************************
; * Revised 6/19/91 to select correct clock; fixes vertical roll *
; * problems on fixed-frequency (IBM 851X-type) monitors.        *
; ****************************************************************
; C near-callable as:
;       void Set320x240Mode(void);
; Tested with TASM
; Modified from public-domain mode set code by John Bridges.

SC_INDEX        equ  03c4h   ;Sequence Controller Index
CRTC_INDEX      equ  03d4h   ;CRT Controller Index
MISC_OUTPUT     equ  03c2h   ;Miscellaneous Output register
SCREEN_SEG      equ  0a000h  ;segment of display memory in mode X

        .model  small
        .data
; Index/data pairs for CRT Controller registers that differ between
; mode 13h and mode X.
CRTParms label  word
        dw      00d06h  ;vertical total
        dw      03e07h  ;overflow (bit 8 of vertical counts)
        dw      04109h  ;cell height (2 to double-scan)
        dw      0ea10h  ;v sync start
        dw      0ac11h  ;v sync end and protect cr0-cr7
        dw      0df12h  ;vertical displayed
        dw      00014h  ;turn off dword mode
        dw      0e715h  ;v blank start
        dw      00616h  ;v blank end
        dw      0e317h  ;turn on byte mode
CRT_PARM_LENGTH equ     (($-CRTParms)/2)

        .code
        public  _Set320x240Mode
_Set320x240Mode proc    near
        push    bp      ;preserve caller's stack frame
        push    si      ;preserve C register vars
        push    di      ; (don't count on BIOS preserving anything)

        mov     ax,13h  ;let the BIOS set standard 256-color
        int     10h     ; mode (320x200 linear)

        mov     dx,SC_INDEX
        mov     ax,0604h
        out     dx,ax   ;disable chain4 mode
        mov     ax,0100h
        out     dx,ax   ;synchronous reset while setting Misc Output
                        ; for safety, even though clock unchanged
        mov     dx,MISC_OUTPUT
        mov     al,0e3h
        out     dx,al   ;select 25 MHz dot clock & 60 Hz scanning rate

        mov     dx,SC_INDEX
        mov     ax,0300h
        out     dx,ax   ;undo reset (restart sequencer)

        mov     dx,CRTC_INDEX ;reprogram the CRT Controller
        mov     al,11h  ;VSync End reg contains register write
        out     dx,al   ; protect bit
        inc     dx      ;CRT Controller Data register
        in      al,dx   ;get current VSync End register setting
        and     al,7fh  ;remove write protect on various
        out     dx,al   ; CRTC registers
        dec     dx      ;CRT Controller Index
        cld
        mov     si,offset CRTParms ;point to CRT parameter table
        mov     cx,CRT_PARM_LENGTH ;# of table entries
SetCRTParmsLoop:
        lodsw           ;get the next CRT Index/Data pair
        out     dx,ax   ;set the next CRT Index/Data pair
        loop    SetCRTParmsLoop

        mov     dx,SC_INDEX
        mov     ax,0f02h
        out     dx,ax   ;enable writes to all four planes
        mov     ax,SCREEN_SEG ;now clear all display memory, 8 pixels
        mov     es,ax         ; at a time
        sub     di,di   ;point ES:DI to display memory
        sub     ax,ax   ;clear to zero-value pixels
        mov     cx,8000h ;# of words in display memory
        rep     stosw   ;clear all of display memory

        pop     di      ;restore C register vars
        pop     si
        pop     bp      ;restore caller's stack frame
        ret
_Set320x240Mode endp
        end

The drawback is the mindbendingly odd way that is needed to plot pixels due to how the memory is laid out. I could try and explain it, but the book does far better and includes two helpful diagrams.

Figure 47.1  Mode X display memory organization.

Figure 47.2  Mode X display memory writing.

Subscribe

Support

Related Content

  • Davinci Resolve Proxy Files March 6, 2025

    thumbnail
  • Davinci Resolve Collaboration March 3, 2025

    thumbnail
  • Borland C Dos Programming February 27, 2025

    thumbnail
  • About Me January 3, 2025

    thumbnail
  • Support January 3, 2025

    thumbnail

Recent Content

  • MS DOS Programming the Mode X Deviation March 29, 2025

    ...
  • Mode X Madness March 22, 2025

    ...
  • Can I Program VGA Graphics in MS DOS? (Watch Me Struggle!) March 10, 2025

    ...