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

{
Mario van den Ancker,
Amsterdam, The Netherlands.
E-mail: mario@astro.uva.nl
WWW: http://www.astro.uva.nl/mario/

----8<---------8<---------8<---------8<---------8<---------8<---------8<----
}
Program SplitScreen;

{ SplitScrn.pas - by Mario van den Ancker
                  E-Mail: mario@astro.uva.nl
                  WWW: http://www.astro.uva.nl/mario/
  Program to demonstrate how to split the screen using the VGA hardware
  in 320x240x256 ModeX. Requires TP 6.0 or higher. Donated to the public
  domain. }

Uses Crt;

type
  RGB = Array[1..3] of Byte;
  TPalette = Array[0..255] of RGB;

const
  MaxX = 319;       { dimensions of screen. }
  MaxY = 239;

  SEGA000: Word = $A000;

  Palette: TPalette = (
        (0, 0, 0), (0, 0, 63), (1, 0, 62), (3, 0, 60), 
        (5, 0, 58), (7, 0, 56), (9, 0, 54), (10, 0, 53), 
        (11, 0, 52), (13, 0, 50), (14, 0, 49), (15, 0, 48), 
        (16, 0, 47), (17, 1, 46), (18, 1, 45), (19, 1, 44), 
        (19, 1, 44), (20, 1, 43), (21, 1, 42), (21, 1, 42), 
        (22, 1, 41), (23, 1, 40), (23, 1, 40), (24, 1, 39), 
        (25, 1, 38), (25, 1, 38), (26, 1, 37), (26, 1, 37), 
        (27, 1, 36), (27, 2, 36), (28, 2, 35), (28, 2, 35), 
        (29, 2, 34), (29, 2, 34), (30, 2, 33), (30, 2, 33), 
        (31, 2, 32), (31, 2, 32), (32, 2, 31), (32, 2, 31), 
        (32, 3, 31), (33, 3, 30), (33, 3, 30), (34, 3, 29), 
        (34, 3, 29), (34, 3, 29), (35, 3, 28), (35, 3, 28), 
        (35, 3, 28), (35, 3, 28), (36, 3, 27), (36, 3, 27), 
        (37, 4, 26), (37, 4, 26), (37, 4, 26), (37, 4, 26), 
        (38, 4, 25), (38, 4, 25), (38, 4, 25), (38, 4, 25), 
        (39, 4, 24), (39, 4, 24), (39, 4, 24), (40, 5, 23), 
        (40, 5, 23), (40, 5, 23), (40, 5, 23), (41, 5, 22), 
        (41, 5, 22), (41, 5, 22), (41, 5, 22), (41, 5, 22), 
        (42, 5, 21), (42, 6, 21), (42, 6, 21), (42, 6, 21), 
        (43, 6, 20), (43, 6, 20), (43, 6, 20), (43, 6, 20), 
        (43, 6, 20), (43, 6, 20), (44, 7, 19), (44, 7, 19), 
        (44, 7, 19), (44, 7, 19), (44, 7, 19), (45, 7, 18), 
        (45, 7, 18), (45, 8, 18), (45, 8, 18), (45, 8, 18), 
        (45, 8, 18), (46, 8, 17), (46, 8, 17), (46, 8, 17), 
        (46, 9, 17), (47, 9, 16), (47, 9, 16), (47, 9, 16), 
        (47, 9, 16), (47, 9, 16), (47, 9, 16), (47, 9, 16), 
        (48, 10, 15), (48, 10, 15), (48, 10, 15), (48, 10, 15), 
        (48, 10, 15), (48, 10, 15), (48, 11, 15), (49, 11, 14), 
        (49, 11, 14), (49, 11, 14), (49, 11, 14), (49, 11, 14), 
        (50, 11, 13), (50, 12, 13), (50, 12, 13), (50, 12, 13), 
        (50, 12, 13), (50, 13, 13), (50, 13, 13), (50, 13, 13), 
        (51, 13, 12), (51, 13, 12), (51, 13, 12), (51, 14, 12), 
        (51, 14, 12), (51, 14, 12), (51, 14, 12), (51, 14, 12), 
        (52, 15, 11), (52, 15, 11), (52, 15, 11), (52, 15, 11), 
        (52, 15, 11), (52, 15, 11), (53, 16, 10), (53, 16, 10), 
        (53, 16, 10), (53, 16, 10), (53, 17, 10), (53, 17, 10), 
        (53, 17, 10), (53, 17, 10), (53, 18, 10), (54, 18, 9), 
        (54, 18, 9), (54, 18, 9), (54, 19, 9), (54, 19, 9), 
        (54, 19, 9), (54, 19, 9), (54, 19, 9), (54, 20, 9), 
        (54, 20, 9), (55, 20, 8), (55, 20, 8), (55, 21, 8), 
        (55, 21, 8), (55, 21, 8), (55, 21, 8), (55, 22, 8), 
        (56, 22, 7), (56, 22, 7), (56, 22, 7), (56, 23, 7), 
        (56, 23, 7), (56, 23, 7), (56, 24, 7), (56, 24, 7), 
        (56, 24, 7), (56, 24, 7), (56, 25, 7), (57, 25, 6), 
        (57, 25, 6), (57, 26, 6), (57, 26, 6), (57, 26, 6), 
        (57, 27, 6), (57, 27, 6), (57, 27, 6), (57, 28, 6), 
        (57, 28, 6), (57, 28, 6), (58, 29, 5), (58, 29, 5), 
        (58, 29, 5), (58, 30, 5), (58, 30, 5), (58, 30, 5), 
        (58, 31, 5), (58, 31, 5), (58, 32, 5), (59, 32, 4), 
        (59, 32, 4), (59, 33, 4), (59, 33, 4), (59, 33, 4), 
        (59, 34, 4), (59, 34, 4), (59, 35, 4), (59, 35, 4), 
        (59, 35, 4), (59, 36, 4), (59, 36, 4), (59, 37, 4), 
        (60, 37, 3), (60, 38, 3), (60, 38, 3), (60, 38, 3), 
        (60, 39, 3), (60, 39, 3), (60, 40, 3), (60, 40, 3), 
        (60, 41, 3), (60, 41, 3), (60, 42, 3), (60, 42, 3), 
        (60, 42, 3), (61, 43, 2), (61, 43, 2), (61, 44, 2), 
        (61, 44, 2), (61, 45, 2), (61, 45, 2), (61, 46, 2), 
        (61, 46, 2), (61, 47, 2), (61, 47, 2), (61, 48, 2), 
        (61, 49, 2), (61, 49, 2), (61, 50, 2), (62, 50, 1), 
        (62, 51, 1), (62, 51, 1), (62, 52, 1), (62, 52, 1), 
        (62, 53, 1), (62, 54, 1), (62, 54, 1), (62, 55, 1), 
        (62, 55, 1), (62, 56, 1), (63, 57, 0), (63, 57, 0), 
        (63, 58, 0), (63, 59, 0), (63, 59, 0), (63, 60, 0), 
        (63, 61, 0), (63, 61, 0), (63, 62, 0), (63, 63, 0));

