LENGUAJES DE MAQUINA

 

Lenguaje de máquina

El lenguaje de máquina de una computadora consta de cadenas de números binarios (ceros y unos) y es el único que "entienden" directamente los procesadores. Todas las instrucciones preparadas en cualquier lenguaje de máquina tienen por lo menos dos partes. La primera es el comando u operación, que dice a la computadora cuál es la función que va a realizar. Todas las computadoras tiene un código de operación para cada una de sus funciones. La segunda parte de la instrucción es el operando, que indica a la computadora donde hallar o almacenar los datos y otras instrucciones que se van a manipular; el número de operandos de una instrucción varía en las distintas computadoras.

En el principio de la computación este era el lenguaje que tenía que "hablar" el ser humano con la computadora y consistía en insertar en un tablero miles de conexiones y alambres y encender y apagar interruptores.

Aunque en la actualidad ya no se emplea, es importante reconocer que ya no es necesario que nos comuniquemos en este lenguaje de "unos" y "ceros", pero es el que internamente una computadora reconoce o "habla".

COMO NACE UNA APLICACIÓN

Todas las "aplicaciones" (software para el "usuario final") que conocemos han debido ser creadas especificando primero sus funciones y traduciendo éstas en "instrucciones" comprensibles por la máquina.
Para ésto, el o los programadores utilizan un determinado "lenguaje de programación", que es como un idioma: cuenta con un diccionario (los "comandos") y una gramática (reglas de sintaxis). El programador traduce las especificaciones de funciones y operaciones (que están en su idioma natural y/o en gráficos que especifican su secuencia) en este idioma, elaborando un producto llamado "programa fuente".
Este programa fuente ha de ser luego traducido (por el mismo computador) en el "lenguaje de máquina", que es el que "entiende" el microprocesador. La traducción, por lo tanto, debe hacerse teniendo en cuenta el "lenguaje" propio del chip procesador (de ahí que un mismo programa tendrá una versión "para PowerPC (PPC)", "para Pentium", etc.
Esta traducción puede hacerse de dos maneras: en forma previa al uso, con lo cual se genera el "programa compilado" o "programa objeto", o bien se instala junto con el programa de traducción en el computador del usuario y se hace que la máquina traduzca las órdenes a medida que lee el programa fuente.
A diferencia de un programa interpretado, un programa compilado (como la mayoría de las aplicaciones que hoy puede comprar el usuario común) sólo puede ser utilizado, pero no puede ser leído, revisado o modificado: la compilación lo hace ilegible. Tratar de "decompilarlo" para conocer su estructura y modo de funcionar (lo cual se llama "reverse engineering") es muy complejo y normalmente prohibido en las condiciones de venta. Así, la versión "fuente" constituye un "secreto comercial", celosamente guardado por los fabricantes. Veremos, al final del capítulo, que este secreto puede ser algunas veces perjudicial para los usuarios. Pero antes hablaremos más detalladamente de los lenguajes de programación.
 

LENGUAJES DE BAJO Y ALTO NIVEL

En efecto, lo único que entienden y pueden manipular la Unidad de Control y la Unidad Aritmética son dígitos binarios o sea series de ceros y unos (paso o no paso de corriente eléctrica). Así, mientras el hombre usa un "lenguaje natural" (idioma) muy rico en significados, la máquina usa un sistema en que existiría un sólo "significado": la diferencia entre 0 y 1 (o sea un bit de información).
¿Cómo lograr más? Simplemente concibiendo un nuevo lenguaje constituido de "bloques" de dígitos binarios (llamados "bytes"). Este es el primer paso o "primer nivel" en la construcción de lenguajes de computación.

1er Nivel
Se dice que el código binario es de "bajo nivel" o "primer nivel" (porque al usar pocos signos logra muy difícilmente expresar cosas complicadas), mientras un lenguaje humano es de "muy alto nivel" (con una cantidad mayor de signos y con reglas combinatorias logra expresar con facilidad cosas muy complicadas). Todo el esfuerzo, entonces, para facilitar la comunicación del hombre con el computador, ha de centrarse en el desarrollo de lenguajes de mayor nivel.
El fabricante de un procesador fija los bloques de bits que llevarán a la CPU (unidad central de procesos) a reconocer y realizar diferentes operaciones. Este el el "código de máquina", primer lenguaje que la máquina puede interpretar y transformar en acciones. Pero es evidentemente muy difícil de usar para un ser humano. Supongamos que quiera hacer imprimir y para ello deba decir "10011101 11100010": ¿cómo recordar órdenes de este tipo y no equivocarse al escribirlas?
Prácticamente nadie trabaja hoy a este nivel, excepto los diseñadores de "chips" procesadores. Del mismo modo que es posible pasar de un sistema binario a un sistema decimal (más comprensible y más desarrollado en términos de signos legibles) es posible asociar a los bloques de bits no solo valores decimales sino también otros signos. Esto lleva a un segundo nivel de expresión.
Hemos de recordar que el teclado equivale a un conjunto de interruptores: cada tecla que pulsamos equivale a cerrar brevemente uno de éstos, es decir produciendo un BIT de información (no teclear = 0, teclear =1). Pero dado que hay muchas teclas, hay que identificar cada una, por lo cual pulsar una tecla significa activar un circuito que generará un "bloque" binario (byte) específico que identifica esa tecla.
A cada tecla está asociado un código decimal y un código hexadecimal. El hexadecimal (16 caracteres: de 0 a 9 y de la A a la F) es el que sirve de intermediario a la máquina, para traducir nuestro código natural (alfanumérico) al código binario. Algunos ejemplos de equivalentes decimales y hexadecimales del teclado son los siguientes:

 

Tecla

Valor Decimal

Valor Hexadecimal

1

49

31

2

50

32

9

57

39

a

97

61

b

98

62

i

105

69

j

106

6A

k

107

6B

o

111

6F

p

112

70

z

122

7A

!

33

21

?

63

3F

=

61

3D

Esto permite no sólo reconocer los signos del teclado y reproducirlos (como en un procesador de palabras), sino también atribuir a cada tecla otra función y hacer -por ejemplo- que transmita una orden a la CPU. Así, podríamos ordenar la impresión con un simple "?", o -como es común hoy en los Macintosh- apretando simultáneamente una tecla de "comando" y la letra "P".

2º Nivel
La creación de un lenguaje más comprensible por el hombre consiste por lo tanto en establecer la equivalencia de bloques binarios con signos de nuestro lenguaje habitual. Para permitir la programación (secuencia de comandos), se usan pequeños conjuntos de signos ("palabras") de fácil memorización, con las cuales se redactan programas, por ejemplo "ADC" significará "sumar con reserva" (en inglés: "ADd with Carry"). Este tipo de lenguaje se llama "ensamblador" o "Assembler" (Vea más adelante la "Descripción" de Lenguajes). La máquina misma hará la tarea de traducirlo en código binario, para seguir las instrucciones, gracias a otro programa cuya función es traducir la expresión humana en "lenguaje de máquina" (binario). Es programa se llama "compilador".
Aunque el Assembler es un inmenso progreso en relación al código binario, su desventaja reside en que permanece estrechamente ligado a los bloques binarios que reconoce la CPU (es decir al "hardware"). Para facilitar más la tarea, se han inventado lenguajes de "alto nivel", es decir más cercanos al modo de expresar del hombre que de operación de la máquina. Los primeros y más comunes son los llamados de "tercera generación", más fáciles de manejar y más independientes de las características técnicas de los procesadores. Ahora, hasta un aficionado puede llegar a redactar un programa, sin tener que preocuparse por el código binario o de ensamble: si un programa traductor podía resolver la transformación de bloques de signos en bloques binarios, era cosa de extender las habilidades del traductor para "enseñar" a la máquina cómo "entender" un lenguaje más complejo y agregar mecanismos automáticos de manejo de la memoria para poder utilizar lenguajes aún más comprensibles.

3er Nivel
El avance en el desarrollo de "compiladores" e "intérpretes" (los dos tipos de programas traductores) ha sido por lo tanto fundamental en el desarrollo de los lenguajes de "3º generación" cuyas ventajas además de la facilidad de aprendizaje y lectura/escritura son las facilidades de corrección, transformación y conversión de un lenguaje a otro.
Los más antiguos son el FORTRAN (para aplicaciones matemáticas y científicas) y el COBOL (para aplicaciones de administración y contabilidad).
 Con los micro-computadores nació el BASIC ("para principiantes"). Mucha importancia tiene el PASCAL, especialmente en la docencia. (Más detalles luego, en la "Descripción" de los lenguajes).

Generadores de aplicaciones o 4º Nivel
Posteriormente, usando estos lenguajes, se han redactado programas destinados a facilitar un número variado de operaciones en campos de aplicación específicos como simulación de fenómenos físicos, manipulación de datos estadísticos, etc. Los más avanzados y flexibles de estos programas son las planillas electrónicas u hojas de cálculo y los programas de administración de archivos o bases de datos (Vea el capítulo "Aplicaciones").
Dados que tales aplicaciones no "hacen nada" sin que el usuario defina ciertas estructuras y ciertas operaciones, pueden ser consideradas como "generadores" de aplicaciones, aunque este nombre se reserva habitualmente para niveles más avanzados en que los usuarios pueden generar sistemas muy diferentes unos de otros, con "herramientas" que se parecen a lenguajes de programación. Estas herramientas conforman los lenguajes de cuarto nivel que son por esencia "programas para crear programas" con una finalidad específica, como el "CASE" destinado a facilitar el trabajo de los analistas de sistemas.
 

ESTRUCTURA DE UN LENGUAJE

Todo lenguaje, para permitir la programación, ha de contener diversos tipos de instrucciones:

Instrucciones simples:

Instrucciones compuestas:

En este ejemplo, se introduce un concepto muy importante en el desarrollo y uso de lenguajes de programación: la recursividad, factible por el hecho de que lo que se manipula es siempre un valor colocado en alguna celda de memoria. Así, si bien la matemática no puede aceptar una ecuación como N=N+1, aquí estamos ante una instrucción (no una ecuación) que significa "tomar el valor que está en una celda llamada N, sumarle 1 y volver a colocar el nuevo valor en la celda llamada N. Ésta es una "instrucción de asignación".
Algunos lenguajes (el Algol y sus descendientes como Pascal y "C", y también Lisp y Prolog en que este procedimiento constituye la esencia del lenguaje) van aún más lejos y permiten una recursividad consistente en la posibilidad de que una instrucción compuesta ordene la ejecución de sí misma. El siguiente podría ser una breve ejemplo:
Para imprimir una lista con una instrucción recursiva, basta tener en cuenta que una lista se compone de una cabeza (primer elemento) y una cola (el resto). Obviamente la cola es también una lista, por lo cual se le puede aplicar el procedimiento consistente en separar su cabeza de su cola, y así sucesivamente, hasta encontrar una cola vacía. Por lo tanto, se puede ordenar algo así:

imprimir lista = imprimir cabeza
lista = cola       [borra de la lista la cabeza ya impresa]
si lista no es vacía, imprimir lista    [orden de recursión]
sino: fin.
 

PROGRAMACIÓN ESTRUCTURADA

Como explicado a propósito de las "Instrucciones", todo programa se compone de una secuencia de instrucciones que pueden ser simples o compuestas. La presencia de instrucciones que deban repetirse muchas veces no sólo se expresa en las iteraciones, donde dicha repetición es inmediata. Existen muchos casos en que la repetición no es un flujo continuo sino dependiente de otras operaciones o condiciones que son muy variables. Es el caso por ejemplo de las instrucciones para leer o grabar datos en un disco.
La programación estructurada, que es una forma de redacción de programas (obligatoria u optativa, según el lenguaje escogido), se hace cargo de este requisito facilitando la constitución de "paquetes" de instrucciones (llamados "sub-rutinas" o "procedimientos"), los que pueden escribirse una sola vez y ser "llamados" (utilizados) las veces que se requiera.
Además, pone énfasis en la conveniencia de facilitar la lectura de los programas haciendo más visible la dependencia jerárquica de las instrucciones compuestas mediante "indentación", es decir modificando el ancho del margen izquierdo para cada grupo de instrucciones.