program mirror_text;

{$G+,X+}

{
 MIRROR2.PAS -- Demonstration of 512-character set application.

 Written by Scott F. Earnest -- Donated to Public Domain
 current e-mail address: sinykal@cyberspace.org

 Thanks to Osmo Ronkanen (ronkanen@cc.helsinki.fi) for pointing out why I
 couldn't get the 9x16 supplemental font set to work correctly.

 This is a demonstration of one application of the 512-character font.  It
 loads a copy of the VGA 9x16 font into memory, modifies it and uses it to
 display a 512-character set on screen.

 Notes:

 - 512-character fonts are also possible in EGA, but this code is set to
   support VGA only.

 - Originally it was my understanding that when using 512 characters, blink
   or bright backgrounds were disabled.  Now that I've played with this, I
   see this isn't true; character set is chosen with bit 3, not bit 7.
   Blinking is still possible.  Though I haven't tested it, bright backrounds
   will very likely not work, since color plane 3 must be disabled.

 - This is written for TP7 and modified to compile under TP6.  Earlier
   versions may need extensive modifications in order to get this to
   compile.

 - Corrected in this version:  This now correctly loads the entire 9x16
   character set, so the mirrored font is now identical to original font
   (except, of course, for the fact that it's been rotated around).

 - This is very simple and could use lots of improvements, but I've tested
   it and it DOES work on VGA.  There's plenty of room for improvement, and
   I think I've commented it well enough.

 Please pass this around.  It would also be a VERY useful addition to SWAG.

 Use this freely, but if you use this or modify it, please give me due
 credit, thanks!
}

{$IFNDEF VER60}
{$IFNDEF VER70}
STOP:  This requires TP 6.0 or TP/BP 7.0x to compile!
{$ENDIF}
{$ENDIF}

uses
  crt;

{$IFDEF VER60}
const
  SegB800 = $b800;
{$ENDIF}

const
  CHARSIZE = 4096;  {Size of memory needed to store 9x16 font set}

type
  {structures for storing/accessing a font in memory}
  Tcharmap = array[0..CHARSIZE-1] of byte;
  Pcharmap = ^Tcharmap;
  Pbyte = ^byte;

procedure reverse_set (var m : Tcharmap);

{
 This function takes a character set and rotates in 180 degrees.  The
 result is a cool upside-down character set.
}

var
  ch : array [0..15] of byte;
  b, c : byte;

  function reverse_byte (b : byte) : byte; assembler;

  asm
    {setup:}
    mov   cx,0008h    {iterate shift 8 times}
    mov   ah,[b]      {number to be reversed}
    xor   al,al       {clear AL to receive shifted value}
  @1:
    rcr   ah,1        {shift lowest bit of AH into CF}
    rcl   al,1        {shift CF into lowest bit of AL}
    loop  @1
  end;

  procedure byteswap (var a, b : byte);

  var
    c : byte;

  begin
    c := a;
    a := b;
    b := c;
  end;

begin
  for c := 0 to 255 do
    begin
      move (m[c*16],ch,16); {get character into temporary array}
      for b := 0 to 7 do
        begin
          {flip the character in array ch[]}
          byteswap (ch[b],ch[15-b]);
          ch[b] := reverse_byte (ch[b]);
          ch[15-b] := reverse_byte(ch[15-b]);
        end;
      move (ch,m[c*16],16); {write modified char map back to font map}
    end;
end;

function getcharsetptr (setnum : byte) : pointer; assembler;

{
 This function uses the video BIOS to return the address of one of the
 character sets in memory.  Those sets are:

   0 -- int 1fh, user 8x8 upper 128 characters
   1 -- int 43h, user 8x8 lower/8x14
   2 -- ROM 8x14
   3 -- ROM 8x8 lower
   4 -- ROM 8x8 upper
   5 -- ROM 9x14 supplement
   6 -- ROM 8x16
   7 -- ROM 9x16 supplement

 Other information is returned in CX and DL, however these are ingored by
 this function.
}

asm
  push  bp
  mov   bh,[setnum]
  mov   ax,1130h
  int   10h
  mov   dx,es
  mov   ax,bp
  pop   bp
end;

procedure Get9x16font (const base : PCharMap);

{
 Loads supplemental font over allocated 8x16 font.  My own implementation,
 but based on code written by Osmo Ronkanen (ronkanen@cc.helsinki.fi).  This
 procedure was rewritten to eliminate some unneeded structures and reduce
 memory requirements.
}

var
  s, w : Pbyte;
  c : byte;

begin
  s := getcharsetptr (7);
  while s^<>0 do
    begin
      w := Pbyte(base);
      inc (w,s^*16);
      inc (s);
      move (s^,w^,16);
      inc (s,16);
    end;
end;

procedure installVGAfont (p : Pcharmap; block : byte); assembler;

{
 Install a user font.  'p' is the pointer to the character map in memory,
 and block is the font number to write.  For VGA, valid values are 0-7.
 Note:  This procedure is hard-coded to write a 16-scanline-per-pixel
 character set.
}

asm
  push  bp
  mov   ax,1110h
  mov   bl,[block]
  mov   bh,10h
  les   bp,[p]
  mov   cx,0100h
  mov   dx,0000h
  int   10h
  pop   bp
end;

procedure set512characters; assembler;

{
 This sets video to use two chained character sets.  In this case, sets 0
 and 1 are set to be used.  This can be rewritten to accept two values,
 which represent which 2 of the 8 sets to use.
}

asm
  {step 1:  disable color plane 3 -- keeps set B from always being bright}
  mov   ax,1000h
  mov   bl,12h
  mov   bh,07h
  int   10h
  {step 2:  enable set A and B -- A=0 (the normal set) B=1 (modified set)}
  mov   ax,1103h
  mov   bl,00000100b
  int   10h
end;

procedure reset256characters; assembler;

{
 This performs the opposite of above to return to a single 256 character set.
}

asm
  mov   ax,1000h
  mov   bl,12h
  mov   bh,0fh
  int   10h
  mov   ax,1103h
  mov   bl,00h
  int   10h
end;

procedure waitkey;

begin
  while keypressed do readkey;
  repeat until keypressed;
  while keypressed do readkey;
end;

procedure writechar (x, y : byte; ch : char; attr : byte);

{
 This just writes a single character to video memory in a specific attribute.
}

var
  m : word;

begin
  m := (pred(y)*80+pred(x))*2;
  mem[SegB800:m] := ord(ch);
  mem[SegB800:succ(m)] := attr;
end;

var
  mirrorfont : Pcharmap;
  fp : pointer;
  xp, yp : byte;

begin
  {allocate memory for modified set}
  new (mirrorfont);
  {load font into memory}
  fp := getcharsetptr (6);
  move (fp^,mirrorfont^,CHARSIZE);
  Get9x16font (mirrorfont);
  {modify the character maps}
  reverse_set (mirrorfont^);
  {draw up a demonstration on the screen}
  textattr := $17;
  clrscr;
  textattr := $1f;
  gotoxy (26,4);
  writeln ('!ereht olleH -- tset a si sihT');
  textattr := $17;
  gotoxy (26,5);
  writeln ('This is a test -- Hello there!');
  for yp := 0 to 7 do
    for xp := 0 to 31 do
      begin
        writechar (6+xp,10+yp,chr(yp*32+xp),$17);
        writechar (75-xp,17-yp,chr(yp*32+xp),$1f);
      end;
  gotoxy (1,22);
  writeln ('This is before . . .');
  {wait for a keypress}
  waitkey;
  {install the font}
  installVGAfont (mirrorfont,1);
  set512characters;
  {now update the banner message and wait once more}
  gotoxy (1,22);
  textattr := $1f;
  writeln ('!retfa si siht dna . . .');
  waitkey;
  {reset fonts and wait once more}
  reset256characters;
  gotoxy (1,22);
  textattr := $17;
  writeln ('And now back to normal! ');
  waitkey;
  {clean up memory and reset the video system}
  dispose (mirrorfont);
  asm mov ax,0003h; int 10h; end;
end.

-- 
Scott F. Earnest       | We now return you to our regularly scheduled |
sinykal@cyberspace.org | chaos and mayhem. . . .                      |