var
  x, y: Integer;
  i: Byte;


{ Waits for VGA's vertical retrace. }
procedure WaitVRetrace; Assembler;
Asm
  mov  dx, 3dah
@@1:
  in   al, dx
  and  al, 08h
  jnz  @@1
@@2:
  in   al, dx
  and  al, 08h
  jz   @@2
end;

{ Sets a complete palette. }
procedure SetPal(var Palet: TPalette); Assembler;
Asm
  call  WaitVRetrace
  push  ds
  lds   si, Palet
  mov   dx, 3c8h
  mov   al, 0
  out   dx, al
  inc   dx
  mov   cx, 768
  rep   outsb
  pop   ds
end;

{ Initializes 320x240x256 ModeX. Virtual screen is 320x800x256. }
procedure SetModeX; Assembler;
Asm
  { First get in MCGA mode. }
  mov    ax, 13h
  int    10h

  { Unchain. }
  cli
  mov    dx, 3c4h
  mov    ax, 0604h
  out    dx, ax
  mov    ax, 0f02h
  out    dx, ax

  { Clear complete VGA memory. }
  mov    ax, 0f02h           { Select all bitplanes. }
  out    dx, ax
  mov    es, SEGA000
  xor    di, di              { Clear di }
  xor    ax, ax              { Clear ax }
  mov    cx, 7fffh
  cld
  rep    stosw               { Clear garbage off the screen. }

  { Now setup 320x240x256 ModeX. }
  mov    dx, 03c2h
  mov    al, 0e3h
  out    dx, al
  mov    dx, 03d4h
  mov    ax, 02c11h
  out    dx, ax
  mov    ax, 0d06h
  out    dx, ax
  mov    ax, 3e07h
  out    dx, ax
  mov    ax, 0ea10h
  out    dx, ax
  mov    ax, 0ac11h
  out    dx, ax
  mov    ax, 0df12h
  out    dx, ax
  mov    ax, 0e715h
  out    dx, ax
  mov    ax, 0616h
  out    dx, ax
  sti
end;

{ Flips screen back to text mode. }
procedure SetTextMode; Assembler;
asm
  mov ax, 0003h
  int 10h
end;


{ Moves offset of screen to (x, y). }
procedure ScreenOffset(x, y: Word); Assembler;
Asm
  cli
  mov    ax, y
  mov    bx, ax
  shl    bx, 6
  shl    ax, 4
  add    bx, ax              { bx = y*80 }
  mov    ax, x
  add    bx, ax              { bx = y*80 + x }
  mov    ah, bh
  mov    al, 0ch

  mov    dx, 3d4h
  out    dx, ax

  mov    ah, bl
  mov    al, 0dh
  out    dx, ax
  sti
end;

{ Splits the screen. The offset of this split screen in VGA memory is always
  0. The split screen can be hidden by setting it to a line > MaxY. }
procedure SetSplitscreen(Line: Word); Assembler;
Asm
  mov    bx, Line
  shl    bx, 1
  dec    bx

  cli
  mov    dx, 3d4h            { CRTC base address }
  mov    ah, bl
  mov    al, 18h             { CRTC line compare reg. index  }
  out    dx, ax
  mov    ah, bh
  and    ah, 1
  shl    ah, 4
  mov    al, 07h             { CRTC overflow register index }
  out    dx, al
  inc    dx
  in     al, dx
  and    al, not 10h
  or     al, ah
  out    dx, al
  dec    dx
  mov    ah, bh
  and    ah, 2
  ror    ah, 3
  mov    al, 09h             { CRTC maximum scan line register index }
  out    dx, al
  inc    dx
  in     al, dx
  and    al, not 40h
  or     al, ah
  out    dx, al
  sti
end;

{ Puts a pixel on the screen in ModeX. x should be between 0 and 319. y 
  should be between 0 and 799. Whether or not the pixel is visible on the 
  screen is determined by what part of the VGA memory we are displaying, as 
  can be set by ScreenOffset and SplitScreen. }
procedure PutPixel(x, y: Word; Color: Byte); Assembler;
Asm
  mov    ax, y
  mov    bx, ax
  shl    bx, 4
  shl    ax, 6
  add    bx, ax              { bx = y*80 }
  mov    ax, x
  mov    cl, al
  and    cl, 03h
  shr    ax, 2
  add    bx, ax
  mov    ah, 1
  shl    ah, cl
  mov    dx, 3c4h            { Sequencer Register    }
  mov    al, 2               { Map Mask Index        }
  out    dx, ax

  mov    es, SEGA000
  mov    al, Color
  mov    es:[bx], al
end;

{ Simple 'n slow rectangle drawing routine. }
procedure Rect(x1, y1, x2, y2: Word; Color: Byte);
var
  i: Integer;
begin
  for i := x1 to x2 do
  begin
    PutPixel(i, y1, Color);
    PutPixel(i, y2, Color);
  end;
  for i := y1 to y2 do
  begin
    PutPixel(x1, i, Color);
    PutPixel(x2, i, Color);
  end;
end;

{ Puts a character on the screen using the VGA ROM font. }
procedure PutChar(x, y: Word; Color: Byte; ch: Char);
var
  i, j: Byte;
  pre: Word;
begin
  pre := $FA6E + (Ord(ch) shl 3);
  for i := 0 to 7 do
    for j := 0 to 7 do
      if (((Mem[$F000:pre+i] shl j) and 128) <> 0) then
        PutPixel(x+j, y+i, Color);
end;

{ Puts a string on the screen using the VGA ROM font. }
procedure PutString(x, y: Word; Color: Byte; Str: String);
var
  i: Byte;
begin
  for i := 1 to length(Str) do
    PutChar(x + ((i-1) shl 3), y, Color, Str[i]);
end;


begin
  { Initialize display and palette. }
  SetModeX;
  SetPal(Palette);

  { Set virtual screen to start at line 64 and put simple background
    pattern on screen. }
  ScreenOffset(0, 64);
  for x := 0 to MaxX do
    for y := 0 to MaxY do
    begin
      i := x xor y;
      if (i = 0) then i := 1;    { Using colour 0 (black) looks ugly... }
      PutPixel(x, y+64, i);
    end;

  { We're gonna use lines 0-63 in VGA memory for our split screen.
    Let's write a background pattern and some text to that region. }
  for i := 0 to 31 do
    Rect(i, i, MaxX-i, 63-i, i shl 2);
  PutString(24, 23, 255, 'Split Screen in 320x240x256 ModeX!');
  PutString(52, 35, 255, 'Press any key to continue...');

  { Now scroll the split screen on the display.... }
  for y := 0 to 63 do
  begin
    SetSplitScreen(MaxY-y);
    WaitVRetrace;
  end;

  Repeat Until KeyPressed;

  { And scroll the split screen off the display... }
  for y := 63 downto 0 do
  begin
    SetSplitScreen(MaxY-y);
    WaitVRetrace;
  end;

  { Finito: back to Text mode! }
  SetTextMode;
end.

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