Section courante

A propos

Section administrative du site

 Langage  Elément  Tutoriel  Annexe  Aide 
ABAP/4
Ada
Assembleur
Assembly & bytecode
ASP (Active Server Pages)
Basic
C
C++
C# (C Sharp)
Cobol
ColdFusion
Fortran
HTML
Java
JavaScript
LISP
Logo
LotusScript
Oberon
Pascal
Perl
PHP
PL/1
Prolog
Python
Rebol
REXX
Ruby
Rust
SAS
NoSQL
SQL
Swift
X++ (Axapta)
GNAT
SMALLAda
VHDL
Assembleur 370
Assembleur 1802
Assembleur 4004
Assembleur 6502
Assembleur 6800
Assembleur 68000
Assembleur 8080 et 8085
Assembleur 8089
Assembleur 80x86
Assembleur AGC4
Assembleur ARM
Assembleur DPS 8000
Assembleur i860
Assembleur Itanium
Assembleur MIPS
Assembleur PDP-11
Assembleur PowerPC
Assembleur RISC-V
Assembleur SPARC
Assembleur SuperH
Assembleur UNIVAC I
Assembleur VAX
Assembleur Z80
Assembleur Z8000
Assembleur z/Architecture
ASSEMBLER/MONITOR 64
Micol Assembler
GFA Assembler
A86
MASM (Macro Assembler)
TASM (Turbo Assembler)
CIL
Jasmin
LLVM
MSIL
Parrot
P-Code (PCode)
SWEET16
G-Pascal
ASP 1.0
ASP 2.0
ASP 3.0
ASP.NET
ASP.NET Core
ABasiC (Amiga)
Adam SmartBASIC
Altair BASIC
AmigaBASIC (Amiga)
AMOS Basic (Amiga)
Atari Basic (Atari 400, 600 XL, 800, 800XL)
Basic Apple II (Integer BASIC/APPLESOFT)
Basic Commodore 64 (CBM-BASIC)
Basic Commodore 128 (BASIC 7.0)
Basic Commodore VIC-20 (CBM-BASIC 2.0)
Basic Coco 1 (Color Basic)
Basic Coco 2 (Extended Color Basic)
Basic Coco 3 (Extended Color Basic 2.0)
BASICA (PC DOS)
Basic Pro
BBC BASIC
Blitz BASIC (Amiga)
DarkBASIC
Dartmouth BASIC
GFA-Basic (Atari ST/Amiga)
GWBASIC (MS-DOS)
Liberty BASIC
Locomotive BASIC (Amstrad CPC)
MSX-Basic
Omikron Basic (Atari ST)
Oric Extended Basic
Power Basic
Quick Basic/QBasic (MS-DOS)
Sinclair BASIC (ZX80, ZX81, ZX Spectrum)
ST BASIC (Atari ST)
Turbo Basic
Vintage BASIC
VBScript
Visual Basic (VB)
Visual Basic .NET (VB .NET)
Visual Basic pour DOS
Yabasic
BeckerBASIC
SIMONS' BASIC
Basic09 d'OS-9
Disk Extended Color Basic
Basic09 d'OS-9
Disk Extended Color Basic
Access
Excel
Visual Basic pour Windows
Visual Basic .NET pour Windows
C Shell Unix (csh)
C pour Amiga
C pour Atari ST
C pour DOS
C pour Falcon030
C pour GEMDOS (Atari ST)
C pour Linux
C pour PowerTV OS
C pour OS/2
C pour Unix
C pour Windows
Aztec C
CoCo-C
GNU C
HiSoft C
IBM C/2
Introl-C
Lattice C
Microsoft C
MinGW C
MSX-C
Open Watcom C
OS-9 C Compiler
Pure C
Quick C
Turbo C
HiSoft C for Atari ST
HiSoft C for CP/M (Amstrad CPC)
C++ pour OS/2
C++ pour Windows
Borland C++
C++Builder
IBM VisualAge C++
Intel C++
MinGW C++
Open Watcom C++
Symantec C++
Turbo C++
Visual C++
Visual C++ .NET
Watcom C++
Zortech C++
C# (C Sharp) pour Windows
Apple III Cobol
Microsoft Cobol
BlueDragon
Lucee
OpenBD
Railo
Smith Project
Microsoft Fortran
WATFOR-77
CSS
FBML
Open Graph
SVG
XML
XSL/XSLT
LESS
SASS
GCJ (GNU)
JSP
Jython
Visual J++
Node.js
TypeScript
AutoLISP
ACSLogo
LotusScript pour Windows
Amiga Oberon
Oberon .NET
Apple Pascal
Delphi/Kylix/Lazarus
Free Pascal
GNU Pascal
HighSpeed Pascal
IBM Personal Computer Pascal
Lisa Pascal
Maxon Pascal
MPW Pascal
OS-9 Pascal
OSS Personal Pascal
Pascal-86
Pascal du Cray Research
Pascal/VS
Pascal-XT
PURE Pascal
QuickPascal
RemObjets Chrome
Sun Pascal
THINK Pascal
Tiny Pascal (TRS-80)
Turbo Pascal
UCSD Pascal
VAX Pascal
Virtual Pascal
Turbo Pascal for CP/M-80
Turbo Pascal for DOS
Turbo Pascal for Macintosh
Turbo Pascal for Windows
CodeIgniter (Cadre d'application)
Drupal (Projet)
Joomla! (Projet)
Phalanger (PHP .NET)
phpBB (Projet)
Smarty (balise)
Twig (balise)
Symfony (Cadre d'application)
WordPress (Projet)
Zend (Cadre d'application)
PL360
PL/M-80
PL/M-86
Turbo Prolog
CPython
IronPython
Jython
PyPy
AREXX
Regina REXX
JMP
Btrieve
Cassandra
Clipper
CouchDB
dBASE
Hbase
Hypertable
MongoDB
Redis
Access
BigQuery
DB2
H2
Interbase
MySQL
Oracle
PostgreSQL
SAP HANA
SQL Server
Sybase
U-SQL
Introduction
Les codes de conditions
Référence des instructions ARM
Les premiers pas
L'ensemble d'instruction ARM
Chargement/entreposage multiple
Ports d'entrée/sortie cartographié en mémoire dans la TI-Nspire
Préface
Notes légal
Dictionnaire
Recherche

Chargement/entreposage multiple

Il est parfois plus efficace de charger (ou d'entreposer) plusieurs valeurs simultanément. Pour cela, nous utilisons les instructions LDM (load multiple) et STM (store multiple). Ces instructions présentent des variantes qui ne diffèrent que par le mode d'accès à l'adresse initiale. C'est le code que nous utiliserons dans cette section. Nous allons détailler chaque instruction étape par étape :

  1. .data
  2.  
  3. tampon_tableau:
  4.  .word 0x00000000             @ tampon_tableau[0]
  5.  .word 0x00000000             @ tampon_tableau[1] 
  6.  .word 0x00000000             @ tampon_tableau[2]. Cet élément a une adresse relative de tampon_tableau+8
  7.  .word 0x00000000             @ tampon_tableau[3]
  8.  .word 0x00000000             @ tampon_tableau[4]
  9.  
  10. .text
  11. .global _start
  12.  
  13. _start:
  14.  adr r0, words+12             @ adresse des words[3] -> r0
  15.  ldr r1, array_buff_bridge    @ adresse de tampon_tableau[0] -> r1
  16.  ldr r2, array_buff_bridge+4  @ adresse de  tampon_tableau[2] -> r2
  17.  ldm r0, {r4,r5}              @ words[3] -> r4 = 0x03; words[4] -> r5 = 0x04
  18.  stm r1, {r4,r5}              @ r4 -> tampon_tableau[0] = 0x03; r5 -> tampon_tableau[1] = 0x04
  19.  ldmia r0, {r4-r6}            @ words[3] -> r4 = 0x03, words[4] -> r5 = 0x04; words[5] -> r6 = 0x05;
  20.  stmia r1, {r4-r6}            @ r4 -> tampon_tableau[0] = 0x03; r5 -> tampon_tableau[1] = 0x04; r6 -> tampon_tableau[2] = 0x05
  21.  ldmib r0, {r4-r6}            @ words[4] -> r4 = 0x04; words[5] -> r5 = 0x05; words[6] -> r6 = 0x06
  22.  stmib r1, {r4-r6}            @ r4 -> tampon_tableau[1] = 0x04; r5 -> tampon_tableau[2] = 0x05; r6 -> tampon_tableau[3] = 0x06
  23.  ldmda r0, {r4-r6}            @ words[3] -> r6 = 0x03; words[2] -> r5 = 0x02; words[1] -> r4 = 0x01
  24.  ldmdb r0, {r4-r6}            @ words[2] -> r6 = 0x02; words[1] -> r5 = 0x01; words[0] -> r4 = 0x00 
  25.  stmda r2, {r4-r6}            @ r6 -> tampon_tableau[2] = 0x02; r5 -> tampon_tableau[1] = 0x01; r4 -> tampon_tableau[0] = 0x00
  26.  stmdb r2, {r4-r5}            @ r5 -> tampon_tableau[1] = 0x01; r4 -> tampon_tableau[0] = 0x00;
  27.  bx lr
  28.  
  29. words:
  30.  .word 0x00000000             @ words[0]
  31.  .word 0x00000001             @ words[1]
  32.  .word 0x00000002             @ words[2]
  33.  .word 0x00000003             @ words[3]
  34.  .word 0x00000004             @ words[4]
  35.  .word 0x00000005             @ words[5]
  36.  .word 0x00000006             @ words[6]
  37.  
  38. array_buff_bridge:
  39.  .word tampon_tableau         @ adresse de tampon_tableau, ou en d'autres termes - tampon_tableau[0]
  40.  .word tampon_tableau+8       @ adresse de  tampon_tableau[2]

Avant de commencer, gardez à l'esprit qu'un .word désigne un bloc de données (mémoire) de 32 bits = 4 octets. Ceci est important pour comprendre le décalage. Le programme se compose donc d'une section .data où nous allouons un tableau vide (tampon_tableau) de 5 éléments. Nous l'utiliserons comme emplacement mémoire inscriptible pour entreposer les données. La section .text contient notre code avec les instructions d'opérations mémoire et un bassin de données en lecture seule contenant deux étiquettes : une pour un tableau de 7 éléments, une autre pour « relier » les sections .text et .data afin d'accéder au tampon_tableau de la section .data.

  1. adr r0, words+12             @ adresse de words[3] -> r0 

Nous utilisons l'instruction ADR (approche paresseuse) pour obtenir l'adresse du 4e élément (mots[3]) dans R0. Nous pointons vers le milieu du tableau de mots, car nous allons opérer en avant et en arrière à partir de là :

gef> break _start
gef> run
gef> nexti

R0 contient désormais l'adresse du mot [3], étant ici 0x80B8. Notre tableau commence donc à l'adresse du mot [0] : 0x80AC (0x80B8 - 0xC).

gef> x/7w 0x00080AC
0x80ac <words>: 0x00000000 0x00000001 0x00000002 0x00000003
0x80bc <words+16>: 0x00000004 0x00000005 0x00000006

Nous préparons R1 et R2 avec les adresses des premier (tampon_tableau[0]) et troisième (tampon_tableau[2]) éléments du tableau tampon_tableau. Une fois les adresses obtenues, nous pouvons commencer à les exploiter.

  1. ldr r1, tampon_tableau_pont    @ adresse de tampon_tableau[0] -> r1
  2. ldr r2, tampon_tableau_pont+4  @ adresse de tampon_tableau[2] -> r2

Après avoir exécuté les deux instructions ci-dessus, R1 et R2 contiennent les adresses de tampon_tableau[0] et tampon_tableau[2].

gef> info register r1 r2
r1      0x100d0     65744
r2      0x100d8     65752

L'instruction suivante utilise LDM pour charger deux valeurs de mots depuis la mémoire pointée par R0. Ainsi, comme R0 a pointé vers l'élément words[3] précédemment, la valeur words[3] est transférée vers R4 et la valeur words[4] vers R5.

  1. ldm r0, {r4,r5}              @ words[3] -> r4 = 0x03; words[4] -> r5 = 0x04

Nous avons chargé plusieurs blocs (2 blocs de données) avec une seule commande, ayant défini R4 = 0x00000003 et R5 = 0x00000004.

gef> info registers r4 r5
r4      0x3      3
r5      0x4      4

Exécutons maintenant l'instruction STM pour stocker plusieurs valeurs en mémoire. L'instruction STM de notre code prend les valeurs (0x3 et 0x4) des registres R4 et R5 et les stocke dans un emplacement mémoire spécifié par R1. Nous avons précédemment configuré R1 pour qu'il pointe vers le premier élément tampon_tableau. Ainsi, après cette opération, tampon_tableau[0] = 0x00000003 et tampon_tableau[1] = 0x00000004. Sauf indication contraire, LDM et STM fonctionnent par pas d'un mot (32 bits = 4 octets).

  1. stm r1, {r4,r5}              @ r4 -> tampon_tableau[0] = 0x03; r5 -> tampon_tableau[1] = 0x04 

Les valeurs 0x3 et 0x4 doivent désormais être stockées aux adresses mémoire 0x100D0 et 0x100D4. L'instruction suivante inspecte deux mots mémoire à l'adresse 0x000100D0.

gef> x/2w 0x000100D0
0x100d0 <tampon_tableau>:  0x3   0x4

Comme mentionné précédemment, LDM et STM présentent des variantes. Le type de variante est défini par le suffixe de l'instruction. Les suffixes utilisés dans l'exemple sont : -IA (augmentation après), -IB (augmentation avant), -DA (diminution après), -DB (diminution avant). Ces variantes diffèrent par la manière dont elles accèdent à la mémoire spécifiée par le premier opérande (le registre stockant l'adresse source ou de destination). En pratique, LDM est identique à LDMIA, ce qui signifie que l'adresse de l'élément suivant à charger est incrémentée après chaque chargement. On obtient ainsi un chargement séquentiel (vers l'avant) des données à partir de l'adresse mémoire spécifiée par le premier opérande (registre stockant l'adresse source) :

  1. ldmia r0, {r4-r6} @ words[3] -> r4 = 0x03, words[4] -> r5 = 0x04; words[5] -> r6 = 0x05;
  2. stmia r1, {r4-r6} @ r4 -> tampon_tableau[0] = 0x03; r5 -> tampon_tableau[1] = 0x04; r6 -> tampon_tableau[2] = 0x05

Après avoir exécuté les deux instructions ci-dessus, les registres R4-R6 et les adresses mémoire 0x000100D0, 0x000100D4 et 0x000100D8 contiennent les valeurs 0x3, 0x4 et 0x5.

gef> info registers r4 r5 r6
r4     0x3     3
r5     0x4     4
r6     0x5     5
gef> x/3w 0x000100D0
0x100d0 <tampon_tableau>: 0x00000003  0x00000004  0x00000005

L'instruction LDMIB augmente d'abord l'adresse source de 4 octets (valeur d'un mot), puis effectue le premier chargement. De cette manière, le chargement des données est toujours séquentiel (vers l'avant), mais le premier élément est décalé de 4 octets par rapport à l'adresse source. C'est pourquoi, dans notre exemple, le premier élément chargé de la mémoire vers R4 par l'instruction LDMIB est 0x00000004 (mots[4]) et non 0x00000003 (mots[3]) comme indiqué par R0.

  1. ldmib r0, {r4-r6}            @ words[4] -> r4 = 0x04; words[5] -> r5 = 0x05; words[6] -> r6 = 0x06
  2. stmib r1, {r4-r6}            @ r4 -> tampon_tableau[1] = 0x04; r5 -> tampon_tableau[2] = 0x05; r6 -> tampon_tableau[3] = 0x06

Après avoir exécuté les deux instructions ci-dessus, les registres R4-R6 et les adresses mémoire 0x100D4, 0x100D8 et 0x100DC contiennent les valeurs 0x4, 0x5 et 0x6.

gef> x/3w 0x100D4
0x100d4 <tampon_tableau+4>: 0x00000004  0x00000005  0x00000006
gef> info register r4 r5 r6
r4     0x4    4
r5     0x5    5
r6     0x6    6

Lorsque nous utilisons l'instruction LDMDA, tout fonctionne à l'envers. R0 pointe vers les mots [3]. Au début du chargement, nous reculons et chargeons les mots [3], [2] et [1] dans R6, R5 et R4. Les registres sont également chargés à l'envers. Ainsi, une fois l'instruction terminée, R6 = 0x00000003, R5 = 0x00000002, R4 = 0x00000001. La logique ici est que nous reculons car nous décrémentons l'adresse source APRÈS chaque chargement. Le chargement du registre à l'envers se produit car à chaque chargement, nous décrémentons l'adresse mémoire et donc le numéro de registre afin de respecter la logique selon laquelle les adresses mémoire supérieures correspondent à des numéros de registre supérieurs. Prenons l'exemple de LDMIA (ou LDM) : nous avons d'abord chargé le registre inférieur car l'adresse source était inférieure, puis le registre supérieur car l'adresse source avait augmenté.

Chargement multiple, décrémentation après :

  1. ldmda r0, {r4-r6} @ words[3] -> r6 = 0x03; words[2] -> r5 = 0x02; words[1] -> r4 = 0x01

Registres R4, R5 et R6 après exécution :

gef> info register r4 r5 r6
r4     0x1    1
r5     0x2    2
r6     0x3    3

Charger plusieurs, décrémenter avant :

  1. ldmdb r0, {r4-r6} @ words[2] -> r6 = 0x02; words[1] -> r5 = 0x01; words[0] -> r4 = 0x00

Registres R4, R5 et R6 après exécution :

gef> info register r4 r5 r6
r4 0x0 0
r5 0x1 1
r6 0x2 2

Entreposer plusieurs valeurs, puis décrémenter après :

  1. stmda r2, {r4-r6} @ r6 -> tampon_tableau[2] = 0x02; r5 -> tampon_tableau[1] = 0x01; r4 -> tampon_tableau[0] = 0x00

Adresses mémoire de tampon_tableau[2], tampon_tableau[1] et tampon_tableau[0] après exécution :

gef> x/3w 0x100D0
0x100d0 <tampon_tableau>: 0x00000000 0x00000001 0x00000002

Entreposer plusieurs, décrémenter avant :

  1. stmdb r2, {r4-r5} @ r5 -> tampon_tableau[1] = 0x01; r4 -> tampon_tableau[0] = 0x00;

Adresses mémoire de tampon_tableau[1] et tampon_tableau[0] après exécution :

gef> x/2w 0x100D0
0x100d0 <tampon_tableau>: 0x00000000 0x00000001

PUSH et POP

Le processus comporte un emplacement mémoire appelé Pile. Le pointeur de pile (SP) est un registre qui, en temps normal, pointe toujours vers une adresse dans la région mémoire de la pile. Les applications utilisent souvent la pile pour le stockage temporaire de données. Comme mentionné précédemment, ARM utilise un modèle de chargement/stockage pour l'accès mémoire, ce qui signifie que les instructions LDR / STR ou leurs dérivées (LDM.. /STM..) sont utilisées pour les opérations mémoire. En x86, on utilise PUSH et POP pour charger et stocker depuis et vers la pile. En ARM, on peut également utiliser ces deux instructions :

Lorsque l'on pousse un élément sur la pile descendante complète, voici ce qui se produit7nbsp;:

Lorsque nous extrayons un élément de la pile, voici ce qui se produit :

Dans l'exemple suivant, nous utilisons à la fois PUSH/POP et LDMIA/STMDB :

  1. .text
  2. .global _start
  3.  
  4. _start:
  5.    mov r0, #3
  6.    mov r1, #4
  7.    push {r0, r1}
  8.    pop {r2, r3}
  9.    stmdb sp!, {r0, r1}
  10.    ldmia sp!, {r4, r5}
  11.    bkpt

Regardons le désassemblage de ce code :

gladir@labs:~$ as pushpop.s -o pushpop.o
gladir@labs:~$ ld pushpop.o -o pushpop
gladir@labs:~$ objdump -D pushpop pushpop: file format elf32-littlearm

Disassembly of section .text:

00008054 <_start>:
8054: e3a00003 mov r0, #3
8058: e3a01004 mov r1, #4
805c: e92d0003 push {r0, r1}
8060: e8bd000c pop {r2, r3}
8064: e92d0003 push {r0, r1}
8068: e8bd0030 pop {r4, r5}
806c: e1200070 bkpt 0x0000

Comme vous pouvez le constater, nos instructions LDMIA et STMDB ont été traduites en PUSH et POP. En effet, PUSH est synonyme de STMDB sp!, reglist et POP est synonyme de LDMIA sp! reglist.

Exécutons ce code dans GDB :

gef> break _start
gef> run
gef> nexti 2
[...]
gef> x/w $sp
0xbefff7e0: 0x00000001

Après avoir exécuté les deux premières instructions, nous avons rapidement vérifié l'adresse mémoire et la valeur vers lesquelles pointe SP. L'instruction PUSH suivante doit diminuer SP de 8 et stocker les valeurs de R1 et R0 (dans cet ordre) sur la pile.

gef> nexti
[...] ----- Stack -----
0xbefff7d8|+0x00: 0x3 <- $sp
0xbefff7dc|+0x04: 0x4
0xbefff7e0|+0x08: 0x1
[...]
gef> x/w $sp
0xbefff7d8: 0x00000003

Ensuite, ces deux valeurs (0x3 et 0x4) sont extraites de la pile et transférées dans les registres, de sorte que R2 = 0x3 et R3 = 0x4. SP est augmenté de 8 :

gef> nexti
gef> info register r2 r3
r2     0x3    3
r3     0x4    4
gef> x/w $sp
0xbefff7e0: 0x00000001


PARTAGER CETTE PAGE SUR
Dernière mise à jour : Vendredi, le 4 avril 2025