AnteriorPosterior

Tema 10: Pantalla en modo texto

  Curso: Curso de Pascal, por Nacho Cabanes

Curso de Pascal. Tema 10: Pantalla en modo texto.

En este tema vamos a ver cómo acceder a la pantalla de texto, y algunos "bonus". :-)

Este tema va a ser específico de Turbo Pascal para DOS. Algunas de las cosas que veremos aparecen en otras versiones de Turbo Pascal (la 3.0 para CP/M, por ejemplo), pero no todas, y de cualquier modo, nada de esto es Pascal estándar. Aun así, como muchas versiones de Pascal posteriores (Tmt Pascal, Virtual Pascal, Free Pascal, etc) buscan una cierta compatibilidad con Turbo Pascal, es fácil que funcione con muchos compiladores recientes.

Me centraré primero en cómo se haría con las versiones 5.0 y superiores de Turbo Pascal. Luego comentaré cómo se haría con Turbo Pascal 3.01.

Vamos allá... En la mayoría de los lenguajes de programación, existen "bibliotecas" (en inglés, "library") con funciones y procedimientos nuevos, que permiten ampliar el lenguaje. En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades" (UNIT), y existen a partir de la versión 5.

Veremos cómo crearlas un poco más adelante, pero de momento nos va a interesar saber cómo acceder a ellas, porque "de fábrica" ;-) Turbo Pascal incorpora unidades que aportan mayores posibilidades de manejo de la pantalla en modo texto o gráfico, de acceso a funciones del DOS, de manejo de la impresora, etc.

Así, la primera unidad que trataremos es la encargada de gestionar (entre otras cosas) la pantalla en modo texto. Esta unidad se llama CRT.

Para acceder a cualquier unidad, se emplea la sentencia "uses" justo después de "program" y antes de las declaraciones de variables:

program prueba;

uses crt;
var
[...]

Voy a mencionar algunos de los procedimientos y funciones más importantes. Al final de esta lección resumo todos los que hay y para qué sirven.

  • ClrScr : Borra la pantalla.
  • GotoXY (x, y) : Coloca el cursor en unas coordenadas de la pantalla.
  • TextColor (Color) : Cambia el color de primer plano.
  • TextBackground (Color) : Cambia el color de fondo.
  • WhereX : Función que informa de la coordenada x actual del cursor.
  • WhereY : Coordenada y del cursor.
  • Window (x1, y1, x2, y2) : Define una ventana de texto.


Algunos "extras" no relacionados con la pantalla son:

  • Delay(ms) : Espera un cierto número de milisegundos.
  • ReadKey : Función que devuelve el carácter que se haya pulsado.
  • KeyPressed : Función que devuelve TRUE si se ha pulsado alguna tecla.
  • Sound (Hz) : Empieza a generar un sonido de una cierta frecuencia.
  • NoSound: Deja de producir el sonido.


Comentarios generales, la mayoría "triviales" guiño :

  • X es la columna, de 1 a 80.
  • Y es la fila, de 1 a 25.
  • El cursor es el cuadrado o raya parpadeante que nos indica donde seguiríamos escribiendo.
  • Los colores están definidos como constantes con el nombre en inglés. Así Black = 0, de modo que TextColor ( Black ) es lo mismo que TextColor(0).
  • La pantalla se puede manejar también accediendo directamente a la memoria de video, pero eso lo voy a dejar, al menos por ahora...


Aquí va un programita de ejemplo que maneja todo esto... o casi

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de la unidad  }
 {    CRT: acceso a panta-  }
 {    lla en modo texto TP  }
 {    EJCRT.PAS             }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.01  }
 {--------------------------}
 
 program PruebaDeCRT;
 
 uses crt;
 
 var
   bucle : byte;
   tecla : char;
 
 begin
   ClrScr;                        { Borra la pantalla }
   TextColor( Yellow );           { Color amarillo }
   TextBackground( Red );         { Fondo rojo }
   GotoXY( 40, 13 );              { Vamos al centro de la pantalla }
   Write(' Hola ');               { Saludamos ;-) }
   Delay( 1000 );                 { Esperamos un segundo }
   Window ( 1, 15, 80, 23 );      { Ventana entre las filas 15 y 23 }
   TextBackground ( Blue );       { Con fondo azul }
   ClrScr;                        { La borramos para que se vea }
   for bucle := 1 to 100
     do WriteLn( bucle );         { Escribimos del 1 al 100 }
   WriteLn( 'Pulse una tecla..');
   tecla := ReadKey;              { Esperamos que se pulse una tecla }
   Window( 1, 1, 80, 25 );        { Restauramos ventana original }
   GotoXY( 1, 24 );               { Vamos a la penúltima línea }
   Write( 'Ha pulsado ', tecla ); { Pos eso }
   Sound( 220 );                  { Sonido de frecuencia 220 Hz }
   Delay( 500 );                  { Durante medio segundo }
   NoSound;                       { Se acabó el sonido }
   Delay( 2000 );                 { Pausa antes de acabar }
   TextColor( LightGray );        { Colores por defecto del DOS }
   TextBackground( Black );       { Y borramos la pantalla }
   ClrScr;
 end. 
 

