Sie sind auf Seite 1von 23

Tipos de Instrucciones.

1. 2. 3. 4. Procesamiento de Datos (Instrucciones aritmtico lgicas) Almacenamiento de Datos (Instrucciones de Memoria) Movimiento de Datos (Instrucciones de E-S) Control (Instrucciones de prueba y bifurcacin).

Una de las principales caractersticas de todo microprocesador es disponer de un buen conjunto de instrucciones que le den la mayor versatilidad posible. Se establecen los siguientes grupos, tipos o conjuntos de instrucciones: 1- ARITMETICAS: Son las instrucciones que realizan operaciones de tipo aritmtico como sumas, restas, incrementos, decrementos, etc. Todas las operaciones de este tipo afectan al registro de estado, es decir a los flags. 2- MOVIMIENTO DE DATOS: Este tipo de instrucciones de movimiento o transferencia de datos, permiten realizar una copia del contenido de un registro o direccin de memoria (origen), en otro registro o direccin de memoria (destino), sin alterar el contenido del origen. 3- INTRUCCIONES LOGICAS: Realizan operaciones lgicas entre los operandos. Afectan a los flags segn sea el caso y las operaciones se realizan bit a bit entre los datos. 4- INSTRUCCIONES DE COMPARACION: Son operaciones de restar o operaciones XOR entre dos operandos. Afectan a los flags pero no se almacena el resultado. 5- INSTRUCCIONES DE SALTO: Son instrucciones de modificacin de secuencia que alteran la ejecucin normal del programa, cargando el contador de programa con la nueva direccin en la que deseamos contine ejecutndose el programa. Los saltos o bifurcaciones pueden ser: - CONDICIONALES: Cuando son controlados por el estado de un indicador o flag, y basndose en dicho estado se decide si se salta o no. - INCONDICIONALES: Cuando modifican el contador de programa sin condicin previa. Dentro de cada uno de estos tipos, podemos encontrar adems la particularidad de que al efectuar los saltos se realicen con posibilidad de retorno o no. 6- INSTRUCCIONES DE ENTRADA/SALIDA: Son en realidad instrucciones de transferencia, pero no entre registros o posiciones de memoria, sino con elementos perifricos que permiten la comunicacin del microprocesador con el exterior. 7- INSTRUCCIONES DE CONTROL: Sirven para actuar internamente sobre el microprocesador, provocando detenciones en la ejecucin del programa, etc. 8INSTRUCCIONES DE BIT: Trabajan o tratan bits independientes.

9- INSRUCCIONES DE DESPLAZAMIENTO: Desplazan o rotan hacia la izquierda o hacia la derecha los bits de un registro (acumulador).

Lenguajes intermedios.
Cdigos Intermedios (CI)
Se refiere a una estructura de cdigo cuya complejidad est entre el cdigo fuente en el LAN y el cdigo de mquina; por lo tanto los CI se pueden considerar como una interfaz entre el generador de cdigo y las fases previas del compilador. Las ventajas de considerar un CI en la traduccin es que podemos considerar una mquina abstracta y con esto: i.- El compilador es independiente de la mquina de destino, ya que las consideraciones particulares de la mquina destino estn consideradas en el generador de cdigo. Ii.- Es fcil llevar a cabo una estrategia de optimizacin. Desventaja: El cdigo que puede generarse a partir del cdigo intermedio por lo general ser menos eficiente que el cdigo de mquina generado directamente, debido al nivel de traduccin adicional. Lenguajes intermedios Un lenguaje intermedio se puede definir como una manera de representar procedimientos y estructuras de datos que sirva como entrada para una MV en alguna parte de su jerarqua, entre el lenguaje de entrada (el nivel ms alto) y el cdigo ejecutado en la mquina (el nivel ms bajo) - tanto en el tiempo de compilacin como en el de ejecucin. Para considerar el papel de los lenguajes intermedios y sus ventajas y desventajas, conviene destacar la diferencia entre la traduccin de un lenguaje de alto nivel a cdigo mquina anteriormente a su ejecucin (su compilacin) y su interpretacin, es decir, la conversin de cada instruccin del lenguaje a cdigo mquina y su ejecucin, una por una, al ejecutar el programa. Este proceso se realiza a travs de una MV de interpretacin que simula un ordenador cuyo cdigo mquina es el lenguaje de alto nivel que est siendo interpretado. Y tpicamente, esta MV se construye a travs de un conjunto de programas de cdigo mquina que representa los algoritmos y estructuras de datos necesarios para la ejecucin de las instrucciones del lenguaje de alto nivel. Hay ventajas y desventajas en cada manera de convertir los lenguajes de alto nivel a cdigo mquina, que se pueden resumir as:

