Tophinus, dasm, ne gère pas les far jump... Et j'arrive pas du tout a calculer ou est le code appelé par le jmp far.
Tophinus, dasm, ne gère pas les far jump... Et j'arrive pas du tout a calculer ou est le code appelé par le jmp far.
(j'oubliais encore des trucs style utiliser le parity flag pour déterminer si deux bits sont égaux. Ca aussi c'est de l'asm inline qu'il faudrait)
Effectivement, c'est ce que je t'avais dis dans le post, il ne "gère" pas les jump far comme il peut gérer les jmp near. J'ai pas encore trouvé de désassembleur qui permette de faire mieux que Dasm (surtout en rapidité et "convivialité"). Tu dois donc faire ton jmp far à la main (shift+F12).
Petite note au cas où : Quand tu sauvegardes ton "projet" ou plutôt ton fichier désassemblé, il te crée deux nouveaux fichiers : .alf et .wpj. En fait, le .alf tu peux l'ouvrir avec wordpad et tu retrouveras le texte de ton fichier désassemblé (ça peut toujours servir).
Je suis désolé que Dasm ne fasse pas les jmp far mais un des autres avantages que j'ai trouvé à Dasm c'est quand tu forces des intructions 32 bits en 16 bits (avec un db 66h ou un db 67h), il te laisse les codes. Par exemple :
Eh bien, dans d'autres désassembleur tu auras seulement db 8bh,0C8h. Par contre de temps à autre, il désassemble mal le forcing en 32 bits. Par exemple
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 mov ecx,eax = db 66h,8bh,0c8h
En fait il te l'affichera de la manière suivante:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 out dx,eax = 66h,0EFh
En gros, faut vérifier qu'il n'y ait pas un db66h qui traîne (en fait à partir du moment où tu trouves une ligne en 32 bits, là il faut penser à scruter après).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 out dx,ax = 66h,0EFh
Bon, j'ai été un peu long, désolé.
Tu passes beaucoup de temps aussi sur ton émulateur. T'arrives à avoir le temps de t'y mettre ?
Ma boule de cristal s'était éclaircie et puis plus rien. Alors je me suis mis à internet et maintenant j'ai plus de renseignements qu'avec cette satané boule .....
bah vi, je suis à la fac, alors j'ai le temps de m'y mettre :))
le problème, c'est que j'arrive pas a calculer ou va le far jump. Le code que je désassemble est enorme. Et l'adresse que je calcule ne tombe pas juste sur une instruction
Je n'ai pas dit que c'est plus rapide !! Je voulais juste te montrer comment on fait en C. Idem pour sahf.Il compile ca comment les switchs le compilateur ?? (j'ai du mal a croire que ca procedure en C pour le mod r/m est plus rapide que la mienne. (Déja au niveau de l'algorithme la mienne est plus rapide)
Oui via la directive ALIGN et il faut absolument le faire. De même pour les procédures alignées sur 16 octets, les labels alignés aussi.Quant a l'alignement des données, je peux le faire moi même. Si je me souviens bien, il y a des directives pour que l'assembleur le fasse.
Les procs (depuis le 8086) ne supporte pas les données non alignés. Depuis il ne supporte pas bcp le code non-aligné non plus.
Sur un 8086, pour lire un WORD dont l'adresse n'est pas divisible par 2, il le charge en deux fois !! Avec les procs actuels, c'est plus compliqué mais toujours trés pénalisant...
- En adaptant les algorithmes, il se peut que le C soit plus rapide: c'est n'est pas forcémenent le cas. Au point ou tu en es, il est effectivement trop tard pour penser au C...J'ai vraiment du mal a voir, comment je pourrais faire mieux en C, d'autant que ca me prendrait encore beaucoup de temps, et que de l'asm inline + des fichiers générés aléatoirement + les optimisations d'algo qui donne des codes tordus, ce sera vraiment un suplice pour les adeptes du C a lire, et ca ne sera pas tellement portable.
- Les fichiers ne sont pas générés alèatoirement, voyons...
- Le code C que tu a vu est portable sur MAC par exemple (c'est du C ANSI). Il est clair qu'il doit être plus lent que le tien: on sacrifie la vitesse sur l'autel de la portabilié. Mais c'est intéressant d'émuler un x86 sur un PowerPC (ou autre).
Donne moi des nouvelles des améliorations (s'il y en a) des call/ret et movzx.
Je te conseille vivement d'éplucher "How To Optimize For The Pentium" dont je t'ai donné le lien.
Oki merci, je vais lire tout ca (il faut aussi que je lise pour les amds, puis aussi il faut que je commprenne comment marche le flouage d'une image et l'alpha blending avec openGL...), comme c'est en anglais ca va me prendre pas mal de temps.
Euh, je suis pas aller bien loin, il y a déja quelque chose que je comprends pas, c'est comment choisir l'alignement? 2, 4, 8, ou 16 ? Je comprends vraiment pas comment choisir. Et de toute facon, j'ai du mal a trouver comment utiliser l'alignment avec MASM. J'ai cru voir que le coded été aligné par défaut sur 4 octets... Mais concretement, il remplit d'une chaine de non-operation juste derrière les ret et les jmp ?
Eh oui, c'est vite la prise de tête. Personnellement, j'aligne les routines sur 16 octets (c'est ce que fait VC).Euh, je suis pas aller bien loin, il y a déja quelque chose que je comprends pas, c'est comment choisir l'alignement? 2, 4, 8, ou 16 ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 ALIGN 16 EmulerBloc proc C NbInstructions:DWORD ... EmulerBloc endpOui, c'est la technique. Il remplit par des nop (ou équivalent style mov eax,eax, lea eax,[eax]: il y a toute une discution sur les neutral code fillers dans la doc d'AMD).Mais concretement, il remplit d'une chaine de non-operation juste derrière les ret et les jmp
L'avantage, c'est que ton jmp est aligné
Exemple
donne
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 jnz label mov eax, edx shr eax, 2 ; par exemple ALIGN 8 label: mov ecx , eax
Ici, label sera toujours divible par 8. Si tu change du code avant label, le nombre de nop va bien sûr changer...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 jnz label mov eax, edx ; shr eax, 2 ; nop ; nop ; autant de nop qu'il faut pour que label soit divisible par 8 nop ; comme je ici je n'en sait rien, j'en met 3 label: mov ecx , eax
La règle est de n'aligner que les labels des boucles critiques (et plutôt sur 16 octets).
Extrait de la doc AMD
Après si MASM fait déjà des alignements, c'est OK.In program hot spots (as determined by either profiling or loop
nesting analysis), place branch targets at or near the beginning
of 16-byte aligned code windows. This guideline improves
performance inside hotspots by maximizing the number of
instruction fills into the instruction-byte queue and preserves Icache
space in branch-intensive code outside such hotspots.
Si je comprend bien, on aligne un dowrd ou un tableau de dword sur 4 octets pour que l'on puisse aller le chercher en une seule fois. Et on peut l'aligner sur 16, si on sait qu'on aura besoin des données qui suivent (cad pour des dwords, si on sait qu'on a besoin des 3 dwords qui suivent). Dans cee cas, pour un maximum d'opimisation assuré, il faudrait spécifier un alignement toujours de 16, le seul interet de le réduire, étant d'economiser la taille du programme (et donc parfois empecher de vider le cache si ca prend vraiemnt trop de place)
Je n'ai qu'une boucle, donc je peux ajouter un ALIGN16 juste devant début boucle. Il me semble que j'ai quand même tout interet à aligner toutes les destinations des jmp, cad vu que la table des labels TabIns16 pointe vers des blocs d'instructions, rajouter des ALIGN 16 devant chaque bloc.Envoyé par AMILIN
L'ennui aussi, c'est que j'ai des problèmes avec MASM, l'alignement est définit par la directive SEGMENT, mais l'aide ne precise pas son utilisation. De plus la directive ALIGN ne peut etre suivi que d'une puissance de 2 inferieure ou égale à l'alignement du segment. Quelqu'un sait comment utiliser l'alignment avec MASM ?
C'est vrai que l'aide de MASM n'est pas prolixe ...
Tu peut essayer ça:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 .386 _TEXT SEGMENT PUBLIC USE32 PAGE 'CODE' ALIGN 32 truc PROC mov ecx,256 mov esi , esi ... ALIGN 16 @@: mov eax , 123456 dec ecx jnz @b ret truc ENDP _TEXT ENDS END
ca c'est pas un partial register stall, puisque les partials registers stall :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 mov edx, [ebx] mov ax, dx shr edx, 16
You don't get a stall when reading a partial register after writing to the full register, or a bigger part of it
Bon j'ai lu la doc à l'arrache, c'est un peu confuis dans ma tête, mais ca pourrait donner ca :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 mov ecx, NbInstructions jecxz FinBloc ; au cas ou mov edx, [ebx] push DebutBloc ALIGN 32 DebutBloc: xor eax, eax ; 1 cycle supplémentaire entre le jnz et le call mov ax, dx call TbIns16[eax*4] mov edx, [ebx] ; 1 cycle suplémentaire entre le ret et le jnz dec ecx jnz FinBloc
Ou alors une version qui me semble bien moins bonne (pour les PII+ ca ralentit bcp, comme tu me l'avais dt pour les predictions des ret)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 mov ecx, NbInstructions ALIGN 32 DebutBloc: dec ecx js FinBloc mov edx, [ebx] push DebutBloc xor eax, eax mov ax, dx jmp TbIns16[eax*4]
Je me posait une autre question, pour ce qui concerne les flags, en particuliers pour les instructions qui modifient le carry flag. J'emploiyais jusqu'a la des structures du types :
qui crée des penalités dues au close jump et des misspredictions.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 jc @f and MachineVirtuelle.r_flags, NOT CARRY FLAG ret @@: or MachineVirtuelle.r_flags, CARRY_FLAG ret
J'ai du mal a voir si il est préférable de remplacer la structure
par une strtucture du style :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 mov ah, byte ptr MachineVirtuelle.r_flags sahf ... lahf mov byte ptr MachineVirtuelle.r_flags
quitte a optimiser la pile, pour mon programme, vu que je sait, qu'elle contient l'adresse de retour de l'instruction, puis l'adresse de retour éventuelle du Mod r/m, je peux reserver 12 octets. Mais je ne suis pas certain qu'aucun élément exterieur ne pourrait me gener :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 push MachineVirtuelle.r_flags popf ;D'après la doc, pas d'exeptions, mais des consequences cachées ? ... pusf pop MachineVirtuelle.r_flags
Je sait pas si ca se fait, et ca a pas l'air d'etre tellement plus rapide.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 Flags dd ? PlancherPile dd 2 dup(?) Pile dd ? ... mov Pile, esp mov Flags, MachineVirtuelle.r_flags mov esp, offsfet Pile ... ; boucle Instructions pop esp ret ... lea esp, [esp-8] popf ... pushf lea esp, [esp+8]
En ce equi concerne les movzx, il est conseillé de toujours les splitter si je me souviens bien, que ce soit pour le pairing ou pour les µops.
Pour movzx, j'avais écrit
De plus:Envoyé par AMILIN
1- Extrait de 'Intel Architecture Optimization"
2- Extrait de "AMD Athlon Processor x86 Code Optimization Guide" (valable aussi pour Duron)Pentium II and Pentium III processors provide special support to XOR a
register with itself, recognizing that clearing a register does not depend on
the old value of the register. Additionally, special support is provided for the
above specific code sequence to avoid the partial stall. See “Partial Register
Stalls” section for more information.
The performance of the movzx instructions has been improved in order to
reduce the prevalence of partial stalls on Pentium II and Pentium III
processors. Use the movzx instructions when coding for these processors.
Le cas que tu site (mov edx,[]) est effectivement différent mais tu peut toujours essayer. Movzx est DirectPath (en gros, il peut en décoder 3 par cycle) sur AMD (avec 1 cycle latence). C'est 1 µop sur P6.Use the MOVZX and MOVSX instructions to zero-extend and
sign-extend byte-size and word-size operands to doubleword
length. Typical code for zero extension that replaces MOVZX,
as shown in Example 1 (Avoid), uses more decode and execution
resources than MOVZX. It also has higher latency due to the
superset dependency between the XOR and the MOV which
requires a merge operation.
Example 1 (Avoid):
XOR EAX, EAX
MOV AL, [MEM]
Example 1 (Preferred):
MOVZX EAX, BYTE PTR [MEM]
Pour POPF/PUSHF.
La doc AMD signale que POPF est une instruction complexe (VectorPath) avec 15 cycles de latence (PUSHF seulement 4).
Pour les P6, la doc "Optimization For..." signale une intruction complexe de 17 µops pour popf (sur 3 ports) et 16 pour pushf (sur 4 ports).
sahf/lahf c'est seulement 1 µop sur P6. C'est VectorPath sur AMD avec respectivement 2 et 3 cycles de latences.
Il n'y a pas photo...
Pour la pile, regarde les docs Intel sur l'alignement de la pile.
A+
le seul problème c'est que lahf et sahf ne sauvegarde pas la veleur du carry flag, et c'est assez lourd avec les sauts conditionels
C'est le flag OF (Overflow Flag) qui n'est géré par sahf/lahf. Il en est de même pour DF et, moins important, TF et IF.sahf ne sauvegarde pas la veleur du carry flag
OF n'est pas bcp utilisé dans les programmes (il est rare de voir des tests jo...). Pour DF (Direction Flag), c'est plus emmerdant: par exp si DF=0 l'instruction movsb/w/d incrémente esi et edi, sinon décrément.
Toutefois, dans les cas réels, DF est rarement à 1...
Si tu veux gérer correctement tous les flags, il ne te reste effectivement que pushf/popf...
vi c'est l'overflow pas le carry, désolé. Mais DF est rarement modifié par les instrtructions il me semble, sauf par STD ou CLD, deux instructions que j'emule presque aussi rapidement qu'un nop. Il vaut bien mieux pour les autres faire un saut conditionel suivant OF et modifier les flags en consequences, qu'utiliser pusf/popf d'après ce que j'ai compris. Donc je reste a l'ancienne methode
Il y a deux petites chose que j'aimerais changer dans mon code. J'aimerais que tu me dises si je me trompe.
D'une part, maintenant que je comprends mieux le fonctionement de la cache, j'ai l'impression, que dans
l'utilisation de edx est superflue : Si j'utilise ca c'est pour ne pas avoir a re-chercher une autre fois les données dans la mémoire. C'est utilie pour :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 mov edx, [ebp] xor eax, eax mov ax, dx
- Les instructions ou il y a un mod r/m + disp8 ou disp16
- Les instructions sans mod r/m mais avec un imm8 ou imm16 qui suit.
Mais maintenant que je comprends mieux comment fonctionne la mémoire cache, je me dis, que se mov est inutile, est peut eviter des problèmes d'alignement des données, notement quand la "limite" de l'alignement tombe entre l'opcode-modrm et l'immediate ou le disp. De plus ca me permettrait de liberer edx.
Sinon, j'aimerais savoir si en général dans les programmes (vieux ou recents) on trouve souvent des prefixes pour inverser la taille d'adressage. Parce que sinon, je vais me permetre de déplacer 12ko de mémoire a chaque fois qu'un tel prefixe a lieu.
Petite erreur, dans ton code il d'agit de mov edx,[ebx]. Et d'ailleurs, tu n'utilise jamais ebp (sauf dans DOS.sam)!!!
Voilà un registre de disponible !!! (d'ailleurs, j'aurais tendance à inverser ebp<->ebx dans ton code).
Dans tous les cas, autant libérer edx . Mais ne te focalise pas trop sur le cache.C'est utilie pour :
- Les instructions ou il y a un mod r/m + disp8 ou disp16
- Les instructions sans mod r/m mais avec un imm8 ou imm16 qui suit.
La structure de ton émulateur (et de n'importe lequel) ne permet pas trop ce genre d'optimisation: tu passe ton temps entre la RAM "émulée" et celle de la gestion de l'émulation (tables et autres). Et tu n'as pas trop le choix...
Sinon, j'aimerais savoir si en général dans les programmes (vieux ou recents) on trouve souvent des prefixes pour inverser la taille d'adressageC'est donc TRES rare et même déconseillé. Personnellement, je ne vois franchement pas l'utilité d'utiliser un mode d'adressage 16 bits en 32 bits (ou l'inverse):Envoyé par Intel
- mov eax , [di] en mp 32 bits quel intérêt... et quel plantage sous Windows. Si je me rapelle bien, les 4 premiers Mo (en adressage virtuel bien sur) sont réservés par le systême. Il doit y avoir contriantes similaires les autres OS en mp.
- mov eax , [edi] en 16 bits se comprend un peu mieux (si edi<16bits). Mais l'intéret quand on peut mettre di..
A mon avis, aucun compilateur n'engendre ce type de préfixe (sauf asm inline). Ce genre de "bizzarie" est le privilège du codeur ASM.
As tu une amélioration de vitesse depuis le début de notre discution ?
vi, c'est vrai ct ebx. De toute facon pour l'indexation ebx est a peu près l'egal de ebp. Cela dit je comprends pas ce que tu essaie ded me dire avec la cache. A chaque instruction je dois utiliser entre 6 à 8 trtuc de 32 octets de la cache, je vide jamais rien, donc je pensais pouvoir utiliser cectte particularité de la cache.
Je vais quand même émuler une floppée de programmes 16 bits, si ils ont des prefixe de taille d'adressage ca va pas etre rapide.
Aucune idée de si il y a une amélioration. J'ai commencé a coder les instruictions d'operand size 32, et tant que j'ai pas fini, je pourrais pas savoir. De plus il faudrait que j'émule exactement les même instructions pour savoir, vu que la vitesse varie enormement, certaines instructions s'emulent trois fois plus lentement que d'autres.
lol tu as regardé jusqu'a Dos.asm, j'aurais jamais pensé que quelqu'un pourrait y metre son nez un jour. Ils sont pas trop pitoyables les commentaires ?
Oui, c'est équivalent même.De toute facon pour l'indexation ebx est a peu près l'egal de ebp
Je pensais seulement que si tu remplace ebx par ebp (dont tu ne te sert pas je le rappelle), tu as un registre général de libre: ebp n'a pas de "forme" 8 bits comme ebx (bl,bh).
Sur x86, le manque de registre se fait cruellement sentir...
Exemple, ayant libéré ebx. La séquence avec OF
peut se remplacer par
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 SaveFlags jo @f ResetFlag OVERFLOW_FLAG ret @@: SetFlag OVERFLOW_FLAG ret ; = sans les macros pour y voir plus clair lahf mov BYTE PTR MachineVirtuelle.r_flags, ah jo @f and WORD PTR MachineVirtuelle.r_flags, NOT OVERFLOW_FLAG ret @@: or WORD PTR MachineVirtuelle.r_flags, OVERFLOW_FLAG ret
grâce aux instruction Conditional Mov (à partir des PPro et K6 pour AMD). Ca marche MAIS met les flags donc tu ne te sert pas actuellement (DF,IF,TF) à zéro.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 mov bx , 0 ; ne pas détruire les flags ; avec un xor bx,bx mov WORD PTR MachineVirtuelle.r_flags,OVERFLOW_FLAG ; r_flags <- 0x800 ; se paire avec le mov bx lahf ; pairing impossible sur P6 cmovo bx , WORD PTR MachineVirtuelle.r_flags ; bx<-0x800 si OF=1 mov bl , ah ; byte inférieur des flags dans bl mov WORD PTR MachineVirtuelle.r_flags, bx ret
NB: L'instruction CMOV est assez contraigante et nécessite:
- reg ou mem en source (ici un immediate aurais été mieux...)
- destination = reg16 ou reg32
J'ai testé les deux codes (en C+asm) vite fait sur mon Athlon. La deuxième version y est à peut près deux fois plus rapide.
Attention: ce résultat dépend d'autres paramètres (notamment le contexte d'exécution) et il se peut que cela soit plus lent dans ton prog !!!
Et d'ailleurs j'ai aussi essayé pushf / pop word ptr MachineVirtuelle.r_flags / ret qui s'est révélé aussi rapide que cmov... mais sur Athlon, pushf n'a que 4 cycles de latences (et popf 15 !)
Pour le cache de donnée, je voulais dire que tu ton programme passe son temps entre les "registres" de la structure X86 (donc qq octets) etCela dit je comprends pas ce que tu essaie ded me dire avec la cache
ce qui est pointé par X86.RAM (taille 1Mo je crois) que ce soit pour lire les intructions ou les accès à la "RAM" du prog émulé.
Toutefois, le nombre d'appels que tu fait à cette structure implique qu'elle doit être dans le L1 rapidement (si le proc n'est pas trop con !).
Il n'y a pas grand chose à optimiser ici (il faudrait optimiser le prog que tu émule !! notamment ses accès mémoire !).
AUCUN compilateur 16 bits que j'ai eu l'occasion d'utiliser (Turbo Pascal 3->6, TC/C++) n'utilisait les instructions du 386 (ce qui d'ailleurs était pénalisant pour l'asm inline: il fallait se taper en hexa les instructions 32 bits).Je vais quand même émuler une floppée de programmes 16 bits, si ils ont des prefixe de taille d'adressage ca va pas etre rapide.
Donc par de préfixe de taille d'adressage. Je n'ai jamais rencontré de tel préfixe... Tu en as déjà rencontré avec ton émulateur (ou ailleurs) ?
Apparamment, ce préfixe doit être utilisé par le systême. Pas par des progs normaux.Envoyé par re-Intel
J'ai juste fait une recherche avec UtraEdit pout rechercher ebp. Les commentaires me semble acceptables...tu as regardé jusqu'a Dos.asm...Ils sont pas trop pitoyables les commentaires ?
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager