Allora, intanto ci ho pensato anch'io, e setcond (poi ti spiego cosa fa) è un tantinello inutile qui.
Questo ciclo a 16 bit funziona tranquillamente:
- Codice: Seleziona tutto
bit_count:
push bp
mov bp,sp
push cx
push dx
mov dx, w[bp+4]
xor ax,ax
l0:
shr dx,1
adc ax,0
loop l0
pop dx
pop cx
pop bp
ret 2
Qualche nota:
- ho utilizzato a86, che è un ottimo assembler a 16 bit che usavo quando ancora non mi cresceva la barba (la data di creazione del file sul disco è il 24/09/1995, tanto per darti un'idea). E' meno brigoso e assai più veloce di qualsiasi nasm, tasm o masm. Ciò detto, se cambi il w[] in word ptr[] ottieni un risultato compilabile anche con gli altri assembly
- la procedura si aspetta di essere chiamata con una word sullo stack che rappresenta la variabile di cui contare i bit, e scarta la word al momento del ritorno (la cosiddetta convenzione di chiamata pascal). Se la chiami dal C, sostituisci il ret 2 con un ret (in C è il chiamante che deve risistemare lo stack, non la procedura chiamata; vedi anche esempio successivo)
- il return value è in ax (anche questo è uno standard di chiamata)
- la procedura funziona così: la funzione shr shifta i bit di un registro verso destra, e l'ultimo bit finisce nel carry. L'istruzione adc somma il secondo operando al primo, e aggiunge anche il bit di carry. Scrivere adc ax, 0 significa aggiungere il bit di carry a ax
- la tua procedura non va bene: intanto è incasinata, e poi se shifti prima del confronto conti un bit in meno con i numeri dispari. Una procedura alternativa potrebbe essere del tipo (in ax ho quello che conto, in dx quanti bit sono settati - non è standard ma è l'uso che fai tu dei registri)
- Codice: Seleziona tutto
...
xor dx,dx
mov cx, 10h
l0:
test ax, 1
jz .skip
inc dx
skip:
shr ax,1
loop l0
Questo codice non è brutto, ma ha un salto condizionale (jz...), che manda in difficoltà la pipeline dei pentium (nel senso: va bene la branch prediction, va bene che è tutto in cache, ma meno salti condizionali ci infili nel codice e meglio è).
Ah, e - a meno di motivi particolari - usa inc ("INCrement") al posto di add <registro>, 1. In termini di velocità è lo stesso (2 cicli se non ci sono accessi in memoria, almeno così dice il manuale del 386), ma è almeno un byte in meno che consumi di memoria.
Ultima cosa: se il ciclo è a 32 bit, il codice - che poi è analogo - è questo:
- Codice: Seleziona tutto
push ecx
push edx
mov edx,dword ptr[esp+12]
mov ecx,32
xor eax,eax
l0:
shr edx,1
adc eax,0
loop l0
pop edx
pop ecx
ret
(questo l'ho testato con il visual c++; visto che la chiamata era in convenzione C, non ho il ret 2; tutti i registri sono a 32 bit; non creo il frame (tanto posso indirizzare direttamente esp, senza bisogno di scomodare ebp), e visto che non mi costa nulla conto i bit in una double word al posto che in una word.
Infine:
Scusa ma non capisco cosa fa l'istruzione SETC ?
Setc (più in generale: SETcond) setta il suo argomento, che è un puntatore ad un byte di memoria o un registro di un byte, a 0 o a 1 a seconda che la cond sia verificata.
SETZ al -> al=1 se lo zero flag è settato, 0 altrimenti
E' un'istruzione presente a partire dal 386 (quando ha cominciato ad avere senso limitare i salti condizionali).
Mi potresti dire con precisione che differenza c'è tra fare l'istruzione AND o TEST?
AND - ovviamente - fa l'and binario fra i due operandi, e il primo operando è modificato di conseguenza.
AND eax, ebx è l'equivalente di eax = eax & ebx, dove l'operatore & confronta bit a bit i due operandi, e setta ogni bit secondo la classica regola:
- se due bit corrispondenti sono a 1, il risultato è 1
- altrimenti il risultato è 0
Nel contempo, viene settato lo zero flag se il risultato dell'operazione è 0; il carry flag invece viene comunque sempre resettato (questa è un'eredità dello Z80, direi, buon vecchio ZX spectrum -> non c'era l'istruzione di azzeramento esplicito del carry - tipo CCF dell'80x86 - e quindi "AND A" era un buon modo di azzerarlo).
TEST è identico ad and, ma non influenza il risultato, solo i flags.
TEST EAX, EBX esegue eax & ebx e butta via il risultato, condizionando solo lo zero e il carry flag.
Nel tuo caso DEVI usare test, se usi AND azzeri per definizione tutti i bit di AX meno l'ultimo.
(ah, che bello, un po' di assembly. Voi mi fate sentire giovane, ragazzi)
Ciao.
Il faut être toujours ivre. Tout est là : c'est l'unique question. Pour ne pas sentir l'horrible fardeau du Temps qui brise vos épaules et vous penche vers la terre,il faut vous enivrer sans trêve...