Actuellement, l'architecture du YASEP définit des instructions de multiplication partielles. Initialement, l'idée était d'accéder un multiplieur multi-cycles au travers des Registres Spéciaux mais cela aurait rendu les changements de contextes trop complexes.
La méthode actuelle calcule un produit en assemblant les résultats de multiplications partielles. C'est un compromis entre la taille du code, la vitesse, la complexité matérielle et le multitasking. D'autres versions pourront utiliser des multiplieurs complets (au besoin, et si la technologie le permet) mais pour l'instant, le système réutilise l'additionneur 16 bits comme étage final, après deux additionneurs partiels de 12 bits, chacun additionnant les résultats sur 8 bits de quatre tables de multiplications 4×4 bits.
Cette méthode ajoute dynamiquement un étage au pipeline, chaque fois qu'une instruction MUL8L ou MUL8H est exécutée. En conséquence, une "bulle" d'un cycle est insérée dans le pipeline lorsque l'instruction suivante n'est pas une multiplication. Morale : il faut grouper les MULs.
Il y a 2 instructions de multiplication : MUL8L et MUL8H. Elles sont identiques mais MUL8H prend l'octet de poids fort de l'opérande SND, au lieu de l'octet de poids faible pour MUL8L. Cela simplifie un peu les séquences de code, en économisant quelques instructions de décalage ou rotation. (voir l'exemple R16×R16 plus bas).
Seules les multiplications non signées sont supportées pour l'instant. Les multiplicandes signés doivent être ajustés manuellement avant et après les séquences d'instructions.
; R1(8 bits) × R2(8 bits) => R2(16 bits) MUL8L R1 R2 ; 2 octets, instruction courte ; R1(8 bits) × R2(8 bits) => R3(16 bits) MUL8L R1 R2 R3 ; 4 octets, instruction longue
; R1(8 bits) × Imm16(8 bits) => R2(16 bits) MUL8L 123 R1 R2 ; 4 octets ; R1(8 bits) × Imm16(8 bits) => R1(16 bits) MUL8L 123 R1 ; est un alias vers : MUL8L 123 R1 R1 ; 4 octets aussi ; R1(8 bits) × Imm4(4 bits) => R1(12 bits) MUL8L 12 R1 ; 2 octets
YASEP16 : 6 instructions
; R1(8 bits) × R2(16 bits) => R3(16 LSB)-R4(8 MSB)
; R5 = temporaire
; moitié haute
MUL8H R1 R2 R3
SHR 8 R3 R4 ; répartit le résultat entre R3 et R4
SHL 8 R3
; moitié basse
MUL8L R1 R2 R5
ADD R5 R3
ADD 1 R4 R4 carry
; R1(8 bits) × Imm16 => R3(16 LSB)-R4(8 MSB)
; R2 = temporaire
; Imm16=1234h dans ces exemple
; moitié haute
MUL8L 12h R1 R3 ; 12h = 1234h>>8
SHR 8 R3 R4 ; répartit le résultat entre R3 et R4
SHL 8 R3
; moitié basse
MUL8L 34h R1 R2 ; 34h = 1234h & 0xFF
ADD R2 R3
ADD 1 R4 R4 carry
; (8 bits) × R2(16 bits) => R3(16 LSB)-R4(8 MSB)
; R5 = temporaire
; Imm8 = 12h dans cet exemple
; moitié haute
MUL8H 12h R2 R3
SHR 8 R3 R4 ; répartit le résultat entre R3 et R4
SHL 8 R3
; moitié basse
MUL8L 12h R2 R5
ADD R5 R3
ADD 1 R4 R4 carry
YASEP16 : 13 instructions
; R1 × R2 => R3-R4 (R5=scratch)
; R2 peut rester modifié (rotation) à la fin de cette séquence.
; les 2 octets du milieu sont calculés ensemble
MUL8H R1 R2 R3
MUL8H R2 R1 R4 ; observez l'échange des opérandes
ADD R4 R3 ; on réutilise la retenue plus tard
SHR 8 R3 R4 ; ajuste entre R3 et R4
SHL 8 R3
MOV 100h R5 ; (retenue spéculative)
OR R4 R5 R4 carry ; et met la retenue dans R4
; octet de poids faible
MUL8L R2 R1 R5
ADD R5 R3
ADD 1 R4 R4 carry
; octet de poids fort
ROL 8 R2
MUL8H R2 R1 R5
ADD R5 R4
; éventuellement :
ROR 8 R2
La table peut être réalisée avec des portes logiques ou une matrice de diodes spécialiée. Cependant certaines réalisations en FPGA peuvent utiliser des petits blocs de SRAM, en particulier sur cible Actel. Pour réaliser une multiplication de 8×8 bits, il faut disposer de 4 blocs de 4×4=8 bits de résultat. Deux blocs "dual port" de SRAM de 256 octets sont nécessaires. Les 4 tables peuvent être initialisées avec des valeurs identiques donc il n'y a que 256 valeurs à stocker et écrire. C'est réalisé par le code suivant :
; Code d'initialisation de la table de multiplication :
; R1 : SND (première adresse + compteur de boucle externe)
; R2 : SI4 (deuxième adresse (octet bas) + valeur initiale (octet haut))
; R3 : accumulator : incrément de la deuxième adresse + incrément initial (octet haut)
; R4 : compteur de boucle interne, encodée par position de bit (1-hot)
; A1 : adresse de la boucle externe
; A2 : adresse de la boucle interne
mov 0 R1
mov 11h R3
mov 1 R4
add 4 PC A1
; boucle externe, 16 itérations :
mov 0 R2
add 4 PC A2
; boucle interne, 16 itérations :
ADD R3 R2
MULI R2 R1
ror 1 R4
mov A2 PC LSB0 R4
add 100h R3
add 1011h R1
mov A1 PC no_carry