Estos dos casos representan los dos extremos porque, como ya se ha visto, existe tambin lo que se llama la compilacin parcial, que es una mezcla de los dos enfoques, donde se compila el lenguaje de alto nivel a un lenguaje intermedio (ms cerca de las estructuras presentes en el cdigo mquina que las del cdigo fuente) y luego se interpreta este lenguaje al ejecutar el programa. Como puede imaginarse, esta tcnica combina las ventajas y desventajas de los dos enfoques anteriores. Un ejemplo de esta combinacin existe en el lenguaje de programacin Java y su entorno. Entre otras cosas, Java empez con la idea de liberar al programador de las dificultades de portar su aplicacin a nuevas plataformas lo cual, si el programa est muy vinculado a algn aspecto del sistema operativo donde fue escrito, podra ser muy difcil. Se compilar el cdigo fuente de Java a un cdigo byte (bytecode) antes de ejecutarlo. Y a la hora de correr el programa, este cdigo, como lenguaje intermedio, sera el lenguaje de entrada para una MV, que con un conjunto de libreras (el entorno de ejecucin de Java, Java Runtime o JRE), la interpretara para su ejecucin. Por lo tanto, este bytecode podra correr en cualquier hardware donde haya una versin del JRE disponible. Como este bytecode est ms cerca del nivel de mquina que de un lenguaje de alto nivel, los programas corrern ms rpidamente que los programas completamente interpretados, aunque ms despacio que los programas previamente compilados al cdigo mquina. Como se puede ver en la figura 1, tanto los programas compilados parcialmente a un lenguaje intermedio (como Java) como los programas escritos en lenguajes de alto nivel que se interpretan (como Lisp) requieren una MV para interpretar el programa. La principal ventaja del lenguaje intermedio en este caso es su proximidad al nivel del cdigo mquina, en el sentido de que supone menos trabajo a la hora de ejecutarlo y, por lo tanto, los programas corren ms rpidamente que los puramente interpretados. Adems del papel de los lenguajes intermedios en la compilacin parcial, se puede destacar su papel en la compilacin estndar. Como ejemplo se puede considerar C como lenguaje intermedio para un lenguaje compilado nuevo (como se plante en la seccin 1.5.1 para C++). Si el autor de un nuevo lenguaje decide utilizar C, por ejemplo, como su lenguaje intermedio, slo tendr que implementar una MV para convertir el cdigo fuente

de su lenguaje a C, ahorrando mucho trabajo. Adems de las razones dadas antes, las ventajas de utilizar un lenguaje tan establecido como C como lenguaje intermedio son: La facilidad de portar el lenguaje a una nueva mquina (slo hay que tener un compilador C disponible all). La generacin de cdigo mquina es una tarea muy compleja que requiere un conocimiento profundo de la arquitectura de la mquina en cuestin y de cada mquina en que se quiere una versin del lenguaje. La facilidad de modificar algn rasgo del comportamiento del lenguaje en alguna mquina en concreto (por ejemplo, caractersticas de memoria o rendimiento se pueden aadir libreras C customizadas sin grandes problemas). Las posibilidades disponibles para mapear estructuras intermedias del nuevo lenguaje a estructuras de datos de C. Y las desventajas son: La depuracin es muy difcil porque, entre otras cosas, los errores que ocurren en el cdigo C no son muy fciles de localizar en lo que ha escrito el programador originalmente en el nuevo lenguaje. Las caractersticas de rendimiento y eficiencia del lenguaje estn determinadas por el compilador C. Habr ocasiones en las que no exista una buena traduccin entre una estructura en el nuevo lenguaje y las estructuras de datos en C, por lo que habr una prdida de eficiencia en el programa resultante (como, por ejemplo, ocurre en la mayora de las ocasiones en que se compilan estructuras de Prolog a C slo se puede expresar iteracin en Prolog utilizando recursin).

Existen dos representaciones intermedias principales:


Notacin sufija Cudruplas

Los operadores didicos (o binarios) pueden especificarse mediante tres notaciones principales:

Prefija: el operador didico es analizado antes que sus operandos. Infija: el operador didico es analizado entre sus operandos. Sufija: el operador didico es analizado despus que sus operandos.

En los lenguajes de programacin clsicos, los operadores didicos se representan usualmente en notacin infija. La notacin prefija permite al operador influir sobre la manera en que se procesan sus operandos, pero a cambio suele exigir mucha ms memoria. La sufija no permite esa influencia, pero es ptima en proceso de memoria y permite eliminar el procesado de los parntesis. Los operadores mondicos slo pueden presentarse en notacin prefija o sufija. Adems, un rbol sintctico puede representarse en forma de tuplas de n elementos, de la forma (operador, operando-1, ..., operando-k, nombre). Las tuplas pueden tener longitud variable o fija (con operandos nulos). Las ms tpicas son las cudruplas, aunque stas pueden representarse tambin en forma de tripletes.

