Componentes básicos del lenguaje ensamblador y estructura de comandos. Formato de datos y estructura de instrucciones en lenguaje ensamblador. Asignatura "Programación de sistemas"

Introducción.

El lenguaje en el que está escrito el programa original se llama aporte idioma, y ​​el idioma al que se traduce para su ejecución por parte del procesador - fin de semana idioma. El proceso de convertir un idioma de entrada en un idioma de salida se llama transmisión. Dado que los procesadores son capaces de ejecutar programas en lenguaje de máquina binario, que no se utiliza para la programación, es necesaria la traducción de todos los programas fuente. conocido dos caminos traducciones: recopilación e interpretación.

En Compilacion el programa fuente primero se traduce completamente a un programa equivalente en el idioma de destino, llamado objeto programa y luego se ejecuta. Este proceso se lleva a cabo utilizando un especial programas, llamado compilador. Un compilador cuyo lenguaje de entrada es una representación simbólica del lenguaje de máquina (salida) de los códigos binarios se llama ensamblador.

En interpretaciones cada línea de texto del programa fuente se analiza (interpreta) y el comando especificado en él se ejecuta inmediatamente. La implementación de este método recae en programa de intérprete. La interpretación lleva mucho tiempo. Para aumentar su eficiencia, en lugar de procesar cada línea, el intérprete convierte preliminarmente todas dominio cadenas a caracteres (

). La secuencia de símbolos generada se utiliza para realizar las funciones asignadas al programa original.

El lenguaje ensamblador que se analiza a continuación se implementa mediante compilación.

Características del lenguaje.

Las principales características del ensamblador:

● en lugar de códigos binarios, el lenguaje utiliza nombres simbólicos - mnemotécnica. Por ejemplo, para el comando de suma (

) se utiliza mnemotécnico

restas (

multiplicación (

Divisiones (

etc. Los nombres simbólicos también se utilizan para referirse a las celdas de memoria. Para programar en lenguaje ensamblador, en lugar de códigos binarios y direcciones, solo necesita conocer los nombres simbólicos que el ensamblador traduce a códigos binarios;

cada afirmación corresponde un comando de máquina(código), es decir, existe una correspondencia uno a uno entre las instrucciones de máquina y los operadores en un programa en lenguaje ensamblador;

● el idioma proporciona acceso a todos los objetos y equipos Los lenguajes de alto nivel no tienen esta capacidad. Por ejemplo, el lenguaje ensamblador le permite verificar un bit de registro de bandera y un lenguaje de alto nivel (por ejemplo,

) no tiene esta capacidad. Tenga en cuenta que los lenguajes para la programación de sistemas (por ejemplo, C) suelen ocupar una posición intermedia. En términos de accesibilidad, están más cerca del lenguaje ensamblador, pero tienen la sintaxis de un lenguaje de alto nivel;

● lenguaje ensamblador no es un lenguaje universal. Cada grupo específico de microprocesadores tiene su propio ensamblador. Los lenguajes de alto nivel no tienen esta desventaja.

A diferencia de los lenguajes de alto nivel, escribir y depurar un programa en lenguaje ensamblador lleva mucho tiempo. A pesar de esto, el lenguaje ensamblador se ha vuelto amplio uso debido a las siguientes circunstancias:

● Un programa escrito en lenguaje ensamblador es mucho más pequeño y mucho más rápido que un programa escrito en un lenguaje de alto nivel. Para algunas aplicaciones, estos indicadores juegan un papel principal, por ejemplo, muchos programas del sistema (incluidos los compiladores), programas en tarjetas de crédito, celulares, controladores de dispositivos, etc.;

● algunos procedimientos requieren Acceso completo al hardware, lo que normalmente no es posible en un lenguaje de alto nivel. Este caso incluye interrupciones y controladores de interrupciones en sistemas operativos, así como controladores de dispositivos en sistemas integrados en tiempo real.

En la mayoría de los programas, solo un pequeño porcentaje del código total es responsable de un gran porcentaje del tiempo de ejecución del programa. Normalmente, el 1 % del programa es responsable del 50 % del tiempo de ejecución y el 10 % del programa es responsable del 90 % del tiempo de ejecución. Por lo tanto, para escribir un programa específico en condiciones reales, se utilizan tanto ensamblador como uno de los lenguajes de alto nivel.

Formato de operador en lenguaje ensamblador.

Un programa en lenguaje ensamblador es una lista de comandos (sentencias, oraciones), cada uno de los cuales ocupa una línea separada y contiene cuatro campos: un campo de etiqueta, un campo de operación, un campo de operando y un campo de comentario. Cada campo tiene una columna separada.

campo de etiqueta.

Para el campo de etiqueta se asigna la columna 1. Una etiqueta es un nombre simbólico, o identificador, direcciones memoria. Es necesario para poder:

● hacer una transición condicional o incondicional al comando;

● obtener acceso al lugar donde se almacenan los datos.

Tales declaraciones están etiquetadas. Para designar un nombre se utilizan letras (mayúsculas) del alfabeto inglés y números. El nombre debe comenzar con una letra y terminar con dos puntos. La etiqueta de dos puntos se puede escribir en una línea separada y el código de operación se puede escribir en la siguiente línea en la columna 2, lo que simplifica el trabajo del compilador. La ausencia de dos puntos hace que sea imposible distinguir entre una etiqueta y un código de operación si están en líneas separadas.

En algunas versiones del lenguaje ensamblador, los dos puntos se colocan solo después de las etiquetas de instrucciones, no después de las etiquetas de datos, y la longitud de la etiqueta se puede limitar a 6 u 8 caracteres.

El campo de la etiqueta no debe contener los mismos nombres, ya que la etiqueta está asociada con las direcciones de los comandos. Si durante la ejecución del programa no hay necesidad de llamar un comando o datos de la memoria, entonces el campo de la etiqueta permanece vacío.

Campo de código de transacción.

Este campo contiene el comando mnemotécnico o pseudo-comando (ver más abajo). El código mnemotécnico del comando lo eligen los desarrolladores del lenguaje. En lenguaje ensamblador

mnemotécnico seleccionado para cargar el registro desde la memoria

), y para almacenar el contenido del registro en la memoria - el mnemotécnico

). En lenguajes ensambladores

puede usar el mismo nombre para ambas operaciones, respectivamente

Si la elección de nombres mnemotécnicos puede ser arbitraria, entonces la necesidad de usar dos instrucciones de máquina se debe a la arquitectura del procesador.

Los mnemotécnicos de registro también dependen de la versión del ensamblador (Tabla 5.2.1).

Campo de operandos.

Aquí se encuentra información adicional necesarios para realizar la operación. En el campo de operandos para instrucciones de salto se indica la dirección donde se quiere saltar, así como direcciones y registros que son operandos para la instrucción máquina. Como ejemplo, aquí están los operandos que se pueden usar para procesadores de 8 bits

● datos numéricos,

presentados en diferentes sistemas numéricos. Para indicar el sistema numérico utilizado, la constante va seguida de una de las letras latinas: B,

En consecuencia, los sistemas numéricos binarios, octales, hexadecimales y decimales (

puede que no se registre). Si el primer dígito del número hexadecimal es A, B, C,

Luego se agrega un 0 insignificante (cero) al frente;

● códigos de registros internos del microprocesador y celdas de memoria

M (fuentes o receptores de información) en forma de letras A, B, C,

M o sus direcciones en cualquier sistema numérico (por ejemplo, 10V - dirección de registro

en sistema binario);

● identificadores,

para parejas de aeronaves registradas,

Las primeras letras B

H; para un par de acumulador y registro de características -

; para el contador de programas -

; para puntero de pila -

● etiquetas que indican direcciones de operandos o instrucciones siguientes en condicional

(cuando se cumple la condición) y transiciones incondicionales. Por ejemplo, operando M1 en el comando

significa la necesidad de una transición incondicional al comando, cuya dirección en el campo de la etiqueta está marcada con el identificador M1;

● expresiones,

que se construyen vinculando los datos discutidos anteriormente usando operadores aritméticos y lógicos. Tenga en cuenta que la forma en que se reserva el espacio de datos depende de la versión del idioma. Desarrolladores de lenguaje ensamblador para

Definir palabra), y más tarde introducido Opción alternativa.

que desde un principio estuvo en el lenguaje de los procesadores

En versión de idioma

usado

definir una constante).

Los procesadores procesan operandos de diferentes longitudes. Para definirlo, los desarrolladores de ensambladores han tomado diferentes decisiones, por ejemplo:

