Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Tabla de saltos en C

Iniciado por Sante, 22 de Noviembre de 2006, 02:50:40 PM

« anterior - próximo »

Sante

Buenas, a ver si alguno me podeis ayudar con esto:

¿es posible obtener la dirección de una etiqueta en C?

Estoy intentando hacer una tabla de saltos, en la que por cada entrada guarde la direccion de una etiqueta, y permita saltar directamente a ella. Algo asi:

p = tabla[27];
goto *p;

......

p:
//Mas código


La idea es que en tabla[27] se guarde de alguna manera la dirección de la etiqueta p. Pero no se si eso es posible (creo que si, pero no en todos los compiladores).

Estoy usando el VC++ 6.0, por si sirve de algo.

Un saludo!

tamat

El goto es una instrucción proscrita entre los programadores de C, puedes  usarla pero se dice que ningun caso justifica su uso, que siempre hay una alternativa más limpia, clara e igual de eficiente.

Seguro que si nos explicas para qué quieres usarla te explicamos la manera de evitarla (seguramente con un switch o con un pointer to function).
Por un stratos menos tenso

MrK

Mirate la instruccion setjmp, seguramente sea mas portable que buscar
direcciones de etiquetas (yo no lo he visto nunca). De todas formas dudo que
funcione con el ejemplo que has puesto.

Por ese mismo ejemplo, parece que lo que quieres hacer se podria solucionar
con un array de funciones, metes cada funcion en un array y dependiendo de
lo que necesitas, llamas a esa funcion.

Si consigues hacerlo avisa, que me pica la curiosidad :) (goto al poder!)


Cita de: "Sante"Buenas, a ver si alguno me podeis ayudar con esto:

¿es posible obtener la dirección de una etiqueta en C?

Estoy intentando hacer una tabla de saltos, en la que por cada entrada guarde la direccion de una etiqueta, y permita saltar directamente a ella. Algo asi:

p = tabla[27];
goto *p;

......

p:
//Mas código


La idea es que en tabla[27] se guarde de alguna manera la dirección de la etiqueta p. Pero no se si eso es posible (creo que si, pero no en todos los compiladores).

Estoy usando el VC++ 6.0, por si sirve de algo.

Un saludo!

Sante

Cita de: "MrK"Mirate la instruccion setjmp, seguramente sea mas portable que buscar
direcciones de etiquetas (yo no lo he visto nunca)

Lo he mirado, pero no es lo que busco, eso es más bien un sistema para tener una "etiqueta dinámica" para decirlo de algun modo.

Cita de: "tamat"El goto es una instrucción proscrita entre los programadores de C, puedes  usarla pero se dice que ningun caso justifica su uso, que siempre hay una alternativa más limpia, clara e igual de eficiente.

Seguro que si nos explicas para qué quieres usarla te explicamos la manera de evitarla (seguramente con un switch o con un pointer to function).

Jejeje.. ya sabía que me ibais a llamar hereje por lo del goto :mrgreen:

Vale, está claro que los punteros a función, o el switch en menor medida son alternativas válidas, pero no son igual de eficientes.

Lo que estamos intentando hacer es un emulador. Básicamente un emulador lo que hace es leer una instrucción, decodificarla, y llamar a una rutina que emula la instrucción dependiendo de su tipo.

Con un switch (primera aproximación), sería:


while (;;){

opcode = decodificar (memoria[PC++]);

switch (opcode) {

case 1:
add ();
break;

case 2:
mov ();
break;

...

}
}


Donde add(), mov(), etc... son las funciones independientes que emulan cada una de las instrucciones de la máquina original. El problema de esto es que el switch() tiene que estar haciendo continuamente comprobaciones, asi que una manera de optimizarlo es crear una tabla:

La tabla en esta segunda aproximacion tendria tantas entradas como opcodes posibles, y para cada una, guardaría un puntero a la función que emula la instrucción de opcode correspondiente al número de entrada:


tabla[1] = add;
tabla[2] = mov;

while (;;){

opcode = decodificar (memoria[PC++]);

(*tabla[opcode]) ();

}


Esto es más eficiente, claro, pero aun tiene un problema, y es el del "call overhead": el coste de llamar a una función, los registros de activación, los marcos, etc...

La tercera aproximación es eliminar ese coste eliminando las llamadas a funciones, y usando etiquetas dentro de una única función más grande que incluya a TODAS las subrutinas (lo que antes eran funciones):


tabla[1] = direccion etiqueta add;
tabla[2] = direccion etiqueta mov;

while (;;){

opcode = decodificar (memoria[PC++]);

goto *(tabla[opcode]);
}

...

add:
//Codigo del add

mov:
//Codigo del mov

...


Esto es lo más eficiente que se puede conseguir, pero claro, para ello hay que saber como obtener las direcciones de las etiquetas. Por lo que hemos visto, parece ser posible en GCC con algo como

tabla[1] = &&add;

Pero por lo que parece esto no funciona en Visual C++, que es el que estamos usando, asi que seguimos en las mismas.

En fin, espero que haya quedado un poco más claro. Desde luego el código no es bonito, pero lo que tiene que ser es eficiente :roll:

zupervaca

¿Un puntero a una funcion sin parametros y sin retorno no se compila como un simple jmp? la verdad es que pregunto por que nunca lo he mirado.

shephiroth

esta un poco lioso, y lo hago de memoria, pero vamos para dar una idea sirve:


int cambioPosicion;
int sumar(char *posicion)
{
int * a = posicion+1;
int * b = posicion+5;
cambioPosicion = 9;
return (*a)+(*b);
}
int restar(char * posicion)
{
int * a = posicion+1;
int * b = posicion+5;
cambioPosicion = 9;
return (*a)+(*b);
}
...
#define saltoSuma 0x54
#define saltoResta 0x12
...
void main()
{
char* codigo_raw;
int posActual;
int * auxiliarNum;
codigo_raw = (leer bytecodes);
posActual = (offset inicio);
while (TRUE)
{
switch(codigo_raw[posActual])
{
case saltoSuma:
auxiliarNum = codigo_raw+posActual+1;
*auxiliarNum = sumar(codigo_raw+posActual)
break;
}
}
}


Esta sería una idea base. Los defines me los invente. Lo unico que faltaria seria crear un delegado para las funciones, hacer un array y meterlas, haciendolas coincidir con su salto.

SUERTE ^^

MrK

pues eso ni idea, la verdad :(

Quizas encontrarias alguna pista en algun emulador de los que corren por ahi con sources (el PCSX por ejemplo) Yo me mire en su tiempo toda la parte del emulador que va interpretada, y utilizaba un array de funciones y un while con el que iba llamandolas, pero quizas la parte que usa recompilacion dinamica tiene alguna pista sobre esto que buscas (esa parte ni me la mire, lo siento). Suele ser lo mas rapido para escribir emuladores.

De todas formas, no se para que maquina intentas emular, pero lo mas seguro es que despues de cada instruccion tengas que comprobar los ciclos o instrucciones que han pasado desde el ultimo retrace y cosas asi, con lo que el overhead de la llamada a funcion se difumina por el resto de basura a comprobar ... :)

fiero

Buenas,

Yo utilizo asm para eso:

void *direccionchunga;

_asm lea eax, dir1
_asm mov direccionchunga,eax

dir1:


Después para saltar a esa direccion tambien utilizo inline asm:

_asm jmp direccionchunga

Aunque estoy deacuerdo en que según lo que vayas a emular te sobra con switch. Un ordenador actual puede ejecutar cientos (o miles) de instrucciones en el mismo tiempo que una sola instrucción de algunas máquinas antiguas, con lo que el C tira de sobra. Yo he visto algun emulador por ahí  de Z80 y se utilizaba switch.

un saludo
www.videopanoramas.com Videopanoramas 3D player

Pogacha

Lo mas usado son punteros a funciones por claridad y facilidad, pero mejores resultados se encuentran si usas asm tal como te muestra fiero.

typedef void (*FuncPtr)(void *this);
FuncPtr Tabla[27] = { Sumar, Restar, Multiplicar, Dividir ... }
enum { SUMAR, RESTAR, MULTIPLICAR }

void Run()
{
while(int OpCode = PC.Feed() && Flags != HALT) Tabla[OpCode](this);
}

Sante

Cita de: "fiero"Buenas,

Yo utilizo asm para eso:

void *direccionchunga;

_asm lea eax, dir1
_asm mov direccionchunga,eax

dir1:


Después para saltar a esa direccion tambien utilizo inline asm:

_asm jmp direccionchunga

Aunque estoy deacuerdo en que según lo que vayas a emular te sobra con switch. Un ordenador actual puede ejecutar cientos (o miles) de instrucciones en el mismo tiempo que una sola instrucción de algunas máquinas antiguas, con lo que el C tira de sobra. Yo he visto algun emulador por ahí  de Z80 y se utilizaba switch.

un saludo

Si, lo he probado y es exactamente lo que necesitábamos, no se me habia ocurrido leer así la dirección de la etiqueta, muchas gracias!

Sobre lo que comentaba antes de poner &&etiqueta, como dije solo funciona en GCC, pero parece ir bien, por si a alguien le interesa hacerlo y tiene ese compilador.

Cita de: "Mrk"De todas formas, no se para que maquina intentas emular, pero lo mas seguro es que despues de cada instruccion tengas que comprobar los ciclos o instrucciones que han pasado desde el ultimo retrace y cosas asi, con lo que el overhead de la llamada a funcion se difumina por el resto de basura a comprobar ... :)

Después de cada instrucción habrá que comprobar si el número de ciclos que queda por ejecutar ha llegado o no a cero (una sola comprobación), nada mas. Todo el tema de las interrupciones, sincronización, etc.. se hace una única vez por cada miles de ciclos ejecutados, asi que no esperamos que sobrecargue mucho.

Y como decia, esta claro que con punteros a funciones sirve igualmente y es mas claro, pero no tan eficiente. Puede que nos de igual a largo plazo, y vaya sobrado de todas formas, pero asi nos curamos en salud, y no "ensucia" mucho el código, porque la mayor parte tendrá que ir en ensamblador de todas formas.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.