Вы находитесь на странице: 1из 12

JUEGO DE INSTRUCCIONES DEL 8086

A continuacin voy a describir brevemente todas las instrucciones que admite un 8086; entre parntesis sealar, si toca, los flags que altera cada una. ecordemos que el registro de flags contiene alguos indicadores que se ponen a !0! o a !"! seg#n el resultado de la #ltima operacin reali$ada %se obtiene un n#mero negativo, &a &abido carry, overflo'..( )on las instrucciones se mostrar* qu flags son modificados por este motivo, pero no se garanti$a que el resto de flags !informativos! no se ver*n alterados por la e+ecucin de la instruccin %o sea, el valor de ciertos flags puede quedar indeterminado tras la operacin(. ,ara detalles escabrosos sobre el funcionamiento de ciertas instrucciones, es me+or consultar una tabla de referencia en condiciones, como las que tiene -ntel por a&. en pdf en su p*gina 'eb, o la que viene con el manual de /A01. Aunque el t.tulo dice !del 8086!, entreme$clo algunas peculiaridades de los procesadores superiores; en la mayor.a de los casos quedar* claro si sirven o no en un 8086 %si usan 2A3, por e+emplo, es evidente que no(. 4na diferencia fundamental del 8086 con sus descendientes es que algunas de estas mismas instrucciones pueden recibir, adem*s de los operandos &abituales, otros adicionales propios de los nuevos procesadores. 1e e5plico. 4no en un 686 puede &acer ADD AX,BX %A37A3893( como un 8086 cualquiera, pero adem*s, por contar con registros de 6: bits, podr* &acer cosas del estilo ADD EAX,EBX. 2n ambos casos se tratar* de la misma instruccin %A;;, sumar(, slo que si el procesador cuenta con m*s registros, o registros e5tendidos, o simplemente capacidad de admitir nuevos operandos, podremos usarlos. ,ara esta instruccin en particular la sinta5is se describir* como ADD destino, origen, pero por destino y origen no ser* necesariamente v*lido cualquier modo de direccionamiento.. ;epender* de la instruccin, y en cierta medida del procesador que se use. 2n la mayor.a de los casos imperar*n las reglas generales !no es v*lido operar de memoria a memoria! y !no es v*lido operar con registros de segmento!. <ay adem*s una cierta lgica en estas restricciones %el operando destino generalmente es un registro de propsito general(, e instrucciones similares entre s. aceptar*n casi con toda seguridad los mismos operandos. 0e &ar*n algunas observaciones sobre los operandos v*lidos para cada instruccin, pero de nuevo para cualquier duda lo me+or ser* ec&ar mano de una tabla !oficial! %que es lo que &acemos todos, no te creas que alguien se lo aprende de memoria(. =os procesadores posteriores al 8086 incluyen no slo me+oras en las instrucciones, sino instrucciones adicionales. ;e stas slo se indicar*n, un tanto desperdigadas por a&., las que puedan ser utilidad. =as dem*s no digo que sean in#tiles, sino que en la mayor.a de los casos no nos estar*n permitidas. <ay un con+unto de instrucciones e5clusivas del modo protegido que slo podr*n ser usadas por el sistema operativo, y se dice que son instrucciones privilegiadas; un programa de usuario que intente e+ecutar una operacin de este tipo ser* detenido, generalmente con un mensa+e que indique un error de proteccin general. )asi todas las instrucciones privilegiadas corresponden a operaciones que pueden afectar al funcionamiento del resto de procesos del sistema %en modo protegido, por lo general, tendremos varios programas funcionando al tiempo, aunque en un momento dado el usuario intente cargar !el suyo!(. 4n e+emplo es INVD, que invalida la informacin de la memoria cac&e interna. 0i no fuera sta una instruccin privilegiada, cualquiera podr.a, en cualquier momento, desbaratar la cac&e con un programa malintencionado %recordemos que puede suceder que &aya varios usuarios traba+ando al tiempo en un cierto ordenador(. =as instrucciones -/>?4@ son usadas ya por el 8086 %de &ec&o son fundamentales(, pero no se incluyen en este cap.tulo porque sin ser propias del modo protegido, cuando el micro funciona en este modo,se consideran instrucciones privilegiadas. A ellas me referir en otro cap.tulo. Ainalmente, los con+untos especiales de instrucciones %coprocesador matem*tico, 113, 002..( se comentar*n en cap.tulos aparte. =a discusin m*s delicada tiene lugar cuando diferenciamos un procesador funcionando !en "6 bits! %modo real, que se comporta como un 8086 aunque no lo sea(, y !en 6: bits! %modo protegido, cualquier 6868 en =inu5, Bindo's, etc(, porque a menudo crea confusin. )uando estamos e+ecutando un programa para 10;?0 %aunque sea desde Bindo's, ya que estar* emulando un entorno de 10;?0(, nuestro procesador dispondr* de, tal ve$, m*s registros, pero como ya &emos visto seguir* obteniendo las direcciones como segmentoC"68offset %aunque pueda reali$ar operaciones con los registros e5tra, o incluso direccionar la memoria con ellos en esta circunstancia(. 25isten instrucciones que ya no seg#n el procesador sino seg#n el modo de funcionamiento se comportan de un modo u otro; se es el caso de LOOP, que en modo real altera )3 y en modo protegido 2)3 %normalmente prefiero decir !6: bits! a !modo protegido!, porque e5isten condiciones e5cepcionales ba+o las cuales se puede e+ecutar cdigo de "6 bits en modo protegido; no entrar de momento en esta discusin(. 2n modo 6: bits, adem*s, los offsets son de este tamao, lo que significa que los punteros de pila, instruccin, etc, ser*n de tipo d'ord. -mportant.simo tenerlo presente al pasar argumentos a subrutinas a travs de la pila %dar muc&o la brasa con esto a lo largo del tutorial(.

- IN !"#$$IONE DE %OVI%IEN!O DE DA!O D MOV destino, origen %1?Ee, mover( 9ueno, qu voy a contar de sta que no &aya dic&o ya.. Eale, s., un pequeo detalle. )uando se carga 00 con 1?E, el micro in&ibe las interrupciones &asta despus de e+ecutar la siguiente instruccin. A#n no &e dic&o lo que son las interrupciones ni qu tiene de especial la pila as. que tardaremos en comprobar qu puede tener de #til este detalle, y la verdad es que normalmente nos dar* lo mismo, pero bueno, por si las moscas. D XCHG destino, origen %e3)<anFe, intercambiar( -ntercambia destino con origen; no se puede usar con registros de segmento. 2sta instruccin encuentra su utilidad en aquellas instrucciones que tienen asociados registros espec.ficos. G OPE"A$IONE A"I!%E!I$A D ADD destino, origen %A;;ition, sumar( H?,0,I,A,,,)J 0uma origen y destino, guardando el resultado en destino. 0i al sumar los dos #ltimos bits !me llevo una! el bit de carry se pone a " %y si no, a 0(. D ADC destino,origen %A;dition 'it& )arry, sumar con acarreo( H?,0,I,A,,,)J 0uma origen, destino y el bit de carry, guardando el resultado en destino. 0irve entre otras cosas para sumar n#meros de m*s de "6 bits arrastrando el bit de carry de una suma a otra. 0i quisiramos sumar dos n#meros enteros de 6K bits almacenados en 2A3G293 y 2)3G2;3, podr.amos sumarlos con ADD EBX,EDX primero y AD$ EAX,E$X despus %para sumar las partes altas de 6: bits con !la que nos llev*bamos! de las partes ba+as(. 0umar con A;) puede generar a su ve$ carry, con lo que tericamente podr.amos sumar n#meros enteros de cualquier tamao, propagando el carry de una suma a otra. ,odemos poner a " el flag de carry directamente mediante STC %0e@ )arry( o a 0 mediante CLC %)=ear )arry(, ambas instrucciones sin argumentos. D INC destino %-/)rement, incrementar( H?,0,I,A,,J -ncrementa el operando destino en ". ,uede ser un operando de tamao !byte! o superior, tanto un registro como una posicin de memoria; en este #ltimo caso &ay que especificar tamao con B? ;, ;B? ; etc, tal y como e5plicamos en el cap.tulo --- con los modos de direccionamiento. 2sta instruccin no modifica el bit de carry; si quieres detectar cu*ndo !te pasas! al incrementar un contador, usa el flag !Iero! o el de signo. D SUB destino, origen %049stract, resta( H?,0,I,A,,,)J esta a destino lo que &aya en origen. D SBB destino, origen %0u9stract 'it& 9orro', restar con llevada( H?,0,I,A,,,)J esta a destino lo que &aya en origen. 0i el flag )7" resta uno m*s. An*loga a A;). D DEC destino %;2)rement, decrementar( H?,0,I,A,,J -gual que -/), pero que resta " en ve$ de sumarlo. D IMUL origen %-nteger 14=tiplication, multiplicacin entera con signo( H?,)J 1ultiplica origen, entero con signo %en complemento a dos(, de longitud byte o 'ord, por A= o A3 respectivamente. 0i origen es un byte el resultado se guarda en A3; si es tamao 'ord se almacena en el par ;3GA3 %parte alta en ;3, parte ba+a en A3(. 0i las mitades de mayor peso son distintas de 0, sea cual sea el signo, )A y ?A se ponen a uno. 2n el caso del 6868, adem*s, se puede usar un operando origen de 6: bits. 0i es as. se multiplica entonces por 2A3, de+ando el resultado de 6K bits en el par 2;3G2A3. 2l operando debe ser un registro o un dato de memoria %nada de valores inmediatos(. 2sto se aplica para -14=, 14=, -;-E y ;-E.

I%#L tiene otras dos formas m*s para procesadores posteriores al 8086. =a primera es IMUL destino, origen donde el destino es un registro de propsito general y el origen un valor inmediato, otro registro o una posicin de memoria. =a segunda forma tiene el aspecto IMUL destino, origen&, origen'. Destino es nuevamente un registro de propsito general, origen& un registro o posicin de memoria, y origen' un valor inmediato. =o que &ace es almacenar el producto de los dos operandos origen en destino. 2n cualquiera de las dos formas, si el resultado del producto no cabe en el destino, queda truncado. /os toca a nosotros comprobar, como siempre que sea necesario, los bits de overflo' y carry %si se usa un #nico operando no &ace falta, porque el destino siempre cabe(. D MUL origen %14=tiplication, multiplicacin entera sin signo( H?,)J )omo -14=, salvo que multiplica enteros sin signo. 0lo admite la forma con un #nico operando. D IDIV origen %-nteger ;-Eide, divisin entera con signo( ;ivide n#meros con signo. )alcula el cociente y el resto de dividir A3 entre el operando %tamao byte(. 0i el operando es de "6 bits lo que divide es el par ;3GA3. 0i el operando es de 6: bits %806868(, lo que divide es el par 2;3G2A3. 2l cociente lo guarda en A=, A3 o 2A3 seg#n el caso. 2l resto en A<, ;3 o 2;3. 0i el cociente es mayor que el tamao m*5imo %8, "6 o 6: bits( tanto cociente como resto quedan indefinidos, y salta una interrupcin 0 %luego veremos cmo van estas cosas, pero te puedes &acer una idea de que no es muy sano(. 0i divides por cero pasa lo mismo. D DIV origen %;-Eide, divisin entera sin signo( -gual que -;-E, slo que para n#meros sin signo. G IN !"#$$IONE DE OPO"!E A"I!%(!I$O =a mayor parte de estas instrucciones sirven para e)tender el signo. La &emos visto que puede suceder que tengamos que operar con un n#mero repartido en dos registros como ;3GA3. -magina que quieres cargar un entero con signo de "6 bits en estos dos registrosM Nqu &aces con los otros "6 m*s altosO 0i el entero es positivo basta con poner a 0 ;3, pero si es negativo tendr*s que darle el valor 0AAAA&M "d 7 0000 000"& G"d 7 AAAA AAAA& )omo todo lo que sea abreviar comparaciones y saltos es siempre bienvenido, a alguien se le ocurri &acer instrucciones especialmente para esta tarea. ,uedes e5tender el signo dentro de un registro o &acia otro, traba+ando fundamentalmente sobre el acumulador %por lo que no son demasiado fle5ibles(. =as 1?E03 y 1?EI3 Gdel 686G dar*n m*s +uego. D CWD %)onvert Bord to ;ouble'ord, convertir palabra a palabra doble( 25tiende el signo de A3 a ;3, resultando el n#mero ;3GA3 D CQD %)onvert ;ouble'ord to PuadG'ord, convertir palabra doble a palabra cu*druple( 25tiende el signo de 2A3 a 2;3, resultando el n#mero 2;3G2A3 D CBW %)onvert 9yte to Bord, convertir byte a palabra( 25tiende el signo de A= a A<, resultando el n#mero A3 D CWDE %)onvert Bord to ;ouble'ord 25tended, convertir palabra a palabra doble e5tendida( 25tiende el signo de A3 a 2A3, resultando el n#mero 2A3 D MOVSX destino,origen %1ove 'it& 0ign 25tension, mover con e5tensin de signo( 1ueve origen a destino, e5tendiendo el signo. 0i el destino es de "6 bits, origen &a de ser de 8. 0i destino es de 6: bits, origen puede ser de 8 o de "6. 0lo acepta mover a un registro %de datos(.

D MOVZX destino,origen %1ove 'it& Iero 25tension, mover con e5tensin de ceros( 25actamente como 1?EI3, slo que en ve$ de e5tender el signo rellena de 0s. D NEG destino %/2Fate, negar(H?,0,I,A,,,)J )ambia de signo el n#mero en complemento a dos del destino. 2quivale a &acer /?@ y luego -/). GIN !"#$$IONE L*+I$A D D D D AND destino,origen OR destino,origen XOR destino,origen NOT destino

)reo que si sabes lo que significan estas operaciones lgicas %si no revisa el cap.tulo 0(, estar*n bien claritas M( GDE PLA,A%IEN!O - "O!A$IONE D D D D D D D D SAL destino,origen %0&ift Arit&metic =eft, despla$amiento aritmtico a la i$quierda( H?,0,I,,,)J SAR destino,origen %0&ift Arit&metic ig&t, despla$amiento aritmtico a la derec&a( H?,0,I,,,)J SHL destino,origen %0<ift logic =eft, despla$amiento lgico a la i$quierda( H?,0,I,,,)J SHR destino,origen %0<ift logic ig&t, despla$amiento lgico a la derec&a( H?,0,I,,,)J ROL destino,origen % ?tate =eft, rotacin a la i$quierda( H?,)J ROR destino,origen % ?tate ig&t, rotacin a la derec&a( H?,)J RCL destino,origen % otate 'it& )arry =eft, rotacin a la i$quierda con carry( H?,)J RCR destino,origen % otate 'it& )arry ig&t, rotacin a la derec&a con carry( H?,)J

Ambas despla$an o rotan el operando destino &acia el sentido indicado tantas veces como diga el operando origen. 2ste #ltimo operando puede ser un valor inmediato o )=.

2s bueno recordar que despla$ar un n#mero a la i$quierda un bit equivale a multiplicar por dos, y despla$arlo a la derec&a dividir entre dos. )uando queramos reali$ar alguna de estas operaciones en un factor potencia de dos siempre ser* muc&o m*s r*pido despla$ar un registro %o varios, propagando el carry( que reali$ar una multiplicacin>divisin. <ilando un poco m*s fino, si tenemos que multiplicar un n#mero por algo que no sea una potencia de dos pero !se le pare$ca muc&o!, el mtodo puede seguir compensando. ,ara multiplicar por :0 podemos &acer una copia del registro, despla$arlo por un lado K bits %para multiplicar por "6( y por otro : bits %para multiplicar por K(, y sumar ambos resultados parciales. 0i nuestro procesador est* un poco pasado de moda %686G K86( puede que la me+ora sea a#n significativa.

GIN !"#$$IONE DE $O%PA"A$I*N D CMP operando&,operando' %)o1,are, comparar( H?,0,I,A,,,)J Aunciona e5actamente igual que 049 solamente que sin almacenar el resultado %o sea, efectua la operacin operando"Goperando:, alterando los flags en consecuencia(. Al igual que la instruccin )1, del motorola se utili$a antes de efectuar un salto condicional. D TEST operando&,operando' %@20@, comprobar( H?,0,I,A,,,)J )omo la anterior, pero con la operacin A/; en lugar de 049. GIN !"#$$IONE DE AL!O D JMP direcci.n %Qu1,, saltar( 0alta a la direccin indicada. 2ste salto puede ser tanto le+ano como cercano, y la direccin puede venir dada en valor inmediato %generalmente mediante una etiqueta( o en memoria. 4n salto cercano tiene lugar dentro del mismo segmento %llamado tambin salto intrasegmento por este motivo(, cargando -, %2-, en modo protegido( con el nuevo offset. =os saltos le+anos cambiar el valor tanto de -,%2-,( como de )0. 2l tipo de salto se puede indicar con la direccin mediante los prefi+os near o ar en cada caso. 25iste un tercer tipo de salto denominado corto indicado por el prefi+o !"#r$, cuya direccin viene codificada en un byte. 2ste #ltimo salto es en realidad una forma abreviada de e5presar un salto cercano, donde en lugar de indicar la direccin a la que se salta, se especifica la distancia desde la posicin actual como un n#mero de 8 bits en complemento a dos. D J%% direcci.n !cc! representa la condicin. 0i la condicin es verdadera, salta. 2n caso contrario, contin#a la e+ecucin. =as distintas condiciones se presentan en la tabla inferior. 2l ensamblador de -ntel tiene algunos mnemnicos distintos para las mismas instrucciones, lo cual viene muy bien para recordar qu comparacin usar en cada caso. ,or e+emplo, QI %saltar si cero( y Q2 %saltar si iguales( son e5actamente la misma instruccin. 4na curiosidadM en el 8086 los saltos condicionales slo pod.an ser cortos %lo que significa que la distancia se codificaba en un byte(, obligando a utili$ar la comparacin contraria y un Q1, para poder saltar a m*s de ":R>":8 bytes de distancia. 2n el 80686 estos saltos son de &asta 6: bits, con lo que pr*cticamente no tenemos limitacin alguna. 0i queremos saltar cuando el bit de carry sea ", podemos &acerlo con algo como Q) eti/ueta y olvidarnos de todo lo dem*s. In!$r&%%'(n QI Q/I Q) Q/) Q? Q/? Q0 Q/? Q,>Q,2 Q/,>Q,? Qump if Iero Qump if /ot Iero Qump if )arry Qump if /ot )arry Qump if ?verflo' Qump if /ot ?verflo' Qump if 0ign Qump if /ot 0ign Qump if ,arity %,arity 2ven( salta si cero salta si no cero salta si acarreo salta si no acarreo salta si overflo' salta si no overflo' salta si signo salta si no signo salta si paridad %,aridad ,ar( C#n)'%'(n IA7" IA70 )A7" )A70 ?A7" ?A70 0A7" 0A70 ,A7"

Qump if /ot ,arity %,arity ?dd( salta si no paridad %,aridad ,ar( ,A70

)uando queremos &acer un salto condicionado por una comparacin, y no directamente por el estado de los flags, lo que &acemos es una comparacin )1, A,9. A continuacin usamos una instruccin de salto de entre las siguientesM In!$r&%%'(n QA Qump if Above salta si por encima C#n)'%'(n ASgt9 %sin signo(

QA2 Q9 Q92 Q2 QF QF2 Q= Q=2

Qump if Above or 2qual Qump if 9elo' Qump if 9elo' or 2qual Qump if 2qual Qump if Freater Qump if =ess Qump if =ess or 2qual

salta si por encima o igual AT79 %sin signo( salta si por deba+o salta si igual salta si mayor salta si menor salta si menor o igual ASlt9 %sin signo( A79 ASgt9 %con signo( AT79 %con signo( ASlt9 %con signo( AU79 %con signo( salta si por deba+o o igual AU79 %sin signo(

Qump if Freater or 2qual salta si mayor o igual

)1, equival.a a una resta que no modificaba los operandos; si queremos saltar si AU9, podemos &acer $%P A,B. 0i AU9 al &acer la resta me llevar una, por lo que el bit de carry se pondr* a ". Eemos que Q) es equivalente a Q9. )on similares deducciones se pueden obtener algunos saltos m*s; otros, sin embargo, dependen del resultado de dos flags al tiempo %!menor o igual! saltar* con )A7" y IA7"(, por lo que sus mnemnicos corresponder*n a instrucciones nuevas. ,ara poner las cosas a#n m*s f*ciles, e5isten JNA*JNAE*JNB*JNBE*JNE*JNG* JNGE*JNL* JNLE+ /o &ac.a falta que estuvieran, porque son equivalentes a Q92,Q9,QA2,QA,Q/I, Q=2,Q= y QF, pero as. nos evitan pensar un poco. 4na / delante indica la condicin contraria. 0i uno no quiere l.os, basta que recuerde que 9elo'>Above son para n#meros sin signo y =ess>Freater para con signo. 4na instruccin de salto muy #til en combinacin con las siguientes %=??, y variaciones( es JCXZ %Qump if )3 is Iero, salta si )3 es cero(, o JECXZ si estamos operando en 6: bits. ,ara &acer ciclos %algo as. como for i0& to &1( la familia del 8086 cuenta con =??, y la pare+a =??,2>=??,I %mnemnicos de lo mismo( D LOOP direcci.n %=??,, ciclo( ;ecrementa )3 y si el resultado es distinto de 0 salta a la direccin dada. 2strictamente &ablando no es una direccin sino un despla$amiento en el rango 8":R>G":8 bytes respecto a -, para el 8086, o 8%:V6"G"(>%G:V6"( para el 80686, igual que las instrucciones Qcc. Al programar no usaremos despla$amientos ni direcciones absolutas, sino etiquetas que se encargar* de sustituir el ensamblador, por lo que no debe %en principio( pasar de ser una ancdota. LOOPZ>LOOPE tienen la misma sinta5is que =??,. =o que &acen es decrementar )3 y saltar a la direccin dada si )3 es distinto de 0 y IA7". =a idea es efectuar un ciclo con una condicin dada, que se repita un m*5imo de )3 veces. )uando el modo de funcionamiento es de 6: bits =??,, =??,2 y =??,I operan sobre 2)3. LOOPNZ,LOOPNE son iguales a los anteriores, slo que la condicin adicional al salto es IA70 en ve$ de IA7". )omo cuando )370 %2)370( estos ciclos se e+ecutan en principio :V"6 %:V6:( veces, podemos evitarlo mediante el uso de Q)3I %Q2)3I( +usto antes del ciclo. G%ANE2O DE LA PILA 1encionamos ya brevemente en el cap.tulo -- en qu consiste la pila. A&ora veremos e5actamente qu es, as. como para qu y cmo se usa. =os programas se dividen b*sicamente en una $ona de datos y otra de cdigo, &abitualmente cada cada una en su regin de memoria asociada a un valor de registro de segmento %lo que se llama, simplemente, un segmento(. 0iempre &ay al menos un tercer segmento llamado pila, de suma utilidad en los microprocesadores. 2n l se almacenan valores temporales como las variables locales de las funciones, o las direcciones de retorno de stas. 4na funcin no es m*s que una subrutina, o un fragmento de cdigo al que se le llama generalmente varias veces desde el programa principal, o desde una funcin +er*rquicamente superior. )uando se llama a una funcin se &ace un mero salto al punto donde empie$a ese cdigo. 0in embargo esa subrutina puede ser llamada desde distintos puntos del programa principal, por lo que &ay que almacenar en alg#n sitio la direccin desde donde se &ace la llamada, cada ve$ que esa llamada tiene lugar, para que al finali$ar la e+ecucin de la funcin se retome el programa donde se de+. 2sta direccin puede almacenarse en un sitio fi+o %como &acen algunos microcontroladores(, pero eso tiene el inconveniente de que si esa funcin a su ve$ llama a otra funcin %Wo a s. mismaX( podemos sobreescribir la direccin de retorno anterior, y al regresar de la segunda llamada, no podr.amos

volver desde la primera. Adem*s, es deseable que la funcin guarde los valores de todos los registros que vaya a usar en alg#n sitio, para que el que la llame no tenga que preocuparse de ello %pues si sabe que los registros van a ser modificados, pero no sabe cu*les, los guardar* todos por si acaso(. @odas estas cosas, y algunas m*s, se &acen con la pila. 2l segmento de pila est* indicado por 00, y el despla$amiento dentro del segmento, por 0, %o 20, si estamos en 6: bits; todo lo que se diga de 0, de aqu. en adelante ser* aplicable a 20,, salvo, como se suele decir, error u omisin(. )uando arranca el programa, 0, apunta al final del segmento de pila. 2n el caso de los programas de tipo 232 en 10;?0, por e+emplo, el tamao que se reserva a la pila viene indicado en el e+ecutable %que solicita al sistema operativo ese &ueco; si no &ay memoria suficiente para el cdigo, los datos y la pila, devuelve mensa+e de memoria insuficiente y no lo e+ecuta(. 2n otros, puede que el tamao lo asigne el sistema operativo seg#n su conveniencia. 2l caso es que la manera de meter algo en la pila es decrementar 0, para que apunte un poco m*s arriba y copiarlo a esa posicin de memoria, 00M0,. ,ara sacarlo, copiamos lo que &aya en 00M0, a nuestro destino, e incrementamos el puntero. )omo con todo lo que se &ace con frecuencia, &ay dispuestas instrucciones propias para el mane+o de la pila. =as dos b*sicas son PUSH origen %empu+ar( y POP destino %sacar(. =a primera decrementa el puntero de pila y copia a la direccin apuntada por l %00M0,( el operando origen %de tamao m#ltiplo de "6 bits(, mientras que la segunda almacena el contenido de la pila %elemento apuntado por 00M0,( en destino y altera el puntero en consecuencia. 0i el operando es de "6 bits se modifica en : unidades, de 6: en K, etc. =o que se incrementa>decrementa es siempre 0,, claro, porque 00 nos indica dnde est* ubicado el segmento de pila. PUSHA y POPA %de ,40< All y ,?, All( almacenan en la pila o e5traen de la pila respectivamente los registros b*sicos. ,ara ,40<A se sigue este orden %el inverso para ,?,A(M A3,)3,;3,93,0,,9,,0-,;-. 2n el caso de los registros e5tendidos lo que tenemos son PUSHAD y POPAD, empu+ando a la pila entonces 2A3,2)3,2;3,293,20,,29,,20-,2;-. 2l puntero de pila que se introduce en la misma es el que &ay antes de empu+ar A3>2A3. )uando se e+ecuta ,?,A el contenido de 0,>20, de la pila no se escribe, sino que !se tira!. 0e emplean generalmente al principio y final de una subrutina, para preservar el estado del micro. /o creo que nadie vaya a programar e5actamente en un 686, pero si alguien quisiera tener en cuenta a estos procesadores tendr.a que considerar que la mayor.a tienen un defecto de diseo por el cual ,?,A; no restaura correctamente el valor de 2A3. ,ara evitarlo basta colocar la instruccin /?, detr*s de aqulla. PUSH- y POP- %,40< Alags y ,?, Alags( empu+an a la pila o recuperan de ella el registro de flags. G%ANE2O DE #B"#!INA D CALL direcci.n %)A==, llamar( 2mpu+a a la pila la direccin de retorno %la de la siguiente instruccin( y salta a la direccin dada. )omo en los saltosM una llamada cercana cambia el offset %-,>2-,(, mientras que una le3ana cambia offset y segmento %)0M-,>)0M2-,(. 0lo empu+a a la pila los registros que modifica con el salto %se &abla de punteros cercanos o le+anos en cada caso(. 2l tipo de salto se indica con un prefi+o a la direccin, como con Q1,M /2A para cercano, AA para le+ano. 2n ve$ de la direccin admite tambin un operando en memoria con cualquier modo de direccionamiento indirecto, cargando entonces -,>2-, o )0M-,>)0M2-, con el puntero almacenado en esa posicin de memoria. @ambin se puede &acer un salto cercano al valor almacenado en un registro %de "6 bits o 6: bits seg#n el caso(. ,ara las direcciones de retorno que se guardan en la pila, o los punteros en memoria que se cargan, &ay que recordar que en caso de ser un puntero le+ano primero se almacena el offset y luego el segmento. A ver, para no liarla, vamos con algunos e+emplos de llamadasM call near eti/ueta; salta a la direccin donde se encuentre la etiqueta call far 4di5; salta a la direccin almacenada en la posicin YdsMdiZ como segmentoMoffset call e6); salta al offset eb5 %se sobreentiende que es near( 0i se omite far, se da por sentado que es una llamada cercana, pero por claridad recomiendo ponerlo siempre e5pl.citamente. D RET*IRET % 2@urn, regresar(

25trae una direccin de la pila y salta a ella. ,uede ser un retorno cercano mediante RETN %/ear( o le+ano mediante RET- %Aar(; en el primer caso e5trae el offset y en el segundo segmento y offset. )on el uso genrico de la instruccin 2@ el ensamblador elige la apropiada; sin embargo es buena costumbre indicarlo igual que con call. 2s muy importante que la subrutina de+e la pila como estaba +usto al ser llamada para que la direccin de retorno quede en la parte superior, pues de lo contrario al llegar a 2@ saltar.amos a cualquier sitio menos la direccin correcta. IRET es el retorno de una interrupcin; adem*s de volver, restaura el registro de flags. Al acudir a una interrupcin este registro se guarda en la pila. ;e momento no trataremos interrupciones. =a pila sirve no slo para guardar los registros temporalmente o las direcciones de retorno, sino tambin las variables locales, que son imprescindibles para una programacin cabal. =a idea es que cuando vamos a llamar a una subrutina, ec&amos primero a la pila los argumentos que va a necesitar. =uego la subrutina, nada m*s arrancar salva en la pila los valores de los registros que considera necesarios, y !abre un &ueco! encima de stos, decrementando el puntero de pila en tantas unidades como necesite. =a venta+a fundamental de esto es que cada llamada a la funcin !crea! su propio espacio para variables locales, de modo que una funcin puede llamarse a s. misma sin crear conflictos entre las variables de la funcin !llamadora! y !llamada! %caller y callee en algunos te5tos ingleses, aunque no estoy muy seguro de que el segundo trmino sea muy correcto(. 2l #nico problema que tenemos a&ora es que una ve$ abierto el &ueco y determinadas las posiciones que ocupar*n dentro de la pila nuestras variables, puede suceder que queramos ec&ar m*s cosas a la pila, con lo que 0,>20, se despla$ar* arriba y aba+o durante la e+ecucin de la subrutina; llevar la cuenta de en dnde se encuentra cada variable a lo largo de la funcin es algo poco pr*ctico, y con lo que es f*cil equivocarse. 0ucede aqu. que tenemos un registro que usa por defecto el segmento de pilaM 9, %nuevamente se &a de entender que lo que se aplica a 0,,9, es e5tensible a 20,,29,(. [ste es el registro que emplearemos como puntero inamovible para sealar a argumentos y variables locales, para que no perdamos la pista de estos datos &agamos lo que &agamos con 0,. ,rimero lo &aremos todo paso a paso porque es importante entender el procedimientoM Eamos a crear una subrutina en modo real que sume dos n#meros de "6 bits %un poco est#pido, pero es para &acerlo lo m*s simple posible(. 2l programa principal &ar.a algo as.M

... pus7 8ord Y0umando"Z pus7 8ord Y0umando:Z su6 sp,: ;&ueco para el resultado %queremos que nos lo devuelva en la pila( call 0uma2stupida pop 8ord Y esultadoZ ;saca el resultado add sp,K ;elimina los sumandos de la pila ... y la subrutina ser.aM 0uma2stupidaM ;0, apunta a la direccin de retorno, 0,8: al resultado, 0,8K a 0umando:, 0,86 a 0umando" pus7 bp ;a&ora 0,8K apunta al resultado, 0,86 a 0umando: y 0,88 a 0umando" mov bp,sp ;9,8K apunta al resultado, 9,86 a 0umando: y 9,88 a 0umando" pus7 a5 ;altero 0, pero me da igual, porque tengo 9, como puntero a los argumentos mov a5,Ybp86Z ;cargo el 0umando: add a5,Ybp88Z ;le aado el 0umando" mov Ybp8KZ,a5 ;almaceno el resultado pop a5 ;restauro A3 pop bp ;restauro 9, retn 1ediante la directiva 2P4 podemos &acer por e+emplo 041"79,88 tal que para referirnos al sumando " &agamos Y041"Z, y as. con el resto. RET admite un operando inmediato que indica el n#mero de bytes que queremos despla$ar el puntero de pila al regresar. ;e esta manera, si colocamos primero el &ueco para el resultado y luego los sumandos, mediante un ! 2@/ K! podemos a&orrarnos !A;; 0,,K! tras la llamada. Algunos lengua+es de alto nivel &acen las llamadas a funciones de esta manera; no as. el ). 4sar un A;; en lugar de un ! 2@ 5! tiene la venta+a de que podemos usar un n#mero variable de argumentos por pila; en general al que me$cle ensamblador con ) le parecer* la me+or alternativa.

=os procesadores :868 proporcionan un mtodo alternativo de reali$ar estas operaciones tan comunes en los lengua+es de alto nivel con un cierto nivel de sofisticacin. =a primera instruccin al respecto es ENTER, que recibe dos operandos. 2l primero indica la cantidad de memoria que se reservar* en la pila, en bytes %asignacin de espacio a variables locales(. 2l segundo es el llamado nivel de anidamiento del procedimiento, y &ace referencia a la +erarqu.a que presentan las variables en el programa; determinar* qu variables ser*n visibles para la funcin que &a sido llamada. 2sto encierra bastantes sutile$as que corresponden a cmo se construye el cdigo m*quina de este tipo de estructuras y a este nivel no interesa meterse en beren+enales; si &ubiera que usar 2/@2 , de+ar.amos el segundo operando a 0. Puedmonos #icamente con que si quisiramos reservar K bytes para variables locales, usar.amos la instruccin !2/@2 K,0!; empu+a 9,, copia 0, en 9,, decrementa 0, en K. =a instruccin que des&ace el entuerto es LEAVE, sin argumentos. =o #nico que &ace es copiar 9, en 0, para liberar el espacio reservado, y a continuacin restaurar 9, desde la pila. ,ara ver en m*s detalle el mane+o de la pila en las subrutinas, as. como las posibilidades de 2/@2 >=2AE2 y el concepto de nivel de anidamiento de un procedimiento recomiendo acudir a la documentacin -ntel, al te5to !-ntel Arc&itecture 0oft'are ;eveloper\s 1anual Eolume "M 9asic Arc&itecture!, que pese a lo que aparenta es un te5to muy legible. 2ste tema lo aborda el cap.tulo K, m*s concretamente en !,rocedure calls for bloc]Gstructured languages!. 0i alguien est* pensando en me$clar ) con ensamblador encontrar* este te5to de gran inters. 2l programador de la subrutina &a de tener muy en cuenta dos cosas. =a primera, si la subrutina va a ser llamada desde el mismo segmento u otro %llamada cercana o le+ana(, pues dependiendo de ello )A== introduce distinto n#mero de elementos en la pila. =a segunda es si estamos programando en "6 o en 6: bits, que nos determinar* las dimensiones de lo que ec&emos a la pila. )ualquier consideracin errnea de este tipo nos &ar* intentar acceder a los argumentos en posiciones equivocadas de la pila. GIN !"#$$IONE PA"A OB!ENE" DI"E$$IONE D LEA destino,origen %=oad 2ffective Address, cargar direccin efectiva( )arga la direccin efectiva del operando origen en destino. !=2A A3,Y938;-8:Z! calcular.a la suma 938;-8: e introducir.a el resultado en A3 %y no el contenido de la direccin apuntada por 938;-8:, pues eso ser.a un 1?E(. )omo destino no se puede usar un registro de segmento. 0i el destino es de 6: bits, el offset que se carga es de este tipo. 2n modo protegido slo usaremos este #ltimo, pues offsets de "6 bits carecer*n de sentido. D LDS destino,origen %=oad pointer using ;0, cargar puntero usando ;0( 2sta instruccin y sus variantes a&orran muc&o tiempo e instrucciones en la carga de punteros. origen ser* siempre memoria conteniendo un puntero, es decir, un segmento y un despla$amiento. =a primera palabra corresponde al offset y la segunda al segmento. 2l offset se carga en destino y el segmento en ;0. 0i estamos traba+ando en un modo de 6: bits el despla$amiento ser* de 6: bits y el segmento de "6. 25isten m*s instrucciones, una por cada registro de segmentoM LES*L-S*LGS y LSS %muc&o cuidadito con esta #ltima, porque nos desmadra la pila(. GIN !"#$$IONE DE %ANE2O DE BI! D BT,BTS,BTR,BTC registro,operando %9it @est>@estS0et>@estS eset>@estS)omplement 2stas instrucciones lo primero que &acen es copiar el bit del registro indicado por el operando, sobre el bit de carry. 2l operando puede ser un registro o un valor inmediato de 8 bits. 4na ve$ &ec&o esto no &ace nada %9@(, lo pone a " %9@0(, lo pone a 0 %9@ ( o lo complementa %9@)( seg#n corresponda. GIN !"#$$IONE DE $ADENA <ay un con+unto de instrucciones conocido a veces como !de cadena!, que sirven para mover y comparar elementos dispuestos en array, increment*ndose cada ve$ el puntero a la cadena. [stas se ven afectadas por el bit de direccin %que indica el sentido en que se recorre la cadena(. 1ediante la instruccin STD %0e@ ;irection flag( &acemos ;A7", y con CLD %)=ear ;irection flag( ;A70 LODSB y LODSW %=?a; 0tring, 9yte y =?ad 0tring, Bord(, sin operandos, leen el byte o palabra en la direccin dada por ;0M0-%20-( y la almacenan en A= o A3 respectivamente. ,odemos usar en su lugar LODS operando, con un operando en memoria, especificando con ello si se trata de un =?;09 o =?;0B seg#n el tipo. 0i el operando

lleva segment override ser* de este segmento de donde se tomen los datos con 0-. ,or claridad es preferible usar =?;09 o =?;0B. NPu tiene todo esto que ver con lo mencionado sobre el flag de direccinO 0i el ;A70 0- se incrementa en uno para =?;09 y dos para =?;0B %apunta al siguiente byte>palabra(. 0i ;A7" se lee el array en direccin inversa, es decir, 0- se decrementa. As., por e+emplo, podemos ir cargando en el acumulador el contenido de un array a lo largo de un ciclo para operar con l. STOSB y STOSW %0@?re 0tring, 9yte>Bord( funcionan con el mismo principio en cuanto al flag de direccin, y lo que &acen es almacenar en 20M;-%2;-( el contenido del acumulador %A= o A3 seg#n cada caso(. ;e manera an*loga podemos usar STOS operando. MOVSB y MOVSW %1?E 0tring, 9yte>Bord( van m*s all*; mueven el byte o palabra en ;0M0- a 20M;-. Eemos a&ora que 0- es el ource Inde) o .ndice fuente, y ;- el Destination Inde) o .ndice destino. @ras el movimiento de datos 0- y ;- son incrementados o decrementados siguiendo la lgica descrita para =?;0. 2s admisible adem*s la instruccin 1?E0 destino,origen %ambos operandos en memoria(. 2l ensamblador emplea los operandos para determinar si se trata de un 1?E09 o 1?E0B al codificar la instruccin correspondiente; adem*s el operando origen puede llevar un prefi+o de segmento %no as. el destino( en cuyo caso se emplea se registro de segmento como origen. WA+a+*, as. que a&ora podemos mover arrays de bytes o palabras metiendo estas instrucciones en un ciclo, olvid*ndonos de andar manipulando los registros .ndiceX ,ues no. 2s decir, s., claro que se puede, pero no se &ace as.. 25iste un prefi+o llamado REP que se coloca antes de la instruccin de cadena en cuestin, que lo que &ace es repetir el proceso tantas veces como indique )3%2)3(. 0upongamos que queremos llenar de ceros "00 bytes a partir de la posicin A000&M0000&. ,odemos currarnos un ciclo que mane+e un .ndice, empe$ar a &acer mov\s de 0s a YdiZ.. 4na solucin %en "6 bits( m*s inteligente y r*pida ser.aM mov c5,^0d mov a5,0A000& mov es,a5 5or di,di 5or a5,a5 rep stos' )omo cada palabra son : bytes, lo que &acemos es cargar )3 con ^0 %pondremos a 0 ^0 palabras(, apuntar con 20M;- al primer elemento, poner A3 a 0, y empe$ar a almacenar A3 en cada posicin, &asta ^0 veces. Al finali$ar ese fragmento de cdigo ;- contendr* el valor "00, pues se &a incrementado en : en cada repeticin del ciclo. ,ero como se suele decir, a#n &ay m*s. SCASB y SCASW %0)An 0tring( reali$an la comparacin !)1, A=,20M Y;-Z! o !)1, A3,20MY;-Z!, alterando lgicamente los flags, y a continuacin modificando ;- como es debido. %25iste 0)A0 operando con idnticas consideraciones a las anteriores instrucciones de cadena con operando en memoria(.CMPSB y CMPSW %)o1,are 0tring( equivalen a !)1, ;0MY0-Z,20MY;-Z! tamao byte o 'ord seg#n corresponda, alterando los flags en funcin de la comparacin e incrementando 0- y ;- seg#n el tamao del dato y el flag de direccin %&abiendo asimismo un )1,0 que funciona an*logamente a 1?E0 en cuanto a llevar operandos en memoria(. 2sto puede no parecer impresionante %ya que 2, aqu. no pinta nada(, pero es que e5isten dos prefi+os %en realidad K, pero son pare+as de mnemnicos de instrucciones idnticas( m*s, REPE,REPZ y REPNE,REPNZ. 2stos prefi+os funcionan como 2, en cuanto a que repiten la instruccin que preceden tantas veces como indique )3 e5cepto en que adem9s &a de verificarse la condicin que representan. 2,I se repite mientras el flag de cero est a uno % 2,eat '&ile Iero(, mientras que 2,/I se repite, lgicamente, mientras est a cero % 2,eat '&ile /ot Iero(. ;e esta manera es muy f*cil reali$ar un cdigo que localice una determinada palabra dentro de un array, o que encuentre la diferencia entre dos $onas de memoria. 0i antes de llegar )3 a 0 se produce la condicin contraria a la del prefi+o, se sale del ciclo; ;->0- apuntar*n al elemento siguiente al que provoc este efecto. 0i se termina con )370 &abr* que mirar el flag correspondiente para comprobar si en la #ltima comprobacin la condicin se cumpli o no... pues en ambos casos el ciclo &abr* terminado. =os 6868 incluyen adem*s STOSD*LODSD*MOVSD*SCASD*CMPSD+ =a !;! es de ;ouble 'ord, lo que significa que traba+an con palabras dobles, 6: bits. 2n lugar de A=>A3 las operaciones tienen lugar con 2A3; como todas las anteriores, en modo real usar*n )3 y 0->;-, y en modo protegido 2)3 y 20->2;-. @odas ellas permiten prefi+os 2,.

10

GIN !"#$$IONE B$D D AAA %A0)-- Ad+ust A3 After Addition( HA,)J )onvierte el n#mero almacenado en A= a 9); desempaquetado. =a idea es aplicarlo despus de sumar 9);s no empaquetados. 2sta instruccin mira los K bits m*s ba+os de A=M si es mayor que _ o AA %Alag Au5iliar( es igual a ", suma 6 a A=, " a A<, &ace AA7)A7", y los cuatro bits m*s significativos de A= los de+a a 0. N=o quO Eamos con el e+emplillo de marras. @enemos en A3 el 9); no empaquetado KR %0K0R&( y en 93 "K %0"0K&(. Pueremos sumarlos, y que el resultado siga siendo un 9); no empaquetado, para obtener un resultado co&erente. ,artimos de que AA70. ,rimero sumamos con ADD AX,BX porque no sabemos &acer otra cosa, y el resultado que nos de+a es A370^09&. 4f, ni por el forro. NPu &acemosO Aaa... 2so, la instruccin AAA. )omo la parte ba+a de A= es mayor que _, se da cuenta r*pidamente de que ese n#mero &ay que a+ustarlo %cosa que ya sab.amos, pero en fin(. Aplica la recetaM suma 6 a A=, y " a A<. A3 entonces queda 06""&. )arga con ceros la parte alta de A<, A37060"&. Eaya, esto ya est* me+or. As. que con esto podemos &acer minisumas en 9);. )on los flags, movs y un poco de imaginacin se pueden &acer sumas en 9); m*s grandes. 1i conse+o es que, una ve$ entendido esto, te olvides de las instrucciones para 9);; el coprocesador matem*tico incluye instrucciones de conversin muc&o menos enrevesadas %coges dos n#meros enormes, los sumas, y guardas el resultado gordo directamente como 9);( =o de !A0)--! para este tipo de instrucciones con 9); no empaquetados viene de que se puede obtener f*cilmente el cdigo A0)-- de un n#mero de stosM slo &ay que sumarle el cdigo del cero %K8( a cada d.gito. 0i estuviera empaquetado no podr.amos, porque lo que tenemos es un nibble para cada d.gito, y no un byte %y el ascii es un byte, claro, ya me dir*s cmo le sumas K8 si no(. D DAA %;ecimal Ad+ust A= after Addition( H0,I,A,,,)J Algo parecido a AAA, slo que se usa tras la suma de dos bytes con d.gitos 9); empaquetados %dos n#meros por tanto de dos d.gitos(. 2n ve$ de sumar 6 a A< suma 6 al nibble alto de A=, etc. ,ara sumar dos n#meros de dos d.gitos 9); empaquetados, ADD AL,lo/uesea y luego DAA. D AAS %Ad+ust A3 After 0ubtraction( HA,)J )omo AAA pero para a+ustar despus de una resta, en ve$ de una suma. D DAS %;ecimal Ad+ust A= after 0ubtraction( H0,I,A,,,)J An*loga a ;AA, pero para la resta. D AAM %A0)-- Ad+ust A3 After 1ultiply( H0,I,,J ;e la misma calaa que AAA,AA0. A+usta el resultado guardado en A3 de multiplicar dos d.gitos 9); no empaquetados. ,or e+emplo, si A=70R& y 9=708&, tras %#L BL y AA% tendremos A370^06& %porque R`87^6(. Apasionante. D AAD %A0)-- Ad+ust A3 9efore ;ivision( H0,I,,J 1*s de lo mismo. ,ero o+o, que a&ora AA; se aplica antes de dividir, y no despus. Eolviendo al e+emplo anterior, si con nuestros A370^06&, 9=708& &acemos AAD y luego DIV BL obtenemos.. a+*, A=70R& y 9=708&, lo que confirma nuestra teor.a de que R`87^6. G%I $EL:NEA D HLT %<a=@, parada( ;etiene el microprocesador &asta la llegada de una interrupcin o de una seal de reset. 0e encuentra entre las instrucciones denominadas privilegiadas.

11

D NOP %/o ?,eration, no &acer nada( /o &ace nada m*s que consumir los ciclos de relo+ necesarios para cargar la instruccin y procesarla. )orresponde a la codificacin de 3)<F A3,A3 D INT inmediato 0alta al cdigo de la interrupcin indicada por el operando inmediato %0G:^^(. Aunque &ay un cap.tulo que e5plica m*s en detalle las interrupciones, se puede decir que una instruccin de este tipo reali$a una llamada le+ana a una subrutina determinada por un cierto n#mero. 2n el caso de =inu5, una interrupcin 80& cede la e+ecucin al sistema operativo, para que realice alguna operacin indicada a travs de los valores de los registros %abrir un arc&ivo, reservar memoria, etc(. =a diferencia fundamental con las subrutinas es que al estar numeradas no es necesario conocer la posicin de memoria a la que se va a saltar. =a caracter.stica !estrella! de estas llamadas cuando tienen lugar en modo protegido es que se produce un cam6io de privilegio; en el caso citado, el sistema operativo contar* con permisos para reali$ar determinadas operaciones que el usuario no puede &acer directamente. ,or e+emplo, si queremos acceder al disco para borrar un arc&ivo, no podemos. =o que &acemos es cederle el control al sistema dicindole que borre ese arc&ivo, pues s. que tiene permiso para ese tipo de acceso %al fin y al cabo es quien controla el cotarro(, y comprobar* si ese arc&ivo nos pertenece, para borrarlo. INTO, sin operandos, es muc&o menos usada. Fenera una interrupcin K si el bit de overflo' est* a ". 2n caso contrario contin#a con la instruccin siguiente, como cualquier salto condicional. 1e de+o algunas instrucciones que no usaremos ya no digamos normalmente, si no en el __.___..a de los casos. LOC. bloquea el bus para reservarlo, all. donde pueda &aber otros aparatos %como otros procesadores, si el ordenador los tiene( que puedan meterle mano. ;e este modo se garanti$a el acceso; pone el cartel de !ocupado! por una determinada l.nea del bus, y &ace lo que tiene que &acer sin ser molestado. )ada ve$ que queramos &acer algo de esto, colocaremos el prefi+o !=?)b! delante de la instruccin que queramos !blindar!. @an pronto se e+ecuta la instruccin, el bloqueo del bus se levanta %a menos que estemos todo el d.a dando la vara con el prefi+o(. elacionado con los coprocesadores tenemos ESC, que pasa a esos dispositivos operandos que puedan necesitar %para acceder a registros, por e+emplo(. Aqu. no pienso &acer nada e5pl.citamente con esta instruccin %ya veremos lo que quiero decir con esto cuando lleguemos al coprocesador matem*tico(, as. que puedes olvidar este p*rrafo. ,ara finali$ar, la instruccin WAIT, que detiene el procesador &asta que le llegue una cierta seal. 0e utili$a para sincroni$ar las operaciones entre el procesador y el coprocesador matem*tico, y tambin comprobaremos que no &ace falta preocuparse de ella.

12

Вам также может понравиться