x86 codice macchina a 32 bit, 24 21 byte
log delle modifiche: -3 byte: sostituisce standard add / cmp / jbe / add con un hack DAS di @peter ferrie
64 bit: ancora 24 byte. La modalità lunga ha rimosso il codice operativo DAS.
Modalità a 16 bit: la dimensione dell'operando predefinita è 16 bit ma la specifica del problema è intrinsecamente a 32 bit. Comprese 8 cifre esadecimali codificate.
Byte-reverse con bswapquindi int-> hex manuale in ordine standard (prima lo stuzzichino più significativo, scrivendo cifre esadecimali su un buffer di output caratteri in ordine crescente). Questo evita di dover srotolare il loop per cambiare l'ordine tra i nibble all'interno di un byte vs. attraverso byte.
Callable void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);come x86-64 System V, tranne che non funziona in modalità 64-bit. (È necessario il puntatore di output in EDI per stosb. Il numero di input può trovarsi in qualsiasi registro diverso da ECX o EAX.)
1 lehex:
2 00000000 0FCE bswap esi
3 00000002 6A08 push 8 ; 8 hex digits
4 00000004 59 pop ecx
5 .loop: ;do{
6 00000005 C1C604 rol esi, 4 ; rotate high nibble to the bottom
7
8 00000008 89F0 mov eax, esi
9 0000000A 240F and al, 0x0f ; isolate low nibble
10 0000000C 3C0A cmp al, 10 ; set CF according to digit <= 9
11 0000000E 1C69 sbb al, 0x69 ; read CF, set CF and conditionally set AF
12 00000010 2F das ; magic, which happens to work
13
14 00000011 AA stosb ; *edi++ = al
15 00000012 E2F1 loop .loop ; }while(--ecx)
16
17 00000014 C3 ret
dimensione = 0x15 = 21 byte.
Caso di prova x86 TIO FASM a 32 bit con un chiamante asm che utilizza una writechiamata di sistema per scrivere l'output dopo averlo chiamato due volte per aggiungere 2 stringhe in un buffer. Verifica tutte le cifre esadecimali 0..F, inclusi 9 e A al confine tra numerale e lettera.
L' DAShacking - x86 ha una bandiera half-carry, per eseguire il nibble basso. Utile per oggetti BCD confezionati come l'istruzione DAS, destinati all'uso dopo aver sottratto due numeri interi BCD a 2 cifre. Con il basso bocconcino di AL che è al di fuori dell'intervallo 0-9, qui stiamo sicuramente abusando.
Si noti la parte if (old_AL > 99H) or (old_CF = 1)THEN AL ← AL − 60H;della sezione Operation del manuale; sbb imposta sempre CF qui in modo che la parte accada sempre. Questo e l'intervallo ASCII per le lettere maiuscole è ciò che motiva la scelta disub al, 0x69
cmp 0xD, 0xA non imposta CF
- sbb esegue il
0xD - 0x69wrapping su AL = 0xA4come input per DAS. (E imposta CF, cancella AF)
- no AL - = 6 nella prima parte di DAS (perché 4> 9 è falso e AF = 0)
- AL - = 0x60 nella seconda parte, lasciando
0x44, il codice ASCII per'D'
vs. un numero:
cmp 0x3, 0xA imposta CF
- FFS
3 - 0x69 - 1= AL = 0x99 e schede CF e AF
- no AL - = 6 nella prima parte di DAS (9> 9 è falso ma AF è impostato), lasciando 0x93
- AL - = 0x60 nella seconda parte, lasciando 0x33, il codice ASCII per
'3'.
La sottrazione 0x6ain FFS imposta AF per ogni cifra <= 9, quindi tutti i numeri seguono la stessa logica. E lascialo deselezionato per ogni cifra alfabetica esadecimale. cioè sfruttando correttamente la gestione suddivisa in 9 / A di DAS.
Normalmente (per le prestazioni) useresti una tabella di ricerca per un ciclo scalare, o possibilmente un 2x senza rami leae cmp/cmovun'aggiunta condizionale. Ma le al, imm8istruzioni a 2 byte sono una grande vittoria per la dimensione del codice.
versione x86-64 : solo la parte diversa, tra and al, 0xfe stosb.
;; x86-64 int -> hex in 8 bytes
10 0000000C 0430 add al, '0'
11 0000000E 3C39 cmp al, '9'
12 00000010 7602 jbe .digit
13 00000012 0427 add al, 'a'-10 - '0' ; al = al>9 ? al+'a'-10 : al+'0'
14 .digit:
Si noti che viene add al, '0' sempre eseguito e l'aggiunta condizionale aggiunge solo la differenza tra 'a'-10e '0', per renderlo solo un ifinvece di if/ else.
Testato e funzionante, usando lo stesso mainchiamante della mia risposta C , che usa char buf[8]e printf("%.8s\n", buf).