Problema con ejercicio usando la poco habitual instrucción "GOTO"

Estoy intentando hacer un ejercicio sencillo usando la instrucción GOTO para mostrar su "utilidad" prácita. El ejemplo es sencillo, está basado en un ejemplo que encontré en una página llamada stackoverflow, de programación, y sirve simplemente para sustituir lo que serían 10 sentencias IF o un SWITCH, y hacer que tarde lo mismo en acceder al código del IF, independientemente de si es el primer IF o el noveno tras pasar por ocho ELSE IF el que se va a ejecutar.
Creo una matriz con punteros void para guardar las direcciones de los saltos que haré mas adelante. Llamo a la función Actualizar() y los inicializo, pongo la variable situacion a 0 y entro en loop()
Tras llamar a Hacer_salto(), desde Hacer_salto() se realiza un goto *saltos[situacion], aquí se actualizaría la variable, y se hace el return, que sustituye al return de Hacer_salto(), volviendo a loop() y se haría Serial.println( "Volvemos de Hacer_salto()...." );

No entiendo por que obtengo este resultado:

Llamamos a Actualizar() para inicializar los saltos.
Ponemos situacion=0
Salimos de Setup...
Llamamos a Hacer_salto()....
Entra en Hacer_salto()

Ponemos situacion=0
Salimos de Setup...
Llamamos a Hacer_salto()....
Entra en Hacer_salto()

Ponemos situacion=0
Salimos de Setup...
Llamamos a Hacer_salto()....
Entra en Hacer_salto()

Ponemos situacion=0
Salimos de Setup...
Llamamos a Hacer_salto()....
Entra en Hacer_salto()
......

El programa es este, sencillo, pero no doy con el porque se hace un bucle, no se resetea, pero hace mal los saltos y se queda haciendo siempre lo mismo, como si hiciera dos return, o algo así, no se, no veo donde se me vuelve loca la pila.

void *saltos[10];
int situacion;

void setup() {
  Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT);
  Serial.println( "Llamamos a Actualizar() para inicializar los saltos." );
  Actualizar();     Serial.println( "Ponemos situacion=0" );
  situacion = 0; Serial.println( "Salimos de Setup..." );
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println( "Llamamos  a Hacer_salto()...." );
  Hacer_salto();
  Serial.println( "Volvemos de Hacer_salto()...." );
  
  digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000);

  Serial.print( " Situación=" );
  Serial.println( situacion );
}

void Hacer_salto()
{
  Serial.println( " Entra en Hacer_salto()" );
  goto *saltos[ situacion ];
  return;
}

void Actualizar()
{
  saltos[0] = &&etiqueta00; saltos[1] = &&etiqueta01; saltos[2] = &&etiqueta02; saltos[3] = &&etiqueta03; saltos[4] = &&etiqueta04;
  saltos[5] = &&etiqueta05; saltos[6] = &&etiqueta06; saltos[7] = &&etiqueta07; saltos[8] = &&etiqueta08; saltos[9] = &&etiqueta09;
//  saltos[0] = &etiqueta00; saltos[1] = &etiqueta01; saltos[2] = &etiqueta02; saltos[3] = &etiqueta03; saltos[4] = &etiqueta04;
//  saltos[5] = &etiqueta05; saltos[6] = &etiqueta06; saltos[7] = &etiqueta07; saltos[8] = &etiqueta08; saltos[9] = &etiqueta09;
  return;
  
  etiqueta00:  Serial.println( " Entra en Actualizar() al 0" ); situacion = 1; return;
  etiqueta01:  Serial.println( " Entra en Actualizar() al 1" ); situacion = 2; return;
  etiqueta02:  Serial.println( " Entra en Actualizar() al 2" ); situacion = 3; return;
  etiqueta03:  Serial.println( " Entra en Actualizar() al 3" ); situacion = 4; return;
  etiqueta04:  Serial.println( " Entra en Actualizar() al 4" ); situacion = 5; return;
  etiqueta05:  Serial.println( " Entra en Actualizar() al 5" ); situacion = 6; return;
  etiqueta06:  Serial.println( " Entra en Actualizar() al 6" ); situacion = 7; return;
  etiqueta07:  Serial.println( " Entra en Actualizar() al 7" ); situacion = 8; return;
  etiqueta08:  Serial.println( " Entra en Actualizar() al 8" ); situacion = 9; return;
  etiqueta09:  Serial.println( " Entra en Actualizar() al 9" ); situacion = 0; return;

}

/*
  https://stackoverflow.com/questions/14268325/exposing-goto-labels-to-symbol-table

  int main () {
  void *ret_p = &&ret;
  printf("ret: %p\n", ret_p);
  goto *ret_p;

  return 1;

  ret:
  asm("RET:")

  return 0;
  }
*/

Apuesto que te hace un reset.
Partimos de la base que haces un "GOTO" desde un void y después intentas hacer un "return" pero la pila seguramente no está limpia (tendría que investigar en ensamblador cómo está haciendo la pila y la llamada. Depende del procesador hay jmp far, short,...
Esto simplificado sí funciona:

void *salto;
void *retorno;
void setup() 
{
    Serial.begin(9600); 
    Serial.println("Inicio (1)");
    salto=&&etiqueta;
    goto *salto;
    Serial.println("Este no debería realziarse (2)");
    etiqueta:
    Serial.println("Final(3)");
    while(1);
    {
    }
}
void loop()
{
}

Ampliando un poco más la respuesta anterior, he realizado esta prueba en un esp8266 (Wemos D1):

void *salto;
void setup() 
{
    Serial.begin(9600); 
    Serial.println("Inicio (1)");
    salto=&&etiqueta;
    if (1==0) //para que no entre aquí
    {
       etiqueta:
        Serial.println("Final(3)");
        while(1);    //aquí se queda para siempre
    }
}
void loop()
{    
   Serial.println("Comienza loop (2)");
   goto *salto;
}

Y hace reset cuando realiza el "GOTO". por lo que entiendo que el direccionamiento puede dar problemas. como antes comenté habría descompilar a ensamblador y ver las llamadas que está haciendo.
Sea como fuere, y suponiendo que ya sabes que el uso de "GOTO" está contraindicado, yo diría que jugar con saltos de este tipo puede que te funcione en algún tipo de chip pero no para otros por lo que romperías la compatibilidad de fuentes. en el link de stackoverflow indica usar ensamblador, concretamente la instrucción "RET". Normalmente se habla de procesadores iAPX8086 para usar esa instrucción (si mi memoria no me falla existía FAR RET y NEAR RET) pero repito esto sería válido para chips con código máquina compatibles con el 8086 de intel y no para otros.
Opino que deberás usar un case en su lugar.
Saludos.

Si te fijas en las lineas que obtengo con el serial.print, no se produce un reset, el goto que hago es desde una subrutina, a otra subrutina, y el return, deberia volver a la linea siguiente dentro de loop, puesto que el return de la funcion Actualizar, sustituye al return de la funcion Hacer_salto.
Lo que sucede es que ese 'return' lleva a la linea siguiente a la llamada dentro de setup(), y entonces aparece otra vez el mensaje 'Ponemos situacion=0'
Si las direcciones que guardo en la matriz fueran erroneas, si entenderia un reset (que no se produce por que saldría otr vez el mensaje 'Llamamos a Actualizar() para inicializar los saltos.'), por que serian aleatorias. Si se hace un jmp relativo, en lugar de uno absoluto, me parece muchisima casualidad que vaya justo a esa linea.
Tus ejemplos estan bien, pero lo que intento hacer es guardar las posiciones de las etiquetas que sustituyen los 'if', y poder saltar a esas direcciones en funcion del valor de la variable, pero desde otra subrutina.
Es decir, en lugar de llamar a Hacer_salto(), y desde Hacer_salto() llamar a Actualizar(), hacer los 'if' poner al día los valores, etc, y luego volver a Hacer_salto(), y de Hacer_salto() volver a Loop() y continuar el programa, volver directamente a Loop() desde Actualizar().

void *retorno; //añadir arriba
...
void Hacer_salto()
{
  Serial.println( " Entra en Hacer_salto()" );
  retorno=&&lbl_retorno; //añadir
  goto *saltos[ situacion ];
  lbl_retorno:  //añadir
  return;
}
...
//cambiamos return's por goto *retorno
 etiqueta00:  Serial.println( " Entra en Actualizar() al 0" ); situacion = 1; goto *retorno;
 

[/quote]
Sin poder probarlo dado que todo lo que tengo son wemos y demás,
Intenta probar eso, a ver si te fucniona. lo que más chirria son los returns de las etiquetaXX
Saludos

Gracias por la sugerencia, la probaré en cuanto pueda, con esto de las fiestas navideñas no ando con mucho tiempo libre, pondré aquí los resultados.

Hi,
Mi sugerencia es que leeas como usar el goto. Creo que lo esta haciendo mal. Para hacer un goto tienes que poner donde vas a goto ejemplo salto: Enotonces para ir al salto haz goto salto. Adjunto link que explica como hacerlo.
https://www.arduino.cc/reference/tr/language/structure/control-structure/goto/

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.