Turbo Pascal Forever – Begin

Quem acompanhou o ultimo post da série Turbo Pascal Forever, já teve um primeiro contato com os comandos do editor do Turbo, bem como já aprendeu como é a estrutura de um programa escrito em Pascal.

A partir desse post vamos começar a entrar em detalhes sobre as estruturas da linguagem Pascal e aos poucos vou introduzindo conceitos avançados sobre essa poderosa linguagem que torna possível o desenvolvimento de softwares básicos como sistemas operacionais, drivers de acesso a dispositivos e também jogos, que é o forte de máquinas como o MSX, por exemplo.

A minha meta é liberar durante o ano o código que tenho escrito para o MSX, sob a licença GPL V3, no meu repositório de Software Open Source para máquinas old skool, o Old Skool Tech, dentre esses códigos estão drivers de acesso a IDE do MSX (Sunrise-like), implementação de FAT16/32, Frameworks de testes unitários, bibliotecas de acesso a funções do MSX-DOS, MSX BIOS e também uma parte do software que eu estava desenvolvendo em 1995, o MSX Disk Doctor (MSXDD), já redesenhado para utilizar as novas tecnologias existentes, tudo isso escrito em Pascal, utilizando técnicas avançadas da linguagem, dentre elas a integração com hardware utilizando opcodes do Z80.

Mas antes disso o curso deverá estar em um estágio mais avançado do que estamos agora…..então vamos, sem muita “enrolação“, avançar. :).

Símbolos

O vocabulário básico aceito pelo Turbo Pascal consiste de símbolos básicos divididos entre letras, números e símbolos especiais.

  • Letras : A..Z, a..z e _ (underscore);
  • Digitos: 0..9;
  • Símbolos especiais: * – * / = ^ < > ( ) [ ] { } . , : ; ‘ # $

Lembrando que o Turbo Pascal não existe distinção entre maiúsculas e minúsculas, sendo reconhecida como uma linguagem case-insensitive, quanto ao uso de seus identificadores.

Operadores

  • Atribuição (:=) Operador básico de atribuição de valores a variáveis;
  • Inicialização de constantes (=) Operador de inicialização de constantes;
  • Lógicos (< <= > >= = <> and or xor not) Operadores utilizados para testes lógicos e condicionais. Lembrando que o operador =, além de inicialização de constantes é também utilizado como operador lógico de comparação de igualdade, sendo o seu inverso, o diferente, <>.
  • Aritméticos (* + – / div mod and or xor not) Operadores utilizados em operações matemáticas que vão desde somas até operações de divisão e multiplicação;
  • Unário (-) Operador unário utilizado em qualquer tipo de dado numérico, inteiro ou de ponto flutuante, para negação do operando;
  • Bit a bit (shl shr and or not xor) Operadores utilizados na manipulação de bits, envolvendo operações booleanas e deslocamento de bits;
  • Delimitador de faixa (..) O delimitador de faixa é utilizado basicamente para definir os limites de arrays e conjuntos;

Palavras reservadas

A linguagem C tem a fama de, com aproximadamente apenas 40 palavras reservadas, proporcionar ao programador escrever desde a mais simples biblioteca para exibir textos na tela, até o mais complexo sistema operacional. A linguagem Pascal, através do Turbo Pascal também possibilita ao programador, com as suas 48 palavras reservadas, fazer exatamente o mesmo que seria feito em C, por exemplo, o que a torna uma das mais poderosas linguagens de alto nível existentes, senão a mais poderosa, lembrando que C é considerada de médio nível.

Abaixo segue a lista de palavras reservadas de Turbo Pascal, até a versão 3.

*absolute, and, array, begin, case, const, div, do, downto, else, end, *external, file, forward, for, function, goto, *inline, if, in, label, mod, nil, not, *overlay, of, or, packed, procedure, program, record, repeat, set, *shl, *shr, *string, then, type, to, until, var, while, with, *xor.

(*) Não definidos no Pascal padrão, sendo extensões do Turbo Pascal e algumas, senão todas,  aceitas em outros compiladores Pascal mais modernos como o FreePascal e até o Delphi.

Delimitadores

Os elementos da linguagem Pascal podem ser separados por pelo menos um dos seguintes delimitadores.

  1. Espaço em branco;
  2. Final de linha;
  3. Comentário (* *) ou { };

TIP
Uma observação importante sobre as linhas de um programa no Turbo Pascal é quanto ao número máximo de caracteres aceito pelo editor do Turbo Pascal. O tamanho máximo aceito pelo editor de Turbo Pascal é 127 caracteres, sendo os caracteres subsequentes ignorados pelo compilador, entretanto se o programador escrever o seu código em outros editores que suportem linhas com mais de 127 caracteres, o mesmo será perfeitamente compilado no Turbo Pascal, sendo os caracteres excedentes aos 127 possíveis, automaticamente quebrados para a próxima linha no editor do Turbo Pascal.

Tipos escalares padrão

O Turbo Pascal 3 possui um conjunto bem poderoso de tipos built-in para uma linguagem do inicio da década de 80, onde o poder computacional e de memória das máquinas era bem limitado, onde esses recursos eram gerenciados praticamente a conta-gotas.

Abaixo descrevo os principais tipos escalares do Turbo Pascal 3:

  • Byte – Tipo de dado inteiro, ocupando 1 byte de memória e aceitando a faixa de valores entre 0 até 255.
  • Integer – Tipo de dado inteiro, ocupando 2 bytes na memória e aceitando a faixa de valores entre -32768 até 32767.
  • Real – Tipo de dado numérico capaz de aceitar valores em ponto flutuante, ocupando 6 bytes na memória e aceita valores na faixa entre 1E-38 até 1E+38 com a mantissa acima de 11 digitos significantes.
  • Boolean – Tipo de dado lógico que aceita valores lógicos para especificar estado de ligado/desligado ou verdadeiro/falso de uma determinada condição lógica. Os valores possíveis para o tipo booleano são especificados pelos identificadores padrão, true (verdadeiro) ou false (falso). O tipo Boolean ocupa 1 byte na memória.
  • Char – Tipo que representa um caractere da tabela ASCII da máquina. Os caracteres ASCII são ordenados de acordo com uma tabela especificada pela American Standard Code for Information Interchange, presente na BIOS/ROM da grande maioria dos computadores até os dias atuais. Os valores possíveis são quaisquer um da tabela ASCII (ex: ‘A’, ‘B’, …), sempre especificados entre ‘ ‘. O tipo Char ocupa 1 byte na memória.

Strings

Turbo Pascal implementou o conceito de strings, diferente do Pascal padrão que não possuía essa característica, sendo similar a linguagens como C padrão que tratam arrays de caracteres como strings.

A definição de string pode ser descrita como um conjunto de caracteres individuais concatenados entre ‘ ‘ (apóstrofos), conforme pode ser visto no exemplo abaixo.

‘Esse é um exemplo de String’

