Assembleur résultat d'une multiplication sur 16 bits

Bonsoir,

j'étudie toujours l'assembleur sur l'Atmega328p qui dispose de registres de 8 bits.
J'ai trouvé en ce qui concerne la multiplication de nombres non signés que le résultat est placé par le µC directement dans les registres généraux R0 ou R0 et R1.

  • Lorsque le résultat est sur 8 bits, il est stocké dans R0 ;
  • Lorsque le résultat est sur 16 bits, il est stocké dans R0 et R1 où R1 prend les bits de poids fort et R0 les bits de poids faible;

1/ Par exemple pour une multiplication dont le résultat est sur un octet :

ldi r16, 4
ldi r17, 3
mul r16, r17

Dans ce cas le registre R0 contient le résultat de la multitplication sur un octet : soit 0x0c en hexadécimal, 12 en décimal.

2/ Pour une multiplication dont le résultat est sur deux octets :

ldi r16, 18 ; 0x12
ldi r17, 52 ; 0x34

mul r16, r17

Ici le résultat se trouve dans les registres R1 et R0.
Le registre R1 contient les bits de poids fort : 0x03.
Le registre R0 contient les bits de poids faible : 0xa8.
Le résultat de la multiplication 0x12 x 0x34 = 0x3a8 soit 936 en décimal.

Ma question est la suivante : étant donné qu'il suffit de prendre R1 et de "l'accoler" à R0 pour obtenir le résultat de la multiplication sur 16 bits, comment pourrai-je procéder pour obtenir le résultat dans l'IDE arduino :

volatile uint16_t resultat16_bits ;

void setup() {
  Serial.begin(115200);
  asm
  (
    "ldi r18, 150  \n" 
    "ldi r19, 2  \n"
    "mul r18, r19 \n" 
  
  );
  Serial.println(resultat16_bits);
 
}

void loop() {}

Ici resultat16_bits afficherait 300 en décimal.

Merci par avance à ceux qui voudront bien m'aider.
Bonne soirée.

PS :
Je ne suis pas sûr d'avoir bien compris mais il semble que les registres :

  • R27 et R26 peuvent peut-être constituer un registre X sur 16 bits
    Idem pour R29 et R28 (registre Y) et R31 et R30 (registre Z) mais il s'agit de mes petites suppositions de débutant en assembleur.

hello philippe
je dois m'absenter, alors vite fais ....
oui, c'est bien ça pour les registres x,y,z

pour jouer avec les retours, veux tu regarder ce code ( pour mega 2560)
asm_clavier_64_touches_ok.zip (2,9 Ko)

Merci @dfgh,
Je regarderai tout ça mercredi ou jeudi car d’ici là je serai sur la route et en forêt pour faire des photos.

Merci beaucoup
Très bonne soirée à toi.

bonjour, voir dans le manuel
tout ce qui commence par MUL

de mémoire et tapé ici sans tester

volatile uint16_t resultat = 0;

void setup() {
  Serial.begin(115200); Serial.println();
  asm volatile (
    "ldi r18, 144 \n"
    "ldi r19, 3 \n"
    "mul r18, r19 \n"
    "sts (resultat), r0 \n"           // LSB
    "sts (resultat + 1), r1 \n"       // MSB
    "eor r1,r1 \n"                    // on remet r1 à 0
    :::"r19", "r19"
  );

  Serial.print("resultat = "); Serial.println(resultat);
}

void loop(void) { }

les points notables

  • je fais un clear (par un ou exclusif eor) de r1 à la fin car si ce registre est modifié il faut le remettre à 0.
  • je donne une clobber list qui dit quels sont les registres que j'ai modifié

ou si vous voulez utiliser des variables sur un octet pour la multiplication

volatile uint16_t resultat = 0;
volatile uint8_t a = 144;
volatile uint8_t b = 3;

void setup() {
  Serial.begin(115200); Serial.println();

  // asm volatile(“code” : output operand list : input operand list : clobber list);

  asm volatile (
    "mul %0, %1 \n"
    "sts (resultat), r0 \n"           // LSB
    "sts (resultat + 1), r1 \n"       // MSB
    "eor r1,r1 \n"                    // on remet r1 à 0
    : 
    : "a" (a), "a" (b)
    :
  );

  Serial.print("resultat = "); Serial.println(resultat);
}

void loop(void) { }

je précise dans la liste des opérandes en entrée que a et b doivent être mis dans dans un registre de la partie haute (celui que le compilateur choisira dans r16 à r23) et je peux faire référence à ces registres par %0 et %1

➜ dans ce cas je n'ai plus besoin de la clobber list puisque le compilateur sait quels sont les registres choisis et donc s'est chargé de les restaurer à leur valeur s'il en avait besoin

