Estaba mirando un código con respecto al bucle.
loopinner ....
SUBS R2,R2,#1 ; j--
BGT loopinner ;in this case, loop should continue when j>1
En este caso, no estoy seguro de cómo BGT vuelve a ramificarse en loopinner. ¿No necesito especificar qué es mayor que? Dado que SUBS invoca las banderas, digamos si j-- se convierte en el valor de 1. ¿Cómo sabe la rama qué valor es mayor que?
A partir de los condicionales ARM, puede encontrar fácilmente que la instrucción examina los indicadores y bifurcaciones de estado Z, N y V cuando Z=0 y N=V. Dado que examina el indicador de estado V y no el indicador de estado C, esto claramente pretende ser una prueba firmada . ( Esto significa para mí que esto no es útil para el control de bucle sin firmar, para tu información ) .
Escribí esto no hace mucho tiempo, con suficiente información para entender lo que está pasando. Pero puedo resumirlo aquí.
Usemos palabras más simples de 4 bits donde solo hay 16 símbolos:
Word Signed Subtrahend
0000 0 1111
0001 1 1110
0010 2 1101
0011 3 1100
0100 4 1011
0101 5 1010
0110 6 1001
0111 7 1000
1000 -8 0111
1001 -7 0110
1010 -6 0101
1011 -5 0100
1100 -4 0011
1101 -3 0010
1110 -2 0001
1111 -1 0000
Arriba, la tercera columna es lo que la ALU realmente usa al restar ese valor. Simplemente invierte cada bit antes de agregar. (La ALU nunca resta nada. Ni siquiera sabe cómo). Entonces, la instrucción SUB en realidad realiza la suma, usando la forma de sustraendo del valor al sumar. (Si desea comprender la semántica de los bits de estado, es muy importante que domine este concepto, ya que lo ayudará cuando de lo contrario estaría confundido).
Pégalo en tu frente...
UNA CPU SOLO AÑADE. NO SE PUEDE RESTAR .
Si alguna vez siente la tentación de seguir el camino primitivo de creer que cualquier tipo de instrucción de resta en realidad resta, y esto incluye todas las instrucciones de comparación que establecen bits de estado pero no cambian los valores de registro, simplemente patéese muy duro, muy rápido. no sucede
UNA CPU SOLO AÑADE. NO SE PUEDE RESTAR .
Todo tiene que ser moldeado en semántica de adición. Todo.
Un SUBS R2, R2, #1 , en este universo de 4 bits que acabo de crear, agregaría 1110 más un arrastre de 1 también. Solo hay 16 posibilidades:
Actual Operation Operation Result Operation Comparison
R2 SUBS OP Z N V C ALU Semantics Semantics Z=0 & N=V?
0000 + 1110 + 1 0 1 0 0 1111 0 - 1 = -1 0 > 1 ? False
0001 + 1110 + 1 1 0 0 1 0000 1 - 1 = 0 1 > 1 ? False
0010 + 1110 + 1 0 0 0 1 0001 2 - 1 = 1 2 > 1 ? True
0011 + 1110 + 1 0 0 0 1 0010 3 - 1 = 2 3 > 1 ? True
0100 + 1110 + 1 0 0 0 1 0011 4 - 1 = 3 4 > 1 ? True
0101 + 1110 + 1 0 0 0 1 0100 5 - 1 = 4 5 > 1 ? True
0110 + 1110 + 1 0 0 0 1 0101 6 - 1 = 5 6 > 1 ? True
0111 + 1110 + 1 0 0 0 1 0110 7 - 1 = 6 7 > 1 ? True
1000 + 1110 + 1 0 0 1 0 0111 -8 - 1 = -9 E -8 > 1 ? False
1001 + 1110 + 1 0 1 0 1 1000 -7 - 1 = -8 -7 > 1 ? False
1010 + 1110 + 1 0 1 0 1 1001 -6 - 1 = -7 -6 > 1 ? False
1011 + 1110 + 1 0 1 0 1 1010 -5 - 1 = -6 -5 > 1 ? False
1100 + 1110 + 1 0 1 0 1 1011 -4 - 1 = -5 -4 > 1 ? False
1101 + 1110 + 1 0 1 0 1 1100 -3 - 1 = -4 -3 > 1 ? False
1110 + 1110 + 1 0 1 0 1 1101 -2 - 1 = -3 -2 > 1 ? False
1111 + 1110 + 1 0 1 0 1 1110 -1 - 1 = -2 -1 > 1 ? False
En Operation Result , tengo una columna para ALU . El campo ALU es lo que regresa a R2 después de que se completa la instrucción SUBS. (La bandera de estado V es generada por un XOR del acarreo del siguiente bit más significativo durante la operación y el bit de acarreo en sí). Tenga en cuenta también que hay un solo caso marcado con E donde ocurrió un desbordamiento firmado .
Ahora puede ver fácilmente por qué la instrucción BGT aplica esos bits de estado en particular exactamente de la manera en que lo hace. Es cierto que esto usa palabras de 4 bits. Pero exactamente la misma idea se aplica a tamaños de palabra mucho más amplios, sin ningún cambio.
Al volver a mirar la tabla, puede ver que la condición es Verdadera si y solo si R2 era 2 o mayor antes de la resta, y no 1, 0 o menor.
Tu pregunta:
¿No necesito especificar qué es mayor que? Dado que SUBS invoca las banderas, digamos si j-- se convierte en el valor de 1. ¿Cómo sabe la rama qué valor es mayor que?
Comencemos con la siguiente tabla del Manual de referencia de la arquitectura ARMv6-M , página A6-99:
La condición GT se describe como " Firmado mayor que ". La razón por la que la documentación no especifica una constante es que esta prueba ocurre después de alguna instrucción previa. Esa instrucción previa define el contexto. Pero sin tener ese contexto, todo lo que se puede decir es un signo > general .
Entonces, si la instrucción anterior fuera CMP:
Entonces, el contexto sería la comparación de dos valores con signo y la instrucción BGT significaría "bifurcarse cuando el operando con signo 1 es mayor que el operando con signo 2".
Pero en su caso, con "SUBS R2, R2, #1", el contexto cambia y la instrucción BGT significaría "branch mientras R2 firmado sigue siendo mayor que 0".
La instrucción de bifurcación condicional en sí misma no sabe cuál era la instrucción anterior. Tampoco sabe qué registro(s) están involucrados. Ese conocimiento se deja al individuo (o compilador) que está generando el flujo de instrucciones. Entonces, la instrucción de bifurcación en realidad no tiene un valor constante fijo, ni tiene un registro con el cual comparar. Depende completamente de lo que hicieron las instrucciones anteriores con los bits de estado. Simplemente examina el estado resultante y luego hace lo que hace. Depende de usted conocer el contexto y usarlo correctamente.
(Hablando de eso, el comentario del código fuente puede ser engañoso o incorrecto).
Elliot discrepa (ver la discusión a continuación) sin evidencia. Él escribe: "Podría argumentar de manera equivalente que una CPU solo puede restar". Él puede hacer ese argumento, pero es sólo académico. El hecho real del asunto es que las CPU no restan. Agregan.
Entonces, si bien esta es en parte mi respuesta, brindando evidencia clara e inequívoca en apoyo para que incluso Elliot pueda comprender la situación en el terreno, hoy también es una excelente continuación. Así que estoy muy contento por la oportunidad que me brinda Elliot de ampliar la discusión.
Mi primera CPU se fabricó con 7400 piezas que construí y completé con éxito en 1974. Los reporteros de los periódicos, para mi sorpresa, aparecieron y escribieron un artículo al respecto. Esa es mi primera experiencia. Desde entonces, trabajé profesionalmente en Intel realizando pruebas de conjuntos de chips para el conjunto de chips BX y, como cuestión relevante para la enseñanza de esta materia, he impartido clases de arquitectura informática como profesor adjunto en la Universidad Estatal de Portland en la década de 1990, con tamaños de clase de aproximadamente 65-75 estudiantes. Esta es la universidad de 4 años más grande del estado de Oregón.
Siento ambigüedad (que expresa ambivalencia sobre cómo se pueden hacer los cálculos) sobre cómo los procesadores generan sus bits de estado y cómo calculan solo lleva a los estudiantes a una incertidumbre, confusión y dificultad innecesarias que pueden tardar horas, semanas, meses y, a veces, incluso años en corregirse. Así como enseñar álgebra abstracta de teoría de grupos antes de transmitir los conceptos básicos confundiría a la mayoría de los estudiantes de álgebra de primer año, también lo haría enseñar abstracciones académicas sobre cómo las computadoras pueden hacer cosas. Más estudiantes serían dañados que ayudados.
La simple verdad es que la decodificación de instrucciones emite un ADD, incluso cuando el texto de la instrucción (después de todo, es solo texto, no es lo que realmente está sucediendo) dice SUB. La decodificación todavía emite un ADD. Simplemente modifica algunos detalles del operando en el camino.
Del mismo modo, como también debe ser en el caso del procesador ARM, la teoría anterior es todo lo que necesita para comprender cómo se hacen realmente las cosas.
¡Por favor, no te confundas! Las computadoras agregan. No restan. Simplemente juguetean un poco para que parezca que restan.
Para bien o para mal, es importante comprender qué hace realmente una computadora para comprender ciertos bits de estado; lo que hacen y por qué lo hacen. No hay otra forma de evitarlo. El modelo teórico anterior es la forma en que funcionan las cosas en los procesadores modernos y es cómo resolver y comprender los bits de estado correctamente. Hay una buena razón por la que las cosas son como son.
Espero que estos detalles, arriba, y los que escribiré a continuación, sean útiles. Cualquier falta de comunicación aquí es mía y con gusto trabajaré para reparar, enmendar y mejorar este documento donde pueda.
Para continuar, usaré el Manual de referencia de la arquitectura ARMv6-M como referencia.
Comencemos en la página A6-187 (caso de registro):
Aquí puede ver que documentan claramente este comportamiento:
AddWithCarry(R[n], NOT(shifted), '1')
Esta es una suma, con el operando 2 (el sustraendo) invertido y el acarreo establecido en '1'. Justo como escribí sucede, arriba. (Así es como se hace).
En el caso de extensiones de varias palabras, vaya a la página A6-173 y busque SBCS:
Aquí tenga en cuenta que de nuevo usan la suma:
AddWithCarry(R[n], NOT(shifted), APSR.C)
En lugar de que el acarreo sea un '1' codificado de forma rígida, como lo es para la instrucción SUBS, ahora está usando el último valor de acarreo guardado. En este caso, normalmente se espera que se lleve a cabo desde una instrucción SUBS (o SBCS) anterior.
Para operaciones de múltiples palabras, uno comienza con SUBS (o ADDS) y luego continúa el proceso con SBCS (o ADCS) posteriores, que utilizan la ejecución de instrucciones anteriores para respaldar una operación de múltiples palabras.
En la suma de varias palabras, esta comida para llevar se puede considerar simplemente como una comida para llevar , y lo es. Un '1' indica que se produjo un acarreo y que debe solucionarse. Un '0' indica que no se produjo ningún acarreo.
En el caso de la resta de varias palabras, esta transferencia se ve mejor como un préstamo invertido . Un '1' indica que no hubo necesidad de tomar prestado de una palabra de orden superior. Un '0' indica que hay necesidad de pedir prestado. Dado que una instrucción SUBS siempre establece esto en '1', esto significa que no hay préstamo (el resultado de la resta requiere un 'incremento' para compensar el operando invertido 2). Pero para la instrucción SBCS, si APSR.C es un ' 0', entonces no se produce ningún 'incremento' y esto es lo mismo que pedir prestado (dado que se requiere un incremento, si no hay préstamo).
La instrucción ADCS, que se encuentra en la página A6-106 pero que no se muestra aquí, también utiliza la realización de ejecuciones de instrucciones anteriores. No invierte el valor de ejecución ni hace algo extraño o diferente, solo porque es una instrucción ADCS. Hace exactamente lo mismo que la instrucción SBCS, excepto y solo por un detalle menor: la instrucción SBCS invertirá el operando 2 y ADCS no. Eso es todo.
Este es uno de los aspectos realmente geniales sobre la forma en que funcionan estos detalles. Se requiere muy poca lógica adicional para convertir una suma en una resta y/o una suma de varias palabras en una resta de varias palabras.
Y finalmente, para completar la historia, vea la página A2-35:
De acuerdo con mis descripciones de cómo funcionan realmente las cosas, arriba.
Es realmente un placer ver cómo funciona todo esto. Vale la pena jugar con diferentes valores con y sin signo y, a mano, configurar y usar banderas de estado. Realmente profundiza estas ideas. ¡Y son muy buenos!
Todo lo anterior se trata de comprender los bits de estado y cómo se generan y por qué se generan de la forma en que se generan. Si te enfocas en lo que realmente sucede en una CPU, el resto simplemente cae como las consecuencias necesarias y es muy fácil de entender, entonces.
Una CPU solo suma. No puede restar.
SUBS
como AddWithCarry(R[n], NOT(imm32), '1')
(puedo expandir esto si fuera útil), que cubre esta respuesta.En su ejemplo de código, la SUB
instrucción tiene un S
sufijo, esto significa que la subinstrucción establecerá los indicadores de condición, que evaluará BGT
. Para que se tome la rama, la Z
bandera debe ser 0 y la N
bandera debe ser igualV
La documentación del brazo establece claramente que GT es un signo mayor que, se bifurcará cuando Z==0,N==V.
Cuando r2 = 2. Recuerde de la escuela primaria que x - y = x + (-y), y desde el primer día (o poco después) en ingeniería informática/ciencia/cualquiera que sea la negación del complemento a dos es invertir y sumar uno, de modo que x - y = x + (~y) + 1. Esto ahorra en lógica y es cómo hacemos la resta
1 add one
0010
+ 1110 invert
==========
cuatro bits es más que suficiente para ver lo que está pasando, el resultado es el mismo que con 32 bits.
11101
0010
+ 1110
==========
0001
Entonces N = 0 y Z = 0 del resultado. El acarreo y el acarreo del msbit son los mismos, por lo que V = 0 (xor del acarreo y el acarreo del msbit, también puede hacerlo mediante la inspección de los msbits de los operandos y el resultado).
Necesitamos Z == 0 y N == V para hacer la bifurcación, y lo son, por lo que sucede la bifurcación.
Encontrará que este es el caso de los números positivos, ya que este es un signo mayor que, si desea un signo mayor que, entonces use bcs/bhs, la lógica funciona igual, solo se optimiza para usar solo llevar a cabo (puede ver esto también si miras la tabla jonk generada o generas una tú mismo)
Cuando r2 = 1
11111
0001
+ 1110
==========
0000
Z = 1, N = 0, V = 0
N == V pero Z != 0 por lo que la bifurcación no sucede.
eliot alderson
pipí
pedro bennett
jcaron
viejo contador de tiempo
broma
pipí