Code machine x86-64, 24 octets
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Le code ci-dessus dĂ©finit une fonction dans le code machine x86 64 bits qui dĂ©termine si la valeur d'entrĂ©e est divisible par le double de la somme de ses chiffres. La fonction est conforme Ă la convention d'appel System V AMD64, de sorte qu'elle peut ĂȘtre appelĂ©e Ă partir de pratiquement n'importe quel langage, comme s'il s'agissait d'une fonction C.
Il faut un seul paramÚtre en entrée via le EDI
registre, conformĂ©ment Ă la convention d'appel, qui est le nombre entier Ă tester. (Ceci est supposĂ© ĂȘtre un entier positif , compatible avec les rĂšgles de challenge, et est nĂ©cessaire au bon fonctionnement de l' CDQ
instruction utilisée.)
Il retourne son résultat dans le EAX
registre, encore une fois, conformément à la convention d'appel. Le résultat sera 0 si la valeur en entrée était divisible par la somme de ses chiffres et non nul sinon. (Fondamentalement, un booléen inverse, exactement comme dans les rÚgles de challenge.)
Son prototype C serait:
int DivisibleByDoubleSumOfDigits(int value);
Voici les instructions non assemblées du langage d'assemblage, annotées avec une brÚve explication du but de chaque instruction:
; 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
Dans le premier bloc, nous effectuons une initialisation préliminaire des registres:
PUSH
+ les POP
instructions sont utilisées comme moyen lent mais court d'initialisationESI
à 10. Cela est nécessaire car l' DIV
instruction sur x86 nécessite un opérande de registre. (Aucune forme ne divise par une valeur immédiate, disons 10.)
XOR
est utilisé comme un moyen rapide et rapide pour effacer le ECX
registre. Ce registre servira dâaccumulateur Ă lâintĂ©rieur de la prochaine boucle.
- Enfin, une copie de la valeur dâentrĂ©e (de
EDI
) est créée et stockée dansEAX
laquelle elle sera compressée au fur et à mesure de notre progression dans la boucle.
Ensuite, nous commençons Ă boucler et Ă additionner les chiffres de la valeur dâentrĂ©e. Ceci est basĂ© sur lâ DIV
instruction x86 , qui divise EDX:EAX
par son opérande et renvoie le quotient dans EAX
et le reste dans EDX
. Ce que nous ferons ici est de diviser la valeur dâentrĂ©e par 10, de sorte que le reste soit le chiffre de la derniĂšre position (que nous ajouterons Ă notre registre accumulateur,ECX
) et le quotient, les chiffres restants.
- Lâ
CDQ
instruction est un moyen simple de définir la valeur EDX
sur 0. En fait, elle signe-Ă©tend la valeur EAX
Ă EDX:EAX
, qui correspond DIV
au dividende. En fait, nous nâavons pas besoin de lâextension de signe ici, car la valeur en entrĂ©e est non signĂ©e, mais CDQ
vaut 1 octet, par opposition Ă XOR
de clear EDX
, qui serait de 2 octets.
- Ensuite , nous
DIV
Ide EDX:EAX
parESI
(10).
- Le reste (
EDX
) est ajouté à l'accumulateur ( ECX
).
- Le
EAX
registre (le quotient) est testé pour voir s'il est égal à 0. Si c'est le cas, nous l'avons fait à travers tous les chiffres et nous échouons. Sinon, nous avons toujours plus de chiffres à additionner, alors nous retournons au début de la boucle.
Enfin, une fois la boucle terminée, nous implémentons number % ((sum_of_digits)*2)
:
L' LEA
instruction est utilisée comme un moyen rapide de multiplier ECX
par 2 (ou, de maniĂšre Ă©quivalente, de s'ajouter ECX
Ă elle-mĂȘme) et de stocker le rĂ©sultat dans un registre diffĂ©rent (dans ce cas, EAX
).
(Nous aurions aussi pu faire add ecx, ecx
+ xchg ecx, eax
; les deux sont de 3 octets, mais l' LEA
instruction est plus rapide et plus typique.)
- Ensuite, nous faisons Ă
CDQ
nouveau une préparation pour la division. Parce que EAX
sera positif (c'est-à -dire non signé), cela a pour effet de réduire à zéro EDX
, comme avant.
- Vient ensuite la division, divisée cette fois
EDX:EAX
par la valeur d'entrée (une copie non altérée de celle-ci réside toujours dans EDI
). Cela Ă©quivaut Ă modulo, le reste Ă©tant en EDX
. (Le quotient est également ajouté EAX
, mais nous n'en avons pas besoin.)
- Enfin, nous
XCHG
Ă©changeons le contenu de EAX
et EDX
. Normalement, vous feriez MOV
ici, mais ce XCHG
nâest quâun octet (bien que plus lent). Parce que EDX
contient le reste aprÚs la division, il sera 0 si la valeur est divisible de façon égale ou non nulle sinon. Ainsi, lorsque nous RET
urn, EAX
(le résultat) est égal à 0 si la valeur entrée est divisible par le double de la somme de ses chiffres, ou différente de zéro dans le cas contraire.
J'espĂšre que cela suffit pour une explication.
Ce n'est pas l'entrée la plus courte, mais bon, on dirait qu'elle bat presque toutes les langues autres que le golf! :-)