Esta es una pregunta que siempre me hacía mi padre. " ¿Por qué no recorre todas las instrucciones y se detiene al final? "
Veamos un ejemplo patológico. El siguiente código fue compilado en el compilador C18 de Microchip para el PIC18:
void main(void)
{
}
Produce la siguiente salida de ensamblador:
addr opco instruction
---- ---- -----------
0000 EF63 GOTO 0xc6
0002 F000 NOP
0004 0012 RETURN 0
.
. some instructions removed for brevity
.
00C6 EE15 LFSR 0x1, 0x500
00C8 F000 NOP
00CA EE25 LFSR 0x2, 0x500
00CC F000 NOP
.
. some instructions removed for brevity
.
00D6 EC72 CALL 0xe4, 0 // Call the initialisation code
00D8 F000 NOP //
00DA EC71 CALL 0xe2, 0 // Here we call main()
00DC F000 NOP //
00DE D7FB BRA 0xd6 // Jump back to address 00D6
.
. some instructions removed for brevity
.
00E2 0012 RETURN 0 // This is main()
00E4 0012 RETURN 0 // This is the initialisation code
Como puedes ver, se llama a main(), y al final contiene una sentencia return, aunque no la hayamos puesto explícitamente. Cuando main retorna, la CPU ejecuta la siguiente instrucción que es simplemente un GOTO para volver al principio del código. main() es simplemente llamada una y otra vez.
Ahora bien, dicho esto, esta no es la forma en que la gente suele hacer las cosas. Nunca he escrito ningún código incrustado que permita a main() salir así. Mayormente, mi código se vería algo así:
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
Así que normalmente nunca dejaría salir a main().
"Vale, vale", dices. Todo esto es muy interesante que el compilador se asegura de que nunca hay una última declaración de retorno. ¿Pero qué pasa si forzamos la situación? ¿Qué pasa si codifico a mano mi ensamblador, y no pongo un salto de vuelta al principio?
Bueno, obviamente la CPU seguiría ejecutando las siguientes instrucciones. Estas se verían algo así:
addr opco instruction
---- ---- -----------
00E6 FFFF NOP
00E8 FFFF NOP
00EA FFFF NOP
00EB FFFF NOP
.
. some instructions removed for brevity
.
7EE8 FFFF NOP
7FFA FFFF NOP
7FFC FFFF NOP
7FFE FFFF NOP
La siguiente dirección de memoria después de la última instrucción de main() está vacía. En un microcontrolador con memoria FLASH, una instrucción vacía contiene el valor 0xFFFF. En un PIC al menos, ese código op se interpreta como un 'nop', o 'ninguna operación'. Simplemente no hace nada. La CPU continuaría ejecutando esos nops hasta el final de la memoria.
¿Qué hay después de eso?
En la última instrucción, el puntero de instrucción de la CPU es 0x7FFe. Cuando la CPU añade 2 a su puntero de instrucción, obtiene 0x8000, lo que se considera un desbordamiento en un PIC con sólo 32k de FLASH, y por lo tanto se envuelve de nuevo a 0x0000, y la CPU continúa felizmente la ejecución de instrucciones de vuelta al principio del código, como si se hubiera reiniciado.
También has preguntado por la necesidad de apagar. Básicamente puedes hacer lo que quieras, y depende de tu aplicación.
Si tuvieras una aplicación que sólo necesitara hacer una cosa después del encendido, y luego no hacer nada más podrías simplemente poner un while(1); al final de main() para que la CPU deje de hacer nada notable.
Si la aplicación requiere que la CPU se apague, entonces, dependiendo de la CPU, probablemente habrá varios modos de suspensión disponibles. Sin embargo, las CPUs tienen el hábito de despertarse de nuevo, por lo que tendrías que asegurarte de que no hubiera un límite de tiempo para la suspensión, y que no estuviera activo el Watch Dog Timer, etc.
Incluso se podría organizar algún circuito externo que permitiera a la CPU cortar completamente su propia energía cuando hubiera terminado. Ver esta pregunta: Utilización de un pulsador momentáneo como interruptor basculante de encendido y apagado .
23 votos
Depende de sus creencias. Algunos dicen que se reencarnará.
10 votos
¿Es para un misil?
7 votos
Algunos sistemas admiten el HCF (Halt and Catch Fire) instrucción. :)
1 votos
Se bifurcará a la rutina de autodestrucción