Conversion de code ARM en 68000
Cette page traite de la conversion de programmes écrits en assembleur ARM (architecture ARM7/ARMv4) vers l'assembleur Motorola 68000. L'ARM et le 68000 sont tous deux des microprocesseurs RISC/CISC 32 bits, mais leurs philosophies de conception diffèrent significativement : l'ARM est un microprocesseur RISC a ensemble d'instructions fixe de 32 bits avec exécution conditionnelle intégrée, tandis que le 68000 est un microprocesseur CISC a instructions de longueur variable.
Différences fondamentales entre ARM et 68000
| Critère | ARM (ARMv4) | Motorola 68000 |
|---|---|---|
| Type d'architecture | RISC (load/store) | CISC |
| Taille des instructions | 32 bits (fixe) | 16-80 bits (variable) |
| Registres généraux | 16 (R0-R15) | 8 données + 8 adresses |
| Registres d'adresse | (inclus dans généraux) | A0-A7 (séparés) |
| Taille des registres | 32 bits | 32 bits |
| Espace d'adressage | 4 Go (32 bits) | 16 Mo (24 bits) |
| Ordre des octets | Bi-endian (souvent little-endian) | Big-endian |
| Alignement mémoire | Requis (mots sur 4) | Mots sur adresses paires |
| Pile | Décroissante (R13) | Décroissante (A7) |
| Exécution conditionnelle | Toute instruction | Scc + Bcc seulement |
| Barrel shifter | Intégré (opérande 2) | Instructions séparées |
| Drapeaux (flags) | N,Z,C,V (optionnel) | X,N,Z,V,C (toujours) |
| Modes de privilège | 7 modes (USR,SVC,..) | 2 modes (user/super) |
| Coprocesseur flottant | VFP/FPA (optionnel) | 68881/68882 (séparé) |
| Instructions load/store | LDR/STR seulement | MOVE depuis/vers mémoire |
Différences clefs pour la conversion :
- Architecture load/store vs mémoire-mémoire : L'ARM ne peut accéder à la mémoire que via LDR (load) et STR (store). Toutes les opérations arithmétiques/logiques travaillent sur des registres uniquement. Le 68000 peut opérer directement sur la mémoire (ADD.L d(An),Dn par exemple). Lors de la conversion, les séquences LDR+operation+STR de l'ARM peuvent souvent être remplacées par une seule instruction 68000 travaillant directement en mémoire.
- Exécution conditionnelle : Sur ARM, presque toute instruction peut être conditionnelle (suffixe de condition : ADDEQ, MOVNE,...). Le 68000 n'a que les instructions Bcc (branchement conditionnel) et Scc (positionnement conditionnel). Les instructions ARM conditionnelles doivent être converties en séquences Bcc + instruction sur le 68000.
- Barrel shifter intégré : Sur ARM, le second opérande peut être décalé/tourné avant l'opération (ADD R0, R1, R2, LSL #3). Le 68000 n'a pas cette fonctionnalité. Il faut décomposer en un décalage suivi de l'opération.
- Mise a jour des drapeaux : Sur ARM, les drapeaux ne sont mis a jour que si le suffixe S est présent (ADDS, SUBS,...). Sur le 68000, la plupart des instructions mettent a jour les drapeaux automatiquement. Cela peut causer des problèmes si le code ARM dépend de drapeaux non modifies entre deux instructions.
- Registres : L'ARM a 16 registres généraux (R0-R15) dont R13 (SP), R14 (LR) et R15 (PC) ont des rôles spéciaux. Le 68000 sépare les registres en données (D0-D7) et adresses (A0-A7). Certains registres ARM a usage général doivent être cartographiés sur des registres d'adresse du 68000 s'ils servent de pointeurs.
Correspondance des registres
| Registre ARM | Equivalent 68000 | Notes |
|---|---|---|
| R0 | D0 | Registre de travail / retour |
| R1 | D1 | Registre de travail |
| R2 | D2 | Registre de travail |
| R3 | D3 | Registre de travail |
| R4 | D4 | Variable préservée |
| R5 | D5 | Variable préservée |
| R6 | D6 | Variable préservée |
| R7 | D7 | Variable préservée |
| R8 | A0 | Variable préservée / pointeur |
| R9 | A1 | Variable préservée / pointeur |
| R10 | A2 | Variable préservée / pointeur |
| R11 (FP) | A5 ou A6 | Frame pointer |
| R12 (IP) | A3 ou A4 | Scratch / pointeur temporaire |
| R13 (SP) | A7 (SP) | Pointeur de pile |
| R14 (LR) | (sur la pile) | Adresse de retour |
| R15 (PC) | PC | Compteur ordinal |
| CPSR | SR/CCR | Registre d'état |
Notes sur la correspondance :
- R0-R7 → D0-D7 : cartographie naturel car l'ARM utilise R0-R7 principalement pour les données. Les opérations arithmétiques et logiques ARM sur R0-R7 se convertissent directement en opérations sur D0-D7.
- R8-R12 → A0-A4 : les registres hauts de l'ARM servent souvent de pointeurs ou de variables préservées. Ils sont cartographiés sur les registres d'adresse du 68000. Si un registre ARM haut est utilise pour des calculs arithmétiques, il faut utiliser un registre Dn temporaire sur le 68000 car les registres An ne supportent pas toutes les opérations.
- R14 (LR) : L'ARM stocke l'adresse de retour dans LR lors d'un BL (Branch with Link). Le 68000 empile automatiquement l'adresse de retour avec BSR/JSR. Le LR n'a donc pas d'équivalent direct ; l'adresse de retour est sur la pile.
- CPSR → CCR : Les drapeaux ARM (N,Z,C,V) correspondent aux drapeaux 68000 (N,Z,C,V). Le drapeaux X du 68000 n'a pas d'équivalent direct sur ARM. Le champ mode du CPSR (bits 0-4) n'a pas d'équivalent direct car le 68000 n'a que 2 modes (user/superviseur) contre 7 pour l'ARM.
Conversion des instructions de transfert
L'ARM utilise LDR/STR pour les accès mémoire et MOV pour les transferts entre registres. Le 68000 utilise MOVE pour tout.
| ARM | 68000 |
|---|---|
| MOV R0, R1 | MOVE.L D1, D0 |
| MOV R0, #100 | MOVEQ #100, D0 ; si -128..127 ou MOVE.L #100, D0 |
| MOV R0, #0 | CLR.L D0 |
| MOV R0, #-1 | MOVEQ #-1, D0 |
| MVN R0, R1 | MOVE.L D1, D0 NOT.L D0 |
| MVN R0, #0 | MOVEQ #-1, D0 |
Chargement depuis la mémoire :
| ARM | 68000 |
|---|---|
| LDR R0, [R8] | MOVE.L (A0), D0 |
| LDR R0, [R8, #16] | MOVE.L 16(A0), D0 |
| LDR R0, [R8, R1] | MOVE.L 0(A0,D1.L), D0 |
| LDR R0, [R8, #16]! | MOVE.L 16(A0), D0 ; pre-indexe LEA 16(A0), A0 ; maj base |
| LDR R0, [R8], #16 | MOVE.L (A0)+, D0 ; si offset=4 ; sinon : MOVE.L (A0), D0 LEA 16(A0), A0 |
| LDRB R0, [R8] | CLR.L D0 MOVE.B (A0), D0 |
| LDRH R0, [R8] | CLR.L D0 MOVE.W (A0), D0 |
| LDRSB R0, [R8] | MOVE.B (A0), D0 EXT.W D0 EXT.L D0 |
| LDRSH R0, [R8] | MOVE.W (A0), D0 EXT.L D0 |
Entreposage en mémoire :
| ARM | 68000 |
|---|---|
| STR R0, [R8] | MOVE.L D0, (A0) |
| STR R0, [R8, #16] | MOVE.L D0, 16(A0) |
| STR R0, [R8, R1] | MOVE.L D0, 0(A0,D1.L) |
| STR R0, [R8, #16]! | MOVE.L D0, 16(A0) LEA 16(A0), A0 |
| STR R0, [R8], #16 | MOVE.L D0, (A0) LEA 16(A0), A0 |
| STRB R0, [R8] | MOVE.B D0, (A0) |
| STRH R0, [R8] | MOVE.W D0, (A0) |
Transferts multiples (LDM/STM) :
| ARM | 68000 |
|---|---|
| LDMIA R8!, {R0-R3} | MOVEM.L (A0)+, D0-D3 |
| STMDB R13!, {R0-R3} | MOVEM.L D0-D3, -(SP) |
| LDMIA R13!, {R0-R3} | MOVEM.L (SP)+, D0-D3 |
| PUSH {R4-R7, LR} | MOVEM.L D4-D7, -(SP) ; LR est déjà sur la pile via BSR |
| POP {R4-R7, PC} | MOVEM.L (SP)+, D4-D7 RTS |
Note : LDM/STM de l'ARM avec différents modes d'adressage (IA=Increment After, IB=Increment Before, DA=Decrement After, DB=Decrement Before) se convertissent en MOVEM avec les modes(An)+ ou -(An) du 68000. Attention : MOVEM du 68000 ne supporte que (An)+ pour la lecture et -(An) pour l'écriture.
Chargement d'adresses :
| ARM | 68000 |
|---|---|
| ADR R0, label | LEA label, A0 ; ou LEA label(PC), A0 |
| LDR R0, =valeur32 | MOVE.L #valeur32, D0 ; (pas de bassin de constantes) |
Note : ARM utilise souvent LDR Rn, =constante pour charger des constantes 32 bits depuis un bassin (littéral de bassin). Le 68000 charge directement les constantes 32 bits en mode immédiat.
Conversion des instructions arithmétiques
| ARM | 68000 |
|---|---|
| ADD R0, R1, R2 | MOVE.L D1, D0 ADD.L D2, D0 |
| ADD R0, R1, #100 | MOVE.L D1, D0 ADD.L #100, D0 ; ou ADDQ.L #n (1<=n<=8) |
| ADD R0, R0, R1 | ADD.L D1, D0 |
| ADD R0, R0, #1 | ADDQ.L #1, D0 |
| ADDS R0, R1, R2 | MOVE.L D1, D0 ADD.L D2, D0 ; drapeaux mis a jour automatiquement |
| ADC R0, R1, R2 | MOVE.L D1, D0 ADDX.L D2, D0 |
| SUB R0, R1, R2 | MOVE.L D1, D0 SUB.L D2, D0 |
| SUB R0, R0, #1 | SUBQ.L #1, D0 |
| SUBS R0, R1, R2 | MOVE.L D1, D0 SUB.L D2, D0 |
| SBC R0, R1, R2 | MOVE.L D1, D0 SUBX.L D2, D0 |
| RSB R0, R1, #0 | MOVE.L D1, D0 NEG.L D0 |
| RSB R0, R1, R2 | MOVE.L D2, D0 SUB.L D1, D0 |
| MUL R0, R1, R2 | ; 68000 : MULU/MULS 16 bits seul. Pour 32x32 : routine logicielle ou 68020 MULS.L D1,D0 |
| UMULL R0, R1, R2, R3 | ; 68000 : routine logicielle ; 68020 : MULU.L D2,D0:D1 |
| MLA R0, R1, R2, R3 | ; R0 = R1*R2 + R3 ; 68000 : routine logicielle |
| CMP R0, R1 | CMP.L D1, D0 |
| CMP R0, #0 | TST.L D0 |
| CMP R0, #100 | CMP.L #100, D0 |
| CMN R0, R1 | MOVE.L D1, D7 ; copie temp NEG.L D7 CMP.L D7, D0 ; ou ADD.L D1,D0 puis tester drapeaux (mais modifie D0) |
Note importante : Sur ARM, les instructions a 3 opérandes (ADD R0, R1, R2) placent le résultat dans un registre différent des sources. Sur le 68000, les opérations sont a 2 opérandes (ADD.L D2,D0 modifie D0). Il faut donc souvent ajouter un MOVE.L préalable pour préserver l'opérande source.
Si R0 = R1 (destination = première source), le MOVE.L est inutile et la conversion est directe :
- ADD R0, R0, R2 → ADD.L D2, D0
Conversion des instructions logiques et décalage
| ARM | 68000 |
|---|---|
| AND R0, R1, R2 | MOVE.L D1, D0 AND.L D2, D0 |
| AND R0, R0, #$FF | AND.L #$FF, D0 | ORR R0, R1, R2 | MOVE.L D1, D0 OR.L D2, D0 |
| EOR R0, R1, R2 | MOVE.L D1, D0 EOR.L D2, D0 |
| BIC R0, R1, R2 | MOVE.L D2, D7 ; copie masque NOT.L D7 ; inverser MOVE.L D1, D0 AND.L D7, D0 |
| BIC R0, R0, #$80 | AND.L #$FFFFFF7F, D0 ; ou BCLR #7, D0 |
| TST R0, R1 | MOVE.L D0, D7 AND.L D1, D7 ; positionner drapeaux |
| TST R0, #$01 | BTST #0, D0 |
| TEQ R0, R1 | MOVE.L D0, D7 EOR.L D1, D7 ; positionner drapeaux |
| MOV R0, R1, LSL #3 | MOVE.L D1, D0 LSL.L #3, D0 |
| MOV R0, R1, LSR #3 | MOVE.L D1, D0 LSR.L #3, D0 |
| MOV R0, R1, ASR #3 | MOVE.L D1, D0 ASR.L #3, D0 |
| MOV R0, R1, ROR #3 | MOVE.L D1, D0 ROR.L #3, D0 |
| MOV R0, R1, LSL R2 | MOVE.L D1, D0 LSL.L D2, D0 |
| MOV R0, R1, RRX | MOVE.L D1, D0 ROXR.L #1, D0 |
Barrel shifter combine (spécificité ARM) :
| ARM | 68000 |
|---|---|
| ADD R0, R1, R2, LSL #3 | MOVE.L D2, D7 LSL.L #3, D7 MOVE.L D1, D0 ADD.L D7, D0 |
| SUB R0, R1, R2, ASR #2 | MOVE.L D2, D7 ASR.L #2, D7 MOVE.L D1, D0 SUB.L D7, D0 |
| AND R0, R1, R2, ROR #8 | MOVE.L D2, D7 ROR.L #8, D7 MOVE.L D1, D0 AND.L D7, D0 |
| CMP R0, R1, LSL #2 | MOVE.L D1, D7 LSL.L #2, D7 CMP.L D7, D0 |
Note : Le barrel shifter intégré de l'ARM est l'une des différences les plus significatives. Chaque instruction ARM avec un opérande décalé nécessite un registre temporaire et une instruction de décalage supplémentaire sur le 68000. D7 est souvent utilise comme registre temporaire pour cela.
Conversion des instructions de branchement
| ARM | 68000 |
|---|---|
| B label | BRA label |
| BL proc | BSR proc ; ou JSR proc |
| BX LR | RTS |
| BX R0 | JMP (A0) ; si R0 = A0 |
Branchements conditionnels :
| ARM | 68000 |
|---|---|
| BEQ label | BEQ label |
| BNE label | BNE label |
| BCS / BHS label | BCC label ; Attention: inverse! ; ARM CS = Carry Set = C=1 ; 68000 BCS = Branch if Carry Set ; -> utiliser BCS (meme sens) |
| BCS label | BCS label |
| BCC / BLO label | BCC label |
| BMI label | BMI label |
| BPL label | BPL label |
| BVS label | BVS label |
| BVC label | BVC label |
| BHI label | BHI label |
| BLS label | BLS label |
| BGE label | BGE label |
| BLT label | BLT label |
| BGT label | BGT label |
| BLE label | BLE label |
Note sur les conditions : Les conditions ARM et 68000 utilisent les mêmes noms pour les mêmes tests logiques. La correspondance est directe car les deux processeurs utilisent les mêmes drapeaux (N,Z,C,V) avec la même sémantique pour les branchements.
Boucles :
| ARM | 68000 |
|---|---|
|
|
Note : DBRA du 68000 décrémenté un compteur 16 bits et boucle si le résultat n'est pas -1. Pour des compteurs 32 bits, utiliser SUBQ.L #1,Dn / BNE .boucle.
Conversion des modes d'adressage
L'ARM a un nombre limite de modes d'adressage pour LDR/STR. Le 68000 a 14 modes d'adressage utilisables avec la plupart des instructions.
| ARM | 68000 |
|---|---|
|
|
Accès aux tableaux :
Conversion des instructions de pile et appels
Convention d'appel AAPCS (ARM) :
Note : La convention d'appel ARM (AAPCS) passe les 4 premiers arguments dans R0-R3, le reste sur la pile. La convention C classique du 68000 passe tout sur la pile. Lors de la conversion, les arguments dans les registres ARM doivent être empilés pour le 68000, ou bien on peut adopter une convention registre personnalisée pour le 68000 (D0-D3 pour les arguments).
Prologue/épilogue de fonction :
Accès aux paramètres et variables locales :
Sauvegarde/restauration de registres :
- ; ARM : PUSH/POP
- PUSH {R0-R3} MOVEM.L D0-D3, -(SP)
- POP {R0-R3} MOVEM.L (SP)+, D0-D3
-
- ; ARM : STMDB/LDMIA (equivalents de PUSH/POP)
- STMDB SP!, {R4-R11, LR} MOVEM.L D4-D7/A0-A3, -(SP)
- LDMIA SP!, {R4-R11, PC} MOVEM.L (SP)+, D4-D7/A0-A3
- RTS
Conversion des instructions spécifiques ARM
Swap (SWP) :
| ARM | 68000 |
|---|---|
| SWP R0, R1, [R8] | ; Pas d'équivalent atomique MOVE.L (A0), D0 ; lire ancien MOVE.L D1, (A0) ; écrire nouveau ; Attention : non atomique ! ; TAS.B est la seule op. atomique |
Multiplication longue (UMULL, SMULL, UMLAL, SMLAL) :
| ARM | 68000 |
|---|---|
| UMULL R0, R1, R2, R3 | ; R1:R0 = R2 * R3 (64 bits) ; 68000 : routine logicielle ; 68020 : MULU.L D2,D0:D1 |
| SMLAL R0, R1, R2, R3 | ; R1:R0 += R2 * R3 ; 68000 : routine logicielle |
CLZ (Count Leading Zeros, ARMv5+) :
| ARM | 68000 |
|---|---|
| CLZ R0, R1 | ; Pas d'equivalent direct ; Implementer par boucle : MOVEQ #32, D0 TST.L D1 BEQ.S .fin MOVEQ #0, D0 .boucle: BTST D0, D1 BNE.S .fin ADDQ.L #1, D0 CMP.L #32, D0 BLT.S .boucle .fin: ; Note: teste du bit 0 vers 31 ; CLZ compte du bit 31 vers 0 ; Ajuster: D0 = 31 - D0 |
MRS/MSR (accès au registre d'état) :
| ARM | 68000 |
|---|---|
| MRS R0, CPSR | MOVE.W SR, D0 ; (mode superviseur requis ; pour lire SR complet) |
| MSR CPSR_f, R0 | ; Pas d'equivalent simple ; Utiliser MOVE SR,Dn + AND/OR ; pour modifier les drapeaux |
SWI (Software Interrupt) :
| ARM | 68000 |
|---|---|
| SWI #0 | TRAP #0 |
| SWI #imm24 | TRAP #n ; n = 0..15 ; Le 68000 n'a que 16 TRAP ; Pour plus de fonctions, passer ; le numéro dans D0 : MOVE.L #imm24, D0 TRAP #0 |
Instructions Thumb (jeu d'instructions 16 bits) :
Le mode Thumb de l'ARM utilise des instructions 16 bits pour une meilleure densité de code. Le 68000 n'a pas d'équivalent direct. Les instructions Thumb doivent être converties dans leur forme ARM équivalente, puis converties en 68000.
Conversion de l'exécution conditionnelle ARM
L'exécution conditionnelle est une caractéristique majeure de l'ARM. Presque toute instruction peut être rendue conditionnelle par un suffixe (EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL).
Méthode de conversion :
- ; ARM : instruction conditionnelle simple
- MOVEQ R0, #1 BNE.S .skip
- MOVEQ #1, D0
- .skip:
-
- ; ARM : paire conditionnelle (if/else)
- MOVEQ R0, #1 BNE.S .else
- MOVNE R0, #0 MOVEQ #1, D0
- BRA.S .fin
- .else:
- CLR.L D0
- .fin:
-
- ; ARM : sequence conditionnelle longue
- ADDEQ R0, R0, #1 BNE.S .skip
- SUBEQ R1, R1, #1 ADDQ.L #1, D0
- MOVEQ R2, #0 SUBQ.L #1, D1
- CLR.L D2
- .skip:
Optimisation avec Scc du 68000 :
- ; ARM : set conditionnel
- MOVEQ R0, #1
- MOVNE R0, #0
-
- ; 68000 : optimise avec Scc
- SEQ D0 ; D0 = $FF si Z=1, $00 sinon
- AND.L #1, D0 ; D0 = 1 si Z=1, 0 sinon
-
- ; ARM : increment conditionnel
- CMP R1, R2
- ADDGT R0, R0, #1
-
- ; 68000 : equivalent
- CMP.L D2, D1
- BLE.S .skip
- ADDQ.L #1, D0
- .skip:
-
- ; ARM : max(R0, R1)
- CMP R0, R1
- MOVLT R0, R1
-
- ; 68000 : equivalent
- CMP.L D1, D0
- BGE.S .skip
- MOVE.L D1, D0
- .skip:
-
- ; ARM : abs(R0)
- CMP R0, #0
- RSBLT R0, R0, #0
-
- ; 68000 : equivalent (plus simple !)
- TST.L D0
- BPL.S .skip
- NEG.L D0
- .skip:
Note : L'exécution conditionnelle ARM évite les branchements (bénéfique pour le pipeline ARM). Sur le 68000, les branches courtes (BCC.S) avec un déplacement 8 bits sont efficaces et le coût de la conversion est faible.
Exemples complets de conversion ARM → 68000
Exemple 1 : Longueur d'une chaîne (strlen)
- ; --- ARM ---
- strlen:
- MOV R1, R0 ; sauver debut
- .boucle:
- LDRB R2, [R0], #1 ; charger octet, R0++
- CMP R2, #0
- BNE .boucle
- SUB R0, R0, R1 ; longueur = fin - debut
- SUB R0, R0, #1 ; -1 (terminateur)
- BX LR
-
- ; --- 68000 ---
- strlen:
- ; A0 = adresse de la chaine (entree)
- ; D0 = longueur (sortie)
- MOVEA.L A0, A1 ; sauver debut
- .boucle:
- TST.B (A0)+ ; tester octet, A0++
- BNE.S .boucle
- MOVE.L A0, D0
- SUB.L A1, D0 ; fin - debut
- SUBQ.L #1, D0 ; -1 (terminateur)
- RTS
Exemple 2 : Copie de mémoire (memcpy)
- ; --- ARM ---
- memcpy:
- ; R0 = dest, R1 = src, R2 = nombre d'octets
- PUSH {R0} ; sauver dest pour retour
- .boucle:
- SUBS R2, R2, #1
- LDRBPL R3, [R1], #1
- STRBPL R3, [R0], #1
- BPL .boucle
- POP {R0}
- BX LR
-
- ; --- 68000 ---
- memcpy:
- ; A0 = dest, A1 = src, D0 = nombre d'octets
- MOVEA.L A0, A2 ; sauver dest pour retour
- SUBQ.L #1, D0
- BMI.S .fin
- .boucle:
- MOVE.B (A1)+, (A0)+
- DBRA D0, .boucle
- .fin:
- MOVEA.L A2, A0 ; restaurer dest
- RTS
Exemple 3 : Recherche dichotomique (binary search)
- ; --- ARM ---
- bsearch:
- ; R0 = tableau, R1 = taille, R2 = valeur
- MOV R3, #0 ; bas = 0
- .boucle:
- CMP R3, R1
- BGE .pas_trouve
- ADD R4, R3, R1
- MOV R4, R4, LSR #1 ; milieu = (bas+haut)/2
- LDR R5, [R0, R4, LSL #2] ; tab[milieu]
- CMP R5, R2
- BEQ .trouve
- MOVLT R3, R4
- ADDLT R3, R3, #1 ; bas = milieu + 1
- MOVGT R1, R4 ; haut = milieu
- B .boucle
- .trouve:
- MOV R0, R4 ; retourner index
- BX LR
- .pas_trouve:
- MVN R0, #0 ; retourner -1
- BX LR
-
- ; --- 68000 ---
- bsearch:
- ; A0 = tableau, D1 = taille, D0 = valeur cherchee
- ; Retour : D0 = index ou -1
- MOVEM.L D2-D5, -(SP)
- CLR.L D3 ; D3 = bas = 0
- .boucle:
- CMP.L D1, D3
- BGE.S .pas_trouve
- MOVE.L D3, D4
- ADD.L D1, D4
- LSR.L #1, D4 ; D4 = milieu
- MOVE.L D4, D5
- LSL.L #2, D5 ; D5 = milieu * 4
- MOVE.L 0(A0,D5.L), D2 ; D2 = tab[milieu]
- CMP.L D0, D2
- BEQ.S .trouve
- BLT.S .trop_petit
- ; tab[milieu] > valeur : haut = milieu
- MOVE.L D4, D1
- BRA.S .boucle
- .trop_petit:
- ; tab[milieu] < valeur : bas = milieu + 1
- MOVE.L D4, D3
- ADDQ.L #1, D3
- BRA.S .boucle
- .trouve:
- MOVE.L D4, D0
- MOVEM.L (SP)+, D2-D5
- RTS
- .pas_trouve:
- MOVEQ #-1, D0
- MOVEM.L (SP)+, D2-D5
- RTS
Exemple 4 : Inversion d'endianness (utile pour le portage)
- ; --- ARM (little-endian -> big-endian) ---
- bswap32:
- ; R0 = valeur a inverser
- EOR R1, R0, R0, ROR #16
- BIC R1, R1, #$00FF0000
- MOV R0, R0, ROR #8
- EOR R0, R0, R1, LSR #8
- BX LR
-
- ; --- 68000 (inutile si deja big-endian, mais pour reference) ---
- bswap32:
- ; D0 = valeur a inverser
- ROL.W #8, D0 ; swap octets mot bas
- SWAP D0 ; echanger mots
- ROL.W #8, D0 ; swap octets mot haut
- RTS
Exemple 5 : Factorielle récursive
- ; --- ARM ---
- factorielle:
- CMP R0, #1
- MOVLE R0, #1
- BXLE LR
- PUSH {R0, LR}
- SUB R0, R0, #1
- BL factorielle
- POP {R1, LR}
- MUL R0, R0, R1
- BX LR
-
- ; --- 68000 ---
- factorielle:
- ; D0 = n (entree et sortie)
- CMP.L #1, D0
- BGT.S .recurse
- MOVEQ #1, D0
- RTS
- .recurse:
- MOVE.L D0, -(SP) ; sauver n
- SUBQ.L #1, D0
- BSR factorielle ; D0 = fact(n-1)
- MOVE.L (SP)+, D1 ; D1 = n
- ; Multiplication 16 bits (suffisant pour petites valeurs)
- MULU.W D1, D0 ; D0 = n * fact(n-1)
- RTS
Tableau récapitulatif des différences ARM → 68000
| Aspect | ARM | 68000 |
|---|---|---|
| Registres généraux | 16 (R0-R15) | 8 (D0-D7) + 8 (A0-A7) |
| Registres spéciaux | SP(R13),LR(R14),PC(R15) | SP(A7), PC |
| Architecture | RISC load/store | CISC memoire-memoire |
| Taille instructions | 32 bits fixe | 16-80 bits variable |
| Opérandes | 3 (Rd, Rn, Op2) | 2 (source, dest) |
| Barrel shifter | Integre (Op2) | Instructions séparées |
| Exécution conditionnelle | Toute instruction | Bcc/Scc seulement |
| Flags | Optionnels (suffixe S) | Automatiques |
| Chargement mémoire | LDR/STR seulement | MOVE depuis/vers mémoire |
| Transferts multiples | LDM/STM (flexibles) | MOVEM (An)+/-(An) |
| Appel fonction | BL (LR=retour) | BSR/JSR (pile=retour) |
| Retour fonction | BX LR | RTS |
| Interruption logicielle | SWI #imm24 | TRAP #0..15 |
| Endianness | Bi (souvent LE) | Big-endian |
| Alignement | 4 octets requis | 2 octets requis |
| Modes adressage mémoire | ~9 modes (LDR/STR) | 14 modes (toute instruction) |
| Bassin de constantes | Oui (LDR R,=const) | Non (immédiat 32 bits) |
| Multiplication 32 bits | MUL (32x32→32) | Routine logicielle |
| Multiplication 64 bits | UMULL/SMULL | Routine logicielle |
| Division | Aucune (logicielle) | DIVU/DIVS (16 bits) |
| Swap atomique | SWP | TAS.B (octet seulement) |
| Comptage de zéros | CLZ (ARMv5+) | Boucle manuelle |
Résumé
La conversion de code ARM en 68000 présente des défis spécifiques liés aux différences fondamentales entre architectures RISC et CISC. Voici les points clefs :
- Architecture load/store : Les séquences ARM LDR + operation + STR peuvent souvent être remplacées par une seule instruction 68000 opérant directement en mémoire. Cela réduit le nombre d'instructions mais change la structure du code.
- Opérandes a 3 registres : Les instructions ARM a 3 opérandes (ADD Rd, Rn, Op2) nécessitent un MOVE.L préalable sur le 68000 quand la destination diffère de la première source. Quand Rd = Rn, la conversion est directe.
- Barrel shifter : Le décalage intégré dans l'operande 2 de l'ARM n'a pas d'équivalent sur le 68000. Il faut décomposer en une instruction de décalage + l'opération, en utilisant un registre temporaire (typiquement D7).
- Exécution conditionnelle : Les instructions conditionnelles ARM doivent être converties en séquences Bcc.S + instruction sur le 68000. Pour une instruction isolée, le coût est faible (2 octets pour le BCC.S). Pour des séquences conditionnelles, regrouper sous un seul branchement.
- Correspondance des registres : R0-R7 -> D0-D7 pour les données, R8-R12 → A0-A4 pour les pointeurs. R13(SP) → A7, R14(LR) n'a pas d'équivalent (adresse de retour sur pile), R15(PC) → PC.
- Conventions d'appel : L'ARM passe les 4 premiers arguments dans R0-R3 (AAPCS), le 68000 les passe typiquement sur la pile. BL → BSR/JSR, BX LR → RTS. LINK/UNLK remplacent la gestion manuelle du frame pointer ARM.
- Transferts multiples : LDM/STM de l'ARM se convertissent en MOVEM du 68000. Attention : MOVEM ne supporte que les modes (An)+ et -(An), pas les variations IA/IB/DA/DB de l'ARM. LDMIA/STMDB sont les plus courants et se convertissent directement.
- Drapeaux et conditions : Les drapeaux N,Z,C,V sont les mêmes sur les deux architectures. La différence majeure est que les drapeaux ARM ne sont mis a jour que si le suffixe S est présent, tandis que le 68000 les met a jour automatiquement. Vérifier que le code ne dépend pas de drapeaux non modifiés.
- Endianness : L'ARM est souvent en little-endian tandis que le 68000 est big-endian. Pour les données binaires (fichiers, protocoles réseau), inverser l'ordre des octets avec la séquence ROL.W #8 + SWAP + ROL.W #8.
- Multiplication/Division : L'ARM a MUL 32x32->32 mais pas de division matérielle. Le 68000 a DIVU/DIVS 16 bits mais pas de multiplication 32 bits (sauf 68020+). Les deux architectures nécessitent des routines logicielles pour les opérations dépassant leurs capacités matérielles.