Componentes básicos del lenguaje ensamblador y estructura de instrucción. Formato de datos y estructura de comandos en lenguaje ensamblador. En la disciplina "Programación de sistemas"

Introducción.

El lenguaje en el que está escrito el programa fuente se llama entrada idioma, y ​​el idioma al que se traduce para su ejecución por el procesador es en los días libres lengua. El proceso de convertir el lenguaje de entrada en lenguaje 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 programación, es necesaria la traducción de todos los programas fuente. Conocido dos caminos emisiones: recopilación e interpretación.

En Compilacion Primero, el programa fuente se traduce completamente a un programa equivalente en el lenguaje de salida, llamado objeto programa y luego ejecutado. Este proceso se implementa utilizando un especial programas, llamado compilador. Un compilador para el cual el lenguaje de entrada es una forma simbólica de representar el lenguaje de máquina (salida) de códigos binarios se llama ensamblador.

En interpretaciones Cada línea de texto en el programa fuente se analiza (interpreta) y el comando especificado en él se ejecuta inmediatamente. La implementación de este método está encomendada a programa de intérprete. La interpretación lleva mucho tiempo. Para aumentar su eficiencia, en lugar de procesar cada línea, el intérprete primero convierte todas equipo 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 idioma.

Características principales del ensamblador:

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

) se utilizan mnemónicos

Restas (

multiplicación (

Divisiones (

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

cada declaración corresponde un comando de máquina(código), es decir, existe una correspondencia uno a uno entre los comandos de la 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 bits del registro de bandera y el lenguaje de alto nivel (por ejemplo,

) no tiene esta habilidad. Tenga en cuenta que los lenguajes de programación de sistemas (por ejemplo, C) suelen ocupar una posición intermedia. En términos de accesibilidad, se acercan más al 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 este inconveniente.

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 ha recibido amplio uso debido a las siguientes circunstancias:

● un programa escrito en lenguaje ensamblador es mucho más pequeño y se ejecuta 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 compiladores), programas sobre tarjetas de crédito, celulares, controladores de dispositivos, etc.;

● algunos procedimientos requieren Acceso completo al hardware, lo que normalmente es imposible de hacer 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, sólo 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 tanto, para escribir un programa específico en condiciones reales se utiliza 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 (declaraciones, 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.

La columna 1 está asignada para el campo de etiqueta. La etiqueta es un nombre simbólico o identificador. direcciones memoria. Es necesario para que puedas:

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

● obtener acceso a la ubicación donde se almacenan los datos.

Estas declaraciones van acompañadas de una etiqueta. Para indicar un nombre se utilizan letras (mayúsculas) del alfabeto inglés y números. El nombre debe tener una letra al principio y dos puntos como separador al final. 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 de la columna 2, lo que simplifica el trabajo del compilador. La ausencia de dos puntos no permite distinguir una etiqueta de un código de operación si están ubicados 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 puede limitarse a 6 u 8 caracteres.

No debe haber nombres idénticos en el campo de etiqueta, ya que la etiqueta está asociada con direcciones de comando. Si durante la ejecución del programa no es necesario llamar un comando o datos de la memoria, entonces el campo de etiqueta permanece vacío.

Campo de código de operación.

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

Se selecciona el mnemotécnico para cargar un registro desde la memoria.

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

). En lenguajes ensambladores

para ambas operaciones puedes usar el mismo nombre, respectivamente

Si la elección de los nombres mnemotécnicos puede ser arbitraria, entonces la necesidad de utilizar dos instrucciones de máquina está determinada por la arquitectura del procesador.

La mnemónica de los registros también depende de la versión del ensamblador (Tabla 5.2.1).

Campo de operando.

aquí se encuentra información adicional, necesario para realizar la operación. En el campo de operando para comandos de salto se indica la dirección a la que se debe realizar el salto, así como las direcciones y registros que son operandos para el comando de la máquina. Como ejemplo, damos operandos que se pueden utilizar 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 binario, octal, hexadecimal y decimal (

No es necesario que lo escribas). Si el primer dígito de un número hexadecimal es A, B, C,

Luego se añade un insignificante 0 (cero) delante;

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

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

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

en sistema binario);

● identificadores,

para pares de aeronaves registrados,

Las primeras letras son B,

NORTE; para un par de acumulador y registro de atributos -

; para el contador de programa -

;para el puntero de la pila -

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

(si se cumple la condición) y transiciones incondicionales. Por ejemplo, el 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 utilizando operadores aritméticos y lógicos. Tenga en cuenta que el método para reservar espacio de datos depende de la versión del idioma. Desarrolladores de lenguaje ensamblador para

Defina la palabra), y luego ingresó Opción alternativa.

que estuvo en el lenguaje para procesadores desde el principio

En versión lingüística

usado

Definir una constante).

Los procesadores procesan operandos de diferentes longitudes. Para definirlo, los desarrolladores del ensamblador tomaron diferentes decisiones, por ejemplo:

Los registros II 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

Para tipo

; sufijo ".B" para tipo

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

) y palabras en un registro de 64 bits usando 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 humanos. Pueden ser necesarios para modificar un programa, que sin dichos comentarios puede resultar completamente incomprensible incluso para programadores experimentados. Un comentario comienza con un símbolo y se utiliza para explicar y documentar programas. El carácter inicial de un comentario puede ser:

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

Punto de exclamación(!) en idiomas para

Cada línea de comentario separada está precedida por un carácter principal.

Pseudocomandos (directivas).

En lenguaje ensamblador existen dos tipos principales de comandos:

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

pseudo-comandos o directivas, diseñado para dar servicio al proceso de traducción de un programa a un lenguaje de combinación de códigos. Como ejemplo en la tabla. 5.2.2 muestra algunos pseudocomandos del ensamblador

para la familia

.

Al 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 requerida de comandos cada vez que aparezca. Este enfoque conduce a un aumento en el volumen del programa;

● organizar esta secuencia en un procedimiento (subrutina) y llamarla si es necesario. Esta salida tiene sus inconvenientes: cada vez es necesario ejecutar un comando de llamada a un procedimiento especial y un comando de retorno, lo que, si la secuencia es corta y se usa con frecuencia, puede reducir en gran medida la velocidad del programa.

El más simple y método efectivo La repetición repetida de una cadena de comandos consiste en utilizar macro, que se puede representar como un pseudocomando diseñado para volver a traducir un grupo de comandos que se encuentran a menudo en un programa.