II registros de diferentes longitudes tienen diferentes nombres: EAX - para colocar operandos de 32 bits (tipo

); AX - para 16 bits (tipo

y AN - para 8 bits (tipo

● para procesadores

se agregan sufijos a cada código de operación: sufijo

por tipo

; sufijo ".B" para el tipo

para operandos de diferentes longitudes, se utilizan diferentes códigos de operación, por ejemplo, para cargar un byte, una media palabra (

) y las palabras en el registro de 64 bits usan códigos de operación

respectivamente.

Campo de comentarios.

Este campo proporciona explicaciones sobre las acciones del programa. Los comentarios no afectan el funcionamiento del programa y están destinados a una persona. Pueden ser necesarios para modificar un programa que, sin tales comentarios, puede resultar completamente incomprensible incluso para los programadores experimentados. Un comentario comienza con un carácter y se usa para explicar y documentar programas. El carácter de inicio de un comentario puede ser:

● punto y coma (;) en idiomas para procesadores de la empresa

Punto de exclamación(!) en idiomas para

Cada línea separada reservada para un comentario está precedida por un carácter de inicio.

Pseudocomandos (directivas).

En lenguaje ensamblador, se pueden distinguir dos tipos principales de comandos:

básico instrucciones que son equivalentes al código máquina del procesador. Estos comandos realizan todo el procesamiento proporcionado por el programa;

pseudocomandos o directivas, diseñado para servir al proceso de traducción del programa al lenguaje de combinaciones de códigos. Como ejemplo, en la Tabla. 5.2.2 muestra algunos pseudocomandos del ensamblador

por la familia

.

A la hora de programar, hay situaciones en las que, según el algoritmo, la misma cadena de comandos debe repetirse muchas veces. Para salir de esta situación, puedes:

● escribir la secuencia deseada de comandos siempre que ocurra. Este enfoque conduce a un aumento en el volumen del programa;

● organice esta secuencia en un procedimiento (subrutina) y llámelo si es necesario. Tal salida tiene sus inconvenientes: cada vez que tiene que ejecutar una instrucción de llamada de procedimiento especial y una instrucción de retorno, que, con una secuencia corta y de uso frecuente, puede reducir en gran medida la velocidad del programa.

La más sencilla y metodo efectivo repetición repetida de una cadena de comandos es usar macro, que puede considerarse como un pseudocomando diseñado para volver a traducir un grupo de comandos que se encuentran con frecuencia en un programa.

Una macro, o instrucción de macro, se caracteriza por tres aspectos: definición de macro, inversión de macro y expansión de macro.

definición de macros

Esta es una designación para una secuencia repetidamente repetida de comandos de programa, utilizada para referencias en el texto del programa.

Una macro tiene la siguiente estructura:

Lista de expresiones; definición de macros

Hay tres partes en la estructura de definición de macro anterior:

● encabezado

macro que contiene el nombre

pseudocomando

y un conjunto de parámetros;

● puntos cuerpo macro;

● equipo

graduación

definiciones de macros.

Un conjunto de parámetros de macro contiene una lista de todos los parámetros proporcionados en el campo de operando para el grupo de instrucciones seleccionado. Si estos parámetros se proporcionan anteriormente en el programa, se pueden omitir en el encabezado de definición de macro.

Para volver a ensamblar el grupo de instrucciones seleccionado, se utiliza una llamada, que consiste en el nombre

lista de macros y parámetros con otros valores.

Cuando el ensamblador encuentra una definición de macro durante la compilación, la almacena en la tabla de definición de macro. Con posteriores apariciones en el programa del nombre (

) de una macro, el ensamblador lo reemplaza con el cuerpo de la macro.

El uso de un nombre de macro como código de operación se denomina macro-reversión(llamada macro), y su reemplazo por el cuerpo de la macro - expansión de macros.

Si el programa se representa como una secuencia de caracteres (letras, números, espacios, puntuación y retornos de carro para pasar a una nueva línea), la expansión de macros consiste en reemplazar algunas cadenas de esta secuencia con otras cadenas.

La expansión de macros ocurre durante el proceso de ensamblaje, no durante la ejecución del programa. Las formas de manipular cadenas de caracteres se asignan a herramientas de macros

El proceso de montaje se lleva a cabo en dos pasadas:

● En la primera pasada, se mantienen todas las definiciones de macros y se expanden las llamadas de macros. En este caso, el programa fuente se lee y se convierte en un programa en el que se eliminan todas las definiciones de macro y cada llamada de macro se reemplaza por un cuerpo de macro;

● El segundo paso procesa el programa recibido sin macros.

Macros con parámetros.

Para trabajar con secuencias repetitivas de comandos, cuyos parámetros pueden tomar diferentes valores, se proporcionan definiciones de macro:

● con actual parámetros que se colocan en el campo de operando de la llamada de macro;

● con formal parámetros Durante la expansión de la macro, cada parámetro formal que aparece en el cuerpo de la macro se reemplaza por el parámetro real correspondiente.

Uso de macros con parámetros.

El programa 1 muestra dos secuencias de comandos similares, diferenciándose en que el primero de ellos intercambia P y

Y el segundo

El programa 2 incluye una macro con dos parámetros formales P1 y P2. Durante la expansión de la macro, cada carácter P1 dentro del cuerpo de la macro se reemplaza por el primer parámetro real (P,

), y el símbolo P2 se reemplaza por el segundo parámetro real (

) del programa No. 1. En una llamada de macro

el programa 2 está marcado: P,

El primer parámetro real,

El segundo parámetro real.

Programa 1

Programa 2

MOV EBX,Q MOV EAX,Pl

MOV Q, EAX MOV EBX, P2

MOV P, EBX MOV P2, EAX

Capacidades extendidas.

Considere algunas características avanzadas del lenguaje

Si se llama dos o más veces a una macro que contiene una instrucción de bifurcación condicional y una etiqueta a la que saltar, la etiqueta se duplicará (problema de duplicación de etiquetas), lo que provocará un error. Por lo tanto, a cada llamada se le asigna (por parte del programador) una etiqueta separada como parámetro. en idioma

la etiqueta se declara local (

) y gracias a las funciones avanzadas, el ensamblador genera automáticamente una etiqueta diferente cada vez que se expande la macro.

le permite definir macros dentro de otras macros. Esta función avanzada es muy útil cuando se combina con la vinculación condicional de programas. Considerar

SI PALABRA GT 16 M2 MACRO

Macro M2 se puede definir en ambas partes de la instrucción

Sin embargo, la definición depende de si el programa se ensambla en un procesador de 16 bits o de 32 bits. Si no se llama a M1, la macro M2 no se definirá en absoluto.

Otra característica avanzada es que las macros pueden llamar a otras macros, incluidas ellas mismas: recursivo llamar. En este último caso, para evitar un bucle infinito, la macro debe pasarse un parámetro a sí misma, que cambia con cada expansión, y también controlar este parámetro y finaliza la recursión cuando el parámetro alcanza un cierto valor.

Sobre el uso de macros en ensamblador.

Al usar macros, el ensamblador debe poder realizar dos funciones: guardar definiciones de macros Y expandir las llamadas de macro.

Guardar definiciones de macros.

Todos los nombres de las macros se almacenan en una tabla. Cada nombre va acompañado de un puntero a la macro correspondiente para poder llamarla si es necesario. Algunos ensambladores tienen una tabla separada para los nombres de las macros, otros tienen una tabla común en la que, junto con los nombres de las macros, se encuentran todos los comandos y directivas de la máquina.

Al encontrar una macro durante el ensamblaje creado:

nuevo elemento de tabla con el nombre de la macro, el número de parámetros y un puntero a otra tabla de definición de macro donde se almacenará el cuerpo de la macro;

● lista formal parámetros

El cuerpo de la macro, que es simplemente una cadena de caracteres, se lee y almacena en la tabla de definición de macros. Los parámetros formales que ocurren en el cuerpo del bucle están marcados personaje especial.

Representación interna de una macro

del ejemplo anterior para el programa 2 (p. 244) es:

MOV EAX, MOV EBX, MOV MOV &

donde el punto y coma se usa como el carácter de retorno de carro y el ampersand & se usa como el carácter de parámetro formal.

Extensión de macrollamada.

Cada vez que se encuentra una definición de macro durante el ensamblaje, se almacena en la tabla de macros. Cuando se llama a una macro, el ensamblador suspende temporalmente la lectura de los datos de entrada del dispositivo de entrada y comienza a leer el cuerpo de la macro guardada. Los parámetros formales extraídos del cuerpo de la macro se reemplazan por los parámetros reales y los proporciona la llamada. Un ampersand & delante de los parámetros permite que el ensamblador los reconozca.

Aunque existen muchas versiones de ensamblador, los procesos de ensamblaje tienen características comunes y son similares en muchos aspectos. El trabajo de un ensamblador de dos pasos se considera a continuación.

Ensamblador de dos pasos.

El programa consta de una serie de sentencias. Por lo tanto, parecería que la siguiente secuencia de acciones se puede utilizar durante el montaje:

● traducirlo a lenguaje de máquina;

● transferir el código de máquina recibido a un archivo y la parte correspondiente de la lista a otro archivo;

● repita los procedimientos anteriores hasta que se transmita todo el programa.

Sin embargo, este enfoque no es eficiente. Un ejemplo es el llamado problema enlace principal. Si la primera declaración es un salto a la declaración P al final del programa, entonces el ensamblador no puede traducirla. Primero debe determinar la dirección del operador P, y para esto es necesario leer todo el programa. Cada lectura completa del programa original se llama paso. Mostremos cómo podemos resolver el problema de la referencia directa usando dos pases:

en el primer pase recolectar y almacene todas las definiciones de símbolos (incluidas las etiquetas) en la tabla y, en la segunda pasada, lea y ensamble cada operador. Este método es relativamente simple, pero el segundo paso por el programa original requiere tiempo de E/S adicional;

● en la primera pasada, convertir programe en una forma intermedia y guárdelo en una tabla, y el segundo paso se realiza no de acuerdo con el programa original, sino de acuerdo con la tabla. Este método de ensamblaje ahorra tiempo, ya que no se realizan operaciones de E/S en el segundo paso.

Primer pase.

Propósito del primer pase- construir una tabla de símbolos. Como se indicó anteriormente, otro objetivo del primer pase es guardar todas las definiciones de macros y expandir las llamadas a medida que aparecen. Por lo tanto, tanto la definición de caracteres como la expansión de macros ocurren en el mismo paso. El símbolo puede ser cualquiera etiqueta, o significado, al que se le asigna un nombre específico usando la directiva -you:

;Valor - tamaño del búfer

Al dar significado a los nombres simbólicos en el campo de la etiqueta de instrucción, el ensamblador esencialmente establece las direcciones que tendrá cada instrucción durante la ejecución del programa. Para ello, el ensamblador durante el proceso de montaje guarda contador de direcciones de instrucciones(

) como una variable especial. Al comienzo del primer paso, el valor de la variable especial se establece en 0 y se incrementa después de cada comando procesado por la longitud de ese comando. Como ejemplo, en la Tabla. 5.2.3 muestra un fragmento del programa que indica la longitud de los comandos y los valores de los contadores. Las tablas se generan durante el primer paso. nombres de símbolos, directivas Y códigos de operación, y si es necesario literal mesa. Un literal es una constante para la cual el ensamblador reserva automáticamente memoria. Notamos de inmediato que los procesadores modernos contienen instrucciones con direcciones directas, por lo que sus ensambladores no admiten literales.

Tabla de símbolos

contiene un elemento para cada nombre (Tabla 5.2.4). Cada elemento de la tabla de símbolos contiene el nombre en sí (o un indicador), su valor numérico y, a veces, información adicional, que puede incluir:

● la longitud del campo de datos asociado con el símbolo;

● bits de reasignación de memoria (que indican si el valor de un símbolo cambia si el programa se carga en una dirección diferente a la prevista por el ensamblador);

● información sobre si se puede acceder al símbolo desde fuera del procedimiento.

Los nombres simbólicos son etiquetas. Se pueden especificar mediante operadores (por ejemplo,

Cuadro de directivas.

Esta tabla enumera todas las directivas, o pseudo-comandos, que ocurren al ensamblar un programa.

Tabla de códigos de operación.

Para cada código de operación, la tabla tiene columnas separadas: designación del código de operación, operando 1, operando 2, valor hexadecimal del código de operación, longitud de la instrucción y tipo de instrucción (Tabla 5.2.5). Los códigos de operación se dividen en grupos según el número y el tipo de operandos. El tipo de comando determina el número de grupo y especifica el procedimiento que se llama para procesar todos los comandos en ese grupo.

Segundo pase.

Propósito del segundo pase- crear un programa objeto e imprimir, si es necesario, un protocolo de montaje; información de salida que necesita el vinculador para vincular los procedimientos que se ensamblaron en diferentes momentos en un archivo ejecutable.

En el segundo paso (como en el primero), las líneas que contienen las declaraciones se leen y procesan una tras otra. El operador original y la salida derivada de él en hexadecimal objeto el código se puede imprimir o almacenar en búfer para su posterior impresión. Después de restablecer el contador de direcciones de comando, se llama al comando próxima declaración.

El programa original puede contener errores, por ejemplo:

el símbolo dado no está definido o definido más de una vez;

● El código de operación está representado por un nombre no válido (debido a un error tipográfico), no tiene suficientes operandos o tiene demasiados operandos;

● sin operador

Algunos ensambladores pueden detectar un símbolo indefinido y reemplazarlo. Sin embargo, en la mayoría de los casos, cuando se encuentra una declaración con un error, el ensamblador muestra un mensaje de error en la pantalla e intenta continuar con el proceso de ensamblaje.

Artículos dedicados al lenguaje ensamblador.

UNIVERSIDAD NACIONAL DE UZBEKISTÁN NOMBRADA EN LUGAR DE MIRZO ULUGBEK

FACULTAD DE TECNOLOGÍAS INFORMÁTICAS

Sobre el tema: análisis semántico de un archivo EXE.

Terminado:

Taskent 2003.

Prefacio.

Lenguaje ensamblador y estructura de instrucción.

Estructura de archivos EXE (análisis semántico).

Estructura de un archivo COM.

Cómo funciona y se propaga el virus.

desensamblador

Programas.

Prefacio

La profesión de programador es increíble y única. En nuestro tiempo, la ciencia y la vida no se pueden imaginar sin la última tecnología. Todo lo que está relacionado con la actividad humana no puede prescindir Ciencias de la Computación. Y esto contribuye a su alto desarrollo y perfección. Incluso si el desarrollo de las computadoras personales comenzó no hace mucho tiempo, durante este tiempo se dieron pasos colosales en los productos de software y durante mucho tiempo estos productos serán ampliamente utilizados. El campo del conocimiento relacionado con la informática se ha disparado, al igual que la tecnología relacionada. Si no tenemos en cuenta el lado comercial, entonces podemos decir que no hay extraños en esta área de actividad profesional. Muchos están comprometidos en el desarrollo de programas no por ganancias o ganancias, sino por su propia voluntad, por pasión. Por supuesto, esto no debería afectar la calidad del programa, y ​​en este negocio, por así decirlo, existe competencia y demanda de un desempeño de calidad, de un trabajo estable y que cumpla con todos los requisitos de nuestro tiempo. Aquí también cabe destacar la aparición de los microprocesadores en los años 60, que llegaron a sustituir a una gran cantidad de juegos de lámparas. Hay algunas variedades de microprocesadores que son muy diferentes entre sí. Estos microprocesadores difieren entre sí en términos de capacidad de bits y comandos de sistema integrados. Los más comunes son: Intel, IBM, Celeron, AMD, etc. Todos estos procesadores están relacionados con la arquitectura avanzada de los procesadores Intel. La difusión de las microcomputadoras ha provocado un replanteamiento de las actitudes hacia el lenguaje ensamblador por dos razones principales. Primero, los programas escritos en lenguaje ensamblador requieren significativamente menos memoria y tiempo de ejecución. En segundo lugar, el conocimiento del lenguaje ensamblador y el código de máquina resultante permite comprender la arquitectura de la máquina, que difícilmente se proporciona cuando se trabaja en un lenguaje de alto nivel. Aunque la mayoría de los ingenieros de software desarrollan en lenguajes de alto nivel como Pascal, C o Delphi, el que es más fácil de escribir programas, el más potente y eficiente software escrita total o parcialmente en lenguaje ensamblador. Los lenguajes de alto nivel fueron diseñados para evitar especiales características técnicas ordenadores específicos. Y el lenguaje ensamblador, a su vez, está diseñado para las especificaciones específicas del procesador. Por lo tanto, para escribir un programa en lenguaje ensamblador para una computadora en particular, uno debe conocer su arquitectura. Hoy en día, la vista de los principales producto de software es un archivo EXE. Considerando lados positivos esto, el autor del programa puede estar seguro de su inviolabilidad. Pero muchas veces esto está lejos de ser el caso. También hay un desensamblador. Con la ayuda de un desensamblador, puede encontrar interrupciones y códigos de programa. No será difícil para una persona versada en ensamblador rehacer todo el programa a su gusto. Quizás de ahí viene el problema más insoluble: el virus. ¿Por qué la gente escribe un virus? Algunos hacen esta pregunta con sorpresa, otros con ira, pero sin embargo todavía hay personas que están interesadas en esta tarea no desde el punto de vista de causar algún daño, sino como un interés en la programación del sistema. Escribir virus en diferentes razones. A algunos les gustan las llamadas al sistema, otros mejoran sus conocimientos en ensamblador. Trataré de explicar todo esto en mi Papel a plazo. También dice no solo sobre la estructura del archivo EXE, sino también sobre el lenguaje ensamblador.

^ Lenguaje ensamblador.

Es interesante seguir, desde la aparición de los primeros ordenadores hasta la actualidad, la transformación de las ideas sobre el lenguaje ensamblador entre los programadores.

Érase una vez, el ensamblador era un lenguaje sin saberlo, era imposible hacer que una computadora hiciera algo útil. Poco a poco la situación cambió. Aparecieron medios más convenientes de comunicación con una computadora. Pero, a diferencia de otros idiomas, el ensamblador no murió, además, en principio no podía hacer esto. ¿Por qué? En busca de una respuesta, intentaremos comprender qué es el lenguaje ensamblador en general.

En resumen, el lenguaje ensamblador es una representación simbólica del lenguaje máquina. Todos los procesos en la máquina en el nivel de hardware más bajo son impulsados ​​​​solo por comandos (instrucciones) del lenguaje de la máquina. De esto queda claro que, a pesar del nombre común, el lenguaje ensamblador para cada tipo de computadora es diferente. Esto también se aplica apariencia programas escritos en ensamblador, y las ideas que este lenguaje refleja.

Es imposible resolver realmente problemas relacionados con el hardware (o incluso, además, relacionados con el hardware, como mejorar la velocidad de un programa), sin conocimientos de ensamblador.

Un programador o cualquier otro usuario puede usar cualquier herramienta de alto nivel, hasta programas para construir mundos virtuales y, tal vez, ni siquiera sospechar que la computadora está ejecutando realmente no los comandos del lenguaje en el que está escrito su programa, pero su representación transformada en forma de secuencias aburridas y aburridas de comandos de un lenguaje completamente diferente: lenguaje de máquina. Ahora imaginemos que ese usuario tiene un problema no estándar o simplemente algo salió mal. Por ejemplo, su programa debe funcionar con algún dispositivo inusual o realizar otras acciones que requieren el conocimiento de los principios del hardware de la computadora. No importa cuán inteligente sea un programador, no importa cuán bueno sea el lenguaje en el que escribió su maravilloso programa, no puede prescindir del conocimiento de ensamblador. Y no es casualidad que casi todos los compiladores de lenguajes de alto nivel contengan medios para conectar sus módulos con módulos en ensamblador o admitan el acceso al nivel de programación en ensamblador.

Por supuesto, la época de los vagones informáticos ya pasó. Como dice el refrán, no puedes abrazar la inmensidad. Pero hay algo en común, una especie de base sobre la que se construye cualquier educación informática seria. Este es el conocimiento sobre los principios de operación de la computadora, su arquitectura y lenguaje ensamblador como reflejo y encarnación de este conocimiento.

Una computadora moderna típica (basada en i486 o Pentium) consta de los siguientes componentes (Figura 1).

Arroz. 1. Computadora y periféricos

Arroz. 2. Diagrama de bloques computadora personal

De la figura (Figura 1) se puede ver que la computadora se compone de varios dispositivos físicos, cada uno de los cuales está conectado a una unidad, llamada unidad del sistema. Lógicamente, está claro que juega el papel de algún dispositivo de coordinación. Miremos dentro de la unidad del sistema (no es necesario intentar ingresar al monitor; no hay nada interesante allí, además de que es peligroso): abrimos la carcasa y vemos algunas placas, bloques, cables de conexión. Para entender su propósito funcional, veamos el diagrama de bloques de una computadora típica (Fig. 2). No pretende una precisión absoluta y solo pretende mostrar el propósito, la interconexión y la composición típica de los elementos de una computadora personal moderna.

Analicemos el diagrama de la Fig. 2 en un estilo poco convencional.
Es la naturaleza humana, al encontrarse con algo nuevo, buscar algunas asociaciones que puedan ayudarlo a conocer lo desconocido. ¿Qué asociaciones evoca la computadora? Para mí, por ejemplo, la computadora se asocia a menudo con la persona misma. ¿Por qué?

Una persona que creaba una computadora en algún lugar en lo más profundo de sí mismo pensó que estaba creando algo similar a sí mismo. La computadora tiene órganos de percepción de información del mundo exterior: este es un teclado, un mouse, unidades de disco magnético. En la fig. 2 estos órganos están ubicados a la derecha de los buses del sistema. La computadora tiene órganos que "digieren" la información recibida; estos son UPC y memoria de trabajo. Y, finalmente, la computadora tiene órganos de voz que dan los resultados del procesamiento. Estos son también algunos de los dispositivos de la derecha.

Computadoras modernas, por supuesto, lejos de ser humano. Pueden compararse con seres que interactúan con el mundo exterior al nivel de un conjunto grande pero limitado de reflejos no condicionados.
Este conjunto de reflejos forma un sistema de instrucciones de máquina. No importa qué tan alto sea el nivel de comunicación con una computadora, al final todo se reduce a una secuencia aburrida y monótona de instrucciones de máquina.
Cada comando de máquina es una especie de estímulo para la excitación de tal o cual reflejo incondicionado. La reacción a este estímulo siempre es inequívoca y está "programada" en el bloque de microcomandos en forma de microprograma. Este microprograma implementa acciones para la implementación de un comando de máquina, pero ya al nivel de señales dadas a ciertos lógica computadora, controlando así los diversos subsistemas de la computadora. Este es el llamado principio de control de microprogramas.

Continuando con la analogía con una persona, notamos: para que una computadora coma correctamente, se han inventado muchos sistemas operativos, compiladores para cientos de lenguajes de programación, etc.. Pero todos ellos son, de hecho, solo un plato en el que La comida (programas) se entrega de acuerdo con ciertas reglas estomacales (computadora). Solo el estómago de una computadora ama la comida dietética y monótona: bríndele información estructurada, en forma de secuencias estrictamente organizadas de ceros y unos, cuyas combinaciones forman el lenguaje de la máquina.

Por lo tanto, al ser aparentemente políglota, la computadora solo entiende un idioma: el idioma de las instrucciones de la máquina. Por supuesto, para comunicarse y trabajar con una computadora, no es necesario conocer este lenguaje, pero casi cualquier programador profesional tarde o temprano se enfrenta a la necesidad de aprenderlo. Afortunadamente, el programador no necesita tratar de averiguar el significado de varias combinaciones de números binarios, ya que en los años 50, los programadores comenzaron a usar el análogo simbólico del lenguaje de máquina para programar, que se denominó lenguaje ensamblador. Este lenguaje refleja con precisión todas las características del lenguaje máquina. Por eso, a diferencia de los lenguajes de alto nivel, el lenguaje ensamblador es diferente para cada tipo de computadora.

De lo anterior, podemos concluir que, dado que el lenguaje ensamblador para la computadora es “nativo”, solo se puede escribir en él el programa más eficiente (siempre que esté escrito por un programador calificado). Aquí hay un pequeño "pero": este es un proceso muy laborioso que requiere mucha atención y experiencia práctica. Por lo tanto, en realidad, el ensamblador escribe principalmente programas que deberían proporcionar trabajo eficiente con herrajes A veces, las partes críticas del programa en términos de tiempo de ejecución o consumo de memoria se escriben en ensamblador. Posteriormente, se realizan en forma de subrutinas y se combinan con código en un lenguaje de alto nivel.

Tiene sentido comenzar a aprender el lenguaje ensamblador de cualquier computadora solo después de descubrir qué parte de la computadora queda visible y disponible para programar en este lenguaje. Este es el llamado modelo de programa de computadora, parte del cual es el modelo de programa de microprocesador, que contiene 32 registros que están más o menos disponibles para que los use el programador.

Estos registros se pueden dividir en dos grandes grupos:

^16 registros personalizados;

16 registros del sistema.

Los programas en lenguaje ensamblador usan registros en gran medida. La mayoría de los registros tienen un propósito funcional específico.

Como su nombre lo indica, los registros de usuario se llaman porque el programador puede usarlos al escribir sus programas. Estos registros incluyen (Fig. 3):

Ocho registros de 32 bits que los programadores pueden usar para almacenar datos y direcciones (también llamados registros de propósito general (RON)):

registros de seis segmentos: cs, ds, ss, es, fs, gs;

registros de estado y control:

Las banderas registran eflags/banderas;

registro de puntero de comando eip/ip.

Arroz. 3. Registros de usuarios de microprocesadores i486 y Pentium

¿Por qué muchos de estos registros se muestran con una barra oblicua? No, estos no son registros diferentes, son partes de un gran registro de 32 bits. Se pueden utilizar en el programa como objetos separados. Esto se hizo para garantizar la operatividad de los programas escritos para los modelos de microprocesador de 16 bits más jóvenes de Intel, comenzando con el i8086. Los microprocesadores i486 y Pentium tienen en su mayoría registros de 32 bits. Su número, a excepción de los registros de segmento, es el mismo que el del i8086, pero la dimensión es mayor, lo que se refleja en sus designaciones: tienen
prefijo e (Extendido).

^ Registros de propósito general
Todos los registros de este grupo permiten acceder a sus partes “inferiores” (ver Fig. 3). Mientras observa esta figura, tenga en cuenta que solo las partes inferiores de 16 y 8 bits de estos registros se pueden usar para el autodireccionamiento. Los 16 bits superiores de estos registros no están disponibles como objetos independientes. Esto se hace, como señalamos anteriormente, por compatibilidad con los modelos de microprocesador de 16 bits más jóvenes de Intel.

Enumeremos los registros pertenecientes al grupo de registros de propósito general. Dado que estos registros están ubicados físicamente en el microprocesador dentro de la unidad lógica aritmética (ALU), también se denominan registros ALU:

eax/ax/ah/al (registro acumulador) - acumulador.
Se utiliza para almacenar datos intermedios. En algunos comandos, el uso de este registro es obligatorio;

ebx/bx/bh/bl (Registro base) - registro base.
Se utiliza para almacenar la dirección base de algún objeto en la memoria;

ecx/cx/ch/cl (registro de conteo) - registro de contador.
Se utiliza en comandos que realizan algunas acciones repetitivas. Su uso suele estar implícito y oculto en el algoritmo del comando correspondiente.
Por ejemplo, el comando de organización de bucles, además de transferir el control a un comando ubicado en una determinada dirección, analiza y decrementa en uno el valor del registro ecx/cx;

edx/dx/dh/dl (Registro de datos) - registro de datos.
Al igual que el registro eax/ax/ah/al, almacena datos intermedios. Algunos comandos requieren su uso; para algunos comandos esto sucede implícitamente.

Los dos registros siguientes se utilizan para admitir las llamadas operaciones en cadena, es decir, operaciones que procesan secuencialmente cadenas de elementos, cada uno de los cuales puede tener 32, 16 u 8 bits de longitud:

esi/si (registro de índice de origen) - índice de origen.
Este registro en operaciones en cadena contiene la dirección actual del elemento en la cadena fuente;

edi/di (registro de índice de destino) - índice del receptor (destinatario).
Este registro en operaciones en cadena contiene la dirección actual en la cadena de destino.

En la arquitectura del microprocesador a nivel de hardware y software, se admite una estructura de datos como una pila. Para trabajar con la pila en el sistema de instrucciones del microprocesador hay comandos especiales, y en el modelo de software del microprocesador hay registros especiales para esto:

esp/sp (registro de puntero de pila) - registro de puntero de pila.
Contiene un puntero a la parte superior de la pila en el segmento de pila actual.

ebp/bp (registro de puntero base): registro de puntero base del marco de pila.
Diseñado para organizar el acceso aleatorio a los datos dentro de la pila.

Una pila es un área de programa para el almacenamiento temporal de datos arbitrarios. Por supuesto, los datos también se pueden almacenar en el segmento de datos, pero en este caso, para cada dato almacenado temporalmente, se debe crear una celda de memoria separada con nombre, lo que aumenta el tamaño del programa y la cantidad de nombres utilizados. La conveniencia de la pila es que su área se reutiliza, y el almacenamiento de datos en la pila y su obtención desde allí se realiza mediante comandos push y pop eficientes sin especificar ningún nombre.
La pila se utiliza tradicionalmente, por ejemplo, para almacenar el contenido de los registros utilizados por el programa antes de llamar a una subrutina que, a su vez, utilizará los registros del procesador "para sus propios fines". El contenido original de los registros se filtra de la pila al regresar de la subrutina. Otra técnica común es pasar los parámetros que requiere a una subrutina a través de la pila. La subrutina, sabiendo en qué orden se colocan los parámetros en la pila, puede tomarlos de allí y usarlos en su ejecución. Rasgo distintivo stack es una especie de orden de muestreo de los datos contenidos en él: en cualquier momento, solo el elemento superior está disponible en la pila, es decir, el último elemento cargado en la pila. Sacar el elemento superior de la pila hace que el siguiente elemento esté disponible. Los elementos de la pila se ubican en el área de memoria asignada para la pila, comenzando desde la parte inferior de la pila (es decir, desde su dirección máxima) hacia direcciones decrecientes sucesivamente. La dirección del elemento superior accesible se almacena en el registro de puntero de pila SP. Como cualquier otra área de la memoria del programa, la pila debe incluirse en algún segmento o formar un segmento separado. En cualquier caso, la dirección de segmento de ese segmento se coloca en el registro de pila de segmento SS. Por lo tanto, un par de registros SS:SP describe la dirección de una celda de pila disponible: SS almacena la dirección del segmento de la pila y SP almacena el desplazamiento de los últimos datos almacenados en la pila (Fig. 4, a). Prestemos atención al hecho de que en el estado inicial, el puntero de pila SP apunta a una celda que se encuentra debajo de la parte inferior de la pila y no está incluida en ella.

Fig 4. Organización de la pila: a - estado inicial, b - después de cargar un elemento (en este ejemplo, el contenido del registro AX), c - después de cargar el segundo elemento (contenido del registro DS), d - después de descargar uno elemento, e - después de descargar dos elementos y volver al estado original.

La carga en la pila se lleva a cabo mediante un comando especial de pila de empuje. Esta instrucción primero disminuye el contenido del puntero de pila en 2 y luego coloca el operando en la dirección en SP. Si, por ejemplo, queremos guardar temporalmente el contenido del registro AX en la pila, debemos ejecutar el comando

La pila pasa al estado que se muestra en la Fig. 1.10, b. Se puede ver que el puntero de la pila se desplaza dos bytes hacia arriba (hacia direcciones más bajas) y el operando especificado en el comando push se escribe en esta dirección. El siguiente comando para cargar en la pila, por ejemplo,

moverá la pila al estado que se muestra en la Fig. 1.10, c. La pila ahora tendrá dos elementos, y solo se accederá al superior, señalado por el puntero de pila SP. Si después de un tiempo necesitamos restaurar el contenido original de los registros guardados en la pila, debemos ejecutar los comandos pop (pop) desde la pila:

estallar DS
pop hacha

¿Qué tan grande debe ser la pila? Depende de la intensidad con la que se utilice en el programa. Si, por ejemplo, planea almacenar una matriz de 10 000 bytes en la pila, entonces la pila debe tener al menos ese tamaño. Debe tenerse en cuenta que, en algunos casos, el sistema utiliza automáticamente la pila, en particular, al ejecutar el comando de interrupción int 21h. Con este comando, el procesador primero empuja la dirección de retorno a la pila y luego DOS empuja allí el contenido de los registros y otra información relacionada con el programa interrumpido. Por lo tanto, incluso si el programa no usa la pila en absoluto, aún debe estar presente en el programa y tener un tamaño de al menos varias decenas de palabras. En nuestro primer ejemplo, pusimos 128 palabras en la pila, lo que definitivamente es suficiente.

^ Estructura del programa de montaje

Un programa en lenguaje ensamblador es una colección de bloques de memoria llamados segmentos de memoria. Un programa puede constar de uno o más de estos segmentos de bloque. Cada segmento contiene una colección de oraciones de lenguaje, cada una de las cuales ocupa una línea separada de código de programa.

Las declaraciones de ensamblaje son de cuatro tipos:

comandos o instrucciones que son contrapartes simbólicas de las instrucciones de máquina. Durante el proceso de traducción, las instrucciones de ensamblaje se convierten en los comandos correspondientes del conjunto de instrucciones del microprocesador;

macrocomandos: oraciones del texto del programa que están diseñadas de cierta manera y se reemplazan por otras oraciones durante la traducción;

directivas que le dicen al compilador del ensamblador que realice alguna acción. Las directivas no tienen equivalentes en la representación de máquinas;

líneas de comentarios que contengan cualquier carácter, incluidas las letras del alfabeto ruso. Los comentarios son ignorados por el traductor.

^ Sintaxis del lenguaje ensamblador

Las oraciones que componen un programa pueden ser una construcción sintáctica correspondiente a un comando, macro, directiva o comentario. Para que el traductor ensamblador los reconozca, deben estar formados de acuerdo con ciertas reglas sintácticas. Para ello, lo mejor es utilizar una descripción formal de la sintaxis del lenguaje, como las reglas de la gramática. Las formas más comunes de describir un lenguaje de programación de esta manera son los diagramas de sintaxis y las formas extendidas de Backus-Naur. Para uso práctico los diagramas de sintaxis son más convenientes. Por ejemplo, la sintaxis de las declaraciones en lenguaje ensamblador se puede describir utilizando los diagramas de sintaxis que se muestran en las siguientes figuras.

Arroz. 5. Formato de oraciones en ensamblador

Arroz. 6. Directrices de formato

Arroz. 7. Formato de comandos y macros

En estos dibujos:

nombre de la etiqueta: un identificador cuyo valor es la dirección del primer byte de la oración del código fuente del programa que denota;

nombre - un identificador que distingue esta directiva de otras directivas del mismo nombre. Como resultado del procesamiento por parte del ensamblador de una determinada directiva, se pueden asignar ciertas características a este nombre;

el código de operación (COP) y la directiva son designaciones mnemotécnicas de la correspondiente instrucción de máquina, instrucción de macro o directiva de traducción;

operandos: partes de las directivas de comando, macro o ensamblador, que denotan objetos en los que se realizan operaciones. Los operandos del ensamblador se describen mediante expresiones con constantes numéricas y de texto, etiquetas de variables e identificadores que utilizan signos de operación y algunas palabras reservadas.

^ ¿Cómo usar diagramas de sintaxis? Es muy simple: todo lo que necesita hacer es encontrar y luego seguir la ruta desde la entrada del diagrama (izquierda) hasta su salida (derecha). Si tal camino existe, entonces la oración o construcción es sintácticamente correcta. Si no existe tal ruta, el compilador no aceptará esta construcción. Cuando trabaje con diagramas de sintaxis, preste atención a la dirección del bypass indicada por las flechas, ya que entre los caminos puede haber aquellos que se pueden seguir de derecha a izquierda. De hecho, los diagramas sintácticos reflejan la lógica del traductor al analizar las oraciones de entrada del programa.

Los caracteres permitidos al escribir el texto de los programas son:

Todo letras: A-Z, a-z. En este caso, las letras mayúsculas y minúsculas se consideran equivalentes;

Números del 0 al 9;

¿Signos?, @, $, _, &;

Separadores, . ()< > { } + / * % ! " " ? \ = # ^.

Las oraciones ensambladoras se forman a partir de lexemas, que son secuencias sintácticamente inseparables de símbolos de lenguaje válidos que tienen sentido para el traductor.

Las fichas son:

Los identificadores son secuencias de caracteres válidos que se utilizan para designar objetos de programa, como códigos de operación, nombres de variables y nombres de etiquetas. La regla para escribir identificadores es la siguiente: un identificador puede constar de uno o más caracteres. Como caracteres, puede usar letras del alfabeto latino, números y algunos caracteres especiales: _, ?, $, @. Un identificador no puede comenzar con un carácter de dígito. La longitud del identificador puede ser de hasta 255 caracteres, aunque el traductor acepta solo los primeros 32 e ignora el resto. Puede ajustar la longitud de los posibles identificadores utilizando la opción línea de comando m.v. Además, es posible decirle al traductor que distinga entre letras mayúsculas y minúsculas o que ignore su diferencia (lo que se hace de forma predeterminada).

^ Comandos en lenguaje ensamblador.

Los comandos del ensamblador abren la posibilidad de transferir sus requisitos a la computadora, el mecanismo para transferir el control en el programa (bucles y saltos) para comparaciones lógicas y organización del programa. Sin embargo, las tareas de programación rara vez son tan simples. La mayoría de los programas contienen una serie de bucles en los que se repiten varias instrucciones hasta que se alcanza un determinado requisito y varias comprobaciones para determinar cuál de las diversas acciones realizar. Algunos comandos pueden transferir el control cambiando la secuencia normal de pasos modificando directamente el valor de compensación en el puntero del comando. Como se mencionó anteriormente, existen diferentes comandos para diferentes procesadores, pero consideraremos algunos comandos para los procesadores 80186, 80286 y 80386.

Para describir el estado de las banderas después de ejecutar un determinado comando, usaremos una selección de la tabla que refleja la estructura del registro de banderas eflags:

La fila inferior de esta tabla enumera los valores de las banderas después de ejecutar el comando. En este caso, se utilizan las siguientes notaciones:

1: después de ejecutar el comando, se establece la bandera (igual a 1);

0: después de ejecutar el comando, la bandera se restablece (igual a 0);

r: el valor de la bandera depende del resultado del comando;

Después de ejecutar el comando, la bandera no está definida;

espacio: después de ejecutar el comando, la bandera no cambia;

La siguiente notación se utiliza para representar operandos en diagramas de sintaxis:

r8, r16, r32 - operando en uno de los registros de tamaño byte, palabra o palabra doble;

m8, m16, m32, m48 - operando en tamaño de memoria de bytes, palabra, palabra doble o 48 bits;

i8, i16, i32 - operando inmediato de tamaño byte, palabra o palabra doble;

a8, a16, a32 - dirección relativa (desplazamiento) en el segmento de código.

Comandos (en orden alfabético):

*Estos comandos se describen en detalle.

AGREGAR
(Suma)

Suma

^ Esquema de comando:

añadir destino, fuente

Propósito: adición de dos operandos de origen y destino de dimensiones de byte, palabra o palabra doble.

Algoritmo de trabajo:

agregue los operandos de origen y destino;

escribir el resultado de la suma al receptor;

establecer banderas.

Estado de las banderas después de la ejecución del comando:

Solicitud:
El comando add se utiliza para sumar dos operandos enteros. El resultado de la suma se coloca en la dirección del primer operando. Si el resultado de la suma va más allá de los límites del operando de destino (se produce un desbordamiento), esta situación debe tenerse en cuenta analizando el indicador cf y luego posiblemente utilizando el comando adc. Por ejemplo, agreguemos los valores en el registro ax y el área de memoria ch. Al agregar, debe tener en cuenta la posibilidad de desbordamiento.

Registro más registro o memoria:

|000000dw|modregr/rm|

Registro AX (AL) más valor inmediato:

|0000010w|--datos--|datos si w=1|

Registro o memoria más valor inmediato:

|100000sw|mod000r/m|--datos--|datos si BW=01|

LLAMAR
(LLAMAR)

Llamar a un procedimiento o tarea

^ Esquema de comando:

Objetivo:

transferencia de control a un procedimiento cercano o lejano con el almacenamiento de la dirección del punto de retorno en la pila;

cambiar de tarea.

Algoritmo de trabajo:
determinado por el tipo de operando:

La etiqueta está cerca: el contenido del puntero de comando eip / ip se coloca en la pila y se carga un nuevo valor de dirección correspondiente a la etiqueta en el mismo registro;

Etiqueta lejana: el contenido del puntero de comando eip/ip y cs se coloca en la pila. Luego, los nuevos valores de dirección correspondientes a la marca lejana se cargan en los mismos registros;

R16, 32 o m16, 32: define un registro o celda de memoria que contiene compensaciones en el segmento de instrucción actual, donde se transfiere el control. Cuando se transfiere el control, el contenido del puntero de comando eip/ip se coloca en la pila;

Puntero de memoria: define una ubicación de memoria que contiene un puntero de 4 o 6 bytes al procedimiento que se llama. La estructura de dicho puntero es 2+2 o 2+4 bytes. La interpretación de dicho puntero depende del modo de funcionamiento del microprocesador:

^ Estado de las banderas después de la ejecución del comando (excepto el cambio de tarea):

la ejecución del comando no afecta a las banderas

Cuando se cambia una tarea, los valores de las banderas se cambian de acuerdo con la información sobre el registro de banderas electrónicas en el segmento de estado TSS de la tarea a la que se cambia.
Solicitud:
El comando de llamada le permite organizar una transferencia de control flexible y multivariante a una subrutina manteniendo la dirección del punto de retorno.

Código objeto (cuatro formatos):

Direccionamiento directo en un segmento:

|11101000|disp-bajo|dip-alto|

Direccionamiento indirecto en un segmento:

|11111111|mod010r/m|

Direccionamiento indirecto entre segmentos:

|11111111|mod011r/m|

Direccionamiento directo entre segmentos:

|10011010|desplazamiento-bajo|desplazamiento-alto|seg-bajo|seg-alto|

CMP
(comparar operandos)

Comparación de operandos

^ Esquema de comando:

cmp operando1, operando2

Propósito: comparación de dos operandos.

Algoritmo de trabajo:

realizar la resta (operando1-operando2);

dependiendo del resultado, establezca banderas, no cambie el operando1 y el operando2 (es decir, no almacene el resultado).

Solicitud:
Este comando se utiliza para comparar dos operandos por resta, mientras que los operandos no cambian. Los indicadores se establecen como resultado de la ejecución del comando. La instrucción cmp se usa con las instrucciones de salto condicional y la instrucción set byte por valor setcc.

Código objeto (tres formatos):

Registro o Memoria Registrada:

|001110dw|modreg/m|

Valor inmediato con registro AX (AL):

|0011110w|--datos--|datos si w=1|

Valor inmediato con registro o memoria:

|100000sw|mod111r/m|--datos--|datos si sw=0|

DIC
(Disminuir el operando en 1)

Operando decrementado en uno

^ Esquema de comando:

operando dec

Propósito: disminuir el valor del operando en memoria o registro en 1.

Algoritmo de trabajo:
la instrucción resta 1 al operando. Estado de las banderas después de la ejecución del comando:

Solicitud:
El comando dec se usa para disminuir el valor de un byte, palabra, palabra doble en memoria o registro en uno. Tenga en cuenta que el comando no afecta a la bandera cf.

Registro: |01001reg|

^ Registro o memoria: |1111111w|mod001r/m|

DIV
(DIVide sin firmar)

División sin firmar

Esquema de comando:

divisor div

Propósito: realizar una operación de división en dos valores binarios sin signo.

^ Algoritmo de trabajo:
El comando requiere dos operandos: el dividendo y el divisor. El dividendo se especifica implícitamente y su tamaño depende del tamaño del divisor, que se especifica en el comando:

si el divisor está en bytes, entonces el dividendo debe ubicarse en el registro ax. Después de la operación, el cociente se coloca en al y el resto en ah;

si el divisor es una palabra, entonces el dividendo debe ubicarse en el par de registros dx:ax, con la parte baja del dividendo en ax. Después de la operación, el cociente se coloca en ax y el resto en dx;

si el divisor es una palabra doble, entonces el dividendo debe ubicarse en el par de registros edx:eax, con la parte baja del dividendo en eax. Después de la operación, el cociente se coloca en eax y el resto en edx.

^ Estado de las banderas después de la ejecución del comando:

Solicitud:
El comando realiza una división entera de los operandos, devolviendo el resultado de la división como cociente y el resto de la división. Al realizar una operación de división, puede ocurrir una excepción: 0 - error de división. Esta situación ocurre en uno de dos casos: el divisor es 0 o el cociente es demasiado grande para caber en el registro eax/ax/al.

Código de objeto:

|1111011w|mod110r/m|

EN T
(Interrumpir)

Llamar a una rutina de servicio de interrupción

^ Esquema de comando:

int interrupt_number

Propósito: llamar a la rutina de servicio de interrupción con el número de interrupción especificado por el operando de instrucción.

^ Algoritmo de trabajo:

empuje el registro eflags/flags y la dirección de retorno en la pila. Al escribir la dirección de retorno, primero se escribe el contenido del registro del segmento cs, luego el contenido del puntero de comando eip/ip;

restablecer los indicadores if y tf a cero;

transferir el control al manejador de interrupciones con el número especificado. El mecanismo de transferencia de control depende del modo de funcionamiento del microprocesador.

^ Estado de las banderas después de la ejecución del comando:

Solicitud:
Como puede ver en la sintaxis, hay dos formas de este comando:

int 3 - tiene su propio código de operación individual 0cch y ocupa un byte. Esta circunstancia hace que sea muy conveniente usarlo en varios depuradores de software para establecer puntos de interrupción reemplazando el primer byte de cualquier instrucción. El microprocesador, al encontrar un comando con el código de operación 0cch en la secuencia de comandos, llama al controlador de interrupción con el vector número 3, que sirve para comunicarse con el depurador de software.

La segunda forma de la instrucción tiene una longitud de dos bytes, tiene un código de operación de 0cdh y le permite iniciar una llamada a una rutina de servicio de interrupción con un número de vector en el rango 0-255. Las características de la transferencia de control, como se señaló, dependen del modo operativo del microprocesador.

Código objeto (dos formatos):

Registro: |01000reg|

^ Registro o memoria: |1111111w|mod000r/m|

JCC
JCXZ/JECXZ
(Saltar si condición)

(Saltar si CX=Cero/ Saltar si ECX=Cero)

Saltar si se cumple la condición

Saltar si CX/ECX es cero

^ Esquema de comando:

etiqueta jcc
etiqueta jcxz
etiqueta jecxz

Propósito: transición dentro del segmento actual de comandos, dependiendo de alguna condición.

^ Algoritmo de comando (excepto jcxz/jecxz):
Comprobación del estado de las banderas según el código de operación (refleja la condición que se está comprobando):

si la condición bajo prueba es verdadera, vaya a la celda indicada por el operando;

si la condición que se está comprobando es falsa, pase el control al siguiente comando.

Algoritmo de comando jcxz/jecxz:
Comprobando la condición de que el contenido del registro ecx/cx sea igual a cero:

si la condición marcada

Estructura de instrucciones en lenguaje ensamblador La programación a nivel de instrucciones de máquina es el nivel mínimo en el que es posible la programación de computadoras. El sistema de instrucciones de la máquina debe ser suficiente para implementar las acciones requeridas mediante la emisión de instrucciones al hardware de la máquina. Cada instrucción de máquina consta de dos partes: una parte operativa que define "qué hacer" y un operando que define los objetos de procesamiento, es decir, "qué hacer". La instrucción de máquina del microprocesador, escrita en lenguaje ensamblador, es una sola línea, que tiene la siguiente forma: etiqueta instrucción/directiva operando(s); comentarios La etiqueta, el comando/directiva y el operando están separados por al menos un espacio o carácter de tabulación. Los operandos de instrucción están separados por comas.

Estructura de una instrucción en lenguaje ensamblador Una instrucción en lenguaje ensamblador le dice al compilador qué acción debe realizar el microprocesador. Las directivas de ensamblaje son parámetros especificados en el texto del programa que afectan el proceso de ensamblaje o las propiedades del archivo de salida. El operando especifica el valor inicial de los datos (en el segmento de datos) o los elementos sobre los que actuará la instrucción (en el segmento de código). Una instrucción puede tener uno o dos operandos, o no tener operandos. El número de operandos está implícitamente especificado por el código de instrucción. Si el comando o la directiva deben continuar en la siguiente línea, se usa el carácter de barra invertida: "" . De forma predeterminada, el ensamblador no distingue entre letras mayúsculas y minúsculas en los comandos y directivas. Ejemplos de directivas y comandos Count db 1 ; Nombre, directiva, un operando mov eax, 0 ; Comando, dos operandos

Los identificadores son secuencias de caracteres válidos que se utilizan para designar nombres de variables y nombres de etiquetas. El identificador puede constar de uno o más de los siguientes caracteres: todas las letras del alfabeto latino; números del 0 al 9; caracteres especiales: _, @, $, ? . Se puede utilizar un punto como primer carácter de la etiqueta. Los nombres de ensamblador reservados (directivas, operadores, nombres de comando) no se pueden usar como identificadores. El primer carácter del identificador debe ser una letra o un carácter especial. Longitud máxima identificador 255 caracteres, pero el traductor acepta los primeros 32, el resto se ignora. Todas las etiquetas que se escriben en una línea que no contiene una directiva de ensamblador deben terminar con dos puntos ":". La etiqueta, el comando (directiva) y el operando no tienen que comenzar en ninguna posición particular de la cadena. Se recomienda escribirlos en una columna para una mayor legibilidad del programa.

Etiquetas Todas las etiquetas que se escriben en una línea que no contiene una directiva de ensamblador deben terminar con dos puntos ":". La etiqueta, el comando (directiva) y el operando no tienen que comenzar en ninguna posición particular de la cadena. Se recomienda escribirlos en una columna para una mayor legibilidad del programa.

Comentarios El uso de comentarios en un programa mejora su claridad, especialmente cuando el propósito de un conjunto de instrucciones no está claro. Los comentarios comienzan en cualquier línea de un módulo fuente con un punto y coma (;). Todos los caracteres a la derecha de "; ' al final de la línea hay comentarios. El comentario puede contener cualquier carácter imprimible, incluido "espacio". El comentario puede abarcar toda la línea o seguir el comando en la misma línea.

Estructura de un programa en lenguaje ensamblador Un programa en lenguaje ensamblador puede estar compuesto de varias partes, llamadas módulos, cada uno de los cuales puede definir uno o más segmentos de datos, pilas y códigos. Cualquier programa completo en lenguaje ensamblador debe incluir un módulo principal desde el cual comienza su ejecución. El módulo puede contener segmentos del programa, datos y segmentos de pila declarados usando las directivas apropiadas.

Modelos de memoria Antes de declarar segmentos, debe especificar el modelo de memoria mediante una directiva. Modificador MODEL memory_model, call_convention, OS_type, stack_parameter Modelos básicos de memoria en lenguaje ensamblador: Modelo de memoria Direccionamiento de código Direccionamiento de datos Sistema operativo Intercalado de datos y código PEQUEÑO CERCA DE MS-DOS Permitido PEQUEÑO CERCA DE MS-DOS, Windows No MEDIO LEJOS CERCA DE MS-DOS, Windows No COMPACTO CERCA DE LEJOS MS-DOS, Windows No GRANDE LEJOS MS-DOS, Windows No ENORME LEJOS MS-DOS, Windows No CERCA DE Windows 2000, Windows XP, Windows Permitido FLAT CERCA DE NT,

Modelos de memoria El modelo diminuto solo funciona en aplicaciones MS-DOS de 16 bits. En este modelo, todos los datos y el código residen en un segmento físico. El tamaño del archivo del programa en este caso no supera los 64 KB. El modelo pequeño admite un segmento de código y un segmento de datos. Los datos y el código cuando se usa este modelo se tratan como cerca (near). El modelo mediano admite varios segmentos de código y un segmento de datos, con todos los enlaces en los segmentos de código considerados lejanos (lejos) de forma predeterminada, y los enlaces en el segmento de datos se consideran cercanos (cerca). El modelo compacto admite varios segmentos de datos que usan direccionamiento de datos lejanos (lejos) y un segmento de código que usa direccionamiento de datos cercanos (cerca). El modelo grande admite múltiples segmentos de código y múltiples segmentos de datos. De forma predeterminada, todas las referencias de código y datos se consideran lejanas. El modelo enorme es casi equivalente al modelo de memoria grande.

Modelos de memoria El modelo plano asume una configuración de programa no segmentada y solo se usa en sistemas operativos de 32 bits. Este modelo es similar al modelo diminuto en que los datos y el código residen en el mismo segmento de 32 bits. Desarrollar un programa para el modelo plano antes de la directiva. modelo de piso se debe colocar una de las directivas: . 386, . 486, . 586 o. 686. La elección de la directiva de selección del procesador determina el conjunto de comandos disponibles al escribir programas. La letra p después de la directiva de selección del procesador significa modo de operación protegido. El direccionamiento de datos y códigos está cerca, con todas las direcciones y punteros de 32 bits.

modelos de memoria MODEL modificador memory_model, call_convention, OS_type, stack_parameter El parámetro modificador se usa para definir tipos de segmento y puede tomar los siguientes valores: use 16 (los segmentos del modelo seleccionado se usan como 16 bits) use 32 (los segmentos del modelo seleccionado se usan como 32 bits). El parámetro call_convention se usa para determinar cómo se pasan los parámetros cuando se llama a un procedimiento desde otros lenguajes, incluidos lenguajes de alto nivel (C++, Pascal). El parámetro puede tomar los siguientes valores: C, BASIC, FORTRAN, PASCAL, SYSCALL, STDCALL.

modelos de memoria Modificador MODEL memory_model, call_convention, OS_type, stack_parameter El parámetro OS_type es OS_DOS por defecto, y en este momento este es el único valor admitido para este parámetro. El parámetro stack_param se establece en: NEARSTACK (el registro SS es igual a DS, los datos y las regiones de la pila están ubicados en el mismo segmento físico) FARSTACK (el registro SS no es igual a DS, los datos y las regiones de la pila están ubicados en diferentes segmentos físicos). El valor predeterminado es NEARSTACK.

Un ejemplo de un programa de "no hacer nada". 686 P. MODELO FLAT, STDCALL. DATOS. CÓDIGO INICIO: RET END START RET - comando del microprocesador. Asegura la correcta terminación del programa. El resto del programa está relacionado con el funcionamiento del traductor. . 686 P: se permiten los comandos en modo protegido Pentium 6 (Pentium II). Esta directiva selecciona el conjunto de instrucciones del ensamblador compatible especificando el modelo de procesador. . MODEL FLAT, stdcall - modelo de memoria plana. Este modelo de memoria se utiliza en el sistema operativo Windows. stdcall es la convención de llamada de procedimiento a usar.

Un ejemplo de un programa de "no hacer nada". 686 P. MODELO FLAT, STDCALL. DATOS. CÓDIGO INICIO: RET FIN INICIO . DATOS - segmento de programa que contiene datos. Este programa no usa la pila, por lo que segment. Falta la PILA. . CÓDIGO - un segmento del programa que contiene el código. INICIO - etiqueta. END START: el final del programa y un mensaje al compilador de que el programa debe iniciarse desde la etiqueta START. Cada programa debe contener una directiva END que marque el final código fuente programas Se ignoran todas las líneas que siguen a la directiva END.La etiqueta después de la directiva END le dice al compilador el nombre del módulo principal desde el cual comienza la ejecución del programa. Si el programa contiene un módulo, se puede omitir la etiqueta después de la directiva END.

Traductores de lenguaje ensamblador Un traductor es un programa o medios tecnicos A que convierte un programa en uno de los lenguajes de programación en un programa en el idioma de destino, llamado código objeto. Además de admitir mnemónicos de instrucciones de máquina, cada traductor tiene su propio conjunto de directivas y macros, a menudo incompatibles con cualquier otra cosa. Los principales tipos de traductores de lenguaje ensamblador son: MASM (Microsoft Assembler), TASM (Borland Turbo Assembler), FASM (Flat Assembler) - un ensamblador multipaso de libre distribución escrito por Tomasz Gryshtar (polaco), NASM (Netwide Assembler) - un El ensamblador gratuito para la arquitectura Intel x 86 fue creado por Simon Tatham con Julian Hall y actualmente está siendo desarrollado por un pequeño equipo de desarrollo en Source. Fragua. neto.

Src="https://present5.com/presentation/-29367016_63610977/image-15.jpg" alt="Traducción del programa en Microsoft Visual Studio 2005 1) Cree un proyecto seleccionando Archivo->Nuevo->Proyecto menú y"> Трансляция программы в Microsoft Visual Studio 2005 1) Создать проект, выбрав меню File->New->Project и указав имя проекта (hello. prj) и тип проекта: Win 32 Project. В дополнительных опциях мастера проекта указать “Empty Project”.!}

Src="https://present5.com/presentation/-29367016_63610977/image-16.jpg" alt="Traducción del programa en Microsoft Visual Studio 2005 2) En el árbol del proyecto (Ver->Explorador de soluciones) agregue"> Трансляция программы в Microsoft Visual Studio 2005 2) В дереве проекта (View->Solution Explorer) добавить файл, в котором будет содержаться текст программы: Source. Files->Add->New. Item.!}