Finalmente, veamos los cambios para Turbo Pascal 3.01: En TP3 no existen unidades, por lo que la línea "uses crt;" no existiría. La otra diferencia es que para leer una tecla se hace con "read(kbd, tecla);" (leer de un dispositivio especial, el teclado, denotado con kbd) en vez de con "tecla := readkey". Con estos dos cambios, el programa anterior funciona perfectamente.


Tema 10.2: Pantalla en modo texto con Surpas.

Para Surpas la cosa cambia un poco:
  • GotoXY empieza a contar desde 0.
  • No existen ClrScr, TextColor ni TextBackground (entre otros), que se pueden emular como he hecho en el próximo ejemplo.
  • No existen Window, Delay, Sound, y no son tan fáciles de crear como los anteriores.


Con estas consideraciones, el programa (que aun así se parece) queda:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Pantalla de texto     }
 {    con Surpas            }
 {    EJCRTSP.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Surpas 1.00         }
 {--------------------------}
 
 program PruebaDeCRT;
 
 { ----- Aqui empiezan las definiciones que hacen que SurPas
         maneje la pantalla de forma similar a Turbo Pascal  ------ }
         { Para comprender de donde ha salido esto, consulta el
           fichero IBMPC.DOC que acompaña a SURPAS }
 
 const
   Black       =  0;
   Blue        =  1;
   Green       =  2;
   Cyan        =  3;
   Red         =  4;
   Magenta     =  5;
   Brown       =  6;
   LightGray   =  7;
   DarkGray    =  8;
   LightBlue   =  9;
   LightGreen  = 10;
   LightCyan   = 11;
   LightRed    = 12;
   LightMagenta= 13;
   Yellow      = 14;
   White       = 15;
 
 procedure ClrScr;
 begin
   gotoxy(0,0);
   write( CLREOS );
 end;
 
 procedure TextColor(n: byte);
 begin
   write( chr(27), 'b', chr(n) );
 end;
 
 procedure TextBackground(n: byte);
 begin
   write( chr(27), 'c', chr(n) );
 end;
 
 { Se podrían añadir otras posibilidades, como TextMode, HighVideo y
   LowVideo, etc, siguiendo este esquema, si se cree necesario }
 
 { ----- Final del añadido ----- }
 var
   bucle : byte;
   tecla : char;
 begin
   ClrScr;                        { Borra la pantalla }
   TextColor( Yellow );           { Color amarillo }
   TextBackground( Red );         { Fondo rojo }
   GotoXY( 30, 13 );              { Vamos al centro de la pantalla }
   Write(' Hola. Pulse una tecla... ');               { Saludamos ;-) }
   read(kbd,tecla);               { Esperamos que se pulse una tecla }
   TextBackground ( Blue );       { Con fondo azul }
   ClrScr;                        { La borramos para que se vea }
   for bucle := 1 to 100
     do Write( bucle, ' ' );      { Escribimos del 1 al 100 }
   WriteLn;                       { Avanzamos una línea }
   WriteLn( 'Pulse otra tecla..');
   read(kbd,tecla);               { Esperamos que se pulse una tecla }
   GotoXY( 0, 12 );               { Vamos al centro de la pantalla }
   Write( 'Ha pulsado ', tecla ); { Pos eso }
   TextColor( LightGray );        { Colores por defecto del DOS }
   TextBackground( Black );       { Y borramos la pantalla }
   GotoXY( 0, 23 );               { Vamos a la penúltima línea }
 end.
 
 




Tema 10.3: Procedimientos y funciones en CRT.

