¿Cómo han influido los idiomas en el diseño de la CPU? [cerrado]

44

A menudo se nos dice que al hardware no le importa en qué idioma está escrito un programa, ya que solo ve el código binario compilado, sin embargo, esta no es toda la verdad. Por ejemplo, considere el humilde Z80; sus extensiones al conjunto de instrucciones 8080 incluyen instrucciones como CPIR, que es útil para escanear cadenas de estilo C (terminadas en NULL), por ejemplo, para realizar strlen(). Los diseñadores deben haber identificado que ejecutar programas en C (a diferencia de Pascal, donde la longitud de una cadena está en el encabezado) era algo para lo que probablemente se usaría su diseño. Otro ejemplo clásico es la máquina Lisp .

¿Qué otros ejemplos hay? Por ejemplo, instrucciones, número y tipo de registros , modos de direccionamiento, que hacen que un procesador en particular favorezca las convenciones de un idioma en particular. Estoy particularmente interesado en las revisiones de la misma familia.

Gayo
fuente
3
No olvide que el Z-80 también tenía la instrucción LDIR, muy útil al copiar cadenas cuando conoce la longitud (como en Pascal, donde la longitud se almacenó en el encabezado).
TMN
27
1. El Z-80 fue diseñado en 1975, cuando Unix y C eran un sistema operativo y un lenguaje oscuros en algunas computadoras, 3 años antes de la primera edición de K&R. 2. No hay nada en Pascal que obligue a que la longitud de la cadena sea "en un encabezado". 3. Las cadenas en CP / M, el sistema operativo de microordenador principal en ese momento, se terminaron con el carácter '$', no '\ 0'. CPIR podría buscar cualquier personaje. 4. CPIR coincide con CPDR (búsqueda hacia atrás), así como otras instrucciones -IR y -DR. Conclusión: CPIR no tiene nada que ver con el lenguaje de programación C. Es solo una instrucción de búsqueda de bytes.
librik
44
La más grande (y una de las más molestas para los diseñadores de hardware) de las cosas forzadas por C es un direccionamiento de bytes. Las CPU habrían sido más simples y rápidas sin esta abominación.
SK-logic
1
@ SK-logic: aunque el estándar POSIX requiere el direccionamiento de bytes, el estándar C no. Cualquier implementación donde sea sizeof(int)igual a 1 debe requerir que se firme el tipo char(ya que un intdebe poder contener todos los valores de tipo char). He escrito código para una máquina donde chary intson enteros con signo de 16 bits; Las mayores dificultades son que no se pueden usar uniones para la conversión de tipos, y el almacenamiento eficiente de una gran cantidad de bytes requiere el empaquetado y desempaquetado manual. Esos problemas son menores en comparación con la posibilidad en C de que sizeof (int) == sizeof (long), ya que ...
supercat
2
... eso significa que no hay un tipo estándar que garantice mantener la diferencia entre dos unsigned intvalores. C99 mejoró esa situación, pero antes de C99 no había una forma segura y segura de un solo paso para comparar un valor potencialmente negativo con un valor de tipo unsigned int(uno tendría que probar si el número era negativo antes de hacer la comparación).
supercat

Respuestas:

20

Las respuestas existentes se centran en los cambios de ISA . También hay otros cambios de hardware. Por ejemplo, C ++ comúnmente usa vtables para llamadas virtuales. Comenzando con el Pentium M , Intel tiene un componente "predictor de rama indirecta" que acelera las llamadas a funciones virtuales.

MSalters
fuente
66
Y la arquitectura Berkeley RISC incluía el concepto de un "archivo de registro", por lo que en lugar de hacer que las funciones "derramen" registros en la pila, se asignó un bloque de 8 registros a cada función. Esto aceleró considerablemente el código orientado a objetos, ya que tiende a consistir en muchas llamadas a métodos cortos.
TMN
1
Este no es un ejemplo válido. El diseño de "Tabla de punteros de función" también se usa en muchos escenarios de enlace dinámico, por ejemplo, a través de la importación y exportación de DLL en Windows, y también se usa en programas en C. Aunque supongo que se podría argumentar que muestra que el procesador está optimizado para un uso específico, no es específico del idioma.
DeadMG
@DeadMG: Otros casos beneficiados, eso es cierto. Pero hasta que C ++ se hizo popular, los diseños de CPU no fueron influenciados . Y esa fue la pregunta planteada. Del mismo modo, TMN tiene un punto sobre los archivos de registro. La asamblea no tenía un concepto tan claro de funciones. Las funciones, como las entendemos hoy en día, se remontan a Algol 60 y, por lo tanto, podemos decir que Algol 60 influyó en el diseño del archivo de registro de la CPU.
MSalters
14

