2017-10-21 210 views
1

Je suis en train d'apprendre l'assemblage au collège et j'ai récemment commencé à écrire des programmes d'assemblage pour allumer des LED sur un simulateur LED 32x32.Pourquoi le décalage logique est-il parti et/ou certaines valeurs dans l'assemblage

Nous avions un laboratoire cette semaine et la première question était "créer un programme qui allume une LED individuelle aléatoire et continue jusqu'à ce que toutes les LED soient allumées". Un de mes amis m'a montré comment ils l'ont fait, mais je ne comprends toujours pas comment fonctionnent certaines options. Heres le code:

.data 
x   DWORD 0 
y   DWORD 0 
row   DWORD 0 
row_copy DWORD 00000001h 

.code 
    main:nop 

     invoke version 
     invoke setPattern, 0 
    row_random: 
     invoke random, 32 ;create a random number between 0-31 
     mov x, eax  ;move that value into memory location x 
     invoke readRow, x ;select a row to be altered 
     mov row, eax 
    row_on: 
     invoke random, 32 
     mov ecx, eax  ;move the random value into ecx 
     shl row_copy, CL ;shift left with carry flag (This is where Im confused) 
     mov eax, row 
     mov ebx, row_copy 
     or eax, ebx ; I'm also unsure as to why this is happening 
     invoke writeRow, x, eax ;alter a pixel at the random row x with the value of eax 
     mov row_copy, 00000001h 
     ;invoke Sleep, 1 
     jmp row_random 
     invoke ExitProcess,0 

À l'origine, quand je l'ai fait je créais un nombre aléatoire entre 0-31 mise en EBX et en utilisant writerow avec x et EBX. Cependant, c'était faux. Quelqu'un pourrait-il m'expliquer pourquoi vous passez logiquement à gauche avec le CL? et pourquoi est-ce nécessaire à ou les deux valeurs? Je pense que le ou est là pour vous assurer que vous n'avez pas accidentellement éteindre une LED si elle était déjà allumée?

Répondre

1

CL est l'octet le plus bas d'ecx. Vous le confondez avec CF, le drapeau de carry dans EFLAGS. x86 variable-count shift instructions require the shift-count to be in cl.

Et juste pour l'enregistrement, ce code est hilarante inefficace. row_copy est décalé avec une instruction de destination de la mémoire (lent), puis chargé, puis remplacé par 1 nouveau! Donc ... vous auriez pu le faire

mov ecx, eax 
mov ebx, 1 
shl ebx, cl 

comme une personne normale. Il n'y a aucune raison d'avoir un emplacement de mémoire pour row_copy, il suffit de le faire dans un registre. Vous n'avez besoin que d'un stockage de mémoire statique lorsque vous n'avez plus de registres.


La logique de base est le code implémente row |= (1 << rand_0_31) pour définir un bit aléatoire (qui pourrait déjà être défini).

Si vous voulez voir comment ce code fonctionne, mettez-le dans un débogueur et observez les valeurs dans les registres. Voir aussi le wiki des balises pour les guides, les documents et les conseils de débogage.


BTW, d'une manière encore plus efficace pour créer un masque avec jeu 1 bit est xor ebx,ebx/bts ebx, eax pour éviter d'avoir le nombre de changement de ECX, mais si vous ne l'avez pas appris BTS encore, il n'a pas faites tout ce que vous ne pouvez pas faire avec d'autres instructions plus simples. Et en fait, BTS signifierait que vous n'avez pas besoin d'un masque séparé et d'une instruction OR, il suffit d'obtenir l'ancienne valeur de la ligne dans un registre, un nombre aléatoire dans l'autre registre et bts ebx, eax pour définir EAX 'peu dans EBX.


En supposant que votre fonction convention d'appel aplatit seulement ECX et EDX (plus EAX avec la valeur de retour), vous ne pas besoin de lieux de stockage statiques pour cela, juste registres. Je ferais quelque chose comme:

; untested 
.code 
    main: 
     push ebx ; save a couple call-preserved registers 
     push edi ; for values that survive across function calls 

     ; nop  ; what's the point of this NOP? 
     invoke version 
     invoke setPattern, 0 

    row_random: 
     invoke random, 32 ;create a random number between 0-31 
     mov ebx, eax   ; eax = ebx = row 
     invoke readRow, eax 
     mov edi, eax   ; edi = old value of row 
     invoke random, 32 

     mov ecx, eax   ; ecx = random column = bit position 
     mov eax, 1 
     shl eax, cl   ; 1 << random 
     or  edi, eax   ; row_value |= 1<<random 

     invoke writeRow, ebx, edi ; pixel[ebx] |= 1<<random 

     jmp row_random 
     ; or loop a finite number of times with dec/jnz. 

     pop edi 
     pop ebx 
     return 
     ; invoke ExitProcess,0 

Le bloc pourrait être bts edi, eax milieu entier (avec la SHL et ou).

invoke est une macro qui pousse probablement et nettoie la pile après la call, alors vous pourriez être encore plus efficace en utilisant mov magasins à la pile et en laissant l'espace là-bas. Aussi, si vous êtes sur un nouveau processeur assez, vous pouvez utiliser rdrand ebx pour le plaisir. Fait amusant: les instructions de décalage masquent le nombre, de sorte qu'elles décalent toujours de 0 à 31 quelle que soit l'entrée utilisée, de sorte que vous n'avez pas besoin de and ecx, 31 après RDRAND ECX pour la position du bit.

De même, vous pouvez appeler random 32*32 et diviser le résultat en bits de ligne et en bits de colonne.

+0

Merci pour votre contribution, mais je suis encore confus quant à savoir pourquoi 'shl' et les opérations' or' sont en cours d'exécution, pourriez-vous expliquer? –

+0

@JamieHyland: Mise à jour de la réponse. Une simple progression dans le code et l'observation des valeurs dans les changements de registres devraient vous aider à comprendre si 'row | = (1 << rand)' ne l'explique pas. –

+1

@JamieHyland: Je voulais voir à quel point la boucle était trop compliquée, donc j'ai réécrit la fonction. Vous avez seulement besoin de 2 registres d'appel préservés et de deux registres de travail, pas de stockage statique. –