I
Estructura de computadores
1.1.- Arquitectura Von NeumannAsí se conoce la forma de estructuración utilizada en los ordenadores actuales; desde 1945 con UNIVAC, se utiliza la arquitectura diferenciadora entre hardware y software que él creó (Von Neumann es junto con Alan Turing padre de la informática moderna, y curiosamente el gran precursor de los virus informáticos en sus estudios sobre autómatas autorreproductores que John Conway continuó en 1970 con el juego "Life", antecesor a su vez de los algoritmos genéticos). Según esta arquitectura, una definición adecuada para un computador sería la siguiente:
Máquina programada de propósito general capaz de realizar una serie de operaciones básicas siguiendo un conjunto de instrucciones que le son proporcionadas a través de un programa encaminado a resolver un problema.
Los elementos básicos de un computador propuestos por Von Neumann y que se utilizan en la actualidad son los siguientes:
- Memoria: Su misión consiste en servir de almacenamiento de la información dentro del computador, sean programas o datos, y sin hacer distinción entre código y datos (no hay una memoria para datos y otra para código ejecutable, está unificada).
- Dispositivos de E/S (Entrada/Salida): Engloban todos aquellos periféricos como puedan ser ratones, monitores, teclados,... es decir, todo lo que proporcione datos al computador o a través de lo cual salgan de él.
- BUS de comunicaciones: Las operaciones de accesos a datos, de manejo de periféricos y otras, han de realizarse a través de un BUS (hilos de comunicación); su misión engloba por ejemplo la transferencia de datos entre memoria y procesador.
- CPU - Unidad Central de Proceso (Central Processing Unit): Es la encargada de controlar y ejecutar todas las funciones del computador. Es la que determina en qué condición se ejecuta el código y como han de mandarse los datos, generando además todas las señales de control que afectan al resto de las partes.
1.2.- Memoria
1.2.1.- Jerarquía de memoria
La memoria en un computador se organiza en varios niveles que se organizan en forma piramidal, en el pico aquello que es más rápido y también más escaso (registros) y en la base lo más lento pero al tiempo más abundante (discos):
Pirámide de memorias (según su velocidad y tamaño)
Los registros pertenecientes al microprocesador son los más rápidos (con un tiempo de acceso que suele estar entre 1 y 5 nanosegundos) aunque por su coste el tamaño es reducido (normalmente no más que 256 bytes). La memoria caché, más lenta, tarda entre 5 y 20 ns (tiempo de acceso) pero con un tamaño mayor que a pesar de todo pocas veces sobrepasa el megabyte. La memoria principal, lo que solemos conocer como RAM, tiene ya un tamaño bastante mayor - las configuraciones standard de PCs dificilmente bajan ya de los 128Mb - pero al tiempo un acceso más lento (entre 60 y 200 nanosegundos). Finalmente, con la memoria secundaria hacemos referencia normalmente al disco duro, que es utilizado por el ordenador como memoria virtual.
Entre los distintos niveles de la jerarquía, ha de haber una correspondencia en los datos. Un nivel superior, más pequeño que el inferior, contendrá información proveniente de este nivel más grande que él, información a la que quiere acceder más deprisa. Por ejemplo, cuando accedemos a una base de datos, esta se carga en memoria para que podamos accederla a más velocidad; sin embargo, si modificamos valores de estos datos en memoria, tendremos que hacer una actualización desde la memoria al disco duro para que los cambios sean permanentes; así pues, siempre que modifiquemos algo en un nivel de la jerarquía, tarde o temprano habrá que transferir estos cambios a los niveles inferiores hasta llegar a la base de la pirámide.
Del mismo modo sucede en la relación entre memorias caché y principal; en la memoria caché se van a cargar partes de la memoria principal que se supone van a ser más utilizadas que otras, con lo cual cuando se produzca una modificación de lo que contiene la caché, habrá que actualizar de alguna manera la memoria principal.
Podríamos decir lo mismo de la relación entre caché y registros del micro, pero estos registros han de ser descritos más adelante en detalle pues su importancia va a ser capital para la programación en lenguaje ensamblador.
Es ya cosa de la implementación de cada procesador, decidir cuales son las políticas de extracción (decidir qué información se sube al nivel superior y cuando) y de reemplazo (decidir qué porción de la información de ese nivel superior ha de ser eliminada).
1.2.2.- Memoria virtual
En un sistema dotado de memoria virtual, dos niveles de la jerarquía de memoria son tratados hasta cierto punto como si fueran uno sólo; esto es, las memorias principal y secundaria (memoria RAM y disco duro generalmente) se acceden mediante lo que denominamos direcciones virtuales (no olvidemos en cualquier caso que un programa ha de residir en memoria principal para ejecutarse, al igual que los datos para ser accedidos o modificados).
Para llevar a cabo esta labor, al ejecutarse un programa se asignará un espacio virtual a este, espacio que no va a compartir con ningún otro programa y gracias al cual tampoco va a ver a ningún otro programa más que al propio sistema operativo. Es decir, supongamos que tengo tres programas ejecutándose, P1, P2 y P3, y que nuestro sistema virtual maneja direcciones desde la 0x00000000 a la 0xFFFFFFFFh (en numeración hexadecimal). Cada uno de estos tres programas podrá ocupar la parte que quiera de esta memoria virtual, y aunque dos de ellos ocuparan la misma dirección virtual no se "pisarían" dado que son procesador y sistema operativo quienes mediante la MMU (Memory Management Unit) deciden a qué parte física de la memoria principal (o a qué zona de la memoria secundaria) corresponde la dirección virtual (ojo, el espacio virtual 0x00000000 a 0xFFFFFFFFFh es independiente en cada uno de los programas). Es por ello, que excepto por mecanismos que se implementen a través del sistema operativo, el código y datos de un programa no podrá ser accedido desde otro.
En cualquier caso, un programa no puede acceder a *todo* el espacio virtual, sino a la parte que le reserva el sistema operativo (volveremos a esto cuando hablemos sobre procesos en la parte dedicada a SSOO), ya que parte de él estará ocupado por código perteneciente al propio sistema operativo.
Por último, destacar que este espacio virtual se divide en páginas virtuales, cada una normalmente de 4Kb de tamaño; sobre estas se mantendrá una tabla de páginas, una estructura que contiene la información acerca de donde residen las páginas de un programa en ejecución. Si se intenta acceder en lectura o escritura sobre una página que está en la memoria principal no habrá problemas y la MMU traducirá la dirección virtual a la posición física en memoria. Sin embargo, si se intenta acceder a una página que resida en el disco duro, se generará un fallo de página y se cargarán esos 4Kb que estaban en el disco duro sobre la memoria principal, pudiendo, ahora sí, leer o escribir sobre la información contenida en ella.
En un modelo muy simplificado, 1 bit indica si la página pertenece al disco duro o a la memoria, y su dirección real (física)
La aplicación práctica la hemos visto todos en sistemas como Linux o Windows; se reserva un espacio de tamaño variable como "memoria virtual" (el término es en realidad incorrecto tal y como se utiliza en Windows, ya que la memoria virtual abarca la RAM que tenemos y lo que asignemos para disco duro), y esto nos va a permitir como gran ventaja cargar programas más grandes que la memoria que tenemos. Evidentemente, si arrancamos diez programas que ocupan 20Mb cada uno en memoria y nuestra memoria es tan sólo de 128Mb no vamos a poder tenerlos a la vez en memoria, con lo cual el sistema de memoria virtual se hace necesario. Lo mismo sucedería, si intentamos cargar un programa que necesita 140Mb de memoria.
Así pues, aquellos programas que estemos utilizando residirán en la memoria virtual, y aquellos que no, en el disco duro. Cuando utilicemos los otros, las partes menos utilizadas de la memoria principal pasarán al disco duro, y se cargarán desde él las que estemos utilizando.
1.3.- Dispositivos de E/S
Existen de entrada (ratón, teclado, scanner), de salida (monitor), y que cumplen ambas cosas (discos duros, disketeras, módems). Los conocemos más como "periféricos", y en realidad cualquier cosa, desde un detector de movimiento a una cámara de video, pueden utilizarse como dispositivos de E/S.
La tarea más compleja respecto a estos dispositivos, es su control mediante el microprocesador; por ejemplo un módem conectado al puerto serie de un PC ha de ponerse de acuerdo con el procesador respecto a la forma de intercambiar sus datos o la velocidad con la que lo hace. Se necesita que haya una coordinación entre ambos para que uno lea a la misma velocidad a la que el otro escribe, y para interpretar adecuadamente los datos transmitidos. Además, el procesador debe de mantener una jerarquía dando prioridad antes a unos periféricos que a otros (atender una petición del disco duro puede resultarnos más importante que atender una del teclado).
1.3.1.- Control de dispositivos de E/S
Existen tres modos básicos de realizar operaciones de E/S: programada, por interrupciones y por DMA (direct memory access):
* La E/S programada exige que el procesador esté pendiente de las operaciones realizadas por los periféricos; por ejemplo, en caso de controlar mediante este sistema un disco duro, la CPU tendría que ordenar la lectura de disco y estar pendiente mediante comprobaciones de si esta lectura ha sido realizada hasta que esto sea así; este es el método menos efectivo, puesto que mientras la CPU está haciendo estos chequeos para saber si la operación ha concluído no puede hacer otras cosas, lo cual reduce mucho su efectividad.
* Con un sistema de E/S por interrupciones, el procesador se "olvida" una vez mandada en nuestro ejemplo esa órden de lectura de disco, y sigue ejecutando las instrucciones del programa actual. Cuando esta operación haya terminado, y en general cuando un periférico tiene datos dispuestos para enviar o recibir, se generará lo que se conoce como "interrupción"; esto es, que la ejecución del programa por el procesador se detiene, y salta a una rutina de tratamiento de interrupción que hace lo que tenga que hacer con esos datos. Salta a la vista, que este sistema (utilizado con frecuencia) es mucho más efectivo que la E/S programada, puesto que libera al procesador de estar pendiente de la finalización de las operaciones de E/S.
* Finalmente, la E/S por DMA libera por completo al procesador no sólo de la tarea de control sobre estas operaciones como ya hacía el sistema por interrupciones, sino también del tener que preocuparse por la transferencia de los datos. Parte de la memoria virtual se "mapea" sobre el periférico; esto es, si por ejemplo tenemos una pantalla de 80x25 caracteres, en memoria se reservará una zona de 80x25 bytes tales que cada uno representará un carácter (que se halla en ese momento en la pantalla). Así pues, para escribir o leer lo que hay en pantalla en lugar de tener que estar el procesador enviando órdenes, se realizaría de forma transparente, de modo que leyendo de la memoria se leería diréctamente del monitor, y escribiendo en ella se modificaría lo que está escrito sobre él.
1.4.- Buses de comunicación
Todas las operaciones mencionadas, han de realizarse a través de un BUS. Básicamente, tendremos tres tipos de buses:
- BUS de datos: Transfiere información, como su propio nombre indica. Por ejemplo, un bus de datos une el procesador con los discos duros o la memoria, para que estos puedan ser accedidos y su información transferida de un lugar a otro.
- BUS de control: Transporta las señales que se utilizan para configuración y control; pueden ser por ejemplo señales que decidan qué periférico ha de transmitir en un determinado momento, indicaciones para la memoria RAM de si debe de leer o escribir, etc.
- BUS de direcciones: Su utilidad se hace patente en operaciones como accesos a memoria; transportaría la indicación acerca del lugar de donde hay que leer o escribir en la RAM, o en el acceso a un disco duro el lugar físico de este donde se quiere leer o escribir.
Estos buses se combinan constantemente para poder llevar a cabo satisfactoriamente las operaciones requeridas por el procesador central. En una lectura de memoria, la CPU mandaría señales para activar el proceso de lectura en la RAM, mientras que por el bus de direcciones viajaría aquella dirección de la que se quiere leer. Una vez llegados estos datos a la memoria, por el bus de datos viajaría hasta el procesador aquella información que se requirió en un principio.
1.5.- CPU
Se trata del "gran cerebro" del computador, encargada del control de todo lo que sucede y de la ejecución del código. Se compone de tres partes principales; la ALU (Arithmethic-Logic Unit), la Unidad de Control y la Unidad de Registros.
Modelo sencillo de un procesador relacionado con memoria y dispositivos de E/S
1.5.1.- La ALU (Unidad Aritmético-Lógica o Arithmethic-Logic Unit)
Su misión es la de realizar operaciones aritméticas. Dependen del diseño, aunque encontraremos como básicas suma y resta; puede que queramos disponer de otras más complejas como multiplicación y división (para ahorrar tiempo a la hora de hacer una suma reiterada en lugar de una multiplicación si no estuviera implementada, por ejemplo). Además, tendremos operaciones lógicas:
* AND: Un AND hecho a dos bits devuelve 1 sólo si los dos bits son 1 (por ejemplo, 011 AND 101 dará como resultado 001). Equivale al "Y" lógico (es decir, al resultado en lógica de algo como "se da X y se da Y", frase que sólo sería verdadera en caso de darse X e Y).
* OR: Un OR hecho a dos bits devuelve 1 si al menos uno de los dos bits implicado en la operación es 1 (un 011 OR 101 da como resultado 111). Equivale al "O" lógico (el resultado de algo como "se dan X, Y o ambos", sentencia cierta en caso de darse X, Y o ambos).
* XOR: Un XOR, (eXclusive OR, O exclusivo) da 1 operando sobre dos bits si uno de los dos bits es 1 y el otro 0 ( la operación 011 OR 101 resulta 010). Este "O exclusivo" en lógica es el que se daría en un "X está vivo o está muerto"; una de las dos condiciones ha de cumplirse para que esta sentencia sea cierta.
* NOT: Esta operación trabaja con un sólo bit; lo que hace es invertirlo (así, NOT 011 dará 100 como resultado).
Las operaciones con la ALU se pueden indicar mediante una señal de control con los bits suficientes como para diferenciar entre los tipos de operación existentes. Es decir, si tenemos 2 bits para la señal de control, las posibilidades de estos bits serán "00-01-10-11", lo cual da como resultado una ALU que pueda hacer cuatro funciones distintas. Con 3 bits, tendríamos "000-001-010-011-100-101-110-111", es decir, 8 operaciones posibles.
Además de esta señal de control, tendremos dos entradas de datos; esto es, los operandos de la función que se va a realizar. Así, si queremos hacer un AND entre dos números, meteremos cada uno de ellos por una entrada de datos y seleccionaremos el AND. Por supuesto, habrá al menos una salida conectada para el resultado de la operación:
1.5.2.- La Unidad de Control
Es la que realiza el secuenciamiento del programa que estoy ejecutando; esto es, la ejecución de la instrucción actual y la obtención de la siguiente. Su función es obtener las señales de temporización y control para ejecutar según los datos que entran, determinando el funcionamiento de la CPU y su comunicación interna.
Al ir a ejecutar una instrucción, la unidad de control pedirá que sea cargada y la analizará, viendo qué tiene que hacer en la CPU para que lo que la instrucción dice que ha de hacerse, llegue a buen término; por ejemplo, si esta instrucción es un AND de dos elementos, mandaría estos dos a la ALU y activaría las señales de control para que realizase un AND, para después transferir el resultado donde la propia instrucción indicase.
1.5.3.- La Unidad de Registros
Tiene una gran importancia, ya que la CPU usa estos registros para no tener que estar siempre accediendo a la memoria. Un registro no es más que un "pedazo" de memoria con una velocidad de acceso muy grande, normalmente de un tamaño que no supera los 64 bits (siempre una cifra tipo 16, 32, 64, 128...).
Sus usos, son diversos; mientras que por ejemplo cuando se ejecuta una instrucción esta se guarda en un registro mientras dura su procesamiento, pueden usarse también para almacenar datos con los que operar o hacer transferencias con la memoria, etc.
Hay dos tipos básicos de registros
A.- Registros de propósito general
Podemos darles cualquier uso. Son accesibles, visibles al programador, que puede utilizarlos. Sirven para volcado de datos, instrucciones... por ejemplo, en el MC68000 de Motorola, existen 16 registros de 32 bits de propósito general (A0-A7 y D0-D7, para almacenar direcciones y datos respectivamente). En otros como los 80x86 de Intel tenemos otra serie de registros de 32 bits con nombres como EAX, EBX, ECX, EDX, etc...
B.- Registros de propósito específico
Son registros que utilizan la unidad de control; el programador no puede utilizarlos, al menos diréctamente. Los principales (cuyo nombre cambia según cada implementación pero que por lo general se suelen encontrar en toda CPU) son:
- IR: Su misión es contener la instrucción que se está ejecutando por la CPU; es el Registro de Instrucción (o Instruction Register). Mientras la instrucción se esté ejecutando, se contendrá ahí.
- PC: Program Counter o Registro de Contador de Programa. Su misión es contener la dirección de la instrucción siguiente a la que estamos ejecutando. Por ello, permite ejecutar un programa de modo secuencial (línea a línea), tal y como ha sido programado.
- SR: Es el Registro de Estado, o Status Register. Su misión es reflejar en cada momento en qué situación se encuentran algunos detalles de la CPU (por ejemplo, almacena resultados de comparaciones) de cara a tomar decisiones, así como otros parámetros que pueden necesitar ser consultados. En la mayoría de las implementaciones este registro es, al menos, accesible.
- SP: Registro de Pila o Stack Pointer; la función de la "pila" será explicada ya más adelante, pero es necesario para poder hacer llamadas a funciones en programas y para muchas otras cosas.
- MAR: Registro de Dirección de Memoria (Memory Address Register): Es el que finalmente comunica la CPU con el bus externo. Concluída la instrucción, el PC se vuelca en el MAR, y el bus de direcciones localizará la siguiente instrucción según el contenido de este registro.
- MDR: Registro de Datos de Memoria (Data Address Register): Es el que pone en contacto la CPU y el bus de datos, que contiene la información para ser transferida por él o para recibirla.
1.6.- Ejemplo de un procesador simple
Para poner en práctica lo explicado anteriormente, vamos a diseñar y programar un procesador muy simple, que pretende servir para acabar de comprender la forma de relacionarse de todos los elementos descritos.
1.6.1.- Estructura del procesador
Nuestro pequeño ordenador, tendrá las siguientes características:
- Una memoria de 64Kb (65536 bytes), que será direccionable con un registro MAR en el procesador de 16 bits (si se hace la operación, 2 elevado a 16 es 65536, el mayor número que un número binario de 16 cifras puede representar).
- Un registro de propósito general de 16 bits, llamado AC; además, un SR (estado), MAR y MDR (datos y direcciones) y PC.
- Una ALU en el procesador con 8 operaciones: AND, OR, NOT, XOR, ADD (suma), SUB (resta), INC (incrementa en uno) y DEC (resta uno).
1.6.2.- Juego de instrucciones
Las instrucciones que va a poder ejecutar la CPU son las siguientes:
- Aritmético-lógicas: las que realiza la ALU, es decir, AND, OR, NOT, XOR, ADD, SUB, INC y DEC.
- Para mover posiciones de memoria entre sí, al registro AC o desde el registro AC (todo ello reunido en la instrucción "MOV").
- Salto en ejecución (JMP)
- Comparación (CMP): Lo que hará será actualizar el registro de estado en la CPU dependiendo del resultado de la comparación, para permitir saltos condicionales a posteriori.
- Salto condicional (JE -> Salta si Igual (Jump if Equal), JNE -> Salta si no Igual (Jump if Not Equal)).
- Parada del procesador (STOP)
Algunos ejemplos de la implementación de estas instrucciones serían:
* AND [1214h],AC -> realizaría la operación lógica entre el registro AC y los 16 bits contenidos en la dirección de memoria 1214h (h significa que la notación del número es hexadecimal), almacenando en esa posición de memoria el resultado.
* OR [1263h], [1821h] -> haría un OR lógico entre los contenidos de memoria de 1263h y 1821h, almacenando en 1263h el resultado.
* MOV AC,[8241h] -> Movería el contenido de la dirección de memoria 8241h al registro AC.
* JMP 2222h -> Cambiaría el PC (Program Counter, es decir, la siguiente instrucción a ser ejecutada) a la dirección 2222h. La instrucción que se contenga en esa dirección será la siguiente a ser ejecutada.
* CMP AC, 12 -> Si el contenido del registro AC es 12, activaría en el registro de estado de la CPU un bit que indicaría que el resultado de la ultima comparación es "verdadero", con lo que un JE (Jump if Equal o Salta si Igual) se ejecutaría si siguiese a esa instrucción.
* JE 1111h -> En caso de que en una comparación (CMP) anterior el resultado fuera verdadero (por ejemplo, en el caso anterior AC vale 12), el PC cambiaría para contener 1111h como dirección de la siguiente instrucción a ejecutar. En caso de que la comparación hubiera resultado falsa - en el caso anterior que AC no valga 12 -, la instrucción sería ignorada y se ejecutaría la siguiente instrucción.
1.6.3.- Ejecución de una instrucción
Podemos distinguir dos fases:
Fase de Fetch: Al comienzo del procesado de una nueva instrucción, el registro específico PC de la CPU contiene la dirección de donde esta ha de obtenerse. El contenido de este registro se pasará al MAR (Memory Address Register) transfiriéndose a la memoria mediante el bus de direcciones, y se activará esta memoria para indicar que se desea realizar una lectura sobre ella enviando una señal adecuada a través del bus de control. Así, la instrucción llegará hasta el MDR, de donde se enviará a la Unidad de Control para su procesamiento.
Procesamiento: Una vez llega la instrucción a la Unidad de Control, ésta distinguirá según su codificación de qué tipo es y realizará las operaciones necesarias para ejecutarla. Si por ejemplo es un salto tipo JMP, enviará la dirección al PC y dará por terminada la ejecución de la instrucción. Por supuesto hay casos bastante más complejos, como podría ser un ADD AC, 215 (sumar 215 al registro AC). En esta en particular, el procesador enviará esta cifra (215) a una de las entradas de la ALU y el registro AC a la otra, indicando mediante señales de control a ésta que desea activar la operación de sumar, ADD. Una vez realizada la operación dentro de la ALU, su salida se enviará de nuevo al registro AC, con lo que ahora contendrá AC+215, acabando entonces la ejecución de esta instrucción y pasando de nuevo a la fase de fetch (por supuesto no sin antes sumarle al registro de contador de programa la longitud de la instrucción que se acaba de ejecutar, para que al acceder a memoria en el fetch se cargue la siguiente en la Unidad de Control).
Dado que los ejemplos nunca sobran, veamos una instrucción como CMP AC, 12. Una vez llegue tras la fase de fetch a la Unidad de Control, de nuevo se utilizará la ALU; en esta ocasión se la indicará mediante señales de control que realice la operación de resta (SUB), metiendo por un lado el 12 y por otro el registro AC. Sin embargo, la salida de la ALU se perderá pues lo único que no simporta es si el resultado de la operación es 0 (si el contenido de AC - 12 resulta cero, está claro que AC contiene un 12). En caso de ser por tanto AC = 12, se modificará el Registro de Estado para indicar que el resultado de la anterior operación fue cero, es decir, que AC vale efectivamente 12 (aunque no necesariamente AC, podríamos hacer algo como CMP [1212h],16, es decir, comparar 16 con el contenido de la posición de memoria 1212h). A posteriori, de nuevo se sumaría el tamaño de esta instrucción al registro PC y se haría el fetch de la siguiente instrucción.
1.6.4.- Un programa sencillo
Con lo que sabemos, ya podemos escribir un programa sencillo; en este caso y dado que nuestro pequeño ordenador no posee operación de multiplicar, lo que va a hacer la rutina siguiente es la multiplicación entre dos números contenidos en las direcciones de memoria [1000h] y [1002h], almacenando el resultado en [1004h]. MOV AC,[1000h] ; Cargamos en AC el primer operando
MOV [1004h],0 ; Ponemos a cero el resultado
Bucle: DEC AC ; Decrementamos AC en uno
ADD [1004h],[1002h] ; Añadimos al resultado el segundo operando
CMP AC,0
JNE Bucle
STOP
La ejecución de este programa es fácil de comprender; el algoritmo que utiliza para multiplicar los números contenidos en 1000h y 1002h es el de coger uno de ellos y sumarlo tantas veces como el otro indique (por ejemplo, 7*6 se convertiría en 7+7+7+7+7+7). Para ello lo que hace es ir restando uno (con el DEC) cada vez que hace una suma del primer operando sobre el resultado, y cuando este llega a cero, el JNE (Jump if Not Equal, salta si no es igual) no se ejecutará y por tanto llegará al STOP, es decir, que el programa habrá concluído y la operación habrá sido realizada.
1.6.5.- Lenguaje ensamblador
El trozo de código del apartado anterior está escrito en un lenguaje que el microprocesador entiende diréctamente gracias a su unidad de control. Aunque este varíe según el modelo (y tenemos por ejemplo procesadores con pocas instrucciones como son los RISC o con muchas como los CISC), la denominación de este lenguaje básico del procesador como lenguaje ensamblador se mantiene.
Más adelante, habrá que aprender el ensamblador (o ASM) propio del PC de cara a la programación de virus, pues es en este lenguaje en el que se escriben. En realidad, cuando escribimos un programa en cualquier otro lenguaje como pueda ser C, lo que está haciendo el compilador que traduce nuestro código a un formato ejecutable es traducirlo a ensamblador (lógicamente, una máquina no puede interpretar diréctamente el lenguaje C). Una vez el compilador ha realizado ese trabajo de traducción desde el lenguaje en que hemos programado a ensamblador, la máquina ya puede ejecutar nuestro programa. Es por esto, que programar diréctamente en ensamblador nos da grandes ventajas al estar controlando al detalle qué sucede en la máquina, qué está ejecutando el procesador, en lugar de delegar el trabajo a compiladores sobre los que no ejercemos un control directo.
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario