2010-10-04 4 views
4

J'essaie d'apprendre l'assemblage de MSNA, mais il semble que je me bats avec ce qui semble être simplement dans des langages de haut niveau.Impression de chiffres hexadécimaux avec assemblage

Tous les manuels que j'utilise discutent en utilisant des chaînes - en fait, cela semble être l'une de leurs choses préférées. Cependant, j'essaie de comprendre comment incrémenter et imprimer les chiffres hexadécimaux dans l'assemblage de MSNA et je ne sais pas comment procéder. Par exemple, si je veux imprimer # 1 - n en hexadécimal, comment le ferais-je sans l'utilisation de bibliothèques C (que toutes les références que j'ai pu trouver à utiliser)?

Mon idée principale serait d'avoir une variable dans la section .data que je continuerais à incrémenter. Mais comment puis-je extraire la valeur hexadécimale de cet emplacement? Je semble avoir besoin de le convertir en une ficelle d'abord ...?

Un conseil ou un code d'échantillon serait apprécié.

Répondre

6

D'abord écrire une routine simple qui prend une valeur de nybble (0..15) comme entrée et sort un caractère hexadécimal ('0' .. '9', 'A' .. 'F').

Ensuite, écrivez une routine qui prend une valeur d'octet en entrée et appelle ensuite la routine ci-dessus deux fois pour sortir deux caractères hexadécimaux, c'est-à-dire un pour chaque chiffon. Enfin, pour un entier de N octets, vous avez besoin d'une routine qui appelle cette deuxième routine N fois, une fois pour chaque octet.

Vous pourriez trouver utile d'exprimer cela en pseudo-code ou un HLL tel que C d'abord, puis réfléchir à la façon de traduire ceci en asm, par ex.

void print_nybble(uint8_t n) 
{ 
    if (n < 10) // handle '0' .. '9' 
     putchar(n + '0'); 
    else // handle 'A'..'F' 
     putchar(n - 10 + 'A'); 
} 

void print_byte(uint8_t n) 
{ 
    print_nybble(n >> 4); // print hi nybble 
    print_nybble(n & 15); // print lo nybble 
} 

print_int16(uint16_t n) 
{ 
    print_byte(n >> 8); // print hi byte 
    print_byte(n & 255); // print lo byte 
} 
+0

le code C donné ci-dessus produit "(" quand on lui donne une valeur supérieure à 0x80 (c.-à-d. si donné 0x8F il donne "(F") –

+0