(ça fait quelques années (décennies) que je n'ai plus vraiment fait d'assembleur... le compilateur + optimiser est généralement bien meilleur que moi)

Merci @J-M-L,
Je regarde ça dès que je peux.

Très bonne journée à vous.

bonne promenade et bonne chance pour les photos :slight_smile:

Bonsoir @J-M-L,
Ma femme et moi sommes tombés malades. Problèmes pulmonaires sans gravité, fièvre ... nous obligent à rester bien au chaud à la maison :wink: Donc je ne suis pas allé faire de photos.

J'ai regardé le code que vous m'avez fourni et voici comment je le comprends :

volatile uint16_t resultat = 0;

void setup() {
  Serial.begin(115200); Serial.println();
  asm volatile (
    "ldi r18, 144 \n"
    "ldi r19, 3 \n"
    "mul r18, r19 \n"
    "sts (resultat), r0 \n"           // LSB
    "sts (resultat + 1), r1 \n"       // MSB
    "eor r1,r1 \n"                    // on remet r1 à 0
    :::"r18", "r19"
  );

  Serial.print("resultat = "); Serial.println(resultat);
}

void loop(void) { }

C'est celui-ci que je pense avoir compris le plus facilement.
vous utilisez l’assembleur dit inline du langage C. C’est d’ailleurs ce que je demande dans mon sujet.
Vous commencez par déclarer une variable globale non signée sur 2 octets nommée resultat que vous initialisée à 0.
Votre setup contient une instruction d'assembleur en ligne étendue (dans votre deuxième exemple vous décrivez d’ailleurs la structure d’une telle instruction comme suit : asm “code” : output operand list : input operand list : clobber list) :

asm volatile (
    "ldi r18, 144 \n"
    "ldi r19, 3 \n"
    "mul r18, r19 \n"
    "sts (resultat), r0 \n"           // LSB
    "sts (resultat + 1), r1 \n"       // MSB
    "eor r1,r1 \n"                    // on remet r1 à 0
    :::"r18", "r19"
  );

Je décompose votre instruction comme suit :

  • vous placez directement le chiffe 144 dans le registre général 19 (r18) ;
  • vous placez directement le chiffe 3 dans le registre général 20 (r19) ;
  • vous multipliez le contenu des deux registres. On exécute toujours un opcode avec deux opérandes, de droite à gauche donc vous multipliez r19 par r18. Pour le résultat calculé par l’ALU, l’ATmega328p va stocker l’octet de poids faible dans r0 et celui de poids fort dans r1 ;
  • avec l’opcode "sts (resultat), r0 \n", little-endian oblige, vous stockez l'octet le moins significatif (de poids faible) du registre r0 dans la SRAM à l’adresse de la variable resultat ;
  • à l’adresse resultat + 1, vous placez l’octet de poids fort contenu dans r1 ;
  • à l’aide d’un OUEX (deux bits identiques donnent 0) de r1 sur lui même vous le remettez à zéro car comme vous l’expliquez si ce registre est modifié il faut le remettre à 0 ;
  • enfin l’instruction asm inline se termine par la clobber list qui informe le compilateur des registres utilisés.

Le deuxième code m'a demandé plus de recherche :

volatile uint16_t resultat = 0;
volatile uint8_t a = 144;
volatile uint8_t b = 3;

void setup() {
  Serial.begin(115200); Serial.println();

  // asm volatile(“code” : output operand list : input operand list : clobber list);

  asm volatile (
    "mul %0, %1 \n"
    "sts (resultat), r0 \n"           // LSB
    "sts (resultat + 1), r1 \n"       // MSB
    "eor r1,r1 \n"                    // on remet r1 à 0
    : 
    : "a" (a), "a" (b)
    :
  );

  Serial.print("resultat = "); Serial.println(resultat);
}

void loop(void) { }

Pour résumer, vous utilisez l’opcode "mul %0, %1 \n" :
où %0 et %1 représentent les emplacements de substitution pour les valeurs d'opérande a et b à partir des chaînes de contrainte "a" (je présume que "a" signifie que a et b doivent être placés dans dans un registre de la partie haute, celui que le compilateur choisira dans r16 à r23) . Enfin d’un point de vue chronologique, %0 et %1prennent les valeurs telles qu’elles apparaissent successivement dans le code : ici dans la liste des opérandes en entrée a puis b.
Effectivement plus besoin d’informer le compilateur des registres utilisés donc plus besoin de clobber list.

PS : Merci à @trimarco232 pour le document et à @dfgh pour son code.

Bonne soirée à tous.

1 Like

Tout bon

Bon rétablissement

Merci beaucoup.
Bonne soirée à vous.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.