x86-64 Codice macchina, 24 byte
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Il codice precedente definisce una funzione nel codice macchina x86 a 64 bit che determina se il valore di input è divisibile per il doppio della somma delle sue cifre. La funzione è conforme alla convenzione di chiamata AMD64 di System V, quindi è richiamabile praticamente da qualsiasi linguaggio, proprio come se fosse una funzione C.
Prende un singolo parametro come input tramite il EDI
registro, secondo la convenzione di chiamata, che è l'intero da testare. (Si presume che sia un numero intero positivo , coerente con le regole della sfida, ed è richiesto per ilCDQ
corretto funzionamento istruzioni che utilizziamo.)
Restituisce il suo risultato nel EAX
registro, di nuovo, secondo la convenzione di chiamata. Il risultato sarà 0 se il valore di input era divisibile per la somma delle sue cifre e diverso da zero in caso contrario. (Fondamentalmente, un booleano inverso, esattamente come nell'esempio fornito nelle regole della sfida.)
Il suo prototipo C sarebbe:
int DivisibleByDoubleSumOfDigits(int value);
Ecco le istruzioni per il linguaggio dell'assemblea non golfate, annotate con una breve spiegazione dello scopo di ciascuna istruzione:
; EDI == input value
DivisibleByDoubleSumOfDigits:
push 10
pop rsi ; ESI <= 10
xor ecx, ecx ; ECX <= 0
mov eax, edi ; EAX <= EDI (make copy of input)
SumDigits:
cdq ; EDX <= 0
div esi ; EDX:EAX / 10
add ecx, edx ; ECX += remainder (EDX)
test eax, eax
jnz SumDigits ; loop while EAX != 0
lea eax, [rcx+rcx] ; EAX <= (ECX * 2)
cdq ; EDX <= 0
div edi ; EDX:EAX / input
xchg edx, eax ; put remainder (EDX) in EAX
ret ; return, with result in EAX
Nel primo blocco, eseguiamo un'inizializzazione preliminare dei registri:
PUSH
Le POP
istruzioni + sono usate come un modo lento ma breve per inizializzare ESI
a 10. Ciò è necessario perché l' DIV
istruzione su x86 richiede un operando di registro. (Non esiste una forma che si divide per un valore immediato, diciamo, di 10.)
XOR
è usato come un modo breve e veloce per cancellare il ECX
registro. Questo registro fungerà da "accumulatore" all'interno del ciclo imminente.
- Infine,
EDI
viene creata e memorizzata una copia del valore di input (from ) EAX
, che verrà bloccato durante il ciclo.
Quindi, iniziamo il ciclo e sommando le cifre nel valore di input. Questo si basa sull'istruzione x86 DIV
, che si divide EDX:EAX
per il suo operando e restituisce il quoziente in EAX
e il resto in EDX
. Quello che faremo qui è dividere il valore di input per 10, in modo tale che il resto sia la cifra nell'ultimo posto (che aggiungeremo al nostro registro accumulatore ECX
), e il quoziente sono le cifre rimanenti.
- L'
CDQ
istruzione è un breve modo di impostare EDX
a 0. E 'in realtà firmare-estende il valore di EAX
a EDX:EAX
, che è quello che DIV
utilizza come dividendo. In realtà non abbiamo bisogno di estensione del segno qui, perché il valore di input non è firmato, ma CDQ
è 1 byte, invece di utilizzare XOR
per cancellare EDX
, che sarebbe 2 byte.
- Poi abbiamo
DIV
IDE EDX:EAX
da ESI
(10).
- Il resto (
EDX
) viene aggiunto all'accumulatore ( ECX
).
- Il
EAX
registro (il quoziente) viene testato per vedere se è uguale a 0. In tal caso, abbiamo superato tutte le cifre e cadiamo. In caso contrario, abbiamo ancora più cifre da sommare, quindi torniamo all'inizio del ciclo.
Infine, al termine del ciclo, implementiamo number % ((sum_of_digits)*2)
:
L' LEA
istruzione viene utilizzata come un modo breve per moltiplicare ECX
per 2 (o, equivalentemente, aggiungere ECX
a se stesso) e memorizzare il risultato in un registro diverso (in questo caso, EAX
).
(Avremmo anche potuto fare add ecx, ecx
+ xchg ecx, eax
; entrambi sono 3 byte, ma l' LEA
istruzione è più veloce e più tipica.)
- Quindi, facciamo di
CDQ
nuovo un preparativo per la divisione. Perché EAX
sarà positivo (cioè senza segno), questo ha l'effetto di azzerare EDX
, proprio come prima.
- La prossima è la divisione, questa volta che si divide
EDX:EAX
per il valore di input (una cui copia non rimossa risiede ancora EDI
). Questo è equivalente a modulo, con il resto in EDX
. (Viene inserito anche il quoziente EAX
, ma non ne abbiamo bisogno.)
- Infine,
XCHG
scambiamo i contenuti di EAX
e EDX
. Normalmente, faresti un MOV
qui, ma XCHG
è solo 1 byte (anche se più lento). PerchéEDX
contiene il resto dopo la divisione, sarà 0 se il valore fosse equamente divisibile o diverso da zero in caso contrario. Pertanto, quando RET
urliamo, EAX
(il risultato) è 0 se il valore di input era divisibile per il doppio della somma delle sue cifre, o diverso da zero altrimenti.
Spero che questo sia sufficiente per una spiegazione.
Questa non è la voce più breve, ma ehi, sembra che batte quasi tutte le lingue non golf! :-)