Traducción del programa en Microsoft Visual Studio 2005 3) Seleccione el tipo de archivo Código C++, pero especifique el nombre con la extensión. asm:

Traducción del programa en Microsoft Visual Studio 2005 5) Establecer las opciones del compilador. Seleccione con el botón derecho en el menú del archivo del proyecto Reglas de compilación personalizadas...

Traducción del programa en Microsoft Visual Studio 2005 y en la ventana que aparece, seleccione Microsoft Macro Assembler.

Traducción del programa en Microsoft Visual Studio 2005 Revisar con botón derecho en el archivo hola. asm del árbol del proyecto desde el menú Propiedades y establezca General->Herramienta: Microsoft Macro Assembler.

Src="https://present5.com/presentation/-29367016_63610977/image-22.jpg" alt="Traducción del programa en Microsoft Visual Studio 2005 6) Compile el archivo seleccionando Build->Build hello.prj ."> Трансляция программы в Microsoft Visual Studio 2005 6) Откомпилировать файл, выбрав Build->Build hello. prj. 7) Запустить программу, нажав F 5 или выбрав меню Debug->Start Debugging.!}

Programación en OS Windows La programación en OS Windows se basa en el uso de funciones API (Application Program Interface, es decir, interfaz de aplicación de software). Su número llega a 2000. El programa para Windows consiste en gran medida en este tipo de llamadas. Todas las interacciones con dispositivos externos y los recursos del sistema operativo se produce, por regla general, a través de tales funciones. sala de operaciones sistema de ventanas utiliza un modelo de memoria plana. La dirección de cualquier ubicación de memoria estará determinada por el contenido de un registro de 32 bits. Hay 3 tipos de estructuras de programas para Windows: diálogo (la ventana principal es un diálogo), consola o estructura sin ventanas, estructura clásica (ventana, marco).