Notacin sufija Llamada tambin postfija o polaca inversa, se usa para representar expresiones sin necesidad de parntesis. Ejemplos: a*b ab* a*(b+c/d) abcd/+* a*b+c*d ab*cd*+ Los identificadores aparecen en el mismo orden. Los operadores en el de evaluacin (de izquierda a derecha). Problema: operadores mondicos (unarios). O bien se transforman en didicos (binarios) o se cambia el smbolo. Ejemplo: -a se convierte en 0-a o en @a a*(-b+c/d) ab@cd/+* Existen dos problemas principales:

Construir la notacin sufija a partir de la infija. Analizar la notacin sufija en el segundo paso de la compilacin.

Rutina semntica para transformar de infijo a sufijo Si el analizador sintctico es bottom-up, hacemos la siguiente suposicin: "Cuando aparece un no terminal V en el asidero, la cadena polaca correspondiente a la subcadena que se redujo a V ya ha sido generada". Se utiliza una pila donde se genera la salida, inicialmente vaca. Las acciones semnticas asociadas a las reglas son: E ::= E + T E ::= E - T E ::= T T ::= T * F T ::= T / F T ::= F F ::= i F ::= (E) F ::= - F Push + Push Push * Push / Push i Push @

Anlisis de la notacin sufija La gramtica completa que permite analizar la notacin sufija es: <Operando> ::= id |

::= cte | <Operando> <Operando> <Operador didico> | <Operando> <Operador mondico> <Operador didico> ::= + | - | * | / | ... <Operador mondico> ::= @ | ... Algoritmo de evaluacin de una expresin en notacin sufija que utiliza una pila:

Si el prximo smbolo es un identificador, se pasa a la pila. Corresponde a la aplicacin de la regla <Operando> ::= id Si el prximo smbolo es una constante, se pasa a la pila. Corresponde a la aplicacin de la regla <Operando> ::= cte Si el prximo smbolo es un operador didico, se aplica el operador a los dos operandos situados en lo alto de la pila y se sustituyen stos por el resultado de la operacin. Corresponde a la aplicacin de la regla <Operando> ::= <Operando> <Operando> <Operador didico> Si el prximo smbolo es un operador mondico, se aplica el operador al operando situado en lo alto de la pila y se sustituye ste por el resultado de la operacin. Corresponde a la aplicacin de la regla <Operando> ::= <Operando> <Operador mondico>

Ejemplo: calcular ab@cd/+*. Extensin de la notacin sufija a otros operadores

La asignacin, teniendo en cuenta que podemos no querer valor resultante. Adems, no interesa tener en la pila el valor del identificador izquierdo, sino su direccin. a:=b*c+d abc*d+:= La transferencia (GOTO). GOTO L L TR La instruccin condicional if p then inst1 else inst2 se convierte en p L1 TRZ inst1 L2 TR inst2 L1: L2:

Subndices: a[exp1; exp2; ...; expn] se convierte en a exp1 exp2 ... expn SUBIN-n

Cudruplas

Una operacin didica se puede representar mediante la cudrupla (<Operador>, <Operando1>, <Operando2>, <Resultado>) Ejemplo: (*,A,B,T) Una expresin se puede representar mediante un conjunto de cudruplas. Ejemplo: la expresin a*b+c*d equivale a: (*,a,b,t1) (*,c,d,t2) (+,t1,t2,t3) Ejemplo: la expresin c:=a[i;b[j]] equivale a: (*,i,d1,t1) (+,t1,b[j],t2) (:=,a[t2],,c) Tripletes No se pone el resultado, se sustituye por referencias a tripletes. Por ejemplo: la expresin a*b+c*d equivale a: (1) (*,a,b) (2) (*,c,d) (3) (+,(1),(2)) mientras que a*b+1 equivale a: (1) (*,a,b) (2) (*,(1),1) Tripletes indirectos: se numeran arbitrariamente los tripletes y se da el orden de ejecucin. Ejemplo, sean las instrucciones: a := b*c b := b*c Equivalen a los tripletes (1) (*,b,c) (2) (:=,(1),a) (3) (:=,(1),b) y el orden de ejecucin es (1),(2),(1),(3). Esta forma es til para preparar la optimizacin de cdigo. Si hay que alterar el orden de las operaciones o eliminar alguna, es ms fcil hacerlo ah.