Una macro, o macrocomando, se caracteriza por tres aspectos: macrodefinición, macroinversión y macroextensión.

Definición de macros

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

La definición de macro tiene la siguiente estructura:

Lista de expresiones; Definición de macros

En la estructura dada de macrodefinición se pueden distinguir tres partes:

● título

macro, incluido el nombre

Pseudocomando

y un conjunto de parámetros;

● marcado con puntos cuerpo macro;

● equipo

graduación

Definiciones de macros.

El conjunto de parámetros de definición 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 proporcionaron anteriormente en el programa, no es necesario indicarlos en el encabezado de definición de macro.

Para volver a ensamblar el grupo de comandos seleccionado, se utiliza una apelación que consta del nombre.

comandos macro y lista de parámetros con otros valores.

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

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

Usar un nombre de macro como código de operación se llama macro-reversión(llamada de macro), y reemplazándolo con el cuerpo de la macro - expansión macroeconómica.

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

La expansión de macros ocurre durante el proceso de ensamblaje, no durante la ejecución del programa. Los métodos para manipular cadenas de caracteres se asignan a medios macro.

El proceso de montaje se lleva a cabo. en dos pases:

● en la primera pasada, se conservan todas las definiciones de macros y se expanden las llamadas de macros. En este caso, el programa original 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 el cuerpo de la macro;

● la segunda pasada procesa el programa resultante sin macros.

Macros con parámetros.

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

● con actual parámetros que se colocan en el campo 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 un parámetro real correspondiente.

usando macros con parámetros.

El programa 1 contiene dos secuencias similares de comandos, diferenciándose en que la primera 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 la llamada de macro

el programa 2 está marcado: P,

El primer parámetro real,

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 ampliadas.

Veamos algunas características avanzadas del lenguaje.

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

la etiqueta se declara local (

) y gracias a capacidades 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 característica avanzada es muy útil en combinación con el enlace condicional de un programa. Consideremos

SI TAMAÑO DE PALABRAS GT 16 M2 MACRO

La macro M2 se puede definir en ambas partes de la declaración.

Sin embargo, la definición depende del procesador en el que esté ensamblado el programa: 16 bits o 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 el último caso, para evitar un bucle sin fin, 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 recursividad cuando el parámetro alcanza un cierto valor.

Sobre el uso de medios macro en ensamblador.

Cuando se utilizan macros, el ensamblador debe poder realizar dos funciones: guardar definiciones de macros Y ampliar los desafíos 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 general en la que, junto con los nombres de las macros, se encuentran todas las instrucciones y directivas de la máquina.

Al encontrar una macro durante el ensamblaje es 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 macros donde se almacenará el cuerpo de la macro;

● lista formal parámetros.

Luego, el cuerpo de la macro, que es simplemente una cadena de caracteres, se lee y se almacena en la tabla de definición de macro. 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 utiliza como carácter de retorno de carro y el signo & se utiliza como carácter de parámetro formal.

Ampliación de llamadas de macros.

Siempre 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 deja temporalmente de leer los datos de entrada del dispositivo de entrada y comienza a leer el cuerpo de la macro almacenada. Los parámetros formales extraídos del cuerpo de la macro se reemplazan con parámetros reales y los proporciona la llamada. El símbolo comercial y anterior permite al ensamblador reconocerlos.

A pesar de que existen muchas versiones de ensamblador, los procesos de ensamblaje tienen características comunes y son similares en muchos aspectos. A continuación se analiza el funcionamiento de un ensamblador de dos pasos.

Ensamblador de dos pasadas.

Un programa consta de una serie de declaraciones. Por tanto, parecería que a la hora de realizar el montaje se puede utilizar la siguiente secuencia de acciones:

● traducirlo al lenguaje de máquina;

● transferir el código de máquina resultante a un archivo y la parte correspondiente del listado a otro archivo;

● repita los procedimientos enumerados hasta que se traduzca todo el programa.

Sin embargo, este enfoque no es eficaz. Un ejemplo es el llamado problema. enlace directo. Si la primera declaración es un salto a la declaración P, ubicada al final del programa, entonces el ensamblador no puede traducirla. Primero debe determinar la dirección del operador P y, para ello, debe leer el programa completo. Cada lectura completa del programa fuente se llama paso. Vamos a mostrar cómo se puede resolver el problema del enlace de anticipación usando dos pasos:

en la primera pasada deberías 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 una segunda pasada por el programa original requiere tiempo adicional dedicado a las operaciones de E/S;

● en la primera pasada debes convertir el programa en una forma intermedia y lo guarda en una tabla, y realiza la segunda pasada no según el programa original, sino según la tabla. Este método de ensamblaje ahorra tiempo, ya que la segunda pasada no realiza operaciones de E/S.

Primer pase.

Gol del primer pase- construir una tabla de símbolos. Como se señaló anteriormente, otro objetivo del primer paso es preservar todas las definiciones de macros y expandir las llamadas a medida que aparecen. En consecuencia, tanto la definición de símbolo como la expansión macro ocurren en una sola pasada. El símbolo puede ser etiqueta, o significado, al que se le asigna un nombre específico mediante la directiva -you:

;Valor - tamaño del búfer

Al asignar significado a los nombres simbólicos en el campo de etiqueta del comando, el ensamblador esencialmente especifica las direcciones que tendrá cada comando durante la ejecución del programa. Para ello, el ensamblador almacena el contador de direcciones de instrucciones(

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

Tabla de nombres simbólicos

contiene un elemento para cada nombre (Tabla 5.2.4). Cada elemento de la tabla de nombres simbólicos contiene el nombre en sí (o un puntero a él), 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,

Mesa directiva.

Esta tabla enumera todas las directivas, o pseudocomandos, que se encuentran 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 del comando y tipo de comando (Tabla 5.2.5). Los códigos de operación se dividen en grupos según el número y 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 de ese grupo.

Segundo pase.

Gol del segundo pase- creación de un programa objeto e impresión, si es necesario, del protocolo de montaje; genera la información necesaria para que el vinculador vincule procedimientos que se ensamblaron en diferentes momentos en un archivo ejecutable.

En la segunda pasada (como en la primera), las líneas que contienen las declaraciones se leen y procesan una por una. El operador original y el operador de salida derivado de él en hexadecimal. objeto El código se puede imprimir o colocar en un búfer para su posterior impresión. Después de restablecer el contador de dirección del comando, se llama al comando próxima declaración.

El programa fuente puede contener errores, por ejemplo:

el símbolo dado no está definido o está 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 encuentra una declaración de 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 QUE LLAMA EL NOMBRE DE MIRZO ULUGBEK

FACULTAD DE TECNOLOGÍA INFORMÁTICA

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

Terminado:

Taskent 2003.

Prefacio.

Lenguaje ensamblador y estructura de comando.

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

Estructura de archivos COM.

El principio de acción y propagación del virus.

Desmontador.

Programas.

Prefacio

La profesión de programador es asombrosa y única. Hoy en día es imposible imaginar la ciencia y la vida sin las últimas tecnologías. Todo lo relacionado con la actividad humana no se puede hacer sin tecnologia computacional. Y esto contribuye a su alto desarrollo y perfección. Aunque el desarrollo de las computadoras personales comenzó no hace mucho tiempo, durante este tiempo se han dado pasos colosales en el ámbito de los productos de software y estos productos se utilizarán ampliamente durante mucho tiempo. El campo del conocimiento relacionado con la informática ha experimentado una explosión, al igual que la tecnología correspondiente. Si no tenemos en cuenta la vertiente comercial, entonces podemos decir que no hay extraños en este ámbito de la actividad profesional. Muchas personas desarrollan programas no con fines de lucro o ingresos, 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 ejecución de calidad, trabajo estable y cumplimiento de todos los requisitos modernos. Aquí también cabe destacar la aparición de los microprocesadores en los años 60, que llegaron a sustituir un gran número de lámparas. Existen algunos tipos de microprocesadores que son muy diferentes entre sí. Estos microprocesadores se diferencian entre sí por su profundidad de bits y sus comandos integrados en el sistema. 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 provocó una reconsideración de las actitudes hacia el lenguaje ensamblador por dos razones principales. En primer lugar, 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 proporciona una comprensión de la arquitectura de la máquina, que es poco probable que se pueda lograr cuando se trabaja en un lenguaje de alto nivel. Aunque la mayoría de profesionales del software desarrollan en lenguajes de alto nivel como Pascal, C o Delphi, lo que resulta más sencillo a la hora de escribir programas, los más potentes y eficaces software escrito 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 características específicas del procesador. Por lo tanto, para escribir un programa en lenguaje ensamblador para una computadora específica, es necesario conocer su arquitectura. Hoy en día, la visión de los principales producto de software es un archivo EXE. Considerando lados positivos Esto significa que el autor del programa puede confiar en su integridad. Pero a menudo esto está lejos de ser el caso. También hay un desensamblador. Con la ayuda de un desensamblador, puede descubrir interrupciones y códigos de programa. Una persona bien versada en ensamblador no será difícil rehacer todo el programa a su gusto. Quizás aquí es donde surge el problema más insoluble: el virus. ¿Por qué la gente escribe un virus? Algunos hacen esta pregunta con sorpresa, otros con enojo, pero sin embargo sigue habiendo personas que están interesadas en esta tarea no desde el punto de vista de causar algún daño, sino como interés en la programación de sistemas. Los virus están escritos por varias razones. A algunas personas les gustan las llamadas al sistema, otras mejoran sus conocimientos de ensamblador. Intentaré explicar todo esto en mi trabajo del curso. También dice no sólo 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 de los programadores sobre el lenguaje ensamblador.

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

En resumen, el lenguaje ensamblador es una representación simbólica del lenguaje de máquina. Todos los procesos en una máquina en el nivel de hardware más bajo son controlados únicamente por comandos (instrucciones) en lenguaje de máquina. De esto se desprende claramente que, a pesar del nombre común, el lenguaje ensamblador es diferente para cada tipo de computadora. Esto también se aplica a apariencia programas escritos en lenguaje ensamblador e ideas de las que este lenguaje es un reflejo.

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

Un programador o cualquier otro usuario puede utilizar cualquier herramienta de alto nivel, incluso programas para construir mundos virtuales, y tal vez ni siquiera sospechar que, en realidad, la computadora no ejecuta los comandos del lenguaje en el que está escrito su programa, sino su representación transformada. en forma de secuencias aburridas y aburridas de comandos de un lenguaje completamente diferente: el lenguaje de máquina. Ahora imaginemos que dicho usuario tiene un problema no estándar o algo simplemente no funciona. Por ejemplo, su programa debe funcionar con algún dispositivo inusual o realizar otras acciones que requieran conocimiento de los principios operativos del hardware de la computadora. No importa cuán inteligente sea el programador, no importa cuán bueno sea el lenguaje en el que escribió su maravilloso programa, no puede prescindir de conocimientos 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 ensambladores o admitan el acceso al nivel de programación ensamblador.

Por supuesto, la época de los generalistas informáticos ya pasó. Como dicen, no se puede abrazar la inmensidad. Pero hay algo en común, una especie de base sobre la que se construye cualquier educación informática seria. Se trata de conocimiento sobre los principios de funcionamiento de una 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

En 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. Si pensamos con lógica, está claro que desempeña el papel de una especie de dispositivo de coordinación. Miremos el interior de la unidad del sistema (no es necesario intentar entrar en el monitor; no hay nada interesante allí y, además, es peligroso): abra la carcasa y vea algunas placas, bloques y cables de conexión. Para comprender su propósito funcional, veamos el diagrama de bloques de una computadora típica (Fig. 2). No pretende una precisión absoluta y sólo 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 algo poco convencional.
Es común que una persona, cuando se encuentra con algo nuevo, busque algunas asociaciones que puedan ayudarle a comprender lo desconocido. ¿Qué asociaciones evoca la computadora? Por ejemplo, a menudo asocio una computadora con la persona misma. ¿Por qué?

Cuando una persona creaba una computadora, en algún lugar profundo de su interior pensaba que estaba creando algo similar a él. La computadora tiene órganos para recibir información del mundo exterior: un teclado, un mouse y 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 RAM. Y finalmente, la computadora tiene órganos del habla que producen los resultados del procesamiento. Estos también son algunos de los dispositivos de la derecha.

Computadoras modernas, por supuesto, está lejos de ser humano. Se les puede comparar con criaturas que interactúan con el mundo exterior a través de un conjunto amplio pero limitado de reflejos incondicionados.
Este conjunto de reflejos forma un sistema de comandos de la máquina. No importa qué tan alto sea el nivel de comunicación con una computadora, en última instancia todo se reduce a una secuencia aburrida y monótona de comandos de la máquina.
Cada comando de la máquina es una especie de estímulo para excitar uno u otro reflejo incondicionado. La reacción a este estímulo es siempre inequívoca y está "programada" en el bloque de microcomandos en forma de microprograma. Este microprograma implementa acciones para implementar un comando de la máquina, pero a nivel de señales suministradas a ciertos lógica computadora, controlando así varios subsistemas de la computadora. Este es el llamado principio de control de microprogramas.

Continuando con la analogía con una persona, observamos: para que una computadora funcione 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. Sólo al estómago de la computadora le encanta la dieta, la comida monótona: le proporciona información estructurada, en forma de secuencias estrictamente organizadas de ceros y unos, cuyas combinaciones forman el lenguaje de máquina.

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

De todo lo anterior, podemos concluir que dado que el lenguaje ensamblador es "nativo" para una computadora, el programa más efectivo solo se puede escribir en él (siempre que esté escrito por un programador calificado). Aquí hay un pequeño "pero": se trata de un proceso que requiere mucha mano de obra y mucha atención y experiencia práctica. Por lo tanto, en realidad, escriben principalmente programas en ensamblador que deberían proporcionar trabajo eficiente con herrajes. A veces, las secciones del programa que son críticas en términos de tiempo de ejecución o consumo de memoria se escriben en ensamblador. Posteriormente, se formalizan 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 accesible 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, en un grado u otro, disponibles para ser utilizados por el programador.

Estos registros se pueden dividir en dos grandes grupos:

^ 16 registros de usuarios;

16 registros del sistema.

Los programas en lenguaje ensamblador utilizan registros de forma muy intensiva. La mayoría de los registros tienen un propósito funcional específico.

Como sugiere el nombre, los registros de usuario se denominan registros de usuario porque el programador puede utilizarlos al escribir sus programas. Estos registros incluyen (Fig.3):

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

seis registros de segmento: 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 barras? 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 funcionalidad de los programas escritos para modelos más jóvenes de microprocesadores Intel de 16 bits, 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 el tamaño es mayor, lo que se refleja en sus designaciones: tienen
prefijo e (extendido).

^ Registros de propósito general
Todos los registros de este grupo le permiten acceder a sus partes "inferiores" (ver Fig. 3). Al observar esta figura, observe que solo las partes inferiores de 16 y 8 bits de estos registros se pueden utilizar para el autodireccionamiento. Los 16 bits superiores de estos registros no están disponibles como objetos independientes. Esto se hizo, como señalamos anteriormente, por compatibilidad con modelos más jóvenes de microprocesadores Intel de 16 bits.

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

eax/ax/ah/al (Registro del acumulador) - batería.
Se utiliza para almacenar datos intermedios. Algunos comandos requieren el uso de este registro;

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.
Utilizado en equipos que realizan algunas acciones repetitivas. Su uso suele estar implícito y oculto en el algoritmo del comando correspondiente.
Por ejemplo, el comando para organizar un bucle, además de transferir el control a un comando ubicado en una determinada dirección, analiza y disminuye 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. En algunos comandos su uso es obligatorio; Para algunos comandos, esto sucede implícitamente.

Los dos registros siguientes se utilizan para soportar 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 fuente) - índice fuente.
Este registro en operaciones encadenadas 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 encadenadas contiene la dirección actual en la cadena de destino.