Finalmente, y por eso de que lo prometido es deuda, va un resumen de los procedimientos y funciones que tenemos disponibles en la unidad CRT (comprobado con Turbo Pascal 7.0):
  • AssignCrt Asocia un fichero de texto con la pantalla, de modo que podríamos usar órdenes como write(output, 'Hola'),
    más cercanas al Pascal "original". Como creo que nadie la use (ni yo), no cuento más.
  • ClrEol Borra desde la posición actual hasta el final de la línea.
  • ClrScr Borra la pantalla y deja el cursor al comienzo de ésta (en la esquina superior izquierda).
  • Delay Espera un cierto número de milisegundos.
  • DelLine Borra la línea que contiene el cursor.
  • GotoXY Mueve el cursor a una cierta posición de la pantalla.
  • HighVideo Modo de "alta intensidad". Es casi un "recuerdo" de cuando las pantallas monocromas no podían mostrar colores (ni siquiera varios tonos de gris) y sólo podíamos usar dos tonos: "normal" o "intenso" (si alguien conserva una
    tarjeta gráfica Hercules sabrá a qué me refiero, y con una VGA basta con escribir MODE MONO desde el DOS).
  • InsLine Inserta una línea en la posicón del cursor.
  • KeyPressed Dice si se ha pulsado una tecla. El valor de esta tecla se puede comprobar después con ReadKey.
  • LowVideo Texto de "baja intensidad" (ver HighVideo).
  • NormVideo Texto de "intensidad normal" (la que tuviera el carácter del cursor al comenzar el programa).
  • NoSound Para el sonido del altavoz interno.
  • ReadKey Lee un carácter de el teclado. Si no se ha pulsado ninguna tecla, espera a que se pulse.
  • Sound Hace que el altavoz interno comience a producir un sonido. La duración se controlará con Delay o algún método similar, y el sonido se debe parar con NoSound.
  • TextBackground Elige el color de fondo.
  • TextColor Fija el color de primer plano.
  • TextMode Cambia a un cierto modo de pantalla.
  • WhereX Devuelve la posición X en la que se encuentra el cursor.
  • WhereY Posición Y en la que se encuentra.
  • Window Define una ventana de texto.

También tenemos las variables siguientes (cuyo valor podemos leer o cambiar):

  • CheckBreak Boolean. Indica si puede interrumpir el programa pulsando Ctrl+C ó Ctrl+Break.
  • CheckEOF Boolean. Muestra un carácter de fin de fichero o no al pulsar Ctrl+Z.
  • DirectVideo Boolean. Escritura directa a video o no. Si el valor es True (por defecto), Write y WriteLn escriben en la memoria de pantalla. Si es False, las llamadas utilizan servicios de la Bios, más lentos. La pregunta es "¿y quien quiere lentitud? ¿para qué vamos a ponerlo a False?" La respuesta es que a través de los servicios de la Bios podemos usar Write para escribir también en modo gráfico. Se verá un ejemplo del uso de DirectVideo más adelante, en la Ampliación 2 ("Gráficos sin BGI").
  • CheckSnow Boolean. Cuando se escribe directamente en memoria de pantalla con una tarjeta CGA antigua puede aparecer "nieve". Si es nuestro caso, o puede darse en alguien para quien estemos haciendo el programa, deberemos
    añadir CheckSnow := True.
  • LastMode Word. Guarda el modo de pantalla activo cuando comenzó el programa.
  • TextAttr Byte. Atributos (colores) actuales del texto.
  • WindMin Word.
  • WindMax Word. Coordenadas mínimas y máximas de la ventana actual. Cada word son dos bytes: el byte bajo devuelve la coordenada X (p .: lo(WindMin)) y el alto la Y (p .: hi(WindMin)).

Y como constantes (por ejemplo, para poder escribir el nombre de un color en vez de recordar qué número indica ese color, como hemos hecho en los ejemplos anteriores):

  • Black = 0
  • Blue = 1
  • Green = 2
  • Cyan = 3
  • Red = 4
  • Magenta = 5
  • Brown = 6
  • LightGray = 7
  • DarkGray = 8
  • LightBlue = 9
  • LightGreen = 10
  • LightCyan = 11
  • LightRed = 12
  • LightMagenta= 13
  • Yellow = 14
  • White = 15

Las constantes que indican los modos de pantalla son:

  • BW40 = 0 Blanco y negro 40x25 en CGA o superiores.
  • CO40 = 1 Color 40x25 en CGA o superiores.
  • BW80 = 2 Blanco y negro 80x25 en CGA o superiores.
  • CO80 = 3 Color 40x25 en CGA o superiores.
  • Mono = 7 Monocromo 80x25 en MDA, Hercules, EGA Mono o VGA Mono.
  • Font8x8 = 256 Modo de 43 o 50 líneas en EGA o VGA.