Llamar características de Windows API En el archivo de ayuda, cualquier función API se representa como tipo nombre_función (FA 1, FA 2, FA 3) Tipo: tipo de valor de retorno; FAX es una lista de argumentos formales en el orden en que aparecen, por ejemplo, int Mensaje. Cuadro (HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); Esta función muestra una ventana con un mensaje y uno o más botones de salida. Significado de los parámetros: h. Wnd: identificador de la ventana en la que aparecerá la ventana del mensaje, lp. Texto - el texto que aparecerá en la ventana, lp. Título: texto en el título de la ventana, u. Tipo - tipo de ventana, en particular, puede especificar el número de botones de salida.

Llamar a las funciones de la API de Windows int Message. Cuadro (HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); Casi todos los parámetros de la función API son en realidad números enteros de 32 bits: HWND es un número entero de 32 bits, LPCTSTR es un puntero de cadena de 32 bits, UINT es un número entero de 32 bits. El sufijo "A" a menudo se agrega al nombre de las funciones para saltar a versiones más nuevas de funciones.

Llamar a las funciones de la API de Windows int Message. Cuadro (HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); Al usar MASM, debe agregar @N N al final del nombre: la cantidad de bytes que ocupan los argumentos pasados ​​en la pila. Para las funciones de la API Win 32, este número se puede definir como el número de argumentos n por 4 (bytes en cada argumento): N=4*n. Para llamar a una función se utiliza la instrucción CALL del ensamblador. En este caso, todos los argumentos de la función se le pasan a través de la pila (comando PUSH). Dirección de paso del argumento: DE IZQUIERDA A DERECHA - DE ABAJO ARRIBA. El argumento u se colocará primero en la pila. tipo. Llamar a la función especificada se verá así: Mensaje de LLAMADA. caja. [correo electrónico protegido]