En la arquitectura del microprocesador, se admite una estructura de datos como una pila a nivel de hardware y software. Para trabajar con la pila, existen comandos especiales en el sistema de instrucciones del microprocesador, 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 un segmento de datos, pero en este caso, para cada dato almacenado temporalmente, se debe crear una celda de memoria con nombre separada, lo que aumenta el tamaño del programa y la cantidad de nombres utilizados. La conveniencia de la pila radica en el hecho de que su área es reutilizable, y el almacenamiento de datos en la pila y su recuperación desde allí se realiza mediante efectivos comandos push y pop sin especificar ningún nombre.
La pila se utiliza tradicionalmente, por ejemplo, para guardar el contenido de los registros utilizados por un 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 extrae de la pila después de que regresa la subrutina. Otra técnica común es pasar los parámetros necesarios 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 utilizarlos durante su ejecución. Rasgo distintivo La pila es un orden único en el que se recuperan los datos contenidos en ella: en un momento dado, solo el elemento superior está disponible en la pila, es decir. el elemento empujado más recientemente a la pila. Al sacar el elemento superior de la pila, el siguiente elemento está disponible. Los elementos de la pila están ubicados 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) en direcciones decrecientes secuencialmente. 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 ser parte de algún segmento o formar un segmento separado. En cualquier caso, la dirección de segmento de este segmento se coloca en el registro de pila de segmentos SS. Así, el par de registros SS:SP describe la dirección de una celda de la pila accesible: 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). Tenga en cuenta que en el estado inicial, el puntero de la 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 a su estado original.

La carga en la pila se realiza mediante un comando especial para trabajar con la pila (empujar). Esta instrucción primero disminuye el contenido del puntero de la pila en 2 y luego coloca el operando en la dirección en SP. Si, por ejemplo, queremos almacenar 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 inferiores) y el operando especificado en el comando push se escribe en esta dirección. El siguiente comando de carga de pila es, por ejemplo.

pondrá la pila en el estado que se muestra en la Fig. 1.10,c. La pila ahora almacenará dos elementos, y solo se podrá acceder al superior, al que apunta el puntero de la pila SP. Si después de un tiempo necesitamos restaurar el contenido original de los registros almacenados en la pila, debemos ejecutar los comandos pop (push) para descargar de la pila:

pop DS
hacha pop

¿Qué tamaño debe tener 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 este 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 inserta la dirección de retorno en la pila y luego DOS inserta el contenido de los registros y otra información relacionada con el programa interrumpido en la pila. Por lo tanto, incluso si un programa no utiliza una pila en absoluto, ésta debe estar presente en el programa y tener al menos varias docenas de palabras. En nuestro primer ejemplo, asignamos 128 palabras a la pila, lo que sin duda es suficiente.

^ Estructura de un programa ensamblador

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 segmentos de bloque de este tipo. Cada segmento contiene una colección de oraciones del lenguaje, cada una de las cuales ocupa una línea separada de código de programa.

Hay cuatro tipos de declaraciones en ensamblador:

Comandos o instrucciones que son análogos simbólicos de los comandos de la máquina. Durante el proceso de traducción, las instrucciones del ensamblador se convierten en los comandos correspondientes del conjunto de instrucciones del microprocesador;

macrocomandos: oraciones de texto de programa formateadas de cierta manera, reemplazadas durante la transmisión por otras oraciones;

directivas, que son instrucciones para que el traductor ensamblador realice ciertas acciones. Las directivas no tienen equivalente en la representación mecánica;

líneas de comentarios que contengan caracteres, incluidas letras del alfabeto ruso. El traductor ignora los comentarios.

^ Sintaxis ensambladora

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 formarse según ciertas reglas sintácticas. Para ello, lo mejor es utilizar una descripción formal de la sintaxis del idioma, como las reglas gramaticales. 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 oración ensamblada

Arroz. 6. Formato de directiva

Arroz. 7. Formato de comandos y macros.

En estas imágenes:

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

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

un código de operación (OPC) y una directiva son símbolos mnemotécnicos para la correspondiente instrucción de máquina, macroinstrucción o directiva de traducción;

Los operandos son partes de un comando, macro o directiva de ensamblador que designan los objetos sobre los cuales se realizan acciones. Los operandos del lenguaje ensamblador se describen mediante expresiones con constantes numéricas y de texto, etiquetas e identificadores de variables utilizando signos de operador y algunas palabras reservadas.

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

Los caracteres aceptables al escribir texto de programa son:

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

Números del 0 al 9;

Signos ?, @, $, _, &;

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

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

Los lexemas 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 símbolos se pueden utilizar 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 sólo los primeros 32 e ignora el resto. Puede ajustar la longitud de los posibles identificadores utilizando la opción línea de comando mv. Además, es posible indicarle al traductor que distinga entre letras mayúsculas y minúsculas o que ignore su diferencia (lo cual se hace de forma predeterminada).

^Comandos del ensamblador.

Los comandos del ensamblador revelan la capacidad de transferir sus requisitos a la computadora, un mecanismo para transferir el control en un programa (ciclos y transiciones) para comparaciones lógicas y organización del programa. Sin embargo, las tareas programables rara vez son tan sencillas. La mayoría de los programas contienen una serie de bucles en los que se repiten varios comandos hasta que se cumple un determinado requisito, y varias comprobaciones que determinan cuál de varias acciones se debe realizar. Algunas instrucciones pueden transferir el control cambiando la secuencia normal de pasos modificando directamente el valor de compensación en el puntero de instrucción. Como se mencionó anteriormente, existen diferentes comandos para diferentes procesadores, pero veremos 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 una tabla que refleja la estructura del registro de banderas eflags:

La fila inferior de esta tabla muestra los valores de las banderas después de ejecutar el comando. 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;

Una vez ejecutado 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: un operando en uno de los registros de tamaño de byte, palabra o palabra doble;

m8, m16, m32, m48: tamaño del operando de memoria en bytes, palabras, palabras dobles o 48 bits;

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

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

^ Diagrama de comando:

agregar destino, fuente

Finalidad: adición de dos operandos de origen y de destino de tamaño byte, palabra o palabra doble.

Algoritmo de trabajo:

agregue los operandos de origen y destino;

escribir el resultado de la suma en el receptor;

establecer banderas.

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

Solicitud:
El comando agregar 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 del receptor (se produce un desbordamiento), entonces esta situación debe tenerse en cuenta analizando la bandera cf y el posible uso posterior del comando adc. Por ejemplo, agreguemos los valores en el registro ax y el área de memoria ch. Al agregar, tenga en cuenta la posibilidad de que se desborde.

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

^ Diagrama de comando:

Objetivo:

transferir el control a un procedimiento cercano o lejano almacenando la dirección del punto de retorno en la pila;

cambiar de tareas.

Algoritmo de trabajo:
determinado por el tipo de operando:

Cerca de la etiqueta: el contenido del puntero del comando eip/ip se inserta en la pila y el nuevo valor de dirección correspondiente a la etiqueta se carga en el mismo registro;

Etiqueta lejana: el contenido del puntero del comando eip/ip y cs se inserta en la pila. Luego se cargan nuevos valores de dirección correspondientes a la etiqueta lejana 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 al que se transfiere el control. Cuando se transfiere el control, el contenido del puntero del comando eip/ip se inserta en la pila;

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

^ Estado de los indicadores después de la ejecución del comando (excepto el cambio de tareas):

ejecutar el comando no afecta las banderas

Cuando se cambia una tarea, los valores de las banderas se cambian de acuerdo con la información sobre el registro eflags 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|diep-alto|

Direccionamiento indirecto en un segmento:

|11111111|mod010r/m|

Direccionamiento indirecto entre segmentos:

|11111111|mod011r/m|

Direccionamiento directo entre segmentos:

|10011010|compensación-baja|compensación-alta|seg-baja|seg-alta|

CMP
(Compare operandos)

Comparación de operandos

^ Diagrama de comando:

cmp operando1,operando2

Propósito: comparación de dos operandos.

Algoritmo de trabajo:

realizar resta (operando1-operando2);

Dependiendo del resultado, configure las banderas, no cambie el operando1 y el operando2 (es decir, no recuerde el resultado).

Solicitud:
Este comando Se utiliza para comparar dos operandos mediante resta sin cambiar los operandos. Según los resultados del comando, se establecen banderas. El comando cmp se utiliza con los comandos de salto condicional y el comando de configuración de byte por valor setcc.

Código objeto (tres formatos):

Registro o memoria con registro:

|001110dw|modregr/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
(DECrementar operando en 1)

Disminuir un operando en uno

^ Diagrama de comando:

operando dec

Propósito: Disminuir en 1 el valor de un operando en la memoria o registro.

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

Solicitud:
La instrucción dec se utiliza para disminuir en uno el valor de un byte, palabra, palabra doble en la memoria o registro. Sin embargo, tenga en cuenta que el comando no afecta la bandera cf.

Registro: |01001reg|

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

DIV
(DIVide sin firmar)

División sin firmar

Esquema del equipo:

divisor div

Propósito: Realizar una operación de división entre dos valores binarios sin signo.

^ Algoritmo operativo:
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 tiene un tamaño de un byte, 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 tiene un tamaño de palabra, entonces el dividendo debe ubicarse en el par de registros dx:ax, con la parte de orden inferior del dividendo ubicada en ax. Luego de la operación, el cociente se coloca en ax y el resto en dx;