Y por compatibilidad con la versión 3.0:

  • C40 = CO40
  • C80 = CO80


Pues hala, a experimentar...


Tema 10.4: Ejemplo: juego del ahorcado.


Antes de dejar el tema, un ejemplo sencillo que ponga a prueba algunas de las cosas que hemos visto: vamos a hacer el juego del ahorcado:

  • Un primer jugador deberá introducir una frase. La pantalla se borrará, y en lugar de cada letra aparecerá un guión.
  • El segundo jugador deberá ir tecleando letras. Si falla, ha gastado uno de sus intentos. Si acierta, la letra acertada deberá aparecer en las posiciones en que se encuentre.
  • El juego acaba cuando se aciertan todas las letras o se acaban los intentos.

{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Juego del Ahorcado    }
{    AHORCA.PAS            }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Free Pascal 2.2.0w  }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}
 
Program Ahorcado;
 
Uses crt;
 
Var
  palabra, intento, letras:string; { La palabra a adivinar, la que }
                                   {   el jugador 2 va consiguiendo y }
                                   {   las letras que se han probado }
  oportunidades: integer;          { El número de intentos permitido }
  letra: char;                     { Cada letra que prueba el jug. dos }
  i: integer;                      { Para mirar cada letra, con "for" }
  acertado: boolean;               { Si ha acertado alguna letra }
 
 
begin
  clrscr;                        { Valores iniciales, del jugador 1 }
  gotoxy (10,5);
  write ('Jugador 1:  ¿Que frase hay que adivinar?  ');
  readln (palabra);
  gotoxy (10,7);
  write ('¿En cuantos intentos?  ');
  readln (oportunidades);
 
  intento := '';                 { Relleno con _ y " " lo que ve Jug. 2 }
  for i:=1 to length(palabra) do
    if palabra[i]= ' ' then
      intento:=intento+' '
    else
      intento:=intento+'_';
 
  repeat
    clrscr;
    gotoxy (10,6);               { Digo cuantos intentos le quedan }
    writeln('Te quedan ',oportunidades,' intentos');
 
    gotoxy (10,8);               { Le muestro cómo va }
    writeln(intento);
 
    gotoxy (10,10);              { Y le pido otra letra }
    write('Letras intentadas: ', letras);
 
    gotoxy (10,12);              { Y le pido otra letra }
    write('¿Qué letra? ');
    letra := readkey;
    letras := letras + letra;
 
    acertado := false;           { Miro a ver si ha acertado }
    for i:=1 to length(palabra) do
      if letra=palabra[i] then
        begin
        intento[i]:=palabra[i];
        acertado := true;
        end;
 
    if acertado = false then     { Si falló, le queda un intento menos }
      oportunidades := oportunidades-1;
 
  until (intento=palabra)        { Hasta que acierte }
    or (oportunidades=0);        { o gaste sus oportunidades }
 
  gotoxy(10, 15);                { Le felicito o le digo cual era }
  if intento=palabra then
    writeln('¡Acertaste!')
  else
    writeln('Lo siento.  Era: ', palabra);
end.
 
 