Algumas observações sobre strings em Turbo Pascal 3 são feitas a seguir:

  • Strings devem ter seu tamanho definido sempre no Turbo Pascal 3, ao contrário das Strings definidas a partir do Turbo Pascal 4, onde as mesmas já tem o tamanho de 255 caracteres caso não seja especificado seu tamanho;
  • Não é possível definir o tipo string explicitamente nos parâmetros de uma sub-rotina em Turbo Pascal 3, sendo necessário definir um tipo baseado em string com um tamanho especificado e especificar esse novo tipo no parâmetro da sub-rotina em que se deseja passar uma string por parâmetro;
  • Strings são compatíveis com arrays de char do mesmo tamanho;
  • Um caractere de uma string pode ser acessado através de seu índice na string. Lembrando que o indice 0 (zero) de qualquer string contém seu tamanho;
  • Constantes de strings são compatíveis com todos os tipos de strings (qualquer tamanho);
  • Strings também podem conter caracteres de controle concatenados (ex: #13#10 (Carriage Return\Line Feed), #$1B (Escape -Hexa), ^G (Control+G – Bell);

Let’s code.

Passados os detalhes, importantes porém na maioria das vezes chatos, vamos à parte que realmente interessa que é a mão-na-massa.

Podemos definir o programa em Pascal como sendo uma estrutura em camadas e que podem ser melhor compreendidas visualmente conforme a figura abaixo.

Estrutura de um programa em Pascal

Conforme já foi dito no post anterior, o cabeçalho ou Program Head é puramente opcional e geralmente é utilizado para identificar o programa desenvolvido;

Na sequência temos a área de declarações, que é onde definimos as variáveis globais do programa principal, novos tipos de dados, constantes e labels. Vamos a explicação de cada um desses itens.

Definição de labels.

Ao contrário de linguagens desestruturadas como MSX-BASIC, ou pouco estruturadas como Assembly, o Pascal possui um rico conjunto de estruturas de controle que praticamente eliminam a possibilidade de artifícios que tornam os programas pouco compreensíveis e ilegíveis, sendo possíveis de manutenção praticamente apenas pelo programador que fez o programa, como é o caso do famoso e combatido comando de salto incondicional, GOTO dos BASIC clássicos (incluindo o MSX-BASIC).

Apesar de conter toda estrutura de controle que torna o goto inválido e inútil, o Pascal inclui a possibilidade de saltos incondicionais através do comando goto presente no núcleo da linguagem.

Entretato apesar de incluir esse comando que de certa forma torna possível que os programas fiquem desestruturados, o Pascal o faz de uma maneira até que estruturada, através de labels que na realidade são marcadores  que identificam o ponto de salto incondicional. Abaixo um exemplo do uso de labels com o comando goto.

Program Test_Label;
 
Label never_use_goto;
 
Begin { Main Block }
  goto never_use_goto;
  WriteLn( 'This never will be executed' );
 
  never_use_goto:
  WriteLn( 'Tadaaaa !! It works like MSX-BASIC......very trash X.D' );
End.

Definição de Constantes

Em Pascal temos a possibilidade de definir constantes que, resumidamente, é a forma de associar valores a identificadores que não poderão ser mudados nem redefinidos no mesmo escopo do programa, entretanto dentro do escopo de outras sub-rotinas podemos definir constantes com o mesmo nome de identificadores existentes em outros escopos do programa.

Outra curiosidade sobre constantes em Pascal é que as mesmas podem ser definidas de maneira implicitamente e explicitamente tipadas. No caso de constantes implicitamente tipadas, a constante assume o tipo do r-value que está sendo associado ao identificador (l-value) (ex: ‘isso é uma string’ é considerado como uma constante do tipo String, o valor 1000 é considerado como uma constante do tipo Integer e 3.14 do tipo Real).

Segue abaixo um exemplo do uso de constantes.

Program Test_Const;
 
Const
  ctTestStr = 'This is a string constant'; { Implicit typed constant }
  ctTestStr2 : String[50] = 'This is another explicit typed string constant';
  ctInteger = 1000; { Implicit typed constant }
  ctInteger2 : Integer = 2000; { Explicit typed integer constant }
  ctPiReal = 3.14159265; { Implicit typed constant }
  ctRealPi : Real = 3.14159265; { Explicit typed real constant }
 
Begin   { Main Block }
  Writeln( ctTestStr );
  WriteLn( ctTestStr2 );
  WriteLn( ctInteger );
  WriteLn( ctInteger2 );
  WriteLn( ctPiReal );
  WriteLn( ctRealPi );
End.

Definição de tipos

Conforme já foi citado anteriormente, a linguagem Pascal é fortemente tipada, ou seja, considerando as devidas compatibilidades e capacidades dos tipos, a linguagem é extremamente rígida quanto a passagem de parâmetros e entre tipos de variáveis diferentes, por exemplo, caso seja feita a tentativa de atribuir uma variável do tipo Real para alguma do tipo inteiro (Integer ou Byte), ocorrerá um erro de compilação. Apesar disso ainda é possível fazer essa atribuição utilizando uma das funções de conversão entre tipos, existente na linguagem mas isso tem um custo de processamento e esses foi um dos motivos pelos quais a linguagem C se tornou mainstream na preferência dos programadores de sistemas de alta performance, pois a checagem de tipos de C é baixa e na maioria dos casos inexistente, o que pode trazer problemas para detectar bugs críticos mas por outro lado dá poder absoluto ao programador.

Entretanto a forte tipagem de Pascal a torna bastante segura uma vez que, para operações que não envolvem ponteiros e gerenciamento de memória, o programa compilado tem grandes chances de não ter bugs críticos uma vez que o compilador reclama para cada operação desconhecida e/ou suspeita.

Junto com a característica da forte tipagem de Pascal, a linguagem permite a definição de novos tipos baseados nos tipos built-in e também em outros tipos definidos pelo programador. A definição de novos tipos é muito importante e bem vinda em linguagens estruturadas e está presente na grande maioria das linguagens estruturadas modernas (não orientadas a objeto)…..bom, pelo menos nas duas melhores que são Pascal e C :). Um exemplo existente no compilador Turbo Pascal 3, é quanto ao uso do tipo String, pois até essa versão do compilador, principalmente pela baixa capacidade de memória das máquinas da época, uma variável do tipo String só poderia ser definida especificando um tamanho para a mesma.