@AdrianZhang: [Ca marche pour moi] (https://ideone.com/GXg0mH) - as-tu changé quelque chose? Pouvez-vous fournir un [mcve] qui montre le problème? –

+0

Peu importe, il semble que le passage d'un 'uint8_t' à un' char' produit un mauvais résultat. C'est drôle comme c'est arrivé. –

1

Est-ce une tâche à faire?

Les bits sont des bits. Bit, Byte, mot, double mot, ce sont des termes matériels, quelque chose d'ensembles d'instructions/assembleur va faire référence. hex, décimal, octal, non signé, signé, chaîne, caractère, etc sont des manifestations de langages de programmation. De même .text, .bss, .data, etc sont aussi des manifestations d'outils logiciels, l'ensemble d'instructions ne se soucie pas d'une adresse étant .data et l'autre étant .text, c'est la même instruction de toute façon. Il y a des raisons pour lesquelles toutes ces choses de langage de programmation existent, de très bonnes raisons parfois, mais ne soyez pas confus en essayant de résoudre ce problème. Pour convertir des bits en ascii lisible par l'homme, vous devez d'abord connaître votre table ascii et les opérateurs bit à bit, et, ou, décalage logique, décalage arithmétique, etc. Plus charger et stocker et d'autres choses. Pensez mathématiquement ce qu'il faut pour passer d'un certain nombre dans un registre/mémoire à un hexagone ASCII. Say 0x1234 qui est 0b0001001000110100. Pour qu'un humain puisse le lire, oui vous devez l'insérer dans une chaîne faute de meilleur terme, mais vous n'avez pas nécessairement besoin de stocker quatre caractères plus un zéro dans les emplacements mémoire adjacents pour faire quelque chose avec. Cela dépend de votre fonction de sortie. Normalement, les entités de sortie basées sur des caractères se réduisent à un seul output_char() de quelque sorte appelé plusieurs fois.

Vous pouvez convertir en une chaîne mais c'est plus de travail, pour chaque caractère ASCII que vous calculez, vous appelez une sorte de fonction de sortie basée sur un seul caractère. putchar() est un exemple de fonction de type caractère de sortie octet.

Donc pour les binaires, vous voulez examiner un bit à la fois et créer un 0x30 ou 0x31. Pour octal, 3 bits à la fois et créer 0x30 à 0x37. Hex est basé sur 4 bits à la fois.Hex a le problème que les 16 caractères que nous voulons utiliser ne sont pas trouvés adjacents dans la table ASCII. Donc vous utilisez 0x30 à 0x39 pour 0 à 9 mais 0x41 à 0x46 ou 0x61 à 0x66 pour A à F selon vos préférences ou vos besoins. Donc, pour chaque chiffrement vous pourriez ET avec 0xF, comparer avec 9 et ADD 0x30 ou 0x37 (10 + 0x37 = 0x41, 11 + 0x37 = 0x42, etc).

Conversion de bits dans un registre en une représentation ascii de binaire. Si le bit dans la mémoire était un 1 montrer un 1 (0x31 ascii) du bit était un 0 montrer un 0 (0x30 en ASCII).

 
void showbin (unsigned char x) 
{ 
    unsigned char ra; 

    for(ra=0x80;ra;ra>>=1) 
    { 
     if(ra&x) output_char(0x31); else output_char(0x30); 
    } 
} 

Il peut sembler logique d'utiliser unsigned char ci-dessus, mais unsigned int, en fonction du processeur cible, pourrait produire un code beaucoup mieux (nettoyeur/plus rapide). mais qui est un autre sujet

ci-dessus pourrait ressembler pourrait ressembler à ceci en assembleur (intentionnellement pas en utilisant x86)

 
... 
mov r4,r0 
mov r5,#0x80 
top: 
tst r4,r5 
moveq r0,#0x30 
movne r0,#0x31 
bl output_char 
mov r5,r5, lsr #1 
cmp r5,#0 
bne top 
... 

Déroulé est plus facile d'écrire et va être un peu plus vite, le compromis est plus la mémoire utilisée

 
... 
tst r4, #0x80 
moveq r0, #0x30 
movne r0, #0x31 
bl output_char 
tst r4, #0x40 
moveq r0, #0x30 
movne r0, #0x31 
bl output_char 
tst r4, #0x20 
moveq r0, #0x30 
movne r0, #0x31 
bl output_char 
... 

Dites que vous avez eu 9 numéros de bits et je voulais convertir en octal. Prenez trois bits à la fois (rappelez-vous que les humains lisent de gauche à droite, donc commencez avec les bits supérieurs) et ajoutez 0x30 pour obtenir 0x30 à 0x37.

 
... 
mov r4,r0 
mov r0,r4,lsr #6 
and r0,r0,#0x7 
add r0,r0,#0x30 
bl output_char 
mov r0,r4,lsr #3 
and r0,r0,#0x7 
add r0,r0,#0x30 
bl output_char 
and r0,r4,#0x7 
add r0,r0,#0x30 
bl output_char 
... 

Un seul octet (8 bits) en hexadécimal pourrait ressembler à:

 
... 
mov r4,r0 
mov r0,r4,lsr #4 
and r0,r0,#0xF 
cmp r0,#9 
addhi r0,r0,#0x37 
addls r0,r0,#0x30 
bl output_character 
and r0,r4,#0xF 
cmp r0,#9 
addhi r0,r0,#0x37 
addls r0,r0,#0x30 
bl output_character 
... 

faisant une boucle de 1 à N mémoriser cette valeur dans la mémoire et la lecture de la mémoire (.data), la sortie en mode hexadécimal:

 
... 
mov r4,#1 
str r4,my_variable 
... 
top: 
ldr r4,my_variable 
mov r0,r4,lsr #4 
and r0,r0,#0xF 
cmp r0,#9 
addhi r0,r0,#0x37 
addls r0,r0,#0x30 
bl output_character 
and r0,r4,#0xF 
cmp r0,#9 
addhi r0,r0,#0x37 
addls r0,r0,#0x30 
bl output_character 
... 
ldr r4,my_variable 
add r4,r4,#1 
str r4,my_variable 
cmp r4,#7 ;say N is 7 
bne top 
... 
my_variable .word 0 

L'enregistrement dans le RAM est un gaspillage si vous avez suffisamment de registres. Bien qu'avec x86 vous pouvez opérer directement sur la mémoire et ne pas devoir passer par des registres.

x86 n'est pas la même que l'assembleur ci-dessus (ARM) donc il est laissé comme un exercice du lecteur pour élaborer l'équivalent. Le fait est que c'est le déplacement, l'alignement et l'ajout de cette matière, la décomposer en étapes élémentaires et les instructions tombent naturellement à partir de là.

1

GAZ rapide et sale macro

.altmacro 

/* 
Convert a byte to hex ASCII value. 
c: r/m8 byte to be converted 
Output: two ASCII characters, is stored in `al:bl` 
*/ 
.macro HEX c 
    mov \c, %al 
    mov \c, %bl 
    shr $4, %al 
    HEX_NIBBLE al 
    and $0x0F, %bl 
    HEX_NIBBLE bl 
.endm 

/* 
Convert the low nibble of a r8 reg to ASCII of 8-bit in-place. 
reg: r8 to be converted 
Output: stored in reg itself. 
*/ 
.macro HEX_NIBBLE reg 
    LOCAL letter, end 
    cmp $10, %\reg 
    jae letter 
    /* 0x30 == '0' */ 
    add $0x30, %\reg 
    jmp end 
letter: 
    /* 0x57 == 'A' - 10 */ 
    add $0x57, %\reg 
end: 
.endm 

Utilisation:

mov $1A, %al 
HEX <%al> 

<> sont utilisés en raison de .altmacro: Gas altmacro macro with a percent sign in a default parameter fails with "% operator needs absolute expression"

Résultat:

  • %al contient 0x31, qui est '1' en ASCII
  • %bl contient 0x41, qui est 'A' en ASCII

Maintenant, vous pouvez faire ce que vous voulez avec %al et %bl, par exemple:

  • boucle sur plusieurs octets et les copie dans la mémoire (assurez-vous d'allouer deux fois plus de mémoire qu'il y a d'octets)
  • print les avec des appels système ou BIOS