Esto es muy mejorable. La primera mejora será que no haya necesidad de que un primer jugador sea el que escriba la palabra a adivinar y el número de intentos, sino que el número de intentos esté prefijado en el programa, y exista una serie de palabras de las que el ordenador escoja una al azar (para lo que usaremos "random" y "randomize", que se ven con más detalle en la Ampliación 1):

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Juego del Ahorcado    }
 {    (segunda version)     }
 {    AHORCA2.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 
 Program Ahorcado2;
 
 Uses crt;
 
 Const
   NUMPALABRAS = 10;
   MAXINTENTOS = 2;
   datosPalabras: array[1..NUMPALABRAS] of string =
     (
     'Alicante','Barcelona','Guadalajara','Madrid',
     'Toledo','Malaga','Zaragoza','Sevilla',
     'Valencia','Valladolid'
     );
 
 Var
   palabra, intento, letras:string; { La palabra a adivinar, la que }
                                    {   el jugador 2 va consiguiendo y }
                                    {   las letras que se han probado }
   numeroPalabra: word;
   oportunidades: integer;          { El numero de intentos permitido }
   letra: char;                     { Cada letra que prueba el jug. dos }
   i: integer;                      { Para mirar cada letra, con "for" }
   acertado: boolean;               { Si ha acertado alguna letra }
 
 begin
   randomize;                     { Comienzo a generar numeros aleatorios }
   numeroPalabra :=               { Tomo una palabra al azar }
     random(NUMPALABRAS);
   palabra := datosPalabras[numeroPalabra+1];
   oportunidades := MAXINTENTOS;
   intento := '';                 { Relleno con _ y " " lo que ve Jug. 2 }
   for i:=1 to length(palabra) do
     if palabra[i]= ' ' then
       intento:=intento+' '
     else
       intento:=intento+'*';
 
   repeat
     clrscr;
     gotoxy (10,6);               { Digo cuantos intentos le quedan }
     writeln('Te quedan ',oportunidades,' intentos');
     gotoxy (10,8);               { Le muestro como va }
     writeln(intento);
     gotoxy (10,10);              { Y le pido otra letra }
     write('Letras intentadas: ', letras);
     gotoxy (10,12);              { Y le pido otra letra }
     write('¿Qué letra? ');
     letra := upcase(readkey);    { Convierto a mayusculas }
     letras := letras + letra;
 
     acertado := false;           { Miro a ver si ha acertado }
     for i:=1 to length(palabra) do
       if letra=upcase(palabra[i]) then
         begin                    { Comparo en mayusculas }
         intento[i]:=palabra[i];
         acertado := true;
         end;
     if acertado = false then     { Si falla, le queda un intento menos }
       oportunidades := oportunidades-1; 
   until (intento=palabra)        { Hasta que acierte }
     or (oportunidades=0);        { o gaste sus oportunidades } 
   gotoxy(10, 15);                { Le felicito o le digo cual era }
 
   if intento=palabra then
     begin
     gotoxy (10,8);
     writeln(intento);
     gotoxy(10, 15);
     writeln('¡Acertaste!')
     end
   else
     writeln('Lo siento.  Era: ', palabra);
 end.
 

Una segunda mejora podría ser que realmente "se dibujara" el ahorcado en pantalla en vez de limitarse a decirnos cuantos intentos nos quedan. Como todavía no sabemos manejar la pantalla en modo gráfico, dibujaremos de un modo rudimentario, empleando letras. El resultado será "feo", algo parecido a esto:



Y lo podríamos conseguir así:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Juego del Ahorcado    }
 {    (tercera version)     }
 {    AHORCA3.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 
 Program Ahorcado3;
 
 Uses crt;
 
 Const
   NUMPALABRAS = 10;
   datosPalabras: array[1..NUMPALABRAS] of string =
     (
     'Alicante','Barcelona','Guadalajara','Madrid',
     'Toledo','Malaga','Zaragoza','Sevilla',
     'Valencia','Valladolid'
     );
   MAXINTENTOS = 5;  { No debe ser modificado: vamos a "dibujar" 5 cosas }
 
 Var
   palabra, intento, letras:string; { La palabra a adivinar, la que }
                                    {   el jugador 2 va consiguiendo y }
                                    {   las letras que se han probado }
   numeroPalabra: word;
   oportunidades: integer;          { El numero de intentos permitido }
   letra: char;                     { Cada letra que prueba el jug. dos }
   i: integer;                      { Para mirar cada letra, con "for" }
   acertado: boolean;               { Si ha acertado alguna letra }
 
 
 procedure PrimerFallo;       { Primer fallo: }
 var
   j: byte;                   { Dibujamos la "plataforma" }
 begin
   for j := 50 to 60 do
     begin
     gotoxy(j,20);
     write('-');
     end;
 end;
 
 
 procedure SegundoFallo;       { Segundo fallo: }
 var
   j: byte;                   { Dibujamos el "palo vertical" }
 begin
   for j := 14 to 19 do
     begin
     gotoxy(53,j);
     write('|');
     end;
 end;
 
 
 procedure TercerFallo;       { Tercer fallo: }
 var
   j: byte;                   { Dibujamos el "palo superior" }
 begin
   for j := 53 to 57 do
     begin
     gotoxy(j,14);
     write('-');
     end;
 end;
 
 procedure CuartoFallo;       { Cuarto fallo: }
 var
   j: byte;                   { Dibujamos la "plataforma" }
 begin
   gotoxy(57,15);
   write('|');
 end;
 
 
 procedure QuintoFallo;       { Quinto fallo: }
 var
   j: byte;                   { 

Actualizado el: 08-07-2012 12:42

AnteriorPosterior