-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathx86.language.txt
1389 lines (1222 loc) · 86.5 KB
/
x86.language.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
┏━━━━━━━━━━━━━━━━━━┓
┃ ASM_SYNTHESE ┃
┗━━━━━━━━━━━━━━━━━━┛
Différences :
- syntaxe générale, entre Intel et AT&T :
- $1 et $2 inversés
- % devant les R et S, $ devant les I, rien devant les M ([I] uniquement), *% devant les [R] et [S].
- pas toujours mêmes noms d'opcode
- pas de TYPE, mais des opcodes suivis de q, l, w ou b pour indiquer la taille de l'opérande : movq, movl, movw, movb
- TYPE [ R1 + R2 * I + ADR ] -> M(R1,R2,I)
- syntaxe d'un assembleur particulier :
- exemple : différences entre GAS et NASM :
- commentaires avec #, // et /* */
- directives commencent par un point :
- global -> .globl
- section -> .section
- V dLETTRE ... -> V:
.LETTRE ... (et LETTRE = byte, int, long et non b,w,l)
- [ADR] -> (ADR)
________________________________________________________________________________
Insensible à la casse (sauf "STRING")
________________________________________________________________________________
SIGNEDNESS
Une I_BASE est considérée comme :
- unsigned par défaut, sauf si la signedness est explicitée par :
- l'utilisation de - : -45, -0x0d
- tous ses most-significant bits sont écrits, avec le MSB étant 1
On doit utiliser une valeur en fonction de sa signedness, avec des instructions pour signed numbers, ou pour unsigned. Toutefois, certaines instructions (par exemple add) donnent des résultats corrects à la fois si l'on considère l'opérande comme signed ou unsigned.
Néanmoins, il faut y prendre garde, et se souvenir de la signedness des valeurs.
________________________________________________________________________________
GENERAL REGISTERS
+------------------+
| eax (32) |
+--------+---------+
| | ax (16) |
| Inaces.+----+----+
| | ah | al |
+--------+----+----+
General Purpose
Même chose pour ebx/bx/bh/bl, ecx/cx/ch/cl et edx/dx/dh/dl
Par conséquent, ax = (ah * 256) + al
Seul eax existe matériellement, ax, ah et al n'en sont que des sous-ensembles logiques (modifier eax les modifie donc aussi, et inversement), mais on peut les utiliser indépendamment d'eax.
________________________________________________________________________________
POINTERS
+------------------+
| esi (32) |
+--------+---------+
| Inaces.| si (16) |
+--------+---------+
Pointeur, ou general purpose
Même chose pour edi/di.
ebp : 32 bits, base pointer (ou frame pointer), utilisé pour accéder aux variables locales et arguments
esp : 32 bits, stack pointer, utilisé par push et pop
eip : 32 bits, instruction pointer. Avancé à chaque instruction. Instruction courante -> cs:eip
________________________________________________________________________________
SEGMENTS REGISTRES
cs : 16 bits, code segment
ds : 16 bits, data segment
ss : 16 bits, stack segment
es : 16 bits, extra segment
fs : même chose
gs : même chose
________________________________________________________________________________
DEBUG REGISTERS
dr0 à dr7 (32 bits chacun) :
- dr0 à dr3 : 4 registres sauvegardant chacun l'adresse linéaire d'un breakpoint à checker. Les conditions et la nature (data ou instruction) du breakpoint dépendent de dr7. La debug exception lancée est 0x01.
- dr4 et dr5 : pas utilisés
- dr6 : debug status register :
- bit 0 à 3 : activé pour indiquer le/les numéro du breakpoint ayant causé une debug exception
- bit 13 : BD, activé si la prochaine instruction accédera un registre dr*, alors que ICE-386 est en cours d'exécution ???
- bit 14 : BS, activé si la debug exception est due au Trap Flag (TF)
- bit 15 : BT, activé si la debug exception est due au switch vers une task ayant un T-bit activé.
- reste : pas utilisé
- dr7 :
- bit 0 : Si 0, breakpoint 0 désactivé pour la task courante
- bit 1 : Si 0, breakpoint 0 désactivé pour toutes les tasks
- bit 2 et 3 : même chose pour breakpoint 1
- bit 4 et 5 : même chose pour breakpoint 2
- bit 6 et 7 : même chose pour breakpoint 3
- bit 10 à 15 : pas utilisé
- bit 16 et 17 : fixe la condition du breakpoint 0 :
- 0 : exécution d'une instruction à cet endroit (taille du breakpoint doit alors être 0)
- 1 : écriture d'une data à cet endroit
- 3 : écriture ou lecture d'une data à cet endroit
- bit 18 et 19 : fixe la taille du breakpoint 0 : 0 (1 octet), 1 (2 octets) ou 3 (4 octets). Le breakpoint doit etre aligné en conséquence.
- bit 20 à 23 : même chose pour breakpoint 1
- bit 24 à 27 : même chose pour breakpoint 2
- bit 28 à 31 : même chose pour breakpoint 3
dr* ne sont pas changés par un changement de task, sauf les bits 0, 2, 4 et 8 de dr7.
les registres dr* ne sont jamais cleared par le CPU : il faut le faire soi-même.
Manipulés avec mov
________________________________________________________________________________
CONTROL REGISTERS
cr0, cr1, cr2, cr3, cr4 (32 bits chacun).
cr0 (32 bits, comme cr1, cr2 et cr3), "machine status word" :
- bit 0 : PE-bit, Si 0, real mode ; sinon, protected mode
- bit 1 : MP-bit, Si 0, x87 absent ; sinon, présent
- bit 2 : EM-bit, Si 1, x87 est émulé de manière logiciel : les instructions x87 invoqueront donc à chaque fois l'exception 0x07
- bit 3 : TS-bit, activé après chaque task-switch. Utilisé pour sauvegarder les états de x87, mmx et sse entre tasks.
- bit 4 : ET-bit, Si 0, x87 agit comme au temps du 80287
- bit 5 : NE-bit, "numeric error", relatif aux erreurs du FPU
- bit 16 : WP-bit, "Write protect", si 0, kernel mode peut écrire sur un read-only user-mode segment/page
- bit 18 : AM-bit, si 1, et que AC flag d'eflag est 1, alignement checking : exception si pb d'alignement
- bit 29 : NW-bit, Si 1 et que CD == 1, "memory coherency of caches is not maintained"
- bit 30 : CD-bit, Si 0, Caching enabled
- bit 31 : PG-bit, Si 0, Paging enabled ; sinon, pas de paging
- Autres : réservés
- manipulé avec lmsw, smsw ou mov
cr1 : pas utilisé
cr2 :
- prend l'adresse linéaire ayant provoqué un Page Fault
cr3 /PDBR :
- bit 3 : PWT-bit, si 1, write-back caching des pages
- bit 4 : PCD-bit, si 1, caching des pages
- bits 12 à 31 : page directory base adress
- Autres : réservés
cr4 :
- bit 0 : VME-bit, si 1, Virtual Mode 8086 activé
- bit 1 : PVI-bit
- bit 2 : TSD-bit, si 0, rdtsc peut seulement être exécuté en kernel mode
- bit 3 : DE-bit, si 1, référence vers dr4 ou dr5 provoque exception
- bit 4 : PSE-bit, si 1, pages font 4Mo
- bit 5 : PAE-bit, si 1, PAE enabled
- bit 6 : MCE-bit
- bit 7 : PGE-bit, si 1, Global pages, avec le G-bit, possibles
- bit 8 : PCE-bit, si 0, rdpmc peut seulement être exécuté en kernel mode
- bit 9 : OSFXSR-bit, si 1, fxsave et fxrstor enabled
- bit 10 : OSXMMEXCPT-bit
- Autres : réservés
clts clear le TS-bit : cr0[3]
________________________________________________________________________________
MMU REGISTERS
gdtr, ldtr, idtr et tr.
________________________________________________________________________________
AUTRES REGISTRES
Test registers (tr*) :
- tr3 à tr7
- tr6 : test command register, tr7 : test data register
- manipulés avec mov, en kernel-mode
- remplacés après 486 par les MSR
Model specific registers (msr) :
- max 64 bits
- registres spécifiques à une microarchitecture donnée
- plusieurs buts possibles : performance monitoring, debugging, etc.
- manipulés avec rdmsr, wrmsr
- exemples connus :
- tsc (Timestamp Counter) : 64-bits, enregistre nombre de cycle clocks depuis démarrage du CPU actuel
- pmc (Performance Monitor Counter) : 40 bits. Plusieurs possibles pour un seul CPU.
- Machine-check Register : checke des problèmes hardware
________________________________________________________________________________
EFLAGS
eflags : 32 bits, ensemble de 32 flags d'1 bits, dont certains ne sont pas utilisés (alors indiqués par 1 ou 0). Attention, les 32 bits sont inversés tous ensemble, et non octet par octet, en little-endian. Vu en little-endian :
0 0 0 0 0 0 0 0
0 0 ID VIP VIF AC VM RF
0 NT IOPL OF DF IF TF
SF ZF 0 AF 0 PF 1 CF
- 1er octet :
- Carry flag (CF)
- 1
- Parity flag (PF) : 1 si dernier résultat d'une instruction arithmétique est pair. Utilisé par jumps et compagnie.
- 0
- Adjust flag (AF) : utilisé pour l'arithmétique des BCD
- 0
- Zero flag (ZF) : 1 si dernier résultat d'une instruction arithmétique a été == 0. Utilisé par jumps et compagnie.
- Sign flag (SF) : 1 si dernier résultat d'ue instruction arithmétique est négatif. Utilisé par jumps et compagnie.
- 2ème octet :
- Signel-Step Trap flag (TF) : si activé, après la prochaine instruction, l'interruption 1 est lancée et TF est cleared.
- Interrupt enabled flag (IF) : si désactivé, impossibilité de lancer des maskable (non-maskable n'étant pas affectées par IF) IRQ considérées comme privilégiées. Modifié par cli, sti et popf.
- Direction flag (DF) : si activé, l'adresse d'un V indiqueront non pas le début mais la fin des data, et des instructions comme mov le lieront donc depuis la fin vers le début. Utilisé en cas d'overlaping. Modifié par std et cld. Seulement pour les arrays ? (dont string)
- Overflow flag (OF)
- I/O Privilege, 2 bits (IOPL) : ring level minimal pour pouvoir modifier IF (sinon lance une exception). En général, kernel mode est 0, user mode est 3, et IOPL est toujours à 0. IOPL lui-même ne peut être modifié qu'en kernel mode.
- Nested task flag (NT) : indique que l'on est dans une nested interruption, qui retournera vers la task qu'elle a interrompue via iret.
- 0
- 3ème octet :
- Resume flag (RF) : si activé, ignore les debug faults via breakpoints dr*. Automatiquement activé après ces derniers : permetter de progresser après ce breakpoint. Cleared par toute autre instruction.
- Virtual mode flag (VM) : V86 mode enabled
- Alignment check (AC) : cf AM-flag
- Virtual interrupt flag (VIF)
- Virtual interrupt pending (VIP)
- Identification (ID) : instruction cpuid est supportée
- 0
- 4ème octet : que des 0
________________________________________________________________________________
FLAGS
Les flags :
- ne génèrent pas d'exceptions
- sont sticky
- sont modifiés par plusieurs instructions
A savoir :
- inc, dec : ne génèrent jamais CF
- mul, imul : génèrent CF et OF en cas d'overflow sur $1 (et non sur le R accueillant le résultat)
- shl, shr, sar, sal : CF si 1 est dégagé par shift (hors MSB avec sar)
- rol, ror, rcl, rcr : comme shl, etc. pour CF. De plus, ancien CF est réutilisé comme input. OF si MSB change.
- div, idiv : ne modifie aucun flag, mais génère #DE si overflow
Instructions dépendant d'un flag :
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| OF | SF | ZF | PF | CF | AF | TF | IF | DF | NT | RF |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| into | | loope, | | sbb, adc | aaa, aas | | |scas*, ins| iret | |
| | | loopne | | rcl, rcr | | | |cmps*,lods| | |
| | | | +----------+----------+ | |movs, outs| | |
| | | | | daa, das | | |stos | | |
+----------+----------+----------+----------+----------+ | | | | | |
| j*, set*, cmov* | | | | | | |
+------------------------------------------------------+----------+----------+----------+----------+----------+----------+
Instructions modifiant (de manière définie ou indéterminée) un flag :
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| OF | CF | ZF | PF | AF | SF | NT | TF | IF | DF | RF |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| | clc, cmc,| arpl, | | | | int, into | cli | cld, std | |
| | stc | cmpxchg8b| | | | | | | |
+----------+----------+ lar, lsl,| | | | | | | |
| rcl, rcr, rol, ror |verr, verw| | | | | | | |
| +----------+----------+----------+ | | | | | |
| | fcom*, fucom* | | | | | | |
| +--------------------------------+----------+----------+ | | | |
| | sahf | | | | |
+----------+------------------------------------------------------+ | | | |
| dec (pas CF), inc (pas CF), cmps, aaa, aas, aad, add, and, bsf, | | | | |
| bsr, bt*, cmp, cmpxchg, comiss, div, idiv, imul, ucomsid, mov, | | | | |
| mul, neg, or, sal, sar, shl, shr, shld, sub, test, xadd, xor, | | | | |
| sbb, adc, scas*, daa, das | | | | |
+-----------------------------------------------------------------+---------------------+----------+----------+ |
| iret (pas NT), popf | |
+-------------------------------------------------------------------------------------------------------------+----------+
| rsm |
+------------------------------------------------------------------------------------------------------------------------+
________________________________________________________________________________
INSTRUCTION
Deux composants, dans l'ordre : opcode (octet représentant une instruction) + opérande
- opcode : représente la "commande" (1 ou plusieurs octets sous forme machine, mot sous forme ASM). Exemples : mov, nop, int.
- opérande : argument de l'opcode. Nombre (0, 1, 2 ou 3), type et taille des arguments possibles dépendent de l'opcode. Il s'agira de R, S, I ou M
- instruction prefix : rajoutée devant un opcode pour modifier ce dernier (tout ce qui a comme $1 un opcode)
TYPES :
1) Registre :
a) R : registre (dont esi, edi, ebp et esp), non déréférencé. Le registre peut contenir une VAL ou une ADR, renvoyés par R. Ne sont cependant pas compris :
- eip et eflags
- les registres désignés par S, D et C.
b) S : registre de segment : cs, ds, es, ss, fs, gs.
c) D : registre dr* ou cr*
2) I : valeur constante de maximum 4 octets (contenues dans le code segment, non le data segment), non déréférencé. Composé d'une forme simple I_BASE sous forme :
- NOMBRE (décimal)
- 0NOMBRE ou NOMBREo (octale)
- 0xNOMBRE, NOMBREh (si NOMBRE commence par un chiffre) ou 0NOMBREh (sinon) (hexa).
- NOMBREb (binaire)
- "CHAR" ou 'CHAR' (valeur ASCII). Séquence d'échappement backslash impossible. Tout est échappé, sauf " ou ', qu'il faut écrire sous forme hexa.
- "STRING" ou 'STRING' (concaténation (avec endianness) de la valeur ASCII de chaque lettre).
- V
I_BASE peut ensuite être associé à des opérations (avec éventuellement d'autres I_BASE) :
- +, -, *, /
- binaire ^, &, |, <<, >> et ~
- -I_BASE (complément de 2)
L'assembleur inscript une I dans le code segment, pas dans le data segment.
A) V : symbole. Sous-genre de I. Tout V peut donc être utilisé comme un I, mais tout I ne peut pas être utilisé comme un V. Peut :
a) contenir une adresse (par exemple avec "res" ou "d"). La valeur n'est connue que lors du loadtime, elle est remplacée par son symbole en attendant.
1) L : label (entrée de fonction) est un V contenant une adresse.
b) contenir un I (par exemple avec "equ"). La valeur est connue compile-time : elle est donc substitué directement lors de l'assemblage, mais le symbole demeure disponible dans la table des symboles, ou pour un linking externe.
3) M : R ou I, mais déréférencé : [R] ou [I]. De plus
- Lors d'un déréférencement, il est possible d'utiliser +, - et * :
- [S:RMI + Rd + Rd * Ib + I]
- seul RMI est nécessaire, les autres sont facultatifs :
- Premier R : base register
- Deuxième R : index register (ne peut pas être esp)
- Ib : scaling factor (1, 2, 4 ou 8)
- I : displacement
- les Rd et Id sont par défaut 0, Ib est par défaut 1
- la valeur par défaut de S dépend de RMI :
- R (sauf esp, ebp, esi, edi), eip : cs
- M, I, esi : ds
- edi : es
- esp, ebp : ss
AUTRES :
a) A : désigne un I, mais contenant une ADR (L, V, et tout I contenant une ADR). Ne peut être que w (sauf si 64 bits), d ou q (si 64 bits).
b) ADR : désigne un RMI, mais contenant une ADR
Write et read :
- R et S -> write/read
- I -> read only
Déférencement (M) :
- R, S et I (hors V) peuvent contenir une VAL ou une ADR.
- V contient toujours une ADR.
Dans le cas d'une R, S ou I (hors V) contenant une ADR, ou dans le cas d'une V :
- M renvoie non pas ADR, mais *ADR.
- ADR doit alors être une adresse 32 bits. On ne donc par exemple pas faire [ax].
- M, pour pouvoir être écrite doit préciser sa taille soit :
- implicitement, sila deuxième opérande est un R
- explicitement, en étant précédé de TYPE. TYPE est la taille du contenu déréférencé : "byte" (1), "word" (2), "dword" (4), "qword" (8) ou "tword" (10).
________________________________________________________________________________
nasm -f elf SOURCE.asm #Crée un fichier objet ELF SOURCE.o. Il peut être
#exécuté tel quel ? ou bien lancé par un driver en C.
-l FICHIER #Option de nasm qui crée un FICHIER montrant l'assemblage
#de chaque commande ASM. Les parties ne pouvant pas encore
#être sues (les M, car les adresses sont crées run-time)
#sont entre [ ]. De plus l'endianness est respectée :
#attention donc.
________________________________________________________________________________
;COMMENTAIRES
FONC: #Les LABELS sont utilisés comme nom de fonction.
#Le main doit commencer par un _ : semble avoir affaire
#avec l'interfaçage avec C...
section .MOT
segment .MOT #Commence une section MOT
Sections possibles :
- .data : Déclaration de variables, avec initialisation :
- directives
- déclarations
- .bss : Déclaration de variables, sans initialisation :
- directives (sans initialisation)
- déclarations (sans initialisation)
- .text : Code :
- instructions
On peut définir plusieurs sections .data, .text, etc. : elles seront concaténées par l'assembleur avant assemblage. Cela permet par exemple de séparer syntaxiquement (dans un seul but de clarté) les sections .data, etc. de plusieurs fonctions.
________________________________________________________________________________
LINKING
global V[, V]... #Fait que les V désignés peuvent être utilisées par
#d'autres fichiers liés par le linker (linking externe).
#Il s'agit de la déclaration de ces V, non de leur
#initialisation : il faut donc mettre ceci sur une ligne
#séparée (vraiment n'importe où), puis à nouveau déclarer
#un label L:, ou une variable V dLETTRE I[, I]... sur une
#autre ligne.
#Pour pouvoir profiter de ce linking externe, les autres
#fichiers souhaitant utiliser ces V, doivent :
#En ASM :
# - déclarer ces V sur une ligne séparée, exactement
# comme avec "global", mais avec le mot "extern".
#En C :
# - le L d'un fichier ASM devant être utilisé dans un
# fichier C lié, comme fonction, doit être déclaré
# avant :
# - TYPE NOM_LABEL(void);
# TYPE peut être tout TYPE pour interpréter la return
# value (qui peut être un exit code)
# - Pour utiliser un V (hors L) comme une variable, le
# déclarer d'abord sous la forme TYPE NOM_V, puis :
# - la variable V en C sera déréférencé automatiquement
# une fois de plus que le TYPE désigné.
# Ainsi, il est nécessaire de faire &NOM_V en C si le
# TYPE est char*, ou que V ne contenait pas une
# adresse mais un I sans adresse ("equ").
#Ainsi, la fonction principale d'un fichier ASM utilisée par
#un driver en C doit toujours être global.
#Les V ne sont pas par défaut global (contrairement à C).
________________________________________________________________________________
APPELER DE L'ASM DANS DU C
Solution 1) : écrire l'ASM "inline" dans le fichier C, grâce à une macro comprise par le compilateur (laquelle ?), cependant :
- c'est spécifique alors au compilateur
- il faut suivre la syntaxe ASM de ce compilateur.
Solution 2) (mieux) :
Lier le fichier C avec une version assemblée (.o) du fichier ASM, puis utiliser la fonction/data dans le fichier C, après l'avoir déclarée. Attention :
- la fonction/data doit être déclarée global dans le fichier ASM
- son type/celui de sa return value dépend du TYPE de sa déclaration dans le fichier C.
- son nom est celui dans le fichier ASM (nom du LABEL pour une fonction)
- gcc ne le fait pas, mais les autres compilateurs (et assembleur aussi ?) changent le nom des fonctions C (et des symboles ASM ?) en rajoutant un _ devant. Ainsi, il semble nécessaire pour ceux-là de rajouter un _ devant une fonction ASM devant être utilisée en C.
De plus les "conventions C" doivent être respectées, notamment :
- le fichier C doit push les arguments (en reverse order) avant d'appeler la routine ASM
- reverse order : ainsi il est possible d'avoir un nombre d'arguments variable, du moment que les premiers indiquent le nombre d'arguments qui suit (par exemple le nombre de % dans printf())
- le fichier C doit pop les arguments ensuite
- la return value de la routine ASM doit être placé dans le regitre approprié (cf section sur le Stack).
- C assume que toute fonction (donc une routine ASM) ne modifie pas ebx, esi, edi, ebp, cs, ds, ss et es.
- Il faut donc, dans la routine ASM, push les registres parmi ceux-là qui vont être modifiés
- puis les pop à la fin de la routine ASM.
- En général, cs, ds, ss, es, esi et edi ne sont pas modifiés, mais ebp oui (mais est pushed/popped via la convention C), et ebx souvent. Or ebx est utilisé par les GOT (Global Offset Table).
- A noter que par conséquent une routine ASM/fonction C peut modifier ecx et edx, et modifie en général eax.
Cependant, ces conventions dépendent des compilers notamment (par exemple Open Watcom utilise une autre convention). Donc on peut spécifier la convention à utiliser pour une routine ASM donnée dans un fichier C, grâce à des macros, mais ces macros sont spécifiques aux compilers. Pour gcc :
- TYPE FUNC(ARGS) __attribute__ (( CONVENTION ));
Pour MSVC et Borland :
- TYPE __CONVENTION FUNC(ARGS).
La convention (si pas spécifié par une macro) pour les trois est "cdecl" (convention C). Il y a aussi :
- "stdcall" : convention Pascal (utilisé par Win32 API), le callee doit enlever lui-même les paramètres via "ret NOMBRE" (pop NOMBRE / 4 éléments)
- "regparm(NOMBRE)" : gcc uniquement. Les NOMBRE (0 à 3) premiers arguments ne sont pas passés sur le stack, mais via des registres : d'abord eax, puis edx, puis ecx. Est plus rapide.
________________________________________________________________________________
APPELER DU C DANS DE L'ASM
Dans le code ASM, pour une fonction C "FUNC" donnée :
- faire un extern FUNC
- appeler la fonction avec call FUNC
- les considérations des conventions C s'appliques aussi : push/pop des paramètres, modification d'eax, ecx et edx, return value, etc.
Par défaut, la libc est liée à toute compilation de fichier C, donc si le fichier ASM est lié à un fichier C, il pourra utiliser toute fonction de la libc.
________________________________________________________________________________
SPECIAL
$ #Il s'agit d'un V, qui est remplacé lors du run-time par
#l'adresse courante. Exemple d'utilisation :
# - V dLETTRE "STRING", 0
# V equ $ - V
#W sera ici égal à la taille de "STRING" (dont '\0')
________________________________________________________________________________
DIRECTIVES
DIRECTIVES ==> #instruction au preprocesseur ASM, commençant par %
%include "..." #équivaut à #include
%define MOT [I] #equivaut à #define MOT [I]
________________________________________________________________________________
DECLARATION DE "VARIABLES" (V)
LETTRE ==> #LETTRE est un des TYPE suivants (majus) :
# - b : 1 octet (byte)
# - w : 2 octets (word)
# - d : 4 octets (double word)
# - q : 8 octets (quad word)
# - t : 10 octets (ten bytes)
#Ces TYPES peuvent faire référence à des INT ou des
#FLOAT, des CHAR, et dépendent de l'architecture
V equ I #Définit un V contenant I (et non son adresse). Connu
#dès le compile-time.
V dLETTRE I[, I]... #Déclare une V, contenant l'adresse pointant
[dLETTRE I[,I]...]... #vers le premier I.
#Le "TYPE" (nombre d'octets) de son contenu
#déréférencé est égal à LETTRE * nombre de I.
#Une "STRING" compte pour plusieurs I, car
#décomposée en "CHAR", "CHAR", etc.
#V est une ADR, elle pointe vers la première
#I, c'est par son déréférencement qu'on accède
#aux I à proprement parler.
#Si I est une C-string, penser à rajouter un
#nul à la fin :
# - V db 'mot', 0
V times NOMBRE dLETTRE #Equivaut à V dLETTRE I, répété sur
I[, I]... #NOMBRE lignes. Propre à NASM.
V resLETTRE NOMBRE #Déclare V, sans initialiser son contenu
[resLETTRE NOMBRE]... #déréférencé.
#Son "TYPE" est égal à LETTRE * NOMBRE
________________________________________________________________________________
OPERATIONS SIMPLES
mov : = (sans typecasting)
lea : Rd = &M. Comme un mov, mais permet d'utiliser +, - et * dans un $2 contenant une ADR que l'on ne veut pas déréfencer. En effet, dans ce cas, avec un mov :
- sans les crochets, pas légal
- avec les crochets, on assigne pas l'ADR non-déréférencée
- utilisé souvent :
- pour une arithmétique de pointeur : Rd = &[array + index * sizeof(element)]
- parceque le calcul effectué dans M est plus efficient que via add, sub, mul, etc.
neg : -VAL (complément de 2)
inc, dec : ++, --
add : +, +=
sub : -, -=
mul : *, *= (unsigned)
imul : *, *= (signed)
div : /, /=, %, %= (unsigned)
idiv : /, /=, %, %= (signed)
________________________________________________________________________________
OPERATIONS BINAIRES
not : ~ (complement de 1)
and : &
or : |
xor : ^
Utiles pour lire/activer/désactivés des bits précis d'une valeur.
shr : >> (unsigned)
sar : >> (signed, le most-significant bit n'est pas shifted)
sal, shl : << (unsigned ou signed)
shld : fait un >> sur $1$2 (little-endian, $1 contient le LSB). CF = dernier bit dégagé. Permet de faire des shift sur des long long int.
shrd : parail mais avec un << sur $2$1. (ne marche pas ?)
ror, rol : bitwise rotation droite et gauche. Le CF est activé si un 1 a été dégagé. Ainsi, avec une boucle sur tous les bits d'une valeur, cela permet d'avoir à chaque itération la valeur du bit suivant dans CF (par exemple pour compter le nombre de bits activés).
rcr, rcl : même chose, mais en prenant en plus le CF comme input à droite ou à gauche (plutôt que de rajouter par défaut un 0 à droite ou à gauche).
Utiliser << et >> pour diviser et multiplier par un muliple de 2 est plus rapide que mul et imul.
Si VAL2 est une puissance de 2, faire VAL & ( VAL2 - 1 ) est équivalent à VAL % VAL2, mais en plus rapide que via div ou idiv.
xor VAL,VAL est équivalent à mov VAL,0 mais en plus rapide.
________________________________________________________________________________
ARITHMETIQUE ETENDUE
Utiliser le carry flag et les registres avec offset pour aller au-delà des limites d'un type donné.
Registre avec offset :
Soit R un registre, R1:R2 (deux registres de même taille) veut dire R1 * sizeof(R1) + R2. R2 est donc un offset.
Ne semble pas pouvoir être utilisé par aucune instruction, la notation R1:R2 est juste pour expliquer le concept, mais il faut manipuler R1 et R2 avec des instructions séparées.
Utiliser les registres avec offsets :
R1:R2 + R3:R4 (où R3 a une taille <= à R1) :
- add R2, R4
adc R1, R3
R1:R2 - R3:R4 (où R3 a une taille <= à R1) :
- sub R2, R4
sbb R1, R3
Dans une loop, il faut :
- d'abord un add/sub (car si un précédent résultat, n'ayant rien à voir, a activé le CF, on ira 1 en trop)
- puis faire une loop avec adc/sbb.
On peut cependant simplement faire :
- clc
- puis faire le loop
________________________________________________________________________________
OBTENIR VALEUR D'UN SEUL BIT
Les instructions bt* affecte le bit numéro $2 (0 pour le LSB) de $1 à CF, ce qui permet d'extraire un bit singulier.
Ce bit est ensuite (dans $1) :
- bt : laissé tel quel
- bts : mis à 1
- btr : mis à 0
- btc : inversé
Laisse les flags OSZAP dans un état indéterminé.
bsf Rwd RM= #$1 = index du 1er bit == 1 dans $2, en partant de LSB
bsr Rwd RM= #Comme bsf mais part du MSB. L'index est cependant comme pour bsf : 0 pour le LSB.
________________________________________________________________________________
TYPECASTING
Typecasting vers type inférieur :
- mov Rwd RMI= et utiliser la partie basse de Rwd. Cela marche même avec un signed bit (si ax contient 0xffff (-1), al contiendra 0xff (-1)). Risque : la valeur peut être tronquée par le typecasting.
Typecasting vers type supérieur :
- unsigned number :
- mov Rwd, 0 puis utiliser la partie basse de Rwd
- movzx
- signed number :
- cbw, cwd, cwde, cdq ou movsx.
Signed <--> Unsigned :
- si la valeur est comprise entre 0 et le maximum de signed : rien à faire
- sinon : conversion impossible.
________________________________________________________________________________
TESTS
cmp : comme sub, mais n'enregistre pas le résultat (modifie seulement les flags).
test : comme cmp, mais utilise & et non - : plus rapide que cmp, mais ne sert qu'à tester l'égalité à 0 (suivi de jz ou jnz)
Signed et unsigned numbers :
cmp $1,$2, test $1,$2 :
- ZF == 1 --> ( $1 == $2 )
- ZF == 0 --> ( $1 != $2 )
Unsigned numbers :
cmp $1,$2 :
- CF == 1 --> ( $1 < $2 )
- CF == 1 || ZF == 1 --> ( $1 <= $2 )
- CF == 0 && ZF == 0 --> ( $1 > $2 )
- CF == 0 --> ( $1 >= $2 )
Signed numbers :
cmp $1,$2 :
- SF != OF --> ( $1 < $2 )
- SF != OF || ZF == 1 --> ( $1 <= $2 )
- SF == OF && ZF == 0 --> ( $1 > $2 )
- SF == OF --> ( $1 >= $2 )
(En effet, sans overflow, OF == 0, et SF == 1 si $1 < $2. L'overflow a pour effet que OF == 1 et que le résultat change de signe, donc SF == 0 si $1 < $2.)
________________________________________________________________________________
CONDITIONAL STRUCTURES
LABEL: #Crée un label LABEL (sur une seule ligne)
jmp [MOT] A #Goto vers l'adresse désignée par A.
#En fonction de MOT :
# - near (defaut) : near jump (même code segment)
# - far : far jump (autre code segment)
jz [MOT] A #Comme jmp, mais ne marche que si ZF == 1. Sinon ne fait rien.
jnz [MOT] A #Même chose mais pour ZF == 0
Même chose pour jo, jno, js, jns, jc, jnc, jpe et jpo pour OF, SF, CF et PF.
je [MOT] A #jmp, si pour le précédent cmp/test, $1 == $2. Utilise les flags pour ça : regarde en fait si ZF == 1 (comme jz).
jne [MOT] A #Comme je, mais avec $1 != $2
jl, jle, jg et jge : même chose pour <, <=, > et >= pour les signed numbers.
jb, jbe, ja et jae : même chose pour <, <=, > et >= pour les unsigned numbers.
jcxz, jecxz : jump si cx (ou ecx) == 0
Exemple d'implémentation de : "if ( VAL1 >= VAL2 ) ACTION1 else ACTION2" :
- cmp VAL1,VAL2
jge LABEL1
ACTION2
jmp LABEL2
LABEL1:
ACTION1
LABEL2:
Exemple d'implémentation de "while ( VAL1 == VAL2 ) ACTION" :
- test VAL,VAL2
jne LABEL2
LABEL:
ACTION
test VAL,VAL2
je LABEL
LABEL2:
Exemple d'implémentation de "do ACTION while ( VAL1 == VAL2 )" :
- LABEL:
ACTION
test VAL,VAL2
je LABEL
loop, loope et loopne permettent d'implémenter des boucles for et while.
Exemple d'implémentation de "for ( VAL = 0 ; VAL < 10 ; VAL++ ) ACTION" :
- mov ecx,10
LABEL:
ACTION
loop LABEL
Les jmp, loop, je, etc. mettent à mal la parallélisme des processeurs modernes. Pour éviter cela, on peut parfois utiliser setz, etc. de manière ingénieuse.
Par exemple pour :
- VAR = TEST ? VAL1 : VAL2 (par exemple VAR = max(VAL1, VAL2))
On peut faire (pour des VAL de 2 octets) :
xor bx,bx
xor cx,cx
TEST (cmp ou test)
set.. bl
setn. cl (set opposé)
neg bx
neg cx
and VAL1, bx
and VAL2, cx
or VAL1, VAL2
mov R, VAL1
________________________________________________________________________________
A chaque instruction, eip += (taille instruction exécutée), sauf lors de l'entrée ou la sortie d'une nouvelle fonction (ou d'un jmp, etc.)
________________________________________________________________________________
STACK
#Le stack commence à une ADR, et descend de word en word (4 octets sur archi 32 bits) : ADR, ADR - 4, ADR - 8, etc.
#L'alignement (sur 4 octets) implique par exemple que les char et short passés en arguments sont castés vers des int en C.
#Le stack doit toujours être aligné sur 16 bits avant de faire un call. Par conséquent, l'espace alloué aux variables locales est toujours divisible par 0x10.
#esp et ebp contiennent toujours une ADR.
#esp désigne le haut du stack (dernière valeur placée sur le stack). [esp]
#contient donc la valeur en haut du stack, [esp + 4] l'avant-dernière, etc.
#ebp désigne la valeur d'esp lors de l'entrée dans la frame courante.
#Les variables "auto" sont utilisées via le stack (pushed et popped), les variables static et global sont elles celles des V dans .data et .bss.
#Attention, dans une frame donnée, à ce que l'état du stack soit le même au tout début et à la toute fin, sinon ret ne marchera pas.
push IRMd #Met $1 au haut du stack, et esp -= 4.
pusha #Equivaut à faire une série de push sucessifs sur :
# - eax, ebx, ecx, edx, esp, ebp, esi et edi.
#Ne modifie esp qu'après ça (esp -= 0x20), donc l'esp pushed
#est celui d'avant pusha.
pop RMd #La valeur en haut du stack est mise dans $1 ($1 = [esp]), et
#esp += 4.
popa #Equivaut à faire une série de pop successifs, dans l'ordre
#inversé de pusha. A la fin, esp += 0x20
call [MOT] RMA #Fait un push de l'adresse de la prochaine instruction, puis un
#jmp $1
#MOT est comme pour jmp
ret #Fait un pop, et utilise l'adresse popped pour faire un jmp.
ret Iw #Même chose, mais augmente esp de $1, ce qui revient à faire
#$1 / 4 pops (pour la convention Pascal).
retf [Iw] #Fait un far ret.
UTILITE D'UN PUSH ==> # 1) déclarer des variables "auto".
# 2) passer des arguments à une fonctions :
# - push une IRMd contenant une VAL pour passer un
# argument par valeur
# - et contenant une ADR pour le passer par adresse
VARIABLES LOCALES ==> #Elles permettent :
# - reentrancy
# - lifetime limitée à la frame, donc mémoire
# utilisée moins longtemps -> à l'échelle du
# programme, moins de mémoire utilisée.
#On peut déclarer l'espace allouée aux variables
#locales d'une frame en faisant, au début de la frame :
# - sub esp, 4 * NbVarLocales
#Puis, à la fin de la frame, l'esp du début de la frame
#peut être restauré avec :
# - mov esp, ebp
#On accède ensuite aux variables locales (écriture ou
#lecture) avec [ebp - 4] (première variable locale),
#[ebp - 8] (deuxième), etc.
#Pour push l'adresse d'une variable locale, il faut
#utiliser lea, par exemple :
# - lea eax, [ebp - 4]
# push eax
enter I, I #Equivaut à :
# - push ebp
# mov ebp, esp
# sub esp, $1
#$2 est un numéro indiquant la convention utilisée : en
#général 0.
leave #Equivaut à :
# - mov esp, ebp
# pop ebp
RETURN VALUE ==> #La return value de la fonction doit être placée dans :
# - eax si la return value est un TOUINT_VAL <= 32
# bits
# - edx:eax pour un TOUINT_VAL > 32 bits
# - st0 pour un TOUFLOAT_VAL
CONVENTIONS ==> #Il existe plusieurs conventions pour appeler une fonction et
#lui passer des arguments utilisés par les assembleurs :
# - Convention C (utilisé en C, C++, etc.) :
# a) Appel :
# - push PARAM... #Push les paramètres, en
# #commençant par le dernier
# call PROGRAM #Push l'adresse de retour
# b) Fonction (début) :
# - push ebp #Push l'ancien ebp
# mov ebp, esp #Nouveau ebp = esp (permet
# #d'utiliser ebp pour cette frame,
# #après avoir sauvegardé celui de
# #celle d'avant)
# sub esp, $2 #Alloue de l'espace pour $2 / 4
# #variables locales, en modifiant
# #esp
# c) Accès aux paramètres/return infomation/variables
# locales :
# - [ebp - 8] -> 2ème variable locale, etc.
# - [ebp - 4] -> 1ère variable locale
# - [ebp] -> ebp de la frame précédente
# - [ebp + 4] -> Adresse de retour
# - [ebp + 8] -> 1er argument (mais dernier
# pushed par le caller)
# - [ebp + 12] -> 2ème argument, etc.
# Ceci est appelé une stack frame / display.
# d) Fonction (fin) :
# - mov eax, VAL #Met la return value dans eax (ou
# #edx:eax, ou st0)
# mov esp, ebp #Rétablie l'esp du début
# #(désalloue variables locales)
# pop ebp #Pop l'ancien ebp
# ret #Pop l'adresse de retour dans eip
# e) Retour :
# - pop ecx ou #Pop les paramètres (en les
# add esp, 4 #mettant dans la "poubelle" ecx,
# (ou 8, 12, etc.)#ou en modifiant esp)
# - Convention Pascal (utilisé en Pascal, mais aussi parfois
# en C sous Windows), plus efficiente, mais rend difficile
# le fait d'utiliser un nombre variable d'arguments (ex :
# printf()) :
# Identique, sauf que les paramètres ne sont pas popped
# par le caller, mais par le callee, via ret Iw.
STACK OVERFLOW ==>#Le stack segment a une taille limitée. Si elle est dépassée,
#cela peut faire crasher le programme ou permettre un exploit
#via buffer overflow. Exemple :
# - en mettant une variable locale trop grande (array)
# - avec une boucle récursive trop grande (infinie)
________________________________________________________________________________
FONCTIONS
Les fonctions sont identifiées par un LABEL au début, qui est leur nom.
1ère solution (mauvaise) :
a) Faire un jmp vers le LABEL de la fonction pour l'appeler.
b) pour passer des arguments, les mettre dans des registres avant le jmp, la fonction sachant quels registres devront acueillir tel argument (convention).
c) parmi les arguments, mettre l'adresse de retour, pour que la fonction revienne vers cette adresse à la fin de son exécution.
d) Pour la return value, pareil, un registre prévu pour.
2ème solution (syscalls) :
- Même chose, sauf qu'on utilise int et non jmp, et qu'il n'y a pas de c)
2ème solution (mieux) : utiliser le stack (cf précédent)
________________________________________________________________________________
ARRAYS
Lire une array : lods*
Ecrire sur une array : stos*
Copier une array : movs*
Comparer une array et une valeur : cmps*
Comparer deux arrays : scas*
En cas de copie de deux arrays superposées, utiliser cld et std pour modifier le sens de lecture/écriture.
Dans tous les cas, toujours utiliser cld et std avant lods*, stos*, movs*, cmps* ou scas* pour ne pas faire de supposition sur l'état actuel de DF
________________________________________________________________________________
ENDIANNESS
Renverser l'endianness :
- 16 bits (ax) :
- xchg ah,al
- 32 bits (eax) :
- bswap eax
- 64 bits (edx:eax) :
- xchg edx,eax
________________________________________________________________________________
FLAGS
On peut les manipuler aussi via lahf, sahf, popf et pushf.
Avec lahf et sahf, seuls SF, ZF, PF, AF et CF sont modifiés.
Avec pushf et popf, les 32 bits d'eflags sont affectés, mais modifier certains peut provoquer des exceptions dans certains cas (comme pb de privilèges)
salc : al = 0xff si CF == 1, sinon al = 0
________________________________________________________________________________
BCD NUMBERS
Pour faire des +, -, * et / entre unpacked BCD numbers, utiliser :
+ : aaa
- : aas
* : aam, amx
/ : aad, adx
Pour packed BCD numbers :
+ : daa
- : das
Avec x87 : fbld et fbstd
Ne m'intéresse pas
________________________________________________________________________________
TABLE LOOKUP
xlat : al = [ebx + al]. ebx doit donc être le début d'un array de max 256 char, et remplace al par l'index al de cet array. Utile par exemple pour une conversion EBCDIC-ASCII, ou autre.
________________________________________________________________________________
SYNCHRONIZATION
lock O : fait que le ou les M utilisés par l'instruction O sont locked, et pas accessibles par une autre task le temps que l'instruction O s'achève. Ne peut pas être n'importe quel O (cf plus bas)
________________________________________________________________________________
INTERRUPTIONS
int Ib #Appelle l'interruption numero $1.
#Valeur de $1 :
int1 #Lance int 0x01
into #Lance int 0x04 si OF == 1.
bound Rwd M= #M et le M qui suit (M + 2 octets par exemple si Mb) sont le min et max (signed numbers) : si $1 est < min ou > max, un int 0x05 est lancé. Permet de checker des buffer overflow.
cli #IF = 0. cf IF
sti #IF = 1. cf IF
________________________________________________________________________________
R[LETTRE] ==> Un R (par défaut b-d si 32 bits, et b-q si 64 bits). LETTRE peut être :
b : 8 bits
w : 16 bits
d : 32 bits
q : 64 bits
= : même LETTRE qu'argument précédent
< : LETTRE inférieur à argument précédent
NOMBRE : NOMBRE bits
M[LETTRE] ==> Un M (même chose pour LETTRE).
Doit être précédé de TYPE si est un $1, et que $2 n'est pas un R
I[LETTRE] ==> Un I (dont L et V)
A ==> Un A
S ==> Un S
"autre" ==> registre "autre" (ex : ax)
O ==> instruction
Ô ==> instruction, mais pas toutes.
Pour lock (doit contenir un M en argument) :
- adc, add, sbb, sub, and, or, xor (M doit alors être en $1)
- btc, btr, bts, cmpxchg, cmpxchg8b, xadd, xchg
Pour rep : ins, movs, outs, lods, stos
Pour repe/repne : cmps, scas
RM[LETTRE]==> R ou M ont ici tous les deux la même LETTRE
R M[LETTR]==> R ou M ont ici deux LETTRE différentes
Séparer par un espace signifie "ou"
$1 est le premier argument, $2 le deuxième, $3 le troisième.
$2 et $3 ne peut jamais être de taille > $1
SGN : indique si c'est seulement pour les unsigned (U) ou signed (S)
FLAGS : flags pouvant être modifiés/affectés par l'instruction
PRV : condition pour pouvoir exécuter l'instruction sans exception lancée :
- 0 : ring level doit être 0 (kernel mode)
- IPL : IOPL flag doit être < ring level
- DIV : dépend de diverses choses, comme le ring level ou le fait que $2 soit une valeur possible. Dans tous les cas, $1 ne peut pas être cs.
ARCH : architecture minimale pour avoir cette instruction :
- les différences entre 8086, 80186, 80286, 80386 et 80486 ne sont pas notées, seules le sont celles à partir du Pentium I.
- PI : Pentium I
- PMMX : Pentium with MMX
- PPro : Pentium Pro
- PII : Pentium II
- P4 : Pentium 4
- C2 : Core 2
- Ci7 : Core i7
±= : est += si DF == 0, et -= si DF == 1
+-------+--------+--------+---+-----------------------+---+----------+---+-----+-------+
| NOM | ARG1 | ARG2 | A3| EFFET |SGN| FLAGS |PRV| ARCH| 32/64 |
+-------+--------+--------+---+-----------------------+---+----------+---+-----+-------+
| nop | | | | Ne fait rien. | | | | | |
| fndisi| | | | | | | | | |
| fneni | | | | | | | | | |
| nop |RMwd | | | | | | | PPro| |
| ud2 | | | | Génère exception #UD | | | | | |
+-------+--------+--------+---+-----------------------+---+----------+---+-----+-------+
| mov | R, | RMI= | | $1 = $2 | | | | | |
| | M, | RI= | | | | | | | |
| | Mw R, | S | | | | | | | |
| | S, | Mw R | | | | |DIV| | |
| | R, | D | | | | OSZAPC | 0 | | |
| | D, | R | | | | OSZAPC | 0 | | |
| lea | Rwd | M= | | $1 = &$2 | | | | | |
+-------+--------+--------+---+-----------------------+---+----------+---+-----+-------+
| movzx | R | RMwb | | $1 = $2 | U | | | | |
| movsx | R | RMwb | | $1 = $2 | S | | | | |
| movsxd| Rq | RMd | | | | | | | 64 |
| cbw | | | | ax = al | S | | | | |
| cwde | | | | eax = ax | S | | | | |
| cwqe | | | | rax = eax | S | | | | 64 |
| cwd | | | | dx:ax = ax | S | | | | |
| cdq | | | | edx:eax = eax | S | | | | |
| cdqo | | | | rdx:rax = rax | S | | | | 64 |
+-------+--------+--------+---+-----------------------+---+----------+---+-----+-------+
| | | | | | | | | | |
| add | R, | RMI= | | $1 += $2 | | OSZAPC | | | |
| | M, | RI= | | | | | | | |
| adc | R, | RMI= | | $1 += ( $2 + CF ) | | OSZAPC | | | |
| | M, | RI= | | | | | | | |
| sub | R, | RMI= | | $1 -= $2 | | OSZAPC | | | |
| | M, | RI= | | | | | | | |
| sbb | R, | RMI= | | $1 -= ( $2 - CF ) | | OSZAPC | | | |
| | M, | RI= | | | | | | | |
| inc | RM | | | $1++ | | OSZAP | | | |
| dec | RM | | | $1-- | | OSZAP | | | |
| mul | RMb | | | ax = $1 * al | U | OSZAPC | | | |
| | RMw | | | dx:ax = $1 * ax | U | OSZAPC | | | |
| | RMd | | | edx:eax = $1 * eax | U | OSZAPC | | | |