si el divisor tiene un tamaño de palabra doble, entonces el dividendo debe ubicarse en el par de registros edx:eax, con la parte de orden inferior del dividendo ubicada 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, produciendo el resultado de la división como el 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 la rutina del servicio de interrupción

^ Diagrama de comando:

int número_interrupción

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

^ Algoritmo operativo:

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

restablecer los indicadores if y tf a cero;

transferir el control al programa de servicio de interrupción 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 utilizarlo en varios depuradores de software para establecer puntos de interrupción reemplazando el primer byte de cualquier comando. El microprocesador, al encontrar un comando con el código de operación 0cch en la secuencia de comandos, llama al programa de procesamiento de interrupciones con el vector número 3, que sirve para comunicarse con el depurador de software.

La segunda forma del comando ocupa 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 de 0 a 255. Las características de la transferencia de control, como se señaló, dependen del modo de funcionamiento del microprocesador.

Código objeto (dos formatos):

Registro: |01000reg|

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

J.C.C.
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

^ Diagrama de comando:

etiqueta jcc
etiqueta jcxz
etiqueta jecxz

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

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

si la condición que se está probando es verdadera, vaya a la celda indicada por el operando;

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

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

si la condición que se está verificando

Estructura de comandos en lenguaje ensamblador La programación a nivel de comandos de máquina es el nivel mínimo en el que es posible la programación informática. El sistema de comando de la máquina debe ser suficiente para implementar las acciones requeridas mediante la emisión de instrucciones al equipo de la máquina. Cada instrucción de máquina consta de dos partes: una operativa, que determina “qué hacer” y un operando, que determina los objetos de procesamiento, es decir, “qué hacer”. Una instrucción de máquina con microprocesador, escrita en lenguaje ensamblador, es una sola línea que tiene la siguiente forma: etiqueta comando/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 del comando están separados por comas.

Estructura de comandos en lenguaje ensamblador Un comando en ensamblador le dice al traductor 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 cuales se realiza la acción del comando (en el segmento de código). Una instrucción puede tener uno o dos operandos, o ningún operando. El número de operandos está especificado implícitamente en el código de instrucción. Si es necesario continuar con un comando o directiva en la siguiente línea, se utiliza el carácter de barra invertida: "" . De forma predeterminada, el ensamblador no distingue entre letras mayúsculas y minúsculas al escribir 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 indicar 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 comandos) no se pueden utilizar como identificadores. El primer carácter del identificador debe ser una letra o un carácter especial. Longitud máxima El identificador tiene 255 caracteres, pero el traductor acepta los primeros 32 e ignora el resto. Todas las etiquetas escritas en una línea que no contenga 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 línea. Se recomienda escribirlos en una columna para una mayor legibilidad del programa.

Etiquetas Todas las etiquetas escritas 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 línea. 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 comandos no está claro. Los comentarios comienzan en cualquier línea del módulo fuente con un punto y coma (;). Todos los caracteres a la derecha de "; " al final de la línea hay un comentario. Un comentario puede contener cualquier carácter imprimible, incluido el espacio. Un comentario puede abarcar toda la línea o seguir un comando en la misma línea.

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

Modelos de memoria Antes de declarar segmentos, debe especificar el modelo de memoria mediante una directiva. Modificador MODEL modelo_memoria, convención_llamada, tipo_OS, parámetro_pila Modelos de memoria básicos del lenguaje ensamblador: Modelo de memoria Direccionamiento de código Direccionamiento de datos Sistema operativo Intercalado de código y datos PEQUEÑO CERCA DE MS-DOS Aceptable PEQUEÑO CERCA DE MS-DOS, Windows No MEDIANO LEJOS CERCA DE MS-DOS, Windows No COMPACTO CERCA LEJOS DE MS-DOS, Windows No GRANDE LEJOS MS-DOS, Windows No ENORME LEJOS MS-DOS, Windows No NEAR Windows 2000, Windows XP, Windows Aceptable FLAT NEAR NT,

Modelos de memoria El modelo pequeño sólo funciona en aplicaciones MS-DOS de 16 bits. En este modelo, todos los datos y códigos se encuentran 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 se abordan lo más cerca posible cuando se utiliza este modelo. El modelo mediano admite múltiples segmentos de código y un segmento de datos, donde todas las referencias en segmentos de código se consideran lejanas de forma predeterminada y las referencias en un segmento de datos se consideran cercanas. El modelo compacto admite varios segmentos de datos que utilizan direccionamiento de datos lejanos (lejos) y un segmento de código que utiliza direccionamiento cercano (cerca). El modelo grande admite múltiples segmentos de código y múltiples segmentos de datos. De forma predeterminada, todas las referencias a 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 se utiliza sólo en sistemas operativos de 32 bits. Este modelo es similar al modelo pequeño en que los datos y el código están ubicados en un solo segmento, pero es de 32 bits. Desarrollar un programa para el modelo plano ante la directiva. El modelo de piso 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 instrucciones disponibles al escribir programas. La letra p después de la directiva de selección del procesador significa modo de funcionamiento protegido. El direccionamiento de datos y códigos está cerca, y todas las direcciones y punteros son de 32 bits.

Modelos de memoria. Modificador MODEL modelo_memoria, convención_llamada, tipo_OS, parámetro_pila El parámetro modificador se usa para definir tipos de segmentos y puede tomar los siguientes valores: use 16 (los segmentos del modelo seleccionado se usan como 16 bits) use 32 (se usan los segmentos del modelo seleccionado como 32 bits). El parámetro call_convention se utiliza para determinar el método para pasar parámetros al llamar a un procedimiento desde otros lenguajes, incluidos los 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 modelo_memoria, convención_llamada, tipo_OS, parámetro_pila El parámetro tipo_OS es OS_DOS de forma predeterminada y en este momento este es el único valor admitido para este parámetro. El parámetro stack_parameter se establece en: NEARSTACK (el registro SS es igual a DS, los datos y las áreas de pila se asignan en el mismo segmento físico) FARSTACK (el registro SS no es igual a DS, los datos y las áreas de pila se asignan en diferentes segmentos físicos). El valor predeterminado es NEARSTACK.