Llamar a las funciones de la API de Windows int Message. Cuadro (HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); El resultado de ejecutar cualquier función API suele ser un número entero, que se devuelve en el registro EAX. La directiva OFFSET es un "desplazamiento de segmento" o, en términos de lenguaje de alto nivel, un "puntero" al comienzo de una cadena. La directiva EQU, como #define en C, define una constante. La directiva EXTERN le dice al compilador que una función o identificador es externo al módulo.

Un ejemplo del programa "¡Hola a todos!" . 686 P. MODELO FLAT, STDCALL. STACK 4096. DATA MB_OK EQU 0 STR 1 DB "Mi primer programa", 0 STR 2 DB "¡Hola a todos!", 0 HW DD ? Mensaje EXTERNO. caja. [correo electrónico protegido]: CERCA. CÓDIGO INICIO: PUSH MB_OK PUSH OFFSET STR 1 PUSH OFFSET STR 2 PUSH HW CALL Mensaje. caja. [correo electrónico protegido] RET FIN COMIENZO

La directiva INVOKE El traductor de lenguaje MASM también hace posible simplificar la llamada de función utilizando una herramienta macro: la directiva INVOKE: función INVOKE, parámetro1, parámetro2, ... No es necesario agregar @16 a la llamada de función; los parámetros se escriben exactamente en el orden en que se dan en la descripción de la función. Las macros traductoras insertan parámetros en la pila. para usar la directiva INVOKE, debe tener una descripción del prototipo de función usando la directiva PROTO en la forma: Mensaje. caja. UN PROTO: DWORD, : DWORD