Generacin automtica de cudruplas

En un anlisis bottom-up, asociamos a cada smbolo no terminal una informacin semntica, y a cada regla de produccin una accin semntica. Ejemplo, sea la gramtica E ::= E + T E ::= E - T E ::= T T ::= T * F T ::= T / F T ::= F F ::= i F ::= (E) F ::= -F La regla F::=i asocia a F como informacin semntica el identificador concreto. La regla F::=(E) asocia a F como informacin semntica la informacin semntica asociada a E. La regla U::=V asocia a U como informacin semntica la informacin semntica asociada a V. La regla U::=VoW analiza la compatibilidad de los operandos, crea la cudrupla (o,Sem(V),Sem(W),Ti) y asocia a U la informacin semntica Ti. La regla U::=oV crea la cudrupla (o,Sem(V),,Ti) y asocia a U la informacin semntica Ti. La informacin semntica se suele almacenar en otra pila paralela. Ejemplo: anlisis de a*(b+c) Pila Entrada Regla Cudrupla ----------- ------------- ------------ --------| a*(b+c)| |a *(b+c)| F::=i |F(a) *(b+c)| T::=F |T(a) *(b+c)| |T(a)* (b+c)| |T(a)*( b+c)| |T(a)*(b +c)| F::=i |T(a)*(F(b) +c)| T::=F |T(a)*(T(b) +c)| E::=T |T(a)*(E(b) +c)| |T(a)*(E(b)+ c)| |T(a)*(E(b)+c )| F::=i |T(a)*(E(b)+F(c) )| T::=F |T(a)*(E(b)+T(c) )| E::=E+T |T(a)*(E(b)+T(c) )| E::=E+T (+,b,c,T1) |T(a)*(E(T1) )| |T(a)*(E(T1)) | F::=(E)

|T(a)*F(T1) | |T(T2) | |E(T2) |

T::=T*F E::=T

(*,a,T1,T2)

Ejemplo de generacin de cudruplas en anlisis top-down unsigned int E (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; i = V (cadena, i); break; case '(': i++; i = E (cadena, i); i = C (cadena, i); i = V (cadena, i); break; default: return -1; } return i; } unsigned int V (char *cadena, unsigned int i) { unsigned int j; if (i<0) return i; switch (cadena[i]) { case '*': case '/': j = i; i++; i = T (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); i = X (cadena, i); break; case '+': case '-': j = i; i++; i = E (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); break; } return i;

} unsigned int X (char *cadena, unsigned int i) { unsigned int j; if (i<0) return i; switch (cadena[i]) { case '+': case '-': j = i; i++; i = E (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); break; } return i; } unsigned int T (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; i = U (cadena, i); break; case '(': i++; i = E (cadena, i); i = C (cadena, i); i = U (cadena, i); break; default: return -2; } return i; } unsigned int U (char *cadena, unsigned int i) { if (i<0) return i; unsigned int j; switch (cadena[i]) { case '*': case '/': j = i; i++; i = T (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti));

push (Ti); break; } return i; } unsigned int F (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; break; case '(': i++; i = E (cadena, i); i = C (cadena, i); break; default: return -3; } return i; } unsigned int C (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case ')': i++; break; default: return -4; } return i; } Semntica de instrucciones condicionales Sean las reglas <Instr> ::= If <Expr> S2 then <Instr> S1 | If <Expr> S2 then <Instr> else S3 <Instr> S1 Una secuencia de cudruplas equivalente a "If E1 then I1 else I2" (p-1) (?,?,?,t1) Anlisis de E1 (p) (TRZ,(q+1),t1,) S2: Crear cudrupla (p) | Push p ... Anlisis de I1 (q) (TR,(r),,) S3: Crear cudrupla (q) Poner (cl.sig.) en top | Pop

(q+1) ... (r)

Push (q) Anlisis de I2 S1: Poner (cl.sig.) en top | Pop