El conjunto de instrucciones Intel 8086 incluye una variación de "ret" que agrega un valor al puntero de la pila después de abrir la dirección de retorno. Esto es útil para muchas implementaciones de Pascal donde el llamador de una función empujará argumentos a la pila antes de hacer una llamada a la función, y los sacará después. Si una rutina aceptara, por ejemplo, cuatro bytes de parámetros, podría terminar con "RET 0004" para limpiar la pila. En ausencia de tal instrucción, tal convención de llamada probablemente habría requerido que el código muestre la dirección de retorno en un registro, actualice el puntero de la pila y luego salte a ese registro.

Curiosamente, la mayoría del código (incluidas las rutinas del sistema operativo) en el Macintosh original usaba la convención de llamadas Pascal a pesar de la falta de una instrucción facilitadora en el 68000. El uso de esta convención de llamadas ahorró 2-4 bytes de código en un sitio de llamada típico, pero requirió un extra 4-6 bytes de código en el sitio de retorno de cada función que tomó parámetros.

Super gato
fuente
También hay una ENTERcontraparte en esto RET n...
herby
1
@herby: no creo que ENTERexistiera en el 8086 original; vino con procesadores posteriores. Sin embargo, plantea un punto interesante: los modos de direccionamiento basados ​​en BP están claramente diseñados en torno al uso de parámetros apilados y locales a los que se accede a través del puntero de trama. Encuentro esta convención interesante de varias maneras, especialmente considerando que (1) el código de lenguaje ensamblador puro es más apto para usar valores en registros que la pila, pero (2) las ventajas de direccionamiento [BP + nn] sobre [SP + nn] el direccionamiento es más significativo para los programas en lenguaje ensamblador que acceden a cosas en la pila que ...
supercat
... para el código de ensamblaje escrito a mano. Un compilador generalmente sabrá, para cada instrucción generada, cómo se comparan SP y BP; si SP es BP-8, por ejemplo, en realidad no es más fácil para el compilador abordar [BP + 12] que [SP + 20]. Si en una recompilación el compilador tiene que agregar otro PUSH / POP alrededor de un bloque de código, puede ajustar las compensaciones basadas en SP de manera apropiada. Por otro lado, en un ensamblaje escrito a mano, agregar un PUSH / POP probablemente requeriría ajustar el código entre ellos. Por lo tanto, los punteros de trama son principalmente un beneficio para el código combinado de alto nivel / asm.
supercat
Tal vez la posibilidad de reutilizar el código sin su compilación también sea un punto de usabilidad marginal para el direccionamiento BP. Y Dios sabe si BP instrucciones de direccionamiento no más rápido están en los circuitos de SP que tenían la dirección, ya que BP es abordar tipo de norma ...
Herby
3
@herby: En realidad, sospecho que una gran parte de la razón por la que los compiladores generalmente han utilizado punteros de trama tiene mucho que ver con la depuración. Para depurar un programa que no utilizó una convención de este tipo requeriría que el compilador genere, y el depurador utilice, un archivo que enumere el desplazamiento SP-BP para cada instrucción. Estos metadatos detallados son comunes hoy en día (y son una parte esencial de lo que hace que los lenguajes recolectados sean prácticos), pero la cantidad de RAM que requiere habría sido inaceptable hace 30 años.
supercat
10

Un ejemplo es MIPS, que tiene ambos addy addupara atrapar e ignorar el desbordamiento, respectivamente. (También suby subu.) Necesitaba el primer tipo de instrucción para lenguajes como Ada (creo que nunca he usado Ada sin embargo) que se ocupan de los desbordamientos explícitamente y el segundo tipo para lenguajes como C que ignoran los desbordamientos.

Si no recuerdo mal, la CPU real tiene algunos circuitos adicionales en la ALU para realizar un seguimiento de los desbordamientos. Si el único idioma que a la gente le importara fuera el C, no lo necesitaría.

Tikhon Jelvis
fuente
No estoy seguro si está relacionado, pero esas instrucciones probablemente también sean útiles en otras situaciones, como la asignación segura de memoria, es decir, si está asignando nmemb*size+offsetbytes y necesita asegurarse de que no se produce un desbordamiento.
NikiC
@NikC: Estaba pensando que las instrucciones adduy subu(las que no comprueban los desbordamientos) fueron las que se agregaron para hacer feliz a C. Por supuesto, no lo sé, solo lo cubrimos vagamente en clase y ciertamente no soy un experto en arquitectura: P.
Tikhon Jelvis
Oh sí, estaba pensando al revés, lo siento: /
NikiC
8