Un ejemplo de un programa que no hace nada. 686 P. MODELO PLANO, STDCALL. DATOS. CODIGO INICIO: RET FIN INICIO RET - comando del microprocesador. Garantiza que el programa finalice correctamente. El resto del programa se refiere al funcionamiento del traductor. . 686 P: se permiten comandos en modo protegido de Pentium 6 (Pentium II). Esta directiva selecciona el conjunto admitido de instrucciones de ensamblador, indicando el modelo del procesador. . MODELO PLANO, stdcall - modelo de memoria plana. Este modelo de memoria se utiliza en el sistema operativo Windows. stdcall: la convención de llamada al procedimiento utilizada.

Un ejemplo de un programa que no hace nada. 686 P. MODELO PLANO, STDCALL. DATOS. CÓDIGO INICIO: RET FIN INICIO. DATOS es un segmento de programa que contiene datos. Este programa no usa la pila, entonces el segmento. Falta la PILA. . CÓDIGO es un segmento de programa que contiene código. INICIO - etiqueta. END START: el final del programa y un mensaje al compilador de que la ejecución del programa debe comenzar con la etiqueta START. Cada programa debe contener una directiva END para marcar el final. código fuente programas. Todas las líneas que siguen a la directiva END se ignoran. La etiqueta especificada después de la directiva END le dice al traductor 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 Traductor - programa o medios tecnicos, que convierte un programa representado en uno de los lenguajes de programación en un programa en el lenguaje de destino, llamado código objeto. Además de admitir mnemónicos de instrucción automática, cada traductor tiene su propio conjunto de directivas y macroherramientas, a menudo incompatibles con cualquier otra cosa. Los principales tipos de traductores de lenguaje ensamblador: MASM (Microsoft Assembler), TASM (Borland Turbo Assembler), FASM (Flat Assembler), un ensamblador multipaso de distribución gratuita escrito por Tomasz Gryshtar (polaco), NASM (Netwide Assembler), un programa gratuito. ensamblador 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 desarrolladores en Source. Fragua. neto.

Src="https://present5.com/presentation/-29367016_63610977/image-15.jpg" alt="Traducir un programa en Microsoft Visual Studio 2005 1) Cree un proyecto seleccionando Archivo->Nuevo- >Menú Proyecto 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="Traducir el programa en Microsoft Visual Studio 2005 2) En el árbol del proyecto (Ver->Explorador de soluciones) agregar"> Трансляция программы в Microsoft Visual Studio 2005 2) В дереве проекта (View->Solution Explorer) добавить файл, в котором будет содержаться текст программы: Source. Files->Add->New. Item.!}

Traducir el programa a Microsoft Visual Studio 2005 3) Seleccione el tipo de archivo Código C++, pero especifique el nombre con la extensión. conjunto:

Traducir el programa a Microsoft Visual Studio 2005 5) Establecer los parámetros del compilador. Haga clic derecho en el menú Reglas de compilación personalizadas en el archivo del proyecto...

Traduzca el programa a Microsoft Visual Studio 2005 y seleccione Microsoft Macro Assembler en la ventana que aparece.

Traducción del programa en Microsoft Visual Studio 2005 Consulta con el botón derecho en el archivo hola. asm del árbol de proyectos del menú Propiedades e instale 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 el sistema operativo Windows La programación en el sistema operativo Windows se basa en el uso de funciones API (interfaz de programa de aplicación, es decir, interfaz de aplicación de software). Su número llega a 2000. El programa de Windows se compone en gran medida de este tipo de llamadas. Todas las interacciones con dispositivos externos y los recursos del sistema operativo se producen, por regla general, a través de dichas funciones. sala de operaciones sistema windows Utiliza un modelo de memoria plana. La dirección de cualquier celda 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 diálogo), consola o estructura sin ventanas, estructura clásica (ventana, marco).

Llamar Funciones de Windows API En el archivo de ayuda, cualquier función API se presenta en el formato nombre_función tipo (FA 1, FA 2, FA 3) Tipo: tipo de valor de retorno; FAx: una lista de argumentos formales en el orden en que aparecen. Por ejemplo, int Message. Box(HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); Esta función muestra una ventana con un mensaje y un botón (o botones) de salida. Significado de los parámetros: h. Wnd es un descriptor de la ventana en la que aparecerá la ventana del mensaje, lp. Texto - 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 determinar el número de botones de salida.

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

Llamar a las funciones de mensaje int de la API de Windows. Box(HWND h. Wnd, LPCTSTR lp. Texto, LPCTSTR lp. Título, UINT u. Tipo); Cuando use MASM, debe agregar @N N al final del nombre: la cantidad de bytes que ocupan los argumentos pasados ​​​​en la pila. Para las funciones API de Win 32, este número se puede definir como el número de argumentos n multiplicado por 4 (bytes en cada argumento): N=4*n. Para llamar a una función, utilice 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 de argumentos: DE IZQUIERDA A DERECHA - DE ABAJO ARRIBA. El argumento u se colocará primero en la pila. Tipo. La llamada a la función especificada se verá así: CALL Message. Caja. @16

Llamar a las funciones de mensaje int de la API de Windows. Box(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 representa un "desplazamiento en un segmento" o, traducido a términos de lenguaje de alto nivel, un "puntero" al comienzo de una línea. La directiva EQU, como #define en SI, define una constante. La directiva EXTERN le dice al traductor que la función o identificador es externo a este módulo.

Ejemplo de un programa "¡Hola a todos!" . 686 P. MODELO PLANO, 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. A@16: CERCA. CODIGO INICIO: PUSH MB_OK PUSH OFFSET STR 1 PUSH OFFSET STR 2 PUSH HW CALL Mensaje. Caja. A@16 RET FIN INICIO

La directiva INVOKE El traductor de lenguaje MASM también le permite simplificar las llamadas a funciones usando una herramienta macro: la directiva INVOKE: función INVOKE, parámetro1, parámetro2, ... No es necesario agregar @16 a la llamada a la función; Los parámetros se escriben exactamente en el orden en que se proporcionan en la descripción de la función. Mediante macro mediante el traductor, los parámetros se colocan en la pila. Para utilizar la directiva INVOKE, debe tener una descripción del prototipo de función utilizando la directiva PROTO en el formato: Mensaje. Caja. A PROTO: DWORD, : DWORD Si un programa utiliza muchas funciones API de Win 32, es recomendable utilizar la directiva de inclusión C: masm 32includeuser 32. inc

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

A medida que aumenta la duración del programa, resulta cada vez más difícil recordar los códigos de diversas operaciones. Los mnemónicos proporcionan cierta ayuda a este respecto.

El lenguaje de codificación de comandos simbólicos se llama ensamblador.

lenguaje ensamblador Es un lenguaje en el que cada expresión corresponde exactamente a un comando de máquina.

Asamblea Se denomina conversión de un programa desde 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 las direcciones simbólicas con números absolutos o relativos, así como incorporando programas de biblioteca y generando secuencias de instrucciones simbólicas especificando específicas. parámetros en microequipos. Este programa generalmente se encuentra en la ROM o se ingresa en la RAM desde algún medio externo.

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

1. Ésta es una correspondencia uno a uno entre declaraciones en lenguaje ensamblador e instrucciones de máquina.

2. Un programador en lenguaje ensamblador tiene acceso a todos los objetos e instrucciones presentes en la máquina de destino.

Comprender los conceptos básicos de programación en lenguajes orientados a máquinas es útil para:



Mejor comprensión de la arquitectura de las PC y uso más competente de las computadoras;

Desarrollar estructuras más racionales de algoritmos para programas de resolución de 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 a los programas especificados en el depurador de programas DEBUG y descompilando su visualización en ensamblador). idioma);