Una secuencia de cudruplas equivalente a "If E1 then I1" (p-1) (?,?,?,t1) Anlisis de E1 (p) (TRZ,(r),t1,) S2: Crear cudrupla (p) | Push p ... Anlisis de I1 (r) S1: Poner (cl.sig.) en top | Pop Al generar la cudrupla (p) no conocemos el valor de (q). Guardamos en una pila el nmero de la cudrupla asociada y lo rellenamos ms tarde, como indican los ejemplos. Semntica de etiquetas y GOTO Suponemos que las etiquetas aparecen en la tabla de smbolos con tres valores asociados: (tipo=etiqueta, bit=declarada/no declarada, nmero de cudrupla). Sea la regla <Instr> ::= id : <Instr> Semntica asociada: { Buscar id en la tabla de smbolos; if (no est) Insertar id,valor=(etiqueta, declarada, cudrupla siguiente); else { if (tipo==etiqueta && bit==no declarada) { i=nmero de cudrupla; while (i) { j=cudrupla[i][2]; cudrupla[i][2]=cudrupla siguiente; i=j; } Cambiar valor a (etiqueta, declarada, cudrupla siguiente); } else error(); } } Sea la regla <Instr> ::= GOTO id Semntica asociada: {

Buscar id en la tabla de smbolos; if (no est) { Insertar id,valor=(etiqueta, no declarada, cudr.siguiente); Generar cudrupla (TR,,,); } else { if (tipo==etiqueta) { if (bit==declarada) Generar cudrupla (TR,nmero de cudrupla,,); else if (bit==no declarada) { i=nmero de cudrupla; Cambiar valor a (etiqueta, no declarada, cudr.siguiente); Generar cudrupla (TR,i,,); } } else error(); } } Si se permiten etiquetas locales a bloques, podemos encontrar el siguiente caso: L: ... { ... GOTO L; ... Tenemos ambigedad: GOTO L puede ir a la etiqueta externa (ya definida o no) o a una etiqueta local al bloque posterior a la instruccin. Tenemos tres posibilidades:

Un compilador en dos pasos. Forzar declaraciones de etiquetas. Tratar L en el bloque como si fuera local. Si al final del bloque descubrimos que no ha sido declarada, tratarla como si fuera global. La lista de referencias debera fundirse con la de L global (si L global no ha sido definida an) o rellenarse con el valor de L (si ya ha sido definida). Si L global no existe, debe crearse, y pasarle la lista de referencias de L local.

Semntica de bloques Sean las reglas <Instr> ::= do <id> := <Expr> S1 , <Expr> S2 <CD1> <LInstr> end S5 <CD1> ::= , <Expr> S3 | ^ S4 Semntica asociada al anlisis de "do x:=n1,n2,n3; I1; I2; end":

S1:

Generar cudruplas asociadas a instruccin de asignacin x:=n1. Guardar i=nmero de cudrupla siguiente. S2: Guardar j=nmero de cudrupla siguiente. Generar cudrupla (TRG,,x,(n2)). Generar cudrupla (TR,,,). S3: Generar cudrupla (+,x,(n3),x). Generar cudrupla (TR,(i),,). Hacer cudrupla[j+1][2]=cudrupla siguiente. S5: Generar cudrupla (TR,(j+2),,). Hacer cudrupla[j][2]=cudrupla siguiente.

Adems, S4: Generar cudrupla (:=,x,1,x). Generar cudrupla (TR,(i),,). Hacer cudrupla[j+1][2]=cudrupla siguiente. Evaluacin ptima de las expresiones booleanas Las operaciones booleanas usualmente se definen as: O | T F Y | T F NO| T F --|----- --|----- --|----T|T T T|T F |F T F|T F F|F F y la sintaxis adecuada para que la precedencia sea: NO, Y, O. Sin embargo, es posible simplificarlas considerablemente. Basta fijarse en la expresin a Y (b O NO c) Si a es falso, no hace falta calcular el parntesis, sabemos que el resultado es falso. Por tanto, redefiniremos la sintaxis y la semntica as: <ZB> ::= <EB> <EB> ::= <TB> O <EB> | <TB> <TB> ::= <FB> Y <TB> | <FB> <FB> ::= i | ( <EB> ) | NO <FB> a O b <=> if (a) TRUE else b; a Y b <=> if (a) b else FALSE; NO a <=> if (a) FALSE else TRUE;

Anlisis top-down

El programa queda: void ZB (char *cadena) { int FL=0, TL=0, i; cuad (":=", "T", NULL, x); i = EB (cadena, 0, &FL, &TL); FILL (FL); cuad (":=", "F", 0, x); FILL (TL); } unsigned int EB (char *cadena, unsigned int i, int *FL, int *TL) { int tl=0; i = TB (cadena, i, FL, &tl); MERGE (TL, tl); if (i<strlen(cadena)) switch (cadena[i]) { case 'O': i++; FILL (*FL); *FL = 0; i = EB (cadena, i, FL, TL); break; default: break; } return i; } unsigned int TB (char *cadena, unsigned int i, int *FL, int *TL) { int fl=0; i = FB (cadena, i, &fl, TL); MERGE (FL, fl); if (i<strlen(cadena)) switch (cadena[i]) { case 'Y': i++; FILL (*TL); *TL = 0; i = TB (cadena, i, FL, TL); break; default: break; } return i; } unsigned int FB (char *cadena, unsigned int i, int *FL, int *TL) {

if (i<strlen(cadena)) switch (cadena[i]) { case 'i': i++; *TL = cudrupla siguiente; *FL = - cudrupla siguiente; cuad (TRB, id, 0, 0); break; case '(': i++; i = EB (cadena, i, FL, TL); i = C (cadena, i); break; case 'NO': i++; i = FB (cadena, i, FL, TL); *FL <-> *TL break; default: error (cadena, i); } else error (cadena, i); return i; } unsigned int C (char *cadena, unsigned int i) { if (i<strlen(cadena)) switch (cadena[i]) { case ')': i++; break; default: error (cadena, i); } else error (cadena, i); return i; } void FILL (int lista) { int i,j,n; n = lista; while (n) { if (n>0) i=2; else i=3; j = abs (n); n = cuad[j][i]; cuad[j][i] = cudrupla siguiente; } } void MERGE (int *uno, int dos) { int i,k;

if (*uno==0) *uno = dos; else { k = *uno; for (;;) { if (k>0) i=2; else i=3; k = abs (k); if (cuad[k][i]==0) break; k = quad[k][i]; } cuad[k][i] = dos; } } Analicemos "a O b O NO c" ZB ("a O b O NO c") cuad (":=", "T", NULL, "X"); i = EB ("a O b O NO c", 0, &FL, &TL); i = TB ("a O b O NO c", 0, FL, &tl); i = FB ("a O b O NO c", 0, &fl, TL); case id: i=1; *TL = 1; fl = -1; cuad (TRB, a, 0, 0); MERGE (FL, fl); FL = -1; MERGE (TL, tl); TL = 1; case 'O': i=2; FILL (FL); n = -1; while (n) { i = 3; j = 1; (abs(FL)) n = 0; (cuad[1][3]) cuad[1][3] = 2; } FL = 0; i = EB ("a O b O NO c", 2, FL, TL); i = TB ("a O b O NO c", 2, FL, &tl); i = FB ("a O b O NO c", 2, &fl, TL); case 'i': i=3; *TL = 2; *fl = -2; cuad (TRB, b, 0, 0); MERGE (FL, fl); FL = -2;

MERGE (TL, tl); k = 1; for (;;) { i = 2; k = 1; } cuad[1][2] = 2; case 'O': i=4; FILL (FL); n = -2; while (n) { i = 3; j = 2; (abs (n)) n = 0; (cuad[2][3]) cuad[2][3] = 3; } FL = 0; i = EB ("a O b O NO c", 4, FL, TL); i = TB ("a O b O NO c", 4, FL, &tl); i = FB ("a O b O NO c", 4, &fl, TL); case 'NO': i=5; i = FB ("a O b O NO c", 5, FL, TL); case 'i': i=6; *TL = 3; *FL = -3; cuad (TRB, c, 0, 0); FL <-> TL MERGE (FL, fl); FL = 3; MERGE (TL, tl); k = 1; for (;;) { i = 2; k = 1; (abs (k)) k = 2; (cuad[1][2]) } cuad[2][2] = -3; FILL (FL); cuad[3][2] = 4; cuad (":=", "F", 0, "X"); FILL (TL); cuad[1][2] = 5; cuad[2][2] = 5; cuad[3][3] = 5; Cudruplas obtenidas:

0: (":=", "T",, x); 1: ("TRB", a, 5, 2); 2: ("TRB", b, 5, 3); 3: ("TRB", c, 4, 5); 4: (":=", "F",, x);

Las Instrucciones Del Ensamblador


Instrucciones de transferencia Son utilizadas para mover los contenidos de los operandos. Cada instruccin se puede usar con diferentes modos de direccionamiento. MOV MOVS (MOVSB) (MOVSW) Instruccin MOV Propsito: Transferencia de datos entre celdas de memoria, registros y acumulador. Sintaxis: MOV Destino, Fuente Donde Destino es el lugar a donde se movern los datos y fuente es el lugar donde se encuentran dichos datos. Los diferentes movimientos de datos permitidos para esta instruccin son: Destino: memoria. Fuente: acumulador Destino: acumulador. Fuente: memoria Destino: registro de segmento. Fuente: memoria/registro Destino: memoria/registro. Fuente: registro de segmento Destino: registro. Fuente: registro Destino: registro. Fuente: memoria Destino: memoria. Fuente: registro Destino: registro. Fuente: dato inmediato Destino: memoria. Fuente: dato inmediato Ejemplo: MOV AX, 0006h MOV BX, AX MOV AX, 4C00h INT 21H Este pequeo programa mueve el valor 0006H al registro AX, luego mueve el contenido de AX (0006h) al registro BX, por ltimo mueve el valor 4C00h al registro AX para terminar la ejecucin con la opcin 4C de la interrupcin 21h.

La instruccin MOV y los modos de direccionamiento.

He aqu nuestra primera instruccin: MOV destino, origen Efectivamente, sirve para mover. Lo que hace es copiar lo que haya en "origen" en "destino". Lo de que primero vaya el destino y luego el origen es comn a todas las instrucciones del 8086 que tengan dos operandos, lo cual crear ms de un quebradero de cabeza al principio. Existen no obstante algunos ensambladores que en su sintaxis intercambian los operandos, pues realmente no es necesario emplear los mnemnicos en ese orden mientras el cdigo de operacin sea el correcto. No hace falta decir (bueno, por si acaso lo digo) que destino y origen tienen que tener el mismo tamao; as

MOV AX, BL Hara pitar al ensamblador como loco, y con toda la razn. Si intentamos copiar BL (parte baja de BX, de tamao 8 bits) en AX (registro de 16 bits), el ensamblador nos dir que eso no se puede, y que no existe ningn cdigo de operacin para eso. MOV AX, BX sin embargo hace que el procesador coja el contenido de BX y lo copiara en AX; lo que haba anteriormente en AX se pierde (puesto que un registro al fin y al cabo es un nmero, en este caso de 16 bits, y ahora le hemos asignado un nuevo valor), mientras que BX no se ve afectado. Cuando decimos "mover" en realidad sera ms apropiado "copiar", porque no alteramos en absoluto el operando origen. Esta instruccin no slo nos permite mover datos entre registros, sino que podemos mover valores concretos, especificados en la propia instruccin. Por ejemplo, si quisiramos poner a 0 el registro DX podramos poner MOV DX, 0 Muy distinto de MOV DX, [0] Los corchetes significan "lo que haya en la direccin. En este caso el micro cogera la palabra (16 bits, pues es el tamao del operando destino, as que queda implcito) que estuviera en la direccin 0 y la copiara en DX. Ms an. No se trata de la direccin 0 sino del offset 0; de qu segmento? DS, que es el registro de segmento por defecto. Cuando en una operacin de este tipo no especificamos segmento, el procesador coge el valor que haya en DS y lo usa como segmento para formar la direccin completa. Si quisiramos copiar a DX la primera pareja de bytes del segmento apuntado por ES, porque es all donde tenemos el dato, tendramos que poner un prefijo de segmento (o segment override, para los guiris): MOV DX,[ES:0]

Esto va al segmento ES, se desplaza 0 bytes, coge los primeros 2 bytes a partir de esa direccin y los guarda en DX. Complicado? Pues no hemos hecho ms que empezar. Si en este momento andas un poco perdido, chale otro vistazo al captulo anterior y lee esto otra vez con ms calma, porque vamos a seguir aadiendo cosas. Se llama "modo de direccionamiento" a una forma de especificarle una direccin al micro para que acceda a algn dato, como cuando hemos dicho MOV DX,[ES:0]. Decirle al procesador directamente la direccin, en este caso offset 0, es efectivamente un modo de direccionamiento, aunque no demasiado flexible, porque debemos saber la posicin exacta del dato en el momento de hacer el programa. Veamos todos los modos de direccionamiento que permite este micro, sin ms que poner el offset dentro de []: Nombre Absoluto Indirecto con base Indirecto con ndice Ind. con base e ndice Offset Valor inmediato BX+x BP+x DI+x SI+x BX+DI+x BX+SI+x BP+DI+x BP+SI+x Segmento por defecto DS DS SS DS DS DS DS SS SS

Por x queremos decir un nmero en complemento a dos de 8 o 16 bits, es decir, los comprendidos entre -128 y 127 o -32768 y 32767. Supongamos que tenemos una cadena de caracteres en el segmento indicado por ES, offset 100h, y queremos mover un carcter cuya posicin viene indicada por el registro BP, a AL. El offset del carcter sera BP+100h; como el segmento por defecto para ese modo de direccionamiento es SS, es necesario un prefijo de segmento. La instruccin sera: MOV AL,[ES:BP+100h] Observando un poco la tabla podemos darnos cuenta de que todos emplean por defecto el registro de segmento DS excepto los que usan BP, que se refieren a SS. En general no es buena idea usar prefijos de segmento, pues las instrucciones que los usan se codifican en ms bytes y por tanto son ms lentas. As si hemos de referirnos a DS usaremos otros registros distintos de BP siempre que sea posible. El llamado prefijo de segmento es estrictamente hablando un prefijo, pues se codifica como tal, precediendo a la instruccin a la que afecta (un byte extra). En NASM es posible seguir literalmente esta construccin, pudiendo escribir la expresin anterior como es:

MOV AL,[BP+100h] Comprendidos bien los modos de direccionamiento del 8086, voy a aadir algunos ms: los que permiten los 386+. Cuando se emplean en direccionamientos indirectos registros de 32 bits es posible usar cualquiera de ellos. As "MOV EAX,[ECX]" sera perfectamente vlido. Y ms an (y esto es muy importante para manipular arrays de elementos mayores que un byte), el registro "ndice" puede ir multiplicado por 2,4 u 8 si de desea: es posible realizar operaciones del tipo "MOV CL,[EBX+EDX*8]". Al que todo esto le parezca pequeos detallitos en vez de potentes modos de direccionamiento, que se dedique a la calceta porque esto no es lo suyo; para manejar vectores de reales es una ayuda importantsima. No hay que olvidar que aunque estemos usando registros de 32 bits, seguimos limitados a segmentos de 64k si programamos en MSDOS. Bajo esta circunstancia, cuando se emplee un direccionamiento de este tipo hay que asegurarse de que la direccin efectiva (es decir, el resultado de la operacin EBX+EDX*8, o lo que sea) no supera el valor 0FFFFh. La instruccin MOV tiene ciertas limitaciones. No admite cualquier pareja de operandos. Sin embargo esto obedece a reglas muy sencillas: 1. No se puede mover de memoria a memoria 2. No se pueden cargar registros de segmento en direccionamiento inmediato 3. No se puede mover a CS Las dos primeras reglas obligan a pasar por un registro intermedio de datos en caso de tener que hacer esas operaciones. La tercera es bastante obvia, pues si se pudiera hacer eso estaramos haciendo que el contador de instruccin apuntase a sabe Dios dnde. Otra regla fundamental de otro tipo -y que tambin da sus dolores de cabeza- es que cuando se mueven datos de o hacia memoria se sigue la "ordenacin Intel", que no es ms que una manera de tocar las narices: los datos se guardan al revs. Me explico. Si yo hiciera MOV AX,1234h MOV [DS:0],AX Uno podra pensar que ahora la posicin DS: 0 contiene 12h y DS:1 34h. Pues no. Es exactamente al revs. Cuando se almacena algo en memoria, se copia a la posicin sealada la parte baja y luego, en la posicin siguiente, la parte alta. Lo gracioso del asunto (y ms que nada porque si no fuera as Intel tocara bastante ms abajo de las narices) es que cuando se trae un dato de memoria a registros se repite la jugada, de tal manera que al repetir el movimiento en sentido contrario, tenga en el registro el mismo dato que tena al principio. Pero la cosa no se detiene ah. Tras MOV EAX, 12345678h MOV [ES: 0124h], EAX La memoria contendra algo as como:

Segmento Offset: Contenido: ES 0124h 0125h 0126h 0127h 78h 56h 34h 12h

Vamos, que lo que digo para palabras lo digo para palabras dobles. Divertido, eh? Uno se puede preguntar adems: por qu no hacer "MOV [ES:0124h],12345678h"? Se puede, claro, pero no as (bueno, en este caso concreto tal vez, pero como norma general, no). El ensamblador no puede saber el tamao que tiene el operando inmediato, as que no sabe cuntos bytes tiene que escribir. Si t haces "MOV AX,8", est bien claro que tiene que meter un "8" en 16 bits, porque es el tamao que tiene AX, pero cuando el destino es una posicin de memoria, no sabe si poner 08h, 0008h, 00000008h. Hay que especificar si es un byte, un word, o double word con lo que se conocen como typecast: MOV BYTE [DI],0h ;DI es un puntero a byte MOV WORD [DI],0 ; puntero a word MOV DWORD [DI],0h ; puntero a double word Instruccin MOVS (MOVSB) (MOVSW) Propsito: Mover cadenas de bytes o palabras desde la fuente, direccionada por SI, hasta el destino direccionado por DI. Sintaxis: MOVS Este comando no necesita parmetros ya que toma como direccin fuente el contenido del registro SI y como destino el contenido de DI. La secuencia de instrucciones siguiente ilustra esto: MOV SI, OFFSET VAR1 MOV DI, OFFSET VAR2 MOVS Primero inicializamos los valores de SI y DI con las direcciones de las variables VAR1 y VAR2 respectivamente, despus al ejecutar MOVS se copia el contenido de VAR1 a VAR2. Los comandos MOVSB y MOVSW se utilizan de la misma forma que MOVS, el primero mueve un byte y el segundo una palabra.

Das könnte Ihnen auch gefallen