Segue abaixo um exemplo de definição de uma variável do tipo String e também do uso de definição de tipos para suprir essa deficiencia.

Program Test_Type;
  Type TString = String[255]; { New string type defined with 255 chars size }
 
  Var st1 : String[13];       { String must be defined with size in TP3 }
      st2 : TString;          { String variable with no explicit defined size }
 
Begin  { Main Block }
  st1 := 'Test string 1';
  st2 := 'Test string 2';
End.

O exemplo acima serve para ilustrar o poder que Pascal tem para estender os tipos já existentes na linguagem, como no caso acima em que definimos um novo tipo TString com tamanho de 255 bytes. Mas como em qualquer área, é sempre bom utilizar as facilidades com muita parcimônia :). No exemplo acima foi atribuido uma String constante de 13 bytes (‘Test String 1’)  à variável st1, restando 242 bytes de memória, sem uso, ficando particamente perdidos.  Nesses casos vale o olhar clínico do programador, que com o tempo vai se tornando mais aguçado para remover esses desperdícios.

Lembrando também que o tipo String, a partir do Turbo Pascal 4, quando definido sem um tamanho específico assume a capacidade de 255 bytes, o que para o PC já não era muito em épocas passadas, mas nas plataformas de 8 bits com baixa capacidade de memória era algo considerável, talvez por isso tenha sido evitada a definição de tamanho padrão para o tipo String nos compiladores do Turbo Pascal para plataformas de 8 bits.

Definição de variáveis

Podemos definir variáveis exatamente da forma como escrevemos, ou seja, são áreas de memória onde seu conteúdo pode ser modificado mediante a uma instrução explicita do programa para que a mesma seja alterada. Variáveis, não importa o tipo, são representadas por bytes na memória da máquina sendo que para alguns tipos de dados a quantidade necessária de bytes para armazenar uma informação é maior e para outros menor.

Lembrando ainda sobre o conceito de tipagem de Pascal, ao contrário de linguagens onde uma variável criada pode assumir qualquer tipo de dados, variáveis em Pascal devem ter um tipo definido e além disso, devem ser definidas antes de seu uso em uma área dedicada do escopo de utilização. O fato de ser forçada a definição da variável antes de seu uso evita uma série de problemas futuros, a maioria deles relacionados a escopo de utilização das mesmas. Quem já tentou escrever grandes softwares utilizando linguagens com tipagem fraca, como o MSX-BASIC ou PHP, sabe do que estou falando pois a chance de conflito entre variáveis com mesmo nome, mas definidos em escopos diferentes é muito grande e termina, na maioria das vezes, levando a um aumento considerável de tempo nos casos de manutenção do código.

Em Pascal,  forma de se definir uma variável é através da palavra reservada Var, seguida do nome do identificador da variável : Tipo desejado;

Abaixo podemos ver exemplos de definições de variáveis e suas variações.