Compilación de programas para resolver los problemas más críticos (un programa escrito en un lenguaje orientado a máquina suele ser más eficaz: entre un 30 y un 60 por ciento más corto y más rápido que los programas obtenidos como resultado de la traducción de lenguajes de alto nivel)

Implementar los procedimientos incluidos en el programa principal en forma de fragmentos separados en caso de que no puedan implementarse ni en el lenguaje de alto nivel utilizado ni mediante los procedimientos de servicio del sistema operativo.

Un programa en lenguaje ensamblador sólo puede ejecutarse en una familia de computadoras, mientras que un programa escrito en un lenguaje de alto nivel puede potencialmente ejecutarse en diferentes máquinas.

El alfabeto del lenguaje ensamblador se compone de caracteres ASCII.

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

Los números binarios terminan con la letra B;

Números decimales que terminan en la letra D;

Los números hexadecimales terminan con la letra H.

RAM, registros, presentación de datos.

Para una determinada serie de MP, se utiliza un lenguaje de programación individual: el 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 hace un uso más eficiente de las capacidades de una máquina específica (más precisamente, un MP) que un programa en un lenguaje de alto nivel (que es más simple para un programador que para un ensamblador). Veamos los principios básicos de la programación en lenguajes orientados a máquina usando el ejemplo del lenguaje ensamblador para el MP KR580VM80. Se utiliza una metodología general para programar en el lenguaje. Las técnicas técnicas específicas para grabar programas están asociadas con las características de la arquitectura y el sistema de comando del MP objetivo.

modelo de software Sistema de microprocesador basado en MP KR580VM80.

Modelo de software del MPS según la figura 1

Memoria de puertos MP

S z C.A. PAG C

Foto 1

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

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

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

Florida– registro de bandera (registro de signos) Registro de 8 bits en el que se almacenan cinco signos 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, establecida en 1 si el número de unos en los bits del resultado es par.

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

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

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

SP– puntero de pila, un registro de 16 bits, diseñado para almacenar la dirección de la celda de memoria donde se escribió el último byte insertado en la pila.

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

En el área de memoria inicial de la dirección 0000Н – 07FF hay programa de control y programas de demostración. Esta es el área ROM.

0800 – 0AFF - área de direcciones para grabar los programas en estudio. (RAM).

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

0ВВ0 – dirección inicial de la pila. (RAM).

Una pila es un área de RAM especialmente organizada destinada al almacenamiento temporal de datos o direcciones. El último número escrito en la pila aparece primero. El puntero de la pila almacena la dirección de la última celda de la pila en la que se escribe 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, el contenido de todos los registros involucrados en su ejecución se guarda en la pila y al final de la subrutina se recupera de la pila.

Formato de datos y estructura de comando del lenguaje ensamblador.

La memoria del MP KR580VM80 es una matriz de palabras de 8 bits llamadas 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 estar contenidos tanto en ROM como en 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.

Un comando se caracteriza por su formato, es decir, el número de bits asignados a él, 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. Los comandos multibyte deben colocarse en idiomas adyacentes. El formato del comando depende de las características específicas de la operación que se realiza.

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

Determina 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 bytes pueden contener datos sobre los cuales se realizan operaciones o direcciones que indican la ubicación de los datos. Los datos sobre los que se realizan las acciones se denominan operandos.

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

Figura 4

En los comandos en lenguaje ensamblador, el código de operación tiene una forma abreviada de escribir palabras en inglés: una notación mnemotécnica. La mnemónica (del griego mnemotécnico, el arte de memorizar) facilita recordar comandos según 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 ubicarse en los registros internos del MP (el más conveniente y opción rápida). Se pueden ubicar en memoria del sistema(la opción más común). Finalmente, pueden ubicarse 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 metodos, con el cual 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:

Directo;

Registro;

Indirecto;

Apilados.

Directo El direccionamiento supone que el operando (de entrada) está ubicado en la memoria inmediatamente después del código de instrucción. El operando suele ser una constante que debe enviarse a algún lugar, agregarse a algo, etc. los datos están contenidos en el segundo o segundo y tercer bytes del comando, con el byte bajo de datos ubicado en el segundo byte del comando. y el byte alto en el tercer byte de comando.

Derecho El direccionamiento (también conocido como absoluto) supone 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. Utilizado en comandos de tres bytes.

Registro El direccionamiento supone que el operando (entrada o salida) está en el registro interno del MP. Utilizado en comandos de un solo byte

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

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

Sistema de mando

El sistema de mando del 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 (teniendo en cuenta las modificaciones 244).

Se distinguen los siguientes grupos de comandos:

Transmisión de datos;

Aritmética;

Rompecabezas;

Comandos de salto;

Comandos de entrada/salida, control y apilamiento.


Símbolos y abreviaturas utilizadas al describir comandos y componer 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 dispositivo de E/S de 8 bits
BYTE 2 Segundo byte del comando
BYTE 3 Tercer byte de comando
R, R1, R2 Uno de los registros: A, B, C, D, E, H, L.
RP Uno de los pares de registros: B - especifica el par BC; D: especifica un par DE; H: especifica el par HL
RH Primer registro de la pareja
RL Segundo registro de la pareja.
Λ multiplicación lógica
V Suma lógica
Suma módulo dos
METRO Una celda de memoria cuya dirección especifica el contenido del par de registros HL, es decir, M = (HL)



Arriba