La serie 5000 de Burroughs fue diseñada para soportar eficientemente ALGOL, y el iAPX-432 de Intel fue diseñado para ejecutar Ada de manera eficiente. El Inmos Transputer tenía su propio idioma, Occam. Creo que el procesador Parallax "Propeller" fue diseñado para ser programado usando su propia variante de BASIC.

No es un lenguaje, pero el conjunto de instrucciones VAX-11 tiene una sola instrucción para cargar un contexto de proceso, que fue diseñado después de una solicitud del equipo de diseño de VMS. No recuerdo los detalles, pero ISTR tomó tantas instrucciones para implementar que puso un límite superior serio en la cantidad de procesos que podían programar.

TMN
fuente
¿Qué tienen estos diseños que los hace particularmente adecuados? Por ejemplo, ¿de qué característica del iAPX se beneficia particularmente Ada?
Cayo
ISTR dijo que el objetivo de Ada de iAPX-432 era más tratar de salvar un diseño fallido al adjuntarlo a algo con grandes expectativas que cualquier otra cosa.
Programador
@AProgrammer: estoy bastante seguro de que el iAPX-432 fue diseñado desde el principio para usar Ada. Incluso recuerdo algunos rumores de que Intel no iba a publicar el conjunto de instrucciones, para desalentar la programación en lenguaje ensamblador y obligar a las personas a usar Ada para todo.
TMN
1
@ TMN, el proyecto 432 de Intel comenzó en 1975 y se introdujo en 1981 (Wikipedia). Ironman (requisitos finales para Ada), se publicó en enero de 1977, y el verde se eligió en mayo de 1979, se modificó y el resultado final se publicó como un estándar militar en julio de 1980. Existe un problema en la línea de tiempo al afirmar que iAPX-432 se diseñó a partir de el comienzo de usar Ada. (Es un procesador tardío y típico de "cerrar la brecha semántica" con los inconvenientes habituales en un momento en que se comenzaron a buscar alternativas; comercializarlo como procesador Ada era tentativo para salvar un diseño fallido - ISTR que nadie más que Intel lo usó )
Programador del
1
@AProgrammer: Hmmm, parece que tienes razón. Me encontré con este documento del arquitecto principal del 432 y en el resumen dice: "Esta estrecha combinación de arquitectura y lenguaje no se produjo porque el 432 fue diseñado para ejecutar Ada, no fue así". Tendré que desenterrar mi viejo libro 432 y ver qué dice.
TMN
8

Algo que nadie parece haber mencionado hasta ahora es que los avances en la optimización del compilador (donde el lenguaje base es en gran medida irrelevante) impulsaron el cambio de los conjuntos de instrucciones CISC (que fueron diseñados en gran medida para ser codificados por humanos) a los conjuntos de instrucciones RISC (que fueron en gran medida diseñado para ser codificado por compiladores).

cohetes4kids
fuente
5

La familia Motorola 68000 introdujo un modo de dirección de autoincremento que hizo que la copia de datos a través de la CPU fuera muy eficiente y compacta.

[Ejemplo actualizado]

este fue un código c ++ que influyó en el ensamblador 68000

while(someCondition)
    destination[destinationOffset++] = source[sourceOffset++]

implementado en ensamblador convencional (pseudocódigo, olvidé los comandos del ensamblador 68000)

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1)
    move (adressRegister2), akku
    increment(adressRegister1, 1)
    increment(adressRegister2, 1)
}

con el nuevo modo de dirección se convirtió en algo similar a

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1++)
    move (adressRegister2++), akku
}

solo dos instrucciones por ciclo en lugar de 4.

k3b
fuente
1
¿Cómo fue influenciado esto por las convenciones de un idioma en particular?
Cayo
ver ejemplo actualizado
k3b
Ah, me recuerda la optimización del bucle DBxx en el 68010.
Gaius
77
En realidad, creo que tienes esto al revés. El direccionamiento automático [en | de] cremento era parte del conjunto de instrucciones PDP-11, que probablemente influyó en el diseño de C.
TMN
5

El mainframe de la serie Z de IBM es el descendiente del IBM 360 de la década de 1960.