Program Test_Var_1;
 Var
      nInt, nInt2 : Integer;
      nByte       : Byte;
      fReal       : Real;
      bBoolean    : Boolean;
      strString   : String[20];
 
Begin  { Main Block }
  nInt  := 10;
  nInt2 := 20;
  nByte := 255;
  fReal := 1255.543;
  bBoolean  := True;
  strString := 'This is a string';
 
  WriteLn( nInt );
  WriteLn( nInt2 );
  WriteLn( nByte );
  WriteLn( fReal );
  WriteLn( bBoolean );
  WriteLn( strString );
End.

O código acima é um exemplo clássico de definição de variáveis simples, onde na área de variáveis do bloco de programa principal são declarados os identificadores das variáveis e seus tipos correspondentes, e no programa principal atribuimos valores a essas variáveis através do operador :=, conforme já foi explicado um pouco mais acima nesse mesmo post. Variáveis do mesmo tipo, podem ser separadas por virgula (,) no momento de sua definição.

Variáveis do tipo Array

Além da riqueza de tipos builtin e da possibilidade de definição de novos tipos, o Pascal tem também a possibilidade de construção e manipulação de arrays, uni e multi-dimensionais, baseado nos tipos nativos e também nos tipos definidos pelo usuário. Segue abaixo exemplos do uso de arrays em Pascal.

Program Test_Var_Array;
 Type TString = String[20];
 
 Var
      aInt    : Array[0..2] Of Integer;            { Unidimensional array }
      aString : Array[1..2] Of TString;            { Unidimensional array }
      aByte   : Array[0..1, 0..1] Of Byte;         { Multidimensional array }
      aReal   : Array[0..2] Of Array[0..2] Of Real;{ Multidimensional array }
 
Begin  { Main Block }
  { Multiples ways to access a array item }
  aInt[0] := 10;
  aInt[1] := 1000;
  aString[1] := 'Test string 1';
  aString[2] := 'Test string 2';
  aByte[0,0] := 11;
  aByte[0,1] := 12;
  aByte[1,0] := 13;
  aByte[1,1] := 14;
  aReal[0][0] := 1.1;
  aReal[0][1] := 2.1;
  aReal[1][0] := 1.1;
  aReal[1][1] := 2.1;
 
  WriteLn( aInt[0], aInt[1] );
  WriteLn( aString[1], aString[2] );
  WriteLn( aByte[0,0], aByte[0,0], aByte[0,1], aByte[1,0], aByte[1,1] );
  WriteLn( aReal[0,0], aReal[0,0], aReal[0,1], aReal[1,0], aReal[1,1] );
End.

O programa acima demonstra as diversas formas de se definir um variável do tipo array e também as formas de acessar seu conteúdo, sendo que vale a pena analisar as duas formas de definição de variáveis array multi-dimensionais.

      aByte   : Array[0..1, 0..1] Of Byte;         { Multidimensional array }
      aReal   : Array[0..2] Of Array[0..2] Of Real;{ Multidimensional array }
 A primeira mais comum em outras linguagens estruturadas e a segunda mais específica de Pascal. No código de acesso ao conteúdo dos itens de um array também é possível fazê-lo de duas formas, conforme pode ser visto no programa acima e que deixo destacado abaixo.
  aByte[1,1] := 14;
  aReal[0][0] := 1.1;

Quanto a definição de variáveis e manipulação de arrays podemos escrever um post inteiro maior do que esse inclusive mas acredito que já cobrimos bastante sobre o conceito de variáveis e seus modificadores. É claro que vou ampliar essa discussão no decorrer do tutorial, nos próximos posts.

Variáveis com tipos complexos ou compostos.

Conforme descrito no primeiro post da série, Pascal foi criado para ajudar no ensino de boas práticas estruturadas de programação e por isso reconhecemos um conjunto rico de estruturas presente na linguagem. Posso até afirmar que a linguagem Pascal na sua forma padrão é tão poderosa e eficiente que, desde o seu lançamento, teve poucas mudanças substanciais exceto no conceito de orientação a objetos que foi introduzido a partir do Turbo Pascal 5.5, mas se considerarmos o núcleo da linguagem, podemos afirmar que Niklaus Wirth foi tão genial quanto  Dennis Ritchie em sua criação.

Um exemplo da força das estruturas de dados em Pascal pode ser vista quando citamos a possibilidade de definição de tipos complexos ou tipos compostos, através de uma estrutura presente na linguagem, conhecida por Record ou registro, similar a struct, presente na linguagem C. Através de Record podemos agrupar variáveis de diversos tipos builtin e até outros tipos definidos pelo usuário, conforme pode ser visto no exemplo abaixo.

Program Test_Var_Complex_Types;
 Type TString = String[20];
 Type TRecordSample = Record
        f1 : Byte;      { Record field with builtin type }
        f2 : Real;
        f3 : TString;   { Record field with user defined type }
      End;
 
 Var
      Rec1      : Record
                    f1 : Byte;      { Record field with builtin type }
                    f2 : Real;
                    f3 : TString;   { Record field with user defined type }
                  End;
      Rec2      : TRecordSample;
 
Begin  { Main Block }
  Rec1.f1 := 1;
  Rec1.f2 := 1.1;
  Rec1.f3 := 'Test string record 1';
 
  Rec2.f1 := 1;
  Rec2.f2 := 2.1;
  Rec2.f3 := 'Test string record 2';
 
  WriteLn( Rec1.f1, Rec1.f2, Rec1.f3 );
  WriteLn( Rec2.f1, Rec2.f2, Rec2.f3 );
End.

No programa acima podem ser vistas as duas formas de definição de registros sendo a primeira, e mais indicada, a definição de um novo tipo que representa a estrutura a ser manipulada e o segundo modo definindo uma variável registro diretamente na declaração de tipo da própria variável, conforme pode ser visto no trecho de código retirado do programa acima, que reproduzo abaixo.

 Var
      Rec1      : Record
                    f1 : Byte;      { Record field with builtin type }
                    f2 : Real;
                    f3 : TString;   { Record field with user defined type }
                  End;

Essa abordagem pode ser uma opção mais complicada se considerarmos a manutenção de código no futuro, uma vez que pode ser necessária a alteração da estrutura e caso a mesma tenha sido replicada no decorrer do programa, com certeza o trabalho de manutenção será ampliado, sem contar que é uma prática pouco elegante.

Definição de procedures e funções

Nos primórdios da computação e engenharia de software tudo era mais arcaico, os conceitos de encapsulamento, hoje amplamente difundidos nas mais variadas tecnologias existentes, praticamente não existiam, tudo era global, tudo era visível e ao mesmo tempo invisível, podemos dizer que era o caos na terra :).

As linguagens estruturadas vieram para colocar ordem na casa e redefinir os conceitos de engenharia de software para os que conhecemos hoje. Pascal assume essa posição com maestria, principalmente quando percebemos que seu conceito de encapsulamento permitiu uma fácil transição para o paradigma da programação orientada a objetos.

Entre os conceitos de encapsulamento vamos citar primeiramente as procedures ou procedimentos. Procedures, são nada mais nada menos do que sub-rotinas cujo o conceito, no mundo de engenharia de software, é o mesmo que ter um sub-programa ou uma rotina menor dentro de um programa maior e que na maioria das vezes pode funcionar de maneira independente do programa maior, dessa forma, ao criar um programa e dividi-lo em sub-rotinas menores, diminuimos a complexidade do mesmo, encapsulando a resolução de problemas complexos em pequenas peças de software que inclusive poderão ser reaproveitadas no futuro.

Procedures tem a mesma estrutura do programa principal em Pascal, logo podemos ter áreas de declaração de variáveis, labels, constantes, tipos, e inclusive novas procedures e funções locais à procedure que está sendo criada. Então o mesmo diagrama de estrutura do programa principal, vale para procedures e functions, com algumas pequenas adequações de nomenclatura.

Estrutura de sub-rotinas em Pascal

Procedures, assim como o programa principal, pode receber parâmetros do módulo principal que o chamou, então podemos ter um conjunto de procedures e funções que recebem, ou não, parâmetros necessários para resolver o problema ao qual se propõe a resolver. Abaixo segue alguns exemplos de definições e chamadas de procedures.