Tema 2.5 Conceptos básicos de programación del procesador

A medida que aumenta la longitud del programa, se vuelve más difícil recordar los códigos para varias operaciones. Los mnemotécnicos proporcionan algo de ayuda en este sentido.

El lenguaje de codificación de instrucciones simbólicas se llama ensamblador.

lenguaje ensamblador es un lenguaje en el que cada instrucción corresponde exactamente a una instrucción de máquina.

Asamblea llamado convertir un programa del lenguaje ensamblador, es decir, preparar un programa en lenguaje de máquina reemplazando los nombres simbólicos de las operaciones con códigos de máquina y direcciones simbólicas con números absolutos o relativos, así como incluir programas de biblioteca y generar secuencias de instrucciones simbólicas especificando parámetros específicos en microinstrucciones. Este programa generalmente se coloca en la ROM o se ingresa a la RAM desde algún medio externo.

El lenguaje ensamblador tiene varias características que lo distinguen de los lenguajes de alto nivel:

1. Esta es una correspondencia uno a uno entre las declaraciones en lenguaje ensamblador y las instrucciones de la máquina.

2. El programador de lenguaje ensamblador tiene acceso a todos los objetos y comandos presentes en la máquina de destino.

Una comprensión de los conceptos básicos de la programación en lenguajes orientados a máquina es útil para:



