[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]

    {
    KAI ROHRBACHER

    > explain MODE X.

    Well,  I don't care much about Mode X (which is 320x240x256), but use Mode Y
    (=320x200x256)  --at least I think that this mode is called "Mode Y" (as far
    as  I  know, the terms were introduced by a series of Michael Abrash in "Dr.
    Dobb's  Journal" (?)). Nevertheless, things are identical With the exception
    of initialising the VGA card! So here we go; note that the Asm code examples
    were taken from my ANIVGA-toolkit: the PASCAL-equivalents when given are "on
    the  fly"  Asm->PASCAL  translations  For  improved  clarity (I hope...); in
    doubt, rely on the Asm part.

    MODE Y in a nutshell
    ~~~~~~~~~~~~~~~~~~~~

    Basically,  Mode  Y  works  like  this:  use  the BIOS to switch into normal
    320x200x256  mode,  then reProgram the sequencer to unchain the 4 bitplanes.
    This  results  in  a bitplaned VRAM layout very similiar to the EGA/VGA's 16
    color modes:
    }
    Procedure InitGraph; Assembler;
    Asm
      MOV AX,0013h
      INT 10h
      MOV DX,03C4h
      MOV AL,04
      OUT DX,AL
      INC DX
      in  AL,DX
      and AL,0F7h
      or  AL,04
      OUT DX,AL
      MOV DX,03C4h
      MOV AL,02
      OUT DX,AL
      INC DX
      MOV AL,0Fh
      OUT DX,AL
      MOV AX,0A000h
      MOV ES,AX
      SUB DI,DI
      MOV AX,DI
      MOV CX,8000h
      CLD
      REP STOSW

      MOV DX,CrtAddress
      MOV AL,14h
      OUT DX,AL
      INC DX
      in  AL,DX
      and AL,0BFh
      OUT DX,AL
      DEC DX
      MOV AL,17h
      OUT DX,AL
      INC DX
      in  AL,DX
      or  AL,40h
      OUT DX,AL
    end;

    {
    CrtAddress  and  StatusReg  are the port addresses For the VGA ports needed;
    they  are 3B4h and 3BAh on a monochrome display and 3D4h and 3DAh on a color
    display, but can be determined at run-time, too:
    }

    Asm
      MOV DX,3CCh
      in AL,DX
      TEST AL,1
      MOV DX,3D4h
      JNZ @L1
      MOV DX,3B4h
     @L1:
      MOV CrtAddress,DX
      ADD DX,6
      MOV StatusReg,DX
    end;

    {
    The  VRAM  layout  is  this:  underneath  each  memory  address in the range
    $A000:0000..$A000:$FFFF,  there  are  4 Bytes, each representing one pixel's
    color.
    Whenever you Write to or read from such an address, an internal logic of the
    VGA-card determines which one of those 4 pixels is accessed.
    A  line  of  320  pixels (=320 Bytes) thus only takes 320/4=80 Bytes address
    space,  but  to  address  a pixel, you need a) its VRAM address and b) which
    bitplane it's on.
    The  pixels  are arranged linearly: thus, the mapping from point coordinates
    to memory addresses is done by (x,y) <-> mem[$A000: y*80+ (x div 4)] and the
    bitplane is determined by (x mod 4).
    (Note coordinates start With 0 and that "div 4" can be computed very fast by
    "shr 2"; "mod 4" by "and 3").

    So  you  computed the proper address and bitplane. If you want to _read_ the
    pixel's color, you issue commands like this:
     portw[$3CE]:=(bitplane SHL 8)+4; color:=mem[$A000:y*80+(x shr 2)]
    Or For better speed & control, do it in Asm:

     MOV AL,4
     MOV AH,bitplane
     MOV DX,3CEh
     CLI
     OUT DX,AX
     MOV AL,ES:[DI]
     STI

    _Writing_  a pixel's color works similiar, but needs an additional step: the
    mask is computed by 1 SHL bitplane (that is: 1/2/4/8 For mod4 values 0/1/2/3
    respectively):
     portw[$3C4]:=(1 SHL bitplane+8)+2; mem[$A000:y*80+(x shr 2)]:=color
    Or using Asm again:

     MOV CL,bitplane
     MOV AH,1
     SHL AH,CL
     MOV AL,2
     MOV DX,3C4h
     CLI
     OUT DX,AX
     STOSB
     STI

    As  stated  above, one address represents 4 pixels, so 320x200 pixels occupy
    16000  address  Bytes.  We  do  have  65536  (=$A000:0..$A000:$FFFF) though,
    therefore  a  bit  more  than 4 pages are possible. It's up to you to define
    your  pages,  0..15999=page  0,  16000..31999=page  1,  32000..47999=page 2,
    48000..63999=page 3, 64000..65535=unused  is the most obvious layout.

    Which  part  of  the VRAM is actually displayed can be Programmed by writing
    the  offset  part of the starting address to the Crt-controller (the segment
    part is implicitly set to $A000):

    Asm
      MOV DX,CrtAddress
      MOV AL,$0D
      CLI
      OUT DX,AL
      INC DX
      MOV AL,low Byte of starting offset
      OUT DX,AL
      DEC DX
      MOV AL,$0C
      OUT DX,AL
      INC DX
      MOV AL,high Byte of starting offset
      OUT DX,AL
      STI
    end;

    N.B.: if you reProgram the display's starting address more often than "every
    now  and  then",  you  better  synchronize  that  to the vertical retrace or
    horizontal  enable  signal  of  your VGA card; otherwise, an annoying screen
    flicker will become visible during switching!

    For  example,  if  you do a "FOR i:=1 to 100 do SetAddress(i*80)", this will
    result  in a blinding fast hardware scroll: With each iteration of the loop,
    the  display will start 80 address Bytes (=320 pixels = 1 row) later, giving
    the impression of the display scrolling upwards.

    Note  that  Mode  X/Y  do  not differ in any other respect than their memory
    layouts  from  all  the  other  bitplaned VGA modes: palette handling is the
    same,  as  is usage of the VGA's Write modes! In (default) Write mode 0, you
    can access the VRAM by Bytes, Words or dWords. Write mode 1 is handy to copy
    the  contents  of  one  Graphic  page to another: you are restricted to Byte
    accesses, but each one will transfer 4 Bytes at once.
    For example, a sequence like the following...
    portw[$3C4]:=$0f02; portw[$3CE]:=$4105;
    move(mem[$a000:0000],mem[$a000:$3e80],16000);
    portw[$3CE]:=$4005
    ...enables  all 4 planes, switches to Write mode 1, copies the (64000 Bytes)
    contents  of  the  2nd Graphic page to the 1st one and then switches back to
    Write mode 0 again.
    }

[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]