Hubo varias instrucciones que se pusieron allí específicamente para acelerar los programas COBOL y Fortran. El ejemplo clásico es BXLE- "Branch on Index Low or Equal", que es la mayoría de un forbucle Fortran o un COBOL PERFORM VARYING x from 1 by 1 until x > nencapsulado en una sola instrucción.

También hay una familia completa de instrucciones decimales empaquetadas para admitir la aritmética decimal de punto fijo común en los programas COBOL.

James Anderson
fuente
Creo que te refieres a descendiente .
Clockwork-Muse
@ X-Zero - ¡Uy! Temprano por la mañana, no es suficiente caffiene en el sistema, etc .......
James Anderson
1
Más interesante es la instrucción de repetición de bloque de la TI 32050 DSP. Su operando es la dirección de la instrucción que sigue al último en el ciclo; cargar un registro de recuento de bucles y luego realizar la instrucción de repetición de bloque hará que las instrucciones hasta (pero sin incluir) el objetivo se repitan el número especificado de veces. Muy parecido a un DObucle FORTRAN .
supercat
@supercat Cada DSP digno de ese nombre incluye tres características: bucle de sobrecarga cero, instrucción única de acumulación múltiple y un modo de direccionamiento de bits invertidos de algún tipo. Casi todos los algoritmos DSP conocidos por Man usan bucles. Los dos algoritmos más comunes son el filtro FIR, que es un bucle alrededor de una acumulación múltiple, y FFT, para el cual el direccionamiento con inversión de bits es crítico. Muchos DSP incluyen una operación de mariposa de una instrucción radix-2 FFT, o una multiplicación / suma doble que se puede utilizar para hacer una mariposa de una instrucción.
John R. Strohm
@ JohnR.Strohm: Cada DSP que he visto incluye una repetición-multiplicación-acumulación, pero no todas incluyen bucles de sobrecarga cero más generalizados. En realidad, no estoy muy seguro de por qué tales bucles deberían considerarse solo una función "DSP", ya que también serían útiles en muchos códigos de "procesador convencional".
supercat
3

Las primeras CPU de Intel tenían las siguientes características, muchas de ellas ahora obsoletas en el modo de 64 bits:

  • ENTER, LEAVE y RET nn instrucciones [los primeros manuales informaron explícitamente que se introdujeron para los lenguajes estructurados en bloques, por ejemplo, Pascal, que admite procedimientos anidados]
  • instrucciones para acelerar la aritmética BCD (AAA, AAM, etc.); También soporte BCD en x87
  • Instrucciones JCXZ y LOOP para implementar bucles contados
  • INTO, para generar una trampa en el desbordamiento aritmético (por ejemplo, en Ada)
  • XLAT para búsquedas de mesa
  • LÍMITE para verificar los límites de la matriz

El indicador de signo, que se encuentra en el registro de estado de muchas CPU, existe para realizar fácilmente la aritmética firmada Y sin firmar.

El conjunto de instrucciones SSE 4.1 presenta instrucciones para el procesamiento de cadenas, contadas y terminadas en cero (PCMPESTR, etc.)

Además, podría imaginar que una serie de características a nivel de sistema fueron diseñadas para soportar la seguridad del código compilado (verificación de límite de segmento, puertas de llamada con copia de parámetros, etc.)

zvrba
fuente
3

Algunos procesadores ARM, principalmente aquellos en dispositivos móviles, incluyen (d) la extensión Jazelle, que es un intérprete JVM de hardware; interpreta el bytecode de Java directamente. JVM compatible con Jazelle puede usar el hardware para acelerar la ejecución y eliminar gran parte de JIT, pero el respaldo al software VM todavía está asegurado si el código de bytes no se puede interpretar en el chip.

Los procesadores con dicha unidad incluyen la instrucción BXJ, que pone al procesador en un "modo Jazelle" especial, o si la activación de la unidad ha fallado, simplemente se interpreta como una instrucción de bifurcación normal. La unidad reutiliza los registros ARM para mantener el estado JVM.

El sucesor de la tecnología Jazelle es ThumbEE

usoban
fuente
2

Hasta donde yo sé, esto era más común en el pasado.

Hay una sesión de preguntas en la que James Gosling dijo que había personas que intentaban hacer hardware que pudiera manejar mejor el código de bytes JVM, pero luego estas personas encontrarían una manera de hacerlo con intel x86 "genérico" común (tal vez compilando el bytecode de alguna manera inteligente).