Mejor comprensión de la arquitectura de la PC y mejor uso de las computadoras;

Desarrollar estructuras más racionales de algoritmos para programas para resolver problemas aplicados;

La capacidad de ver y corregir programas ejecutables con la extensión .exe y .com, compilados desde cualquier lenguaje de alto nivel, en caso de pérdida de los programas fuente (llamando estos programas al depurador del programa DEBUG y descompilando su visualización en lenguaje ensamblador );

Compilación de programas para resolver las tareas más críticas (un programa compilado en un lenguaje orientado a máquina suele ser más eficiente, más corto y más rápido en un 30-60 por ciento que los programas obtenidos como resultado de la traducción de lenguajes de alto nivel)

Para la implementación de procedimientos incluidos en el programa principal como fragmentos separados en caso de que no puedan implementarse ni en el lenguaje de alto nivel utilizado ni utilizando procedimientos de servicio del sistema operativo.

Un programa en lenguaje ensamblador solo puede ejecutarse en computadoras de la misma familia, mientras que un programa escrito en un lenguaje de alto nivel puede ejecutarse potencialmente en diferentes máquinas.

El alfabeto del lenguaje ensamblador se compone de caracteres ASCII.

Los números son sólo enteros. Distinguir:

Números binarios que terminan con la letra B;

