2011-04-28 4 views
14

tengo un corto, instr, que se ve así:sesión extender un número de nueve bits en C

1110xxx111111111 

que necesito para sacar los bits 0-9, que hago con (instr & 0x1FF). Esta cantidad se almacena en un nuevo corto. El problema es que cuando esto ocurre, se convierte en 0x0000000111111111, no en 0x1111111111111111 como yo quiero. ¿Cómo puedo arreglar esto? ¡Gracias!

EDITAR

Aquí está el código:

short instr = state->mem[state->pc]; 
unsigned int reg = instr >> 9 & 7; // 0b111 
state->regs[reg] = state->pc + (instr & 0x1FF); 

Se trata de un simulador que lee en el montaje. state es la máquina, regs[] son los registros y pc es la dirección de la instrucción actual en mem[].

Esto está bien si los últimos nueve bits representan un número positivo, pero si están representando -1, se almacena como todos los 1, lo cual se interpreta como un valor positivo por mi código.

+1

No estoy seguro de entender la pregunta o el proceso que está utilizando. ¿Te importa compartir el código que estás utilizando para obtener estos resultados? – joce

+0

Esto no está claro, ¿quién debería convertirse en '0x1111111111111111'? – MByD

Respuesta

19

Puede hacerlo manualmente: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0). Esto prueba el bit de signo (el bit más alto que está reteniendo, 0x100) y establece todos los bits encima si se establece el bit de signo. Puede ampliar esto a 5 bits adaptando las máscaras a 0x1F, 0x10 y 0xFFE0, siendo los 5 bits inferiores, el 5º bit en sí y todos los bits 516 respectivamente.

O puede encontrar alguna excusa para asignar los bits a la parte superior de un corto firmado y cambiarlos hacia abajo (obteniendo una extensión de signo en el proceso): short x = (instr & 0x1FF) << 7; x >>= 7; Esto último puede llegar a ser más sencillo en el montaje y no implicará una rama. Si instr es firmado, esto se puede hacer en una sola expresión: (instr & 0x1FF) <<7>> 7. Como eso ya elimina los bits superiores, se simplifica a instr <<7>> 7. Reemplace 7 con 11 por 5 bits (16-5).

+0

¿Cómo se puede adaptar esto para un número de 5 bits? Necesito manejar ambos tipos en este programa. –

+3

En este último caso, ¿no podría simplemente hacer '(instr & 0x1FF << 7) >> 7?? –

+1

He actualizado la respuesta para abordar los comentarios ... –

2

No estoy seguro de cómo obtendrá 13 1 bits después de enmascarar con 0x1ff, pero esto debe firmar-extender un número de 9 bits en un corto de 16 bits. No es bonita (o particularmente eficiente), pero funciona:

 
(instr & 0x1ff) | (0xfe00 * ((instr & 0x100) >> 8)) 

enmascarar el bit de signo, cambie a la posición 1 para obtener 0/1. Multiplique esto por los bits superiores, si el signo es 1, entonces el número de 9 bits se OR'ed con 0xfe, que establecerá todos los bits superiores en 1.

+0

Aún no se ha probado esto, pero ¿cómo se podría adaptar esto para un número de 5 bits? Necesito obtener ambos tipos en este programa. Y tienes razón sobre los resultados; He estado trabajando durante horas, así que estoy agotado. : D –

+0

Enmascare el bit de signo y cambie ese bit a la posición 1 (para que obtenga 1 si tiene un número negativo y 0 en caso contrario). Luego, cree una máscara OR para los bits extendidos y multiplique esto por el signo desplazado. Si su número original fue negativo, esta máscara se mantendrá en todos, y de lo contrario serán todos ceros. O esto con su número enmascarado para llenar los bits superiores con 1. –

4
(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7)) 

¿Cómo funciona? Selecciona tu bit de signo y lo desplaza a la posición 2. Esto se usa para generar el valor 1 (si su bit de signo estuvo ausente) o -1 (si su bit de signo estaba presente).

Esta solución no tiene sucursales y no depende de un comportamiento indefinido.

+3

En un microcontrolador desconocido, probablemente me equivocaría en lugar de multiplicarme. La penalización de rama en tales CPU es pequeña y la penalización si no hay multiplicador de hardware (¡o tal vez incluso si la hay) es mucho más alta. De hecho, dado el riesgo de que no * barrel shifter * quizás la mejor rama/prueba sea mejor. –

2

Acabo de tropezar con esto en busca de otra cosa, tal vez un poco tarde, pero tal vez sea útil para otra persona. AFAIAC todos los programadores C deben comenzar a programar el ensamblador.

De todos modos, la extensión de letreros es mucho más fácil que las otras 2 propuestas. Solo asegúrate de usar variables firmadas y luego usa 2 turnos.

short instr = state->mem[state->pc]; 
unsigned int reg = (instr >> 9) & 7; // 0b111 
instr &= 0x1ff; // get lower 9 bits 
instr = ((instr << 7) >> 7); // sign extend 
state->regs[reg] = state->pc + instr; 

Si la variable está firmado, el compilador de C se traduce >> para Aritmética Shift derecho que preservó señal. Este comportamiento es independiente de la plataforma.

Así, suponiendo que las aperturas de Instr con 0x1ff entonces tenemos, < < 7 será SL (desplazamiento a la izquierda) el valor de modo instr es ahora 0xff80, a continuación, >> 7 se ASR el valor de modo instr es ahora 0xffff.

+0

¿Es necesario "instr & = 0x1ff; // obtener 9 bits más bajos" en este contexto? Estos bits se eliminan de todos modos :) –

8

* * No se requiere ramificación

Ver http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend para obtener una lista de los cortes de bits muy útiles. Específicamente, extender el signo de un número es tan simple como:

/* generate the sign bit mask. 'b' is the extracted number of bits */ 
int m = 1U << (b - 1); 

/* Transform a 'b' bits unsigned number 'x' into a signed number 'r' */ 
int r = (x^m) - m; 

Es posible que necesita borrar los bits superiores de 'x' si no son cero (x = x & ((1U << b) - 1);) antes de utilizar el procedimiento anterior.

Si se conoce el número de bits 'b' en tiempo de compilación (por ejemplo, 5 bits en su caso) hay incluso una solución más simple (esto podría desencadenar una instrucción de extensión de signo específica si el procesador lo admite y el compilador lo suficientemente inteligente):

struct {signed int x:5;} s; 
r = s.x = x; 
1

Esto es más de un refinamiento de las respuestas anteriores, pero no totalmente solución genérica se ha presentado hasta ahora. Esta macro indicará extender un valor v con sb indicando el número de bit basado en 0 del bit de signo.

#define SIGNEX(v, sb) ((v) | (((v) & (1 << (sb))) ? ~((1 << (sb))-1) : 0)) 

int32_t x; 

SIGNEX(x, 15); // Sign bit is bit-15 (16th from the right) 
SIGNEX(x, 23); // Sign bit is bit-23 (24th from the right) 

Utiliza ramificación para maximizar la portabilidad a través de plataformas que carecen de una multiplicación de hardware o de desplazamiento reconfigurable.

0

Una solución más fácil es esto, por x ser un número complementario 5 bits 2 's, mira:

z = (x^16)-16 
Cuestiones relacionadas