Mencionó que hay una ventaja en el uso del chip popular genérico (como Intel) porque tiene una gran corporación que arroja enormes sumas de dinero al producto.

Vale la pena ver el video. Habla de esto en el minuto 19 o 20.

Pedro Henrique A. Oliveira
fuente
2

Hice una búsqueda rápida en la página y parece que nadie ha mencionado las CPU desarrolladas específicamente para ejecutar Forth . El lenguaje de programación Forth está basado en pila, es compacto y se usa en sistemas de control.

Paddy3118
fuente
2

La CPU Intel iAPX fue diseñada específicamente para lenguajes OO. Sin embargo, no funcionó del todo.

El iAPX 432 ( arquitectura Intel Advanced Processor ) fue el primer diseño de microprocesador de 32 bits de Intel, introducido en 1981 como un conjunto de tres circuitos integrados. Estaba destinado a ser el diseño principal de Intel para la década de 1980, implementando muchas funciones avanzadas de gestión de memoria y multitarea. Por lo tanto, el diseño se denominaba Micromainframe ...

El iAPX 432 fue "diseñado para ser programado completamente en lenguajes de alto nivel" , con Ada como principal y soportó la programación orientada a objetos y la recolección de basura directamente en hardware y microcódigo . El soporte directo para varias estructuras de datos también tenía la intención de permitir la implementación de sistemas operativos modernos para el iAPX 432 usando mucho menos código de programa que para los procesadores comunes. Estas propiedades y características dieron como resultado un diseño de hardware y microcódigo que era mucho más complejo que la mayoría de los procesadores de la época, especialmente los microprocesadores.

Utilizando la tecnología de semiconductores de su época, los ingenieros de Intel no pudieron traducir el diseño en una primera implementación muy eficiente. Junto con la falta de optimización en un compilador prematuro de Ada, esto contribuyó a sistemas informáticos bastante lentos pero caros, realizando pruebas típicas a aproximadamente 1/4 de la velocidad del nuevo chip 80286 a la misma frecuencia de reloj (a principios de 1982).

Esta brecha de rendimiento inicial con el perfil bastante bajo y la línea 8086 de bajo precio fue probablemente la razón principal por la cual el plan de Intel para reemplazar el último (más tarde conocido como x86) con el iAPX 432 falló. Aunque los ingenieros vieron formas de mejorar el diseño de la próxima generación, la arquitectura de capacidad iAPX 432 ahora había comenzado a considerarse más como una sobrecarga de implementación en lugar de como el soporte simplificador que pretendía ser.

El proyecto iAPX 432 fue un fracaso comercial para Intel ...

simplemente pasando
fuente
Al leer el documento, parece que muchos aspectos del diseño podrían ser útiles en marcos orientados a objetos como los que son populares hoy en día. Una arquitectura que usara una combinación de una identificación de objeto de 32 bits y una compensación de 32 bits podría en muchos casos ofrecer un mejor rendimiento de almacenamiento en caché que una donde los identificadores de objeto fueran todos de 64 bits (en la mayoría de los casos, una aplicación que usaría miles de millones de objetos en lugar de tener más, más grandes; uno que almacenaría miles de millones de bytes en un objeto sería mejor dividirlo en objetos más pequeños.
supercat
1

El 68000 tenía MOVEM que era más adecuado para insertar múltiples registros en la pila en una sola instrucción, que es lo que muchos idiomas esperaban.

Si vio MOVEM (MOVE Multiple) antes de JSR (Jump SubRoutine) en todo el código, entonces generalmente sabía que estaba tratando con el código C cumplido.

MOVEM permitió el incremento automático del registro de destino, permitiendo que cada uso continúe apilando en el destino, o eliminándose de la pila en caso de disminución automática.

http://68k.hax.com/MOVEM

Myztry
fuente
1

La arquitectura AVR de Atmel está completamente diseñada desde cero para ser adecuada para la programación en C. Por ejemplo, esta nota de aplicación desarrolla más.

En mi opinión, esto está estrechamente relacionado con la excelente respuesta de rockets4kids , con los primeros PIC16-s desarrollados para la programación directa del ensamblador (40 instrucciones en total), con familias posteriores que apuntan a C.

Vorac
fuente
1

Cuando se diseñó el coprocesador numérico 8087, era bastante común que los idiomas realizaran todas las matemáticas de punto flotante utilizando el tipo de mayor precisión, y solo redondean el resultado a menor precisión al asignarlo a una variable de menor precisión. En el estándar C original, por ejemplo, la secuencia:

float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;

promovería ay bque double, agregarlos, promover ca double, agregarlo, y luego almacenar el resultado redondeado al float. Aunque en muchos casos hubiera sido más rápido para un compilador generar código que realizaría operaciones directamente en tipo float, era más simple tener un conjunto de rutinas de punto flotante que operarían solo en tipo double, junto con rutinas para convertir a / desde float, que tener conjuntos separados de rutinas para manejar operaciones en floaty double. El 8087 se diseñó en torno a ese enfoque de la aritmética, realizando todas las operaciones aritméticas utilizando un tipo de punto flotante de 80 bits [80 bits probablemente se eligió porque:

  1. En muchos procesadores de 16 y 32 bits, es más rápido trabajar con una mantisa de 64 bits y un exponente separado que trabajar con un valor que divide un byte entre la mantisa y el exponente.

  2. Es muy difícil realizar cálculos que sean precisos a la precisión total de los tipos numéricos que uno está usando; si uno está tratando de calcular, por ejemplo, algo como log10 (x), es más fácil y rápido calcular un resultado con una precisión de 100ulp de un tipo de 80 bits que calcular un resultado con una precisión de 1ulp de 64 bits tipo, y redondeando el resultado anterior a una precisión de 64 bits producirá un valor de 64 bits que es más preciso que el segundo.

Desafortunadamente, las versiones futuras del lenguaje cambiaron la semántica de cómo deberían funcionar los tipos de punto flotante; mientras que la semántica 8087 habría sido muy agradable si los lenguajes los hubieran apoyado de manera consistente, si las funciones f1 (), f2 (), etc. devuelven el tipo float, muchos autores del compilador se encargarían de crear long doubleun alias para el tipo doble de 64 bits en lugar del tipo de 80 bits del compilador (y no proporcionar otros medios para crear variables de 80 bits), y evaluar arbitrariamente algo como:

double f = f1()*f2() - f3()*f4();

en cualquiera de las siguientes formas:

double f = (float)(f1()*f2()) - (extended_double)f3()*f4();
double f = (extended_double)f1()*f2() - (float)(f3()*f4());
double f = (float)(f1()*f2()) - (float)(f3()*f4());
double f = (extended_double)f1()*f2() - (extended_double)f3()*f4();

Tenga en cuenta que si f3 y f4 devuelven los mismos valores que f1 y f2, respectivamente, la expresión original debería devolver claramente cero, pero muchas de las últimas expresiones pueden no hacerlo. Esto llevó a las personas a condenar la "precisión adicional" del 8087 a pesar de que la última formulación sería generalmente superior a la tercera y, con un código que usara el tipo doble extendido apropiadamente, rara vez sería inferior.

En los años intermedios, Intel ha respondido a la tendencia del lenguaje (en mi humilde opinión) de forzar a que los resultados intermedios se redondeen a la precisión de los operandos diseñando sus procesadores posteriores para favorecer ese comportamiento, en detrimento del código que se beneficiaría con el uso de niveles superiores. precisión en cálculos intermedios.

Super gato
fuente
Tenga en cuenta que ya tiene una respuesta ( arriba ) en esta publicación. ¿Son respuestas que podrían / ​​deberían fusionarse en una sola?
@MichaelT: No lo creo, uno cubre el diseño de la pila y el otro cubre la semántica de punto flotante.
supercat
Solo asegurándome. Personalmente, creo que sería posible dar una respuesta más fuerte (usando encabezados para separar las secciones), pero esa es mi opinión al respecto. Es posible que aún desee utilizar encabezados para identificar claramente en la parte superior lo que aborda cada parte de la respuesta ( ## How the stack changed the processory ## How floating point changed the processor) para que las personas puedan tener la mentalidad adecuada al leerlo y sean menos propensos a pensar que estaba distraído al responder o volver a publicar el mismas (r similares) respuestas.
@MichaelT: Las dos respuestas son lo suficientemente disjuntas que creo que deberían votarse por separado. Aunque el 80486 absorbió las funciones previamente realizadas por el 8087/80287/80387, el 8086 y el 8087 fueron diseñados como chips separados con arquitecturas casi independientes. Aunque ambos ejecutaban el código de una secuencia de instrucciones común, eso se manejaba haciendo que el 8086 tratara ciertas secuencias de bytes como solicitudes para generar solicitudes de lectura / escritura de direcciones mientras ignoraba el bus de datos, y que el 8087 ignorara todo lo demás que estaba sucediendo.
supercat