




                               CAPTULO 10                   
                                          
                   SISTEMAS DE NUMERAO, ENDEREOS E APONTADORES
            
            
            
            10.1. O SISTEMA DE NUMERAO HEXADECIMAL         
            
                  muito  comum o uso do sistema de numerao de base 16
            (hexadecimal) pelos computadores. Talvez seja pela afinidade
            que esse sistema tem com o sistema de numerao binrio.
                 No sistema  hexadecimal, usam-se 16 "dgitos": 0, 1, 2,
            3, 4,  5, 6,  7, 8,  9, A, B,  D, E e F. Por exemplo, 3AB,
            10F2A,  EA0C1  so  nmeros  escritos  usando-se  o  sistema
            hexadecimal.
                 Para se  converter  um  nmero  a_n...a_1a_0 escrito no
            sistema hexadecimal para o sistema decimal, basta calcular o
            somatrio a_n*16^n + a_n-1*16^n-1 +  ...  +  a_1*16 + a_0  e
            substituir A  por 10, B por 11, C por 12, D por 13, E por 14
            e F por 15. Por exemplo, 12AF, em hexadecimal, corresponde a
            1*16^3 + 2*16^2 + 10*16 + 15 =4783 no sistema decimal. Outro
            exemplo: B015A3  em hexadecimal,  corresponde  no  sistema
            decimal a  11*16^6 + 1*16^4 + 5*16^3 + 10*16^2 + 3*16 + 12 =
            184638012. Para  se converter  do  sistema  decimal  para  o
            hexadecimal,  basta  fazer  sucessivas  divises  por  16  e
            considerar os restos de cada diviso.
                 Em Pascal, os nmeros hexadecimais devem ser precedidos
            de  um  cifro,  como  em  $A13F.  Os  nmeros  escritos  em
            hexadecimal podem ser tratados como inteiros comuns:
            
                CONST
                  CaracHex : ARRAY [0..$F] of char = ('0', '1', '2',
                             '3', '4', '5', '6', '7', '8', '9', 'A',
                             'B', 'C', 'D', 'E', 'F');
                  X: integer = $12A; (* O mesmo que X = 298 *)
            
                 O ambiente do Pascal possui uma pequena calculadora que
            efetua operaes  com nmeros  hexadecimais  (converte  para
            decimal, soma,  multiplica, ...).  Pressione ALT-D  para ter
            acesso ao  "Debug" do  menu principal e um "E" de "Evaluate"
            (ou  pressione   CTRL-F4),  e  digite  dentro  da  janelinha
            intitulada "Evaluate"  o  clculo  desejado.  Ao  pressionar
            ENTER, o resultado ser mostrado na janelinha "Result".
            
            10.2. ENDEREOS      
            
                 Cada objeto  de  um  programa,  ocupa  uma  posio  na
            memria do  computador que   definida  na  hora  em  que  o
            programa for  executado. Essa  posio de  memria recebe  o
            nome de  endereo da varivel e  formada de duas partes: um  
            segmento e  um deslocamento  ("offset"), denotados  por dois
            inteiros separados entre si por um sinal de dois pontos. Por
            exemplo, $A000:$3E  (ou 40960:62)  pode ser considerado como
            um endereo de algum lugar da memria.

                                       - 152 -





                 Quando declaramos  uma varivel,  podemos especificar o
            lugar da  memria que  queremos que ela ocupe. Para isso, no
            bloco de  declaraes VAR, ao lado da definio da varivel,
            acrescentamos  a   palavra  ABSOLUTE   seguida  do  endereo
            desejado. Por exemplo,
            
                           VAR
                             m: real ABSOLUTE $b000:$e;
                             i: byte ABSOLUTE $a000:$123;
            
            define a  localizao  na  memria  das  variveis  m  e  i:
            endereos $b000:$e e $a000:$123, respectivamente.
            
                 O uso  aleatrio de  declaraes ABSOLUTE, pode levar a
            erros graves.  O usurio  pode, talvez  sem querer,  alterar
            dados  do  prprio  sistema  operacional  (por  exemplo,  os
            endereos de  0:0 a  0:$4ff) (Para  os inteiros  de 0 a 9 as
            notaes decimal  e hexadecimal  coincidem: 0  = $0, 7 = $7,
            etc.)
            
            Exemplo: Um  endereo muito  interessante   o  endereo  do
            incio da  memria de  vdeo, que   o  $B800:0 para  alguns
            tipos de vdeos.
                 A tela  em modo  texto possui 25 linhas e 80 colunas, o
            que corresponde  a 25*80  = 2000 bytes. No entanto, tem mais
            um byte associado a cada caracter com informaes a respeito
            das cores  do caracter,  chamado byte de atributo. Portanto,
            cada tela  em modo  texto ocupa  2000 +  2000 =  4000 bytes.
            Podemos assim,  pensar na  tela como  sendo um vetor de 4000
            elementos que se inicia no endereo $B800:$0000:
            
                 VAR
                   tela = array [1..4000] of byte ABSOLUTE $b800:0;
            
                 O byte  de atributo se situa logo aps o seu respectivo
            caracter. Logo,  se n for mpar, no vetor TELA acima tela[n]
            refere-se ao  n-simo caracter,  enquanto que  tela[n  +  1]
            refere-se ao  seu atributo.  Percorrer  o  vetor  do  incio
            (tela[1]) para  o fim  (tela[4000]) equivale  a percorrer  a
            tela da  1a  25a linha e da 1a  80a coluna. Atribuir valo-
            res s  componentes do vetor TELA equivale a usar um comando
            WRITE. Por exemplo, uma atribuio como  tela[1] := 65 faz o
            mesmo efeito que GoToXY(1, 1); Write('); (65 = Ord(')).
                 Um vetor  V de  4000 bytes pode ser usado para "salvar"
            uma ou  vrias telas  durante a  execuo  de  um  programa,
            bastando para isso copiar todos os tela[i] para cada V[i]:
            
                      VAR
                        V: array [1..4000] of byte;
                      ...
                      for i := 1 to 4000 do    (* V  uma cpia *)
                        V[i] := tela[i];       (* da tela       *)
                      ...

                                       - 153 -





                 Uma tela  salva dessa forma pode ser gravada em disco e
            pode ser  reconstruda facilmente, bastando para isso copiar
            as componentes de volta para o vetor TELA:
                      ...
                      for i := 1 to 4000 do     (* Restaura V na tela *)
                        tela[i] := V[i];
                      ...
                 No entanto,  h uma  forma melhor  de fazer  a cpia de
            TELA em  V. O Pascal tem um comando bastante apropriado para
            esse caso:
            
                 MOVE(var1, var2, n) ---> Copia n bytes de var1 para
                                          var2
            
                 O comando  MOVE copia  TELA em V de forma mais rpida e
            simples do que o FOR:
            
                                MOVE(tela, V, 4000);
            
            e para restaurar uma tela salva:
            
                                MOVE(V, tela, 4000);
            
                 Essa operao  de salvamento  e restaurao  de telas 
            muito usada em menus. D a impresso de se ter uma tela "por
            trs" de outra.
            
            10.3. O SISTEMA DE NUMERAO BINRIO     
            
                 O sistema  de numerao usado internamente por todos os
            computadores   o sistema  de numerao de base 2, conhecido
            como  sistema   binrio.  Nesse   sistema  so  usados  dois
            algarismos, 0  e 1,  conhecidos como  bits, uma contrao de     
            "BInary digiTS".  Para se  converter um  nmero  do  sistema
            binrio para  o  decimal,  deve-se  fazer  um  somatrio  de
            potncias de 2: a_n...a_1a_0, em binrio, corresponde a a_n*2^n
            + a_n-1*2^n-1  + a_1*2 + a_0 em decimal. Por exemplo, 101011, em
            binrio, corresponde  a 2^5  + 2^3  + 2  + 1  = 43 em decimal;
            11001100, em  binrio, equivale a 2^7 + 2^6 + 2^3 + 2^2 = 204 na
            notao do  sistema decimal.  Para se  converter do  sistema
            decimal para  o sistema  binrio, deve-se  fazer  sucessivas
            divises por  2 e  no final  considerar os  restos  de  cada
            diviso como sendo os bits procurados.
                 Um byte   um  conjunto de 8 bits, ou seja,  um nmero 
            binrio de 8 dgitos, como 11010111 ou 00001010. No  usual
            falar em quantidades de bits que no sejam mltiplas de 8.
                 O Pascal  possui diversos  operadores para  manipulao
            dos bits de inteiros:
            
            X AND Y ---> Inteiro cujos bits so iguais a  1  quando  os
                         respectivos bits de X e Y forem ambos iguais a
                         1, e iguais a 0 em caso contrrio.
            
            X OR Y  ---> Inteiro cujos  bits so  iguais a  0 quando os

                                       - 154 -





                         respectivos bits de X e Y forem ambos iguais a
                         0, e iguais a 1 em caso contrrio.
            
            X XOR Y ---> Inteiro cujos bits so  iguais a  1 quando  os
                         respectivos  bits  de  X  e  Y forem iguais, e
                         iguais a 0 em caso contrrio.
            
            NOT X   ---> Inteiro cujos bits  so todos  respectivamente
                         diferentes dos bits  de X, ou seja, so iguais
                         a 0 onde os bits de X forem 1 e vice-versa.
            
            X SHL n ---> Deslocamento de n bits  para  a  esquerda  dos
                         bits de X (SHL = "shift left").
            
            X SHR n ---> Deslocamento de n bits para a direita dos bits
                         de X (SHR = "shift right").
            
                 A  calculadora   do  Pascal  (aquela  que  aparece  com
            CTRL-F4) aceita todos esses operadores.
            
            Exemplo: Se  X =  120 =  01111000 (em  binrio) e  Y = 204 =
            11001100 (em binrio), ento
            
                      X AND Y = 01001000 (em binrio) =  72
                      X OR Y  = 11111100 (em binrio) = 252
                      X XOR Y = 10110100 (em binrio) = 180
                      NOT X   = 10000111 (em binrio) = 135
                      NOT Y   = 00110011 (em binrio) =  51
                      X SHR 1 = 00111100 (em binrio) =  60
                      X SHR 5 = 00000011 (em binrio) =   3
                      Y SHL 1 = 10011000 (em binrio) = 152
                      Y SHL 4 = 11000000 (em binrio) = 192
            
                 No  deslocamento  de  bits,  alguns  bits  podem  ficar
            perdidos (como  no Y  SHL  4  anterior).  Essa  operao  de
            deslocamento de  bits   muito usada nos programas populares
            UUENCODE, UUDECODE,  XXENCODE  e  XXDECODE,  conhecidos  dos
            usurios de rede de computadores (como a BITNET).
                 A manipulao  de bits  tambm  usada pelos algoritmos
            de compresso de dados (como o algoritmo usado pelo PKZIP) e
             til  para se usar com grficos. Uma tela grfica pode ser
            pensada como  sendo uma  grande matriz (640 x 200, ou maior)
            de  zeros  e  uns,  na  qual  o  1  corresponde  aos  pxeis
            (elementos de  imagem) acesos  e o  0 corresponde aos pxeis
            apagados.
            
            Exemplo: Para  se converter  um  nmero  decimal  X  para  a
            notao do sistema binrio, podemos usar o seguinte:
                 Calculamos X  AND 128.  O resultado  ser 10000000 se o
            oitavo bit  (da direita  para a  esquerda) de  X  for  1,  e
            00000000 em  caso contrrio.  Assim se X AND 128 <> 0, ento
            escrevemos o  oitavo bit  de X  como sendo 1; caso contrrio
            escrevemos um  0. Os  outros bits de X podem ser descobertos

                                       - 155 -





            fazendo-se operaes  AND com  as potncias  de 2 menores do
            que 128:  X AND 64, X AND 32, ..., X AND 1.
                 O programa  a seguir  usa isso,  para mostrar a notao
            decimal de  um inteiro fornecido como parmetro do programa.
            Como todo  parmetro de um programa  considerado como sendo
            do tipo  string, devemos  usar uma  converso de string para
            inteiro. Isto pode ser feito com o procedimento VAL:
            
            VAL(S, N, cod) ---> Calcula o valor numrico  do string S e
                                atribui o resultado  varivel numrica
                                N. Se a varivel inteira COD for  nula,
                                ento a converso foi bem sucedida.
            
                 O  procedimento   que  converte  valores  numricos  em
            strings  o STR cuja sintaxe : STR(var_numrica, string).
            
            PROGRAM Converte;
            
            (*
               Converte um inteiro (byte) fornecido como parmetro para
               a notao binria
            *)
            
            VAR
              s: string;
              n, i: byte;
              cod: integer;
            
            CONST
              PotenciaDe2: array [1..8] of byte =
                           (128, 64, 32, 16, 8, 4, 2, 1);
            
            BEGIN
              if ParamCount = 0 then
              begin
                Writeln('Forma de usar: C:\> CONVERTE numer);
                Halt;
              end;
            
              s := ParamStr(1);
              Val(s, n, cod);
              if (cod <> 0) then
                Writeln(s, ' nao tem valor numrico.')
              else
              begin
                if (n > 255) then
                begin
                  Writeln(n, ' deve ser um inteiro de 0 a 255.');
                  Halt;
                end;
                Writeln;
                for i := 1 to 8 do
                  if (n AND PotenciaDe2[i] <> 0) then
                    Write('1')

                                       - 156 -





                  else
                    Write('0');
              end;
            END.
            
            Exemplo: Outro  endereo interessante   o  $F000:$FA6E.  Os
            1024 (=  128 * 8) bytes situados a partir desse endereo so
            usados para  desenhar os  128 primeiros caracteres da tabela
            ASCII. Cada  grupo de  8 bytes  corresponde ao  desenho de 1
            caracter ASCII  da seguinte forma: cada caracter  desenhado
            em oito  linhas, uma  para cada  byte, sendo  que cada bit 1
            corresponde a  um ponto  (pxel) aceso  na tela e cada bit 0
            corresponde a  um ponto  apagado. Por  exemplo, os  bytes de
            ordem 8*65  + 1  a 8*65 + 8 situados a partir de $F000:$FA6E
            so 48,  120, 204,  204, 252, 204, 204 e 0. Transformando em
            binrios e escrevendo um abaixo do outro, obtemos:
            
                                0 0 1 1 0 0 0 0          
                                0 1 1 1 1 0 0 0                
                                1 1 0 0 1 1 0 0              
                                1 1 0 0 1 1 0 0              
                                1 1 1 1 1 1 0 0              
                                1 1 0 0 1 1 0 0              
                                1 1 0 0 1 1 0 0              
                                0 0 0 0 0 0 0 0
            
            ou, escrevendo  um "."  no lugar do "0" e um "#" no lugar do   
            "1", obtemos o desenho de um "A" (= #65) :
            
                                . . # # . . . .           
                                . # # # # . . .           
                                # # . . # # . .           
                                # # . . # # . .           
                                # # # # # # . .           
                                # # . . # # . .           
                                # # . . # # . .           
                                . . . . . . . .           
            
                 Vamos usar esse endereo e a converso para binrios do
            exemplo anterior, para elaborar o seguinte procedimento, que
            desenha um  caracter ASCII na tela em formato grande, usando
            o #178  no lugar do "#" acima. A posio em que o caracter 
            desenhado na  tela,   especificada nos parmetros "linha" e
            "coluna".
            
            TYPE
              vetor = array [1..128 * 8] of byte;
              letra = array [1..8] of byte;
              matriz_da_tela = array [1..4000] of byte;
            
            VAR
              tabela: vetor ABSOLUTE $F000:$FA6E;
              tela: matriz_da_tela ABSOLUTE $B800:0;
            

                                       - 157 -





            PROCEDURE Caracter(n: byte; VAR L: letra);
            
            (* Devolve no vetor L os elementos de TABELA de ordens
               8*n + 1 at 8*n + 8 *)
            
            VAR
              i: byte;
            
            BEGIN
              for  i := 1 to 8 do
                L[i] := tabela[8*n + i]
            END;
            
            PROCEDURE MostraCaracter(n, linha, coluna: byte);
            
            (* Desenha o #n a partir da posio (linha, coluna) *)
            
            VAR
              i, j: byte;
              L: letra;
            
            CONST
              PotenciasDe2: letra = (128, 64, 32, 16, 8, 4, 2, 1);
            
            BEGIN
              Caracter(n, L);
              for i := 1 to 8 do
              begin
                GoToXY(coluna, linha + i - 1);
            
                for j := 1 to 8 do
                begin
                  if L[i] and PotenciasDe2[j] <> 0 then
                    Write(#178)    (* Para cada bit 1 de L[i]  *)
                  else             (* desenhado um #178, e para *)
                    Write(' ');    (* cada bit 0,  um espao em *)
                end;               (* branco.                   *)
            
                Writeln;
              end;
            END;
            
                 Grave em  disco o  procedimento acima  sob  o  nome  de
            MENU.INC. Podemos us-lo como no programa a seguir, que gera
            aleatoriamente um  caracter de  #1 a #127 e mostra-o na tela
            em uma posio tambm aleatria.
            
            PROGRAM CaracteresGrandes;
            
            USES
              Crt;
            
            {$i menu.inc}
            

                                       - 158 -





            VAR
              x, linha, col: byte;
            
            BEGIN
              Randomize;
              repeat            (* Repetir ... *)
                ClrScr;
                x := Random(127) + 1;      (* 1 <= x <= 127    *)
                linha := Random(15) + 1;   (* 1 <= linha <= 15 *)
                col := Random(70) + 1;     (* 1 <= col <= 70   *)
                MostraCaracter(x, linha, col);
                Delay(500); (* Demora 1/2 segundo *)
              until KeyPressed; (* ... at ser pressionado uma tecla *)
            END.
            
            10.4. APONTADORES         
            
                 Um apontador (pointer) ou ponteiro  uma varivel usada 
            para guardar  um endereo.  Sua declarao   feita  com  um
            acento circunflexo colocado  esquerda do tipo da varivel a
            qual o endereo vai estar associado:
            
                           TYPE
                             apontador = ^TipoBase;
            
                 Um apontador  que no  est associado  a um determinado
            tipo de objeto, pode ser declarado como "pointer".
            
            Exemplo: TYPE
                       apont_inteiro = ^integer; (* tipo endereo de *)
                                                 (* inteiro          *)
                       apont_real = ^real;  (* endereo de  um  real *)
                       vetor = array [1..5] of byte;
                       VetPtr = ^vetor;     (* endereo de um  vetor *)
                       Endereco_X: pointer; (* endereo desvinculado *)
                                            (* de qualquer tipo      *)
            
                 O endereo de um determinado objeto  fornecido atravs
            do operador "@":
            
                           @objeto = endereo do objeto
            
                 O contedo  de  um  determinado  endereo    fornecido
            colocando-se um  acento circunflexo   direita  do  nome  do
            apontador para aquele endereo:
            
                           endere = contedo do endereo
            
            Exemplo: VAR
                       x, y: word;
                       ender = ^word;
            
                     BEGIN
                       x := 3;

                                       - 159 -





            
                       ender := @x; (* ENDER  o endereo de x *)
            
                       y := ender^; (* y  o contedo do endereo *)
                                    (* ENDER, ou seja, y = 3      *)
                     END.
            
            Exemplo: Consideremos  os seguintes apontadores definidos no
            TYPE e as variveis do bloco VAR a seguir:
            
            TYPE
              matriz = array [1..5, 1..5] of longint;
              MatPtr = ^matriz;
              Mat_end = array [1..5, 1..5] of ^longint;
              Mat_end_ptr = ^Mat_end;
            
            VAR
              M: MatPtr;
              X: Mat_end;
              Y: Mat_end_ptr;
            
                 M   um endereo  para uma  matriz 5  x 5.  Logo, M^, o
            contedo de  M,   uma matriz  5 x  5 e  da so  vlidas as
            atribuies:
            
                 M^ [1, 1] := -3;   M^ [1, 3] := 0;   M^ [5, 5] := 1992;
            
                 X   uma matriz  de endereos  do tipo  longint. Assim,
            X[1, 1],  X[1, 2],  etc. so todos endereos cujos contedos
            so X[1, 1]^, X[1, 2]^, etc. e podemos ter:
            
                 X [1, 1]^ := -23;  X [3, 4]^ := 1; ...
            
                 Y  um endereo para uma matriz de endereos. Portanto,
            Y^, o  contedo de  Y,   uma matriz de endereos: Y^[1, 1],
            Y^[3, 3], etc. cujos contedos so Y^[1, 1]^, Y^[3, 3]^, ...
            Podemos ter, por exemplo, Y^[1, 1]^ := 7 e Y^[2, 2]^ := 4.
            
            Exemplo: Como  no exemplo  anterior, considere  os seguintes
            blocos de declaraes:
            
            TYPE
              endereco = record
                rua: string;
                numero: word;
                cidade: string[20];
                estado: string[2]
              end;
            
              cadastro = record
                nome: string;
                matricula: string[10];
                salario: ^real;
                residencia: ^endereco

                                       - 160 -





              end;
            
              EndCadastro = array[1..50] of ^cadastro;
            
            VAR
              X: EndCadastro;
            
                 Antes de  se tentar  atribuir  um  valor  para  X,  uma
            pergunta: por  que se usar apontadores neste caso?  s para
            confundir o  leitor com tantos "^" ? Resposta: ningum sabe.
            Talvez seja  porque assim como est definido, X gasta apenas
            200 bytes  da memria  (X   formado por 50 endereos e cada
            endereo gasta  4 bytes).  Se retirssemos  todos os  "^" do
            bloco TYPE  acima, ento X passaria a gastar 27750 bytes! Um
            nico string  ocupa 256 bytes, mais do que todo o X definido
            com os "^".
                 X   um vetor  de endereos  X[1],  ...,  X[50],  cujos
            contedos so  X[1]^, ..., X[50]^, cada um sendo um registro
            do tipo  "cadastro". Podemos  ter acesso aos campos de X[i]^
            escrevendo-se  X[i]^.nome,   X[i]^.matricula,  X[i]^.salario
            (que     um  endereo,   cujo  contedo  real    dado  por
            X[i]^.salari) e X[i]^.residencia (que tambm  um endereo
            cujo  contedo  X[i]^.residenci    um  registro  do  tipo
            endereo. Vamos "preencher", por exemplo, os dados de X[1]:
            
                 X[1]^.nome := 'Euler Rodrigues de Freitas';
                 X[1]^.matricula := '30.123-X';
                 X[1]^.salario^ := 500000;
                 X[1]^.residencia^.rua := 'Av. 26 de Maio';
                 X[1]^.residencia^.numero := 125;
                 X[1]^.residencia^.cidade := 'Catole do Rocha';
                 X[1]^.residencia^.estado := 'PB';
            
            Exemplo: As  funes do  Pascal  no  podem  retornar  tipos
            compostos como  arrays ou  registros. No entanto, elas podem
            retornar endereos  de quaisquer  tipos. A seguir, definimos
            uma funo  que calcula  a transposta  de uma  matriz 3 x 3.
            Outra vantagem  do uso  de apontadores   que  se passa para
            essa funo  apenas um  dado: o  endereo de  uma matriz  ao
            invs dos 9 elementos da matriz.
            
            TYPE
              matriz = array [1..3, 1..3] of integer;
              MatPtr = ^matriz;
            
            FUNCTION Transposta(m: MatPtr): MatPtr;
            
            VAR
              i, j: byte;
              aux: matriz;
            
            BEGIN
              for i := 1 to 3 do
                for j := 1 to 3 do

                                       - 161 -





                  aux[i, j] := m^[j, i];
              Transposta := @aux; (* Transposta  o endereo de AUX *)
            END;
            
                 Como  exemplo  do  uso  da  funo  anterior,  temos  o
            programa a  seguir, supondo  que a  funo est  definida em
            algum lugar do programa, por exemplo, antes do bloco CONST.
            
            CONST
              mat: matriz = ((1, 2, 3), (-2, 0, 8), (1, -7, 0));
            
            VAR
              m_transp: MatPtr;
              aux: matriz;
            
            BEGIN
              m_transp := Transposta(@mat);
              aux := m_transp^; (* AUX  a transposta da matriz MAT *)
            END.
            
                 Se  o   programa  usar   muitos  apontadores,  ento  
            necessrio reservar previamente o espao da memria que eles
            vo ocupar.  Para se fazer isso, deve-se usar o comando NEW.
            Para se  liberar um espao reservado (e no mais necessrio)
            usa-se um DISPOSE.
            
                 NEW(p)     ---> Aloca memria para o apontador p.
            
                 DISPOSE(p) ---> Libera o espao alocado para p.
            
            Observao: O  Pascal tem  um apontador pr-definido chamado
            NIL que corresponde a nenhum endereo da memria. NIL  til
            para se  usar como  "chave", para  sinalizar alguma situao
            improvvel ou alguma opo invlida.
            
            10.5. MENUS          
            
                 O  objetivo   desta  seo     construir  um  programa
            orientado por  um menu  de 5  tens.   comum em um programa
            desse tipo que a seleo de um tem seja feita com as teclas
            de seta  e ENTER.  Tambm   comum o  salvamento de telas em
            matrizes na  memria para  serem depois recuperadas de forma
            ultra-rpida, dando  a impresso  de se  ter uma  tela  "por
            baixo" de outra.
                 Quase todo  menu usa READKEY e um CASE para executar os
            comandos selecionados.  S  que  tem  um  problema:  ReadKey
            retorna um  valor nulo (#0) para todas as teclas de funes,
            as teclas  de seta e combinaes de teclas envolvendo CTRL e
            ALT. Nestes  casos, deve-se  fazer  uma  segunda  chamada  a
            ReadKey para  que ela  retorne um chamado "cdigo estendido"
            da tecla e, assim, a tecla pressionada seja identificada.
            

                                       - 162 -





            Exemplo: O presente exemplo mostra o cdigo associado a cada
            tecla pressionada.  A execuo   encerrada  pressionando-se
            ESC.
            
            PROGRAM Teclado;
            
            (* Mostra os cdigos das teclas pressionadas *)
            
            USES
              Crt;
            
            VAR
              ch: char;
            
            BEGIN
              repeat
                Writeln;
                ch := ReadKey;
                Writeln('Cdigo da tecla: ', Ord(ch));
                if (ch = #0) then  (* Se for  pressionada  uma tecla *)
                begin              (* especial (setas, F1, F2, etc.) *)
                  ch := ReadKey;   (* ento  necessrio uma segunda *)
                                   (* chamada   a  READKEY  para  se *)
                                   (* descobrir  o cdigo estendido. *)
                  Writeln('Cdigo estendido: ', Ord(ch))
                end;
              until ch = #27;
            END.
            
                 A tabela  a  seguir,  fornece  o  cdigo  estendido  de
            algumas teclas especiais ou combinaes de teclas:
            
            Tecla       Cd.   Tecla Cd.   Tecla   Cd.     Tecla  Cd.
            ------------------------------------------------------------
            Seta p/cima  72    F1    59     Ctrl-F1   94     Alt-F1  104
            Seta p/baixo 80    F2    60     Ctrl-F2   95     Alt-F2  105
            Seta p/esq.  75    F3    61     Ctrl-F3   96     Alt-F3  106
            Seta p/dir.  77    F4    62     Ctrl-F4   97     Alt-F4  107
            PgUp         73    F5    63     Ctrl-F5   98     Alt-F5  108
            PgDn         81    F6    64     Ctrl-F6   99     Alt-F6  109
            Home         71    F7    65     Ctrl-F7  100     Alt-F7  110
            End          79    F8    66     Ctrl-F8  101     Alt-F8  111
            Ins          82    F9    67     Ctrl-F9  102     Alt-F9  112
            Del          83    F10   68     Ctrl-F10 103     Alt-F10 113
            ------------------------------------------------------------
            
                 O fragmento  a seguir  identifica qual  a tecla de seta
            pressionada:
                           ...
                           ch := ReadKey;
                           if (ch = #0) then
                           begin
                             ch := ReadKey;
                             case ch of

                                       - 163 -





                               #72 : Writeln('Seta para cima.');
                               #80 : Writeln('Seta para baixo.');
                               #75 : Writeln('Seta para a esquerda.');
                               #77 : Writeln('Seta para a direita.');
                             end;
                           end;
            
            Exemplo: Alguns caracteres ASCII so ideais para se desenhar
            quadros ou  molduras, tanto  com barras  simples quanto  com
            barras  duplas.  Para  uma  moldura  com  barra  simples,  
            conveniente se  usar os  caracteres #218,  #196, #191, #192,
            #179  e  #217.  E,  para  um  quadro  em  barras  duplas,  
            conveniente usar  #201, #205,  #187, #200,  #186 ou  o #188.
            Grave no  disco o  bloco de declaraes CONST a seguir sob o
            nome de CAR.INC.
            
            
            CONST
            (*
              Caracteres para o desenho de moldura em barras simples
             *)
              SupEsq1 = #218;  (* Canto superior esquerdo *)
              Hor1    = #196;  (* Trao horizontal        *)
              SupDir1 = #191;  (* Canto superior direito  *)
              InfEsq1 = #192;  (* Canto inferior esquerdo *)
              Ver1    = #179;  (* Trao vertical          *)
              InfDir1 = #217;  (* Canto inferior direito  *)
            (*
              Caracteres para o desenho de moldura em barras duplas
             *)
              SupEsq2 = #201;  (* Canto superior esquerdo *)
              Hor2    = #205;  (* Trao horizontal        *)
              SupDir2 = #187;  (* Canto superior direito  *)
              InfEsq2 = #200;  (* Canto inferior esquerdo *)
              Ver2    = #186;  (* Trao vertical          *)
              InfDir2 = #188;  (* Canto inferior direito  *)
            
                 O programa  a seguir,  desenha  na  tela  um  retngulo
            usando alguns dos caracteres mencionados acima.
            
            PROGRAM Retangulo;
            
            {$I CAR.INC}
            
            BEGIN
              Writeln(SupEsq1, Hor1, Hor1, Hor1, SupDir1);
              Writeln(Ver1, ' ':3, Ver1);
              Writeln(Ver1, ' ':3, Ver1);
              Writeln(Ver1, ' ':3, Ver1);
              Writeln(InfEsq1, Hor1, Hor1, Hor1, InfDir1)
            END.
            
            Exemplo: O  procedimento DESENHAMOLDURA  a seguir desenha na
            tela, na  posio especificada  como parmetro, um quadro em

                                       - 164 -





            barras duplas, de largura e altura tambm especificadas como
            parmetro.  Incorpore   ao   arquivo   MENU.INC   mencionado
            anteriormente a  funo REPETE  definida  no  Captulo  5  e
            tambm este procedimento.
            
            
            PROCEDURE DesenhaMoldura(linha, coluna, largura,
                                                          altura: byte);
            
            (* Desenha uma moldura em barras duplas iniciando na posicao
               (linha, coluna) e largura e altura especificadas       *)
            
            {$I CAR.INC}
            
            VAR
              i: byte;
            
            BEGIN
              GoToXY(coluna, linha);
              (* Desenha a parte superior da moldura *)
              Writeln(SupEsq2, Repete(largura, Hor2), SupDir2);
            
              (* Desenha as barras verticais *)
              for i := 1 to altura do
              begin
                GoToXY(coluna, linha + i);
                Write(Ver2, Repete(largura, ' '), Ver2);
              end;
            
              GoToXY(coluna, linha + i + 1);
              (* Desenha a parte inferior da moldura *)
              Writeln(InfEsq2, Repete(largura, Hor2), InfDir2);
            END;
            
            Exemplo: O procedimento a seguir desenha na tela um menu de
            5 tens. Para escolher um tem deve-se destac-lo usando-se
            as setas para cima ou para baixo e pressionar ENTER.
            
            PROCEDURE Menu(VAR op, vez: byte;
                                        VAR TelaDoMenu: matriz_da_tela);
            
            (* Mostra na tela um menu de barra. A variavel OP informa
            qual o item selecionado ao programa principal e VEZ guar-
            da a quantidade de vezes em que "Men for chamado.       *)
            
            CONST
              TeclaENTER = #13;   (* Caracter gerado pela tecla ENTER *)
            
              SetaParaCima = #72;     (* Codigos estendidos das setas *)
              SetaParaBaixo = #80;
            
              itens = 5;  (* Quantidade de itens do menu *)
            
              CorTexto = 7;  (* Cores do menu *)

                                       - 165 -





              CorFundo = 2;
            
              ColunaInicial = 33; (* Posicao do menu na tela *)
              LinhaInicial = 9;
            
              (* Mensagens a serem colocadas sobre a moldura *)
              Mensagem1 = ' M E N U ';
              Mensagem2 = ' Use as setas '+ #24 +  ', ' + #25 +
                                              ' e ENTER para escolher ';
            
              (* Linhas em que as opcoes do menu vao ser impressas *)
              Linha: array[1..itens] of byte =
            
                    (LinhaInicial, LinhaInicial + 2, LinhaInicial + 4,
                     LinhaInicial + 6, LinhaInicial + 8);
            
              (* Matriz com os nomes das opcoes do menu *)
              Opcao: array[1..itens] of string[40] =
            
                            ( '     Tela 1     ',
                              '     Tela 2     ',
                              '     Tela 3     ',
                              '     Tela 4     ',
                              '    Encerrar    ' );
            
              (* ---------------------------------------------------- *)
            
                  PROCEDURE DesenhaBarra(op: byte);
            
                  (* Destaca um item do menu *)
            
                  BEGIN
                    GoToXY(ColunaInicial, linha[op]);
                    Cor(0, CorTexto);
                    Write(opcao[op]);
                  END;
            
              (* ---------------------------------------------------- *)
            
                  PROCEDURE ApagaBarra(op: byte);
            
                  (* Desmarca um item destacado *)
            
                  BEGIN
                    GoToXY(ColunaInicial, linha[op]);
                    Cor(CorTexto, CorFundo);
                    Write(opcao[op]);
                  END;
            
              (* ---------------------------------------------------- *)
            
            VAR
              i: byte;
              ch: char;

                                       - 166 -





            
            BEGIN  (* Inicio do procedimento MENU *)
            
              Inc(vez);
            
              if vez = 1 then  (* Se o procedimento  estiver  sendo *)
              begin            (* chamado pela primeira vez, a tela *)
                               (*  inteiramente construida        *)
                Cor(CorTexto, 0); ClrScr;
                Cor(CorTexto, CorFundo);
                DesenhaMoldura(7, 10, 60, 11);
                GoToXY(ColunaInicial + 4, linha[1] - 2);
                Write(mensagem1);
                for i := 1 to itens do
                begin
                  GoToXY(ColunaInicial, linha[i]);
                  Write(opcao[i]);
                end;
                GoToXY(ColunaInicial - 12, linha[itens] + 2);
                Write(mensagem2);
              end
              else
                Move(TelaDoMenu, tela, 4000);  (* A partir da segunda *)
                     (* vez em que o procedimento for chamado, a tela *)
                     (* nao ser mais reconstruida, ser restaurada *)
                     (* a partir da TELADOMENU previamente salva.     *)
            
              DesenhaBarra(op);    (* Destaca o item OP *)
            
              repeat
                ch := ReadKey;
            
                if ch = #0 then
                begin            (* Se for pressionada  uma  tecla *)
                  ch := ReadKey; (* especial (como as setas ), en- *)
                                 (* tao deve-se fazer uma 2a. cha- *)
                                 (* mada a READKEY para  verificar *)
                                 (* qual o cdigo estendido        *)
                  case ch of
            
                 (* Se for pressionada a seta para cima, o destaque
                    atual  apagado. Se op = 1,  ento deve ser des-
                    tacado o ltimo item (op = 5). Nos demais casos
                    (op <> 1), o tem que deve passar a  ser o tem
                    destacado  o tem anterior,  ou seja,  deve-se
                    decrementar op de  uma  unidade  e  destacar  o
                    item associado ao novo valor de op.             *)
            
                    SetaParaCima:  begin
                                     ApagaBarra(op);
                                     if (op = 1) then
                                       op := 5
                                     else
                                       Dec(op); (* op := op - 1 *)

                                       - 167 -





                                     DesenhaBarra(op);
                                   end;
            
                 (* Se for pressionada a seta para baixo,  o destaque
                    atual  apagado. Se op = 5,  ento deve  ser des-
                    tacado o primeiro item (op = 1). Nos demais casos
                    (op <> 5), o tem que deve passar  a  ser  o tem
                    destacado  o tem  seguinte,  ou  seja,  deve-se
                    incrementar  op  de  uma  unidade  e  destacar  o
                    item associado ao novo valor de op.               *)
            
                    SetaParaBaixo: begin
                                     ApagaBarra(op);
                                     if (op = itens) then
                                       op := 1
                                     else
                                       Inc(op); (* op := op + 1 *)
                                     DesenhaBarra(op);
                                   end;
                  end; (* fim do CASE *)
                end; (* fim do IF ch ... *)
              until (ch = TeclaENTER);
            
              (* Salva a tela do menu em TELADOMENU *)
              Move(tela, TelaDoMenu, 4000)
            END; (* Fim do procedimento MENU *)
            
            
            Exemplo:  O  programa  a  seguir  usa  vrios  procedimentos
            construdos anteriormente,  inclusive o  procedimento "Menu"
            anterior que deve ter sido includo no arquivo MENU.INC.
            
            
            PROGRAM Menu_de_barra;
            
            (* ====================================================== *)
            (*                        MENU.PAS                        *)
            (* ====================================================== *)
            
            USES
              Crt;
            
            {$I MENU.INC}
            
            VAR
              linha_do_cursor, op, vez, k: byte;
              TelaDoMenu, TelaDoDos, m1, m2, m3, m4: matriz_da_tela;
              pausa: char;
              titulo: string;
            
            BEGIN (* Inicio do programa principal *)
              vez := 0; (* Quantidade de vezes em que o procedimento
                           "Menu" e' chamado *)
            

                                       - 168 -





              (* Salva a tela do DOS em TELADODOS e a linha do cursor *)
              Move(tela, TelaDoDos, 4000);
              linha_do_cursor := WhereY;
            
              (* ---------------------------------------------------- *)
            
              (* Desenha as telas que serao apresentadas como opcoes *)
              Cor(7, 5); ClrScr;
            
              (* Constroi a primeira tela e salva-a em M1 *)
              titulo := ' Tela 1';
              for k := 1 to Length(titulo) do
                MostraCaracter(Ord(titulo[k]), 10, 3 + 9*(k - 1));
              Move(tela, m1, 4000);
            
              (* Constroi a segunda tela e salva-a em M2 *)
              titulo := ' Tela 2';
              for k := 1 to Length(titulo) do
                MostraCaracter(Ord(titulo[k]), 10, 3 + 9*(k - 1));
              Move(tela, m2, 4000);
            
              (* Constroi a terceira tela e salva-a em M3 *)
              titulo := ' Tela 3';
              for k := 1 to Length(titulo) do
                MostraCaracter(Ord(titulo[k]), 10, 3 + 9*(k - 1));
              Move(tela, m3, 4000);
            
              (* Constroi a quarta tela e salva-a em M4 *)
              titulo := ' Tela 4';
              for k := 1 to Length(titulo) do
                MostraCaracter(Ord(titulo[k]), 10, 3 + 9*(k - 1));
              Move(tela, m4, 4000);
            
              (* ---------- Fim das construcoes de telas ------------ *)
            
              op := 1; (* Valor inicial do item selecionado no menu *)
              repeat
                Menu(op, vez, TelaDoMenu);
                Move(tela, TelaDoMenu, 4000); (* Salva a tela do menu *)
            
                case op of
                  1 : (* Executa a acao associada 'a opcao 1: *)
                      begin
                         Move(m1, tela, 4000); pausa := ReadKey;
                      end;
                  2 : (* Executa a acao associada 'a opcao 2: *)
                      begin
                         Move(m2, tela, 4000); pausa := ReadKey;
                      end;
                  3 : (* Executa a acao associada 'a opcao 3: *)
                      begin
                         Move(m3, tela, 4000); pausa := ReadKey;
                      end;
                  4 : (* Executa a acao associada 'a opcao 4: *)

                                       - 169 -





                      begin
                         Move(m4, tela, 4000); pausa := ReadKey
                      end
                end;
              until (op = 5); (* Encerra com a opcao 5 *)
            
              (* Restaura a tela do MS-DOS salva em TELADODOS *)
              GoToXY(1, linha_do_cursor);
              Move(TelaDoDos, tela, 4000);
            
            END. (* Fim do programa *)
            
            
            10.6. EXERCCIOS    
            
            1) D  exemplo de  atribuies para as variveis a, b, c e d
            abaixo:
            
                      TYPE
                        nomes = array [1..3] of ^string;
                        NomesPtr = ^nomes;
            
                        ponto = record
                          x, y, z: real
                        end;
            
                        esfera = record
                          centro: ponto;
                          raio: real
                        end;
            
                        piramide = array [1..5] of ^ponto;
                        cubo = array [1..8] of ^ponto;
            
                        figuras = record
                          sol1: ^piramide;
                          sol2: ^cubo;
                          sol3: ^esfera
                        end;
            
                      VAR
                        a: nomes;
                        b: NomesPtr;
                        c: piramide;
                        d: figuras;
            
            2) Elabore dois procedimentos, GravaTela(ARQ) e LeTela(ARQ),
            que grave  e leia  do arquivo ARQ do disco os 4000 bytes que
            formam uma tela no modo texto.
            
            
            
            
            

                                       - 170 -