Números decimales que terminan en D;

Números hexadecimales que terminan en la letra N.

RAM, registros, representación de datos

Para una determinada serie de MP, se utiliza un lenguaje de programación individual: lenguaje ensamblador.

El lenguaje ensamblador ocupa una posición intermedia entre los códigos de máquina y los lenguajes de alto nivel. Programar en este lenguaje es más fácil. Un programa en lenguaje ensamblador usa las capacidades de una máquina en particular (más precisamente, MP) de manera más racional que un programa en un lenguaje de alto nivel (que es más fácil para un programador que para un ensamblador). Consideraremos los principios básicos de la programación en lenguajes orientados a máquina utilizando como ejemplo el lenguaje ensamblador para MP KR580VM80. Para la programación en el lenguaje, se utiliza una técnica general. Las técnicas específicas para grabar programas están relacionadas con la arquitectura y las características del sistema de comando del MP de destino.

modelo de software sistema de microprocesador basado en MP KR580VM80

El modelo de programa del MPS de acuerdo con la Figura 1

Memoria de puertos MP

S Z C.A. PAG C

Foto 1

Desde el punto de vista del programador, el KR580VM80 MP tiene los siguientes registros accesibles por programa.

A– Registro acumulador de 8 bits. Es el registro principal de MP. Cualquier operación realizada en la ALU implica colocar uno de los operandos a procesar en el acumulador. El resultado de la operación en la ALU también suele almacenarse en A.

B, C, D, E, H, L– Registros de propósito general (RON) de 8 bits. memoria interna MP. Diseñado para almacenar la información procesada, así como los resultados de la operación. Al procesar palabras de 16 bits de los registros, se forman los pares BC, DE, HL, y el registro dual se llama la primera letra: B, D, H. En el par de registros, el primer registro es el más alto. Los registros H, L, utilizados tanto para almacenar datos como para almacenar direcciones de 16 bits de celdas RAM, tienen una propiedad especial.

Florida– registro de bandera (registro de características) Un registro de 8 bits que almacena cinco características del resultado de realizar operaciones aritméticas y lógicas en el MP. Formato FL según la imagen.

Bit C (CY - acarreo) - acarreo, establecido en 1 si hubo un acarreo del orden superior del byte al realizar operaciones aritméticas.

Bit P (paridad) - paridad, se establece en 1 si el número de unidades en los bits del resultado es par.

El bit AC es un acarreo adicional, diseñado para almacenar el valor de acarreo de la tétrada inferior del resultado.

Bit Z (cero): se establece en 1 si el resultado de la operación es 0.

El bit S (signo) se establece en 1 si el resultado es negativo y en 0 si el resultado es positivo.

SP-- el puntero de pila, un registro de 16 bits, está diseñado para almacenar la dirección de la ubicación de memoria donde se escribió el último byte ingresado en la pila.

RS– contador de programa (program counter), registro de 16 bits, diseñado para almacenar la dirección de la siguiente instrucción ejecutable. El contenido del contador del programa se incrementa automáticamente en 1 inmediatamente después de que se obtenga el siguiente byte de instrucción.

En el área de memoria inicial de la dirección 0000H - 07FF se encuentra programa de control y programas de demostración. Esta es el área de la ROM.

0800 - 0AFF - área de dirección para grabar los programas en estudio. (RAM).

0В00 - 0ВВ0 - área de dirección para registro de datos. (RAM).

0BB0 es la dirección inicial de la pila. (RAM).

Stack es un área de RAM especialmente organizada diseñada para el almacenamiento temporal de datos o direcciones. El último número colocado en la pila es el primer número sacado de la pila. El puntero de pila almacena la dirección de la última ubicación de pila donde se almacena la información. Cuando se llama a una subrutina, la dirección de retorno al programa principal se almacena automáticamente en la pila. Como regla general, al comienzo de cada subrutina, los contenidos de todos los registros involucrados en su ejecución se almacenan en la pila, y al final de la subrutina, se restauran desde la pila.

Formato de datos y estructura de comandos del lenguaje ensamblador

La memoria MP KR580VM80 es una matriz de palabras de 8 bits denominadas bytes.Cada byte tiene su propia dirección de 16 bits que determina su posición en la secuencia de celdas de memoria. El MP puede direccionar 65536 bytes de memoria, que pueden contener tanto ROM como RAM.

Formato de datos

Los datos se almacenan en la memoria como palabras de 8 bits:

D7 D6 D5 D4 D3 D2 D1 D0

El bit menos significativo es el bit 0, el bit más significativo es el bit 7.

El comando se caracteriza por el formato, es decir, el número de bits que se le asignan, que se dividen byte a byte en determinados campos funcionales.

Formato de comando

Los comandos MP KR580VM80 tienen formato de uno, dos o tres bytes. Las instrucciones de varios bytes deben colocarse en los PL vecinos. El formato del comando depende de los detalles de la operación que se está realizando.

El primer byte del comando contiene el código de operación escrito en forma mnemotécnica.

Define el formato del comando y las acciones que debe realizar el MP sobre los datos durante su ejecución, y el método de direccionamiento, y también puede contener información sobre la ubicación de los datos.

El segundo y tercer byte pueden contener datos sobre los que operar o direcciones que indican la ubicación de los datos. Los datos sobre los que se realizan las operaciones se denominan operandos.

Formato de comando de un solo byte según la Figura 2

Figura 4

En las instrucciones del lenguaje ensamblador, el código de operación tiene una forma abreviada de escribir palabras en inglés: una notación mnemotécnica. La mnemotécnica (del griego mnemónico - el arte de memorizar) facilita recordar los comandos de acuerdo con su propósito funcional.

Antes de la ejecución, el programa fuente se traduce mediante un programa de traducción, llamado ensamblador, al lenguaje de combinaciones de código - lenguaje de máquina, de esta forma se coloca en la memoria del MP y luego se usa al ejecutar el comando.


Métodos de direccionamiento

Todos los códigos de operandos (entrada y salida) deben estar ubicados en algún lugar. Pueden estar en los registros internos del MP (lo más conveniente y opción rápida). Se pueden ubicar en la memoria del sistema (la opción más común). Finalmente, pueden estar en dispositivos de E/S (el caso más raro). La ubicación de los operandos está determinada por el código de instrucción. Existir diferentes métodos, con lo que el código de instrucción puede determinar dónde tomar el operando de entrada y dónde colocar el operando de salida. Estos métodos se denominan métodos de direccionamiento.

Para MP KR580VM80 existen los siguientes métodos de direccionamiento:

Inmediato;

Registro;

indirecto;

Pila.

Inmediato el direccionamiento asume que el operando (entrada) está en la memoria inmediatamente después del código de instrucción. El operando suele ser una constante que debe enviarse a alguna parte, agregarse a algo, etc. Los datos están contenidos en el segundo o segundo y tercer byte del comando, con el byte de datos bajo en el segundo byte de comando y el byte de datos alto en el tercer byte de comando.

Derecho El direccionamiento (también conocido como absoluto) asume que el operando (entrada o salida) está ubicado en la memoria en la dirección cuyo código se encuentra dentro del programa inmediatamente después del código de instrucción. Se utiliza en comandos de tres bytes.

Registro el direccionamiento asume que el operando (entrada o salida) está en el registro MP interno. Usado en comandos de un solo byte

Indirecto El direccionamiento (implícito) supone que el registro interno del MP no es el operando en sí, sino su dirección en la memoria.

Pila direccionamiento asume que el comando no contiene una dirección. Direccionamiento a celdas de memoria por el contenido del registro SP de 16 bits (puntero de pila).

sistema de mando

El sistema de comando MP es una lista completa de acciones elementales que el MP es capaz de realizar. El MP controlado por estos comandos realiza acciones simples, como operaciones aritméticas y lógicas elementales, transferencia de datos, comparación de dos valores, etc. El número de comandos del MP KR580VM80 es 78 (incluidas las modificaciones 244).

Existen los siguientes grupos de comandos:

Transmisión de datos;

Aritmética;

Rompecabezas;

Comandos de salto;

Comandos de entrada-salida, control y trabajo con la pila.


Símbolos y abreviaturas utilizados para describir comandos y escribir programas

Símbolo Reducción
DIRECCIÓN dirección de 16 bits
DATOS datos de 8 bits
DATOS 16 datos de 16 bits
PUERTO Dirección de E/S de 8 bits (dispositivos de E/S)
BYTE 2 Segundo byte de comando
BYTE 3 Tercer byte de comando
R, R1, R2 Uno de los registros: A, B, C, D, E, H, L
PR Uno de los pares de registros: B - establece un par de aviones; D - establece un par de DE; H - especifica un par de HL
RH Primer registro de la pareja.
RL Segundo registro de la pareja
Λ multiplicación booleana
V suma booleana
Adición módulo dos
METRO Celda de memoria cuya dirección especifica el contenido del par de registros HL, es decir, M = (HL)



Arriba