Program Test_Procedure;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue );
End.

O exemplo acima ilustra bem o conceito de encapsulamento presente em Pascal, uma vez que a procedure define elementos com mesmo nome de identificadores presentes no escopo global e ainda assim cada variável e constante com nomes comuns tiveram seus valores devidamente preservados em seus respectivos contextos.

Abaixo segue um outro exemplo que demonstra uma procedure que contém outra procedure interna, ou seja, podemos definir diversos níveis de encapsulamento inclusive dentro de sub-rotinas, sejam procedures ou functions.

Program Test_Procedure2;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Byte;
 
 Var
       nValue : TMyType;
 
  { child Subroutine head - not optional }
  Procedure ChildProcedure( nGlobalConstValue : Integer );
  Const ctTest : Integer = 300;{ Local definition of same name global constant }
  Type  TMyType = Integer;
 
  Var
        nValue : TMyType;
 
  Begin       { Subroutine-child main block }
    nValue := ctTest;
    WriteLn( 'Subroutine ctTest constant value', nGlobalConstValue );
    WriteLn( 'Local ctTest constant value', nValue );
  End;
 
Begin { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
   ChildProcedure( nValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue );
End.

Nos exemplos anteriores todas as procedures recebem pelo menos 1 parâmetro sendo que para passar mais de 1 parâmetro basta seguir as seguintes regras:

  1. Identificadores de mesmo tipo podem ser separados por virgula(,);
  2. Identificadores de tipos diferentes devem, ser separados por ponto-e-virgula(;) logo após a definição do ultimo tipo;
Program Test_Procedure3;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue  : Integer;
 
 { Subroutine head - not optional }
 Procedure ThisIsATestProcedure( nGlobalConstValue, nNumber : Integer; fValue : Real );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   WriteLn( 'Global ctTest constant value', nGlobalConstValue );
   WriteLn( 'Local ctTest constant value', nValue );
   WriteLn( 'Global number value', nNumber );
   WriteLn( 'Global Real value', fValue );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  ThisIsATestProcedure( nValue, 10, 10.59 );
End.

Outro conceito interessante de Pascal e de muitas outras linguagens modernas é quanto a passagem de parâmetros. Existem duas formas de passagem de parâmetros para sub-rotinas em Pascal, a primeira é a passagem de parâmetros por valor, onde o conteúdo da variável passada por parâmetro não é alterado caso a sub-rotina altere essa variável internamente. A segunda é passagem de parâmetro por referência, que por sua vez possibilita a alteração do conteúdo da variável passada por parâmetro para uma sub-rotina. Abaixo podemos ver um exemplo que demonstra os dois casos.

Program Test_Procedure4;
 
 Const ctTest : Integer = 100;
 
 Var
      nValue, nValue2  : Integer;
 
 { Subroutine head - not optional }
 Procedure ValuePassingTest( nGlobalConstValue : Integer; Var nValue2 : Integer );
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 
 Begin       { Subroutine main block }
   nGlobalConstValue := ctTest;
   nValue2 := 2000;
 End;
 
Begin  { Main Block }
  nValue  := ctTest;
  nValue2 := 0;
  WriteLn( nValue2 );
  ValuePassingTest( nValue, nValue2 );
  WriteLn( nValue2 );
End.

O detalhe da passagem de parâmetro por referência está no uso da palavra Var, na definição da sub-rotina, como pode ser visto no código acima. O uso de var na especificação do parâmetro de entrada do protótipo da função, especifica que aquele parâmeto é possível de alteração pela sub-rotina.

Por ultimo vamos falar sobre as Functions, ou funções, que em Pascal seguem as mesmas regras de escopo e passagem de parâmetros das procedures sendo a única diferença que uma Function retorna valor à rotina chamadora. Abaixo o exemplo simples de implementação e chamada de uma Function.

Program Test_Function;
 
 Const ctTest : Integer = 100;
 Type  TString = String[20];
 
 Var
      nValue, nRes  : Integer;
 
 { Subroutine head - not optional }
 Function SumConstants( nValue1 : Integer ) : Integer;
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   SumConstants := ( nValue + nValue1 );
 End;
 
Begin  { Main Block }
  nValue := ctTest;
  nRes := SumConstants( nValue, 10, 10.59 );
  WriteLn( nRes );
End.

Ao contrário de outras linguagens, o Turbo Pascal 3 implementa uma forma peculiar de retorno de valores para a função chamadora, que é atribuir o valor ao nome da função definida, como podemos ver no código destacado abaixo.

 { Subroutine head - not optional }
 Function SumConstants( nValue1 : Integer ) ; Integer;
 Const ctTest : Integer = 200;{ Local definition of same name global constant }
 Type  TMyType = Integer;
 
 Var
       nValue : TMyType;
 
 Begin       { Subroutine main block }
   nValue := ctTest;
   SumConstants := ( nValue + nValue1 );
 End;

A função SumConstants assume o valor da operação matemática entre os dois operandos nValue e nValue1. Essa forma peculiar de retorno de valores de funções perdurou até o Turbo Pascal 7.0, sendo que a primeira versão do Delphi , padronizou a palavra reservada Result, para retornar valores de função, sendo que até hoje é possível retornar valores de função da mesma forma como faziam Turbo Pascal 3 e o Pascal padrão, até em compiladores Delphi e FreePascal mais modernos.

Uma observação é quanto a declaração do tipo a ser retornado pela função. Conforme pode ser visto no código acima, o tipo do retorno é especificado da mesma forma como se estivessemos definindo uma variável, ou seja, Function Nome_da_função : tipo a ser retornado.

Amigos vou fechando esse post, que ficou bem extenso porém como se trata de questões fundamentais da linguagem não poderia ser diferente, mas a partir do próximo post vou tratar de assuntos que podem ser encapsulados em sub-rotinas menores :), então por enquanto divirtam-se.

Até a próxima.

[]’s
PopolonY2k

Referência na internet

MSX-DOS (Wikipedia)
http://en.wikipedia.org/wiki/MSX-DOS

MSX BIOS (MSX Assembly pages)
http://map.grauw.nl/resources/msxbios.php

FAT (File allocation table)
http://en.wikipedia.org/wiki/File_Allocation_Table

GPL v3 (Wikipedia)
http://en.wikipedia.org/wiki/GPL#Version_3

Opcodes (Wikipedia)
http://en.wikipedia.org/wiki/Opcode

Z80 (Wikipedia)
http://en.wikipedia.org/wiki/Z80

Open Source Software (Wikipedia)
http://en.wikipedia.org/wiki/Open-source_software

PlanetaMessenger.org’s Old Skool Tech (Sourceforge.net)
http://sourceforge.net/projects/oldskooltech/

Anuncio do lançamento do Old Skool Tech (PopolonY2k Rulezz)
http://www.popolony2k.com.br/?p=377

Free Pascal (Official website)
http://www.freepascal.org/

Embarcadero Delphi
http://www.embarcadero.com/br/products/delphi

ASCII (Wikipedia)
http://en.wikipedia.org/wiki/ASCII

MSX-BASIC (Wikipedia)
http://en.wikipedia.org/wiki/MSX_BASIC

Assembly language (Wikipedia)
http://en.wikipedia.org/wiki/Assembly_language

GOTO Statement (Wikipedia)
http://en.wikipedia.org/wiki/Goto

Program scope (Wikipedia)
http://en.wikipedia.org/wiki/Scope_%28computer_science%29

Values r-value and l-value  (Wikipedia)
http://en.wikipedia.org/wiki/Value_%28computer_science%29

Object oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Object-oriented_programming

PHP (Wikipedia)
http://en.wikipedia.org/wiki/PHP

Turbo Pascal Forver – Introdução – PopolonY2k Rulezz
http://www.popolony2k.com.br/?p=1058

Encapsulation (Wikipedia)
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

Sub-routine (Wikipedia)
http://en.wikipedia.org/wiki/Subroutine

Evaluation strategy (Wikipedia)
http://en.wikipedia.org/wiki/Evaluation_strategy