        NAME    main
        
        PUBLIC  __iar_program_start
        
        SECTION .intvec : CODE (2)
        CODE32
        
__iar_program_start
        B       main

; Table de pages (correspondance page / trame)
tableDePages            DC32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
; Table de pages inverse (correspondance trame / page)
tableDePagesInverse     DC32 -1, -1, -1
; Compteurs d'accès à chaque trame
tableAcces              DC32 0, 0, 0

        SECTION .text : CODE (2)
        CODE32

;###############################################################################

DecomposeAdresse
  ; Paramètres d'entrée :
  ; - R8 : Adresse virtuelle à décomposer
  ;
  ; Valeurs de sortie :
  ; - R0 : Index de la page correspondant à cette adresse, numéroté à partir de 0
  ; - R1 : Décalage (offset) à l'intérieur de cette page
  ;
  ; Description :
  ; Cette fonction doit, à partir d'une adresse virtuelle, déterminer à quelle
  ; page cette adresse appartient. Elle doit également calculer le décalage
  ; requis à partir du début de la page pour atteindre l'adresse demandée.
  ; La fonction doit retourner dans R0 l'index de la page en question et dans
  ; R1 le décalage calculé.
  ; 
  ; Suppositions nécessaires :
  ; - Ce système utilise des pages de 32 octets.
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 3 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI
  
  BX LR

;###############################################################################
  
TrouverPage
  ; Paramètres d'entrée :
  ; - R0 : Numéro de la page demandée
  ;
  ; Valeurs de sortie :
  ; - R2 : -1 si cette page n'est pas présente en RAM (défaut de page), sinon 
  ;        le numéro de la trame en RAM contenant cette page
  ;
  ;
  ; Description :
  ;
  ; Cette fonction doit déterminer la trame contenant une page en RAM. 
  ; Pour ce faire, elle utilise le numéro de page i reçu en argument dans R0, et 
  ; retourne la valeur contenu à la position i de la table de pages. Par exemple, 
  ; si la table de pages contient les valeurs 1, -1, -1, 2, -1, 0, -1, ... et que 
  ; l'on demande la page numéro 3, alors cette fonction doit retourner 2 
  ; (puisque la valeur du 4e élément de la table de page est 2, et qu'on commence 
  ; à compter à partir de zéro). Si on avait plutôt demandé la page numéro 0, 
  ; alors la fonction aurait dû retourner 1. Si on avait demandé la page numéro 5, 
  ; alors le retour devrait être 0. Finalement, si on demande une page inexistante 
  ; en RAM (qui n'est contenue dans aucune trame), alors il faut retourner -1. 
  ; Par exemple, si on avait demandé la page numéro 2, ou la page numéro 4, 
  ; il aurait fallu retourner -1, puisque c'est la valeur de la table de pages 
  ; à ces positions.
  ; 
  ; Suppositions nécessaires :
  ; - Chaque entrée de la table de pages fait 4 octets.
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 5 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI

  BX LR
  
;###############################################################################

CopieMemoire
  ; Paramètre d'entrée :
  ; - R10 : Adresse source
  ; - R11 : Adresse destination
  ; - R12 : Nombre d'octets à copier
  ;
  ; Valeurs de sortie :
  ; - Aucune valeur de sortie requise
  ; - La copie doit cependant bien avoir été effectuée
  ;
  ; Description :
  ; Cette fonction doit copier les données d'un endroit à l'autre de la mémoire.
  ; Elle est analogue à celle du TP3, mais la formulation de ses arguments
  ; diffère légèrement, faites donc attention!
  ; 
  ; Suppositions nécessaires :
  ; - Le nombre d'octets à copier sera toujours un multiple de 4.
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 9 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI

  BX LR

;###############################################################################
  
TrouverValeurMaximum
  ; Paramètres d'entrée :
  ; - Aucun paramètre d'entrée
  ;
  ; Valeurs de sortie :
  ; - R2 : index de la trame possédant le compteur le plus élevé
  ;
  ; Description :
  ; Cette fonction doit retourner l'index de la trame qui a été le moins
  ; récemment utilisée. Pour ce faire, elle doit lire le contenu de tableAcces
  ; et déterminer l'index de la plus GRANDE valeur. Par exemple, si tableAcces
  ; contient 8, 3, 5, alors cette fonction doit retourner 0x00 dans R2. Si elle
  ; contient plutôt 2, 7, 1, alors c'est 0x01 qui doit être retourné (la position
  ; du 7 dans le tableau).
  ;
  ; Suppositions nécessaires :
  ; - La RAM contient 3 trames (donc il y a 3 compteurs).
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 14 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI

  BX LR

;###############################################################################

ChargerPage
  ; Paramètres d'entrée :
  ; - R0 : index de la page à charger
  ; - R2 : index de la trame où la charger
  ;
  ; Valeurs de sortie :
  ; - Aucun registre modifié
  ; - Une copie doit être faite entre la page à charger sur le disque et la
  ;   trame où la charger en RAM.
  ; - La table des pages et la table des pages inverse doivent être mises à
  ;   jour pour refléter ce changement
  ;
  ; Description :
  ; Cette fonction doit copier une page à partir du disque dur dans une trame
  ; en mémoire vive. Elle doit également mettre à jour la table des pages et la
  ; table des pages inverse.
  ; CopieMemoire vous sera certainement utile dans cette fonction.
  ;
  ; Suppositions nécessaires :
  ; - Ce système utilise des pages de 32 octets.
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 13 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI

  BX LR
 
;###############################################################################
 
  
EvincerPage
  ; Paramètres d'entrée :
  ; - Aucun paramètre d'entrée
  ;
  ; Valeurs de sortie :
  ; - R2 : index de la trame à utiliser, trame qui doit préalablement avoir été
  ;   sauvegardée sur le disque dur
  ; - La table des pages doit être mise à jour pour indiquer que la page qui
  ;   occupait précédemment cette trame n'est plus en mémoire
  ;
  ; Description :
  ; Cette fonction doit d'abord utiliser la fonction TrouverValeurMaximum pour
  ; déterminer la trame ayant été accédée le moins récemment. Par la suite, elle
  ; doit copier cette page de la RAM vers le disque, afin de permettre son
  ; remplacement. Finalement, la table des pages doit être mise à jour.
  ; Il existe une EXCEPTION à ce comportement : si la table de pages inverse
  ; indique que la trame mémoire visée n'a jamais contenu une page valide jusqu'à
  ; maintenant (la valeur de la table de pages inverse à cet endroit est -1),
  ; alors il ne faut PAS essayer de copier la trame, puisque copier une zone
  ; mémoire non initialisée générera une erreur.
  ; En résumé, la copie doit être effectuée SAUF SI la table de pages inverse
  ; contient -1 à l'index correspondant à la trame.
  ; CopieMemoire vous sera certainement utile dans cette fonction.
  ; 
  ; Suppositions nécessaires :
  ; - Ce système utilise des pages de 32 octets.
  ;
  ; Nombre d'instructions suggéré, incluant le BX LR :
  ; 17 instructions (CECI N'EST PAS UNE OBLIGATION, VOYEZ L'ÉNONCÉ)
  ; 

  ; ÉCRIVEZ VOTRE CODE ICI

  BX LR
 
 
 
;###############################################################################
 
; NE RIEN MODIFIER À PARTIR D'ICI
; TOUTES LES FONCTIONS SUBSÉQUENTES SONT DÉJÀ CODÉES POUR VOUS

AjusteCompteursAcces
  ; Paramètres d'entrée :
  ; - R2 : Numéro de la trame (frame) à laquelle on accède
  ;
  ; Valeurs de sortie :
  ; - Aucune valeur de sortie
  ;
  ; Description :
  ; Cette fonction met à jour les compteurs d'accès de chaque trame en mémoire.
  ; Une trame qui est accédée voit son compteur remis à 0, alors que toutes
  ; les autres ont leur compteur incrémenté.
  ; 
  ; Suppositions nécessaires :
  ; Vous pouvez supposer que nous utilisons un système ne pouvant stocker que
  ; trois (3) pages dans sa RAM.
  ;
  ; CETTE FONCTION EST DÉJÀ CODÉE POUR VOUS!
  ; VOUS N'AVEZ PAS À LA MODIFIER!
  ;
  PUSH {R3, R4, R5}
  LDR R3, =tableAcces
  MOV R4, #2                      ; Index de la page dont on ajuste le compteur.
                                  ; On commence à la fin pour simplifer la condition de sortie de la boucle
boucleAjusteCompteursAcces
  CMP R4, R2
  MOVEQ R5, #0                    ; Trame à laquelle on accède : on remet son compteur à 0
  LDRNE R5, [R3, R4, LSL #2]      ; Page non-accédée, on ajoute 1 à son compteur
  ADDNE R5, R5, #1
  STR R5, [R3, R4, LSL #2]        ; On inscrit la nouvelle valeur en mémoire
  SUBS R4, R4, #1                 ; On passe à la page précédente
  BPL boucleAjusteCompteursAcces  ; Si on n'a pas traité toutes les pages, on continue!
  
  POP {R3, R4, R5}
  BX LR

;###############################################################################

PrepareAccesMemoire
  ; Paramètres d'entrée :
  ; - R8 : Adresse VIRTUELLE à convertir vers une adresse PHYSIQUE
  ;
  ; Valeurs de sortie :
  ; - R3 : Adresse PHYSIQUE demandée. Cette adresse doit OBLIGATOIREMENT se situer
  ;        à l'intérieur des limites de la variable MaRAM, soit entre
  ;        0x100000 et 0x10005F
  ;
  ; Description :
  ; Cette fonction doit retrouver l'adresse physique correspondant à une adresse
  ; virtuelle passée en paramètre. Cette adresse virtuelle peut correspondre à
  ; une page qui n'est PAS chargée en RAM, auquel cas il faut la charger!
  ; 
  ; CETTE FONCTION EST DÉJÀ CODÉE POUR VOUS!
  ; VOUS N'AVEZ PAS À LA MODIFIER!
  ;
  PUSH {R0-R2,LR}
  
  BL DecomposeAdresse             ; Décomposition de l'adresse en # de page (dans R0) et offset en octets par rapport au début de cette page (dans R1)
  BL TrouverPage                  ; Cette page est-elle présente en mémoire? Si oui R2 contiendra l'index en RAM de la page.
                                  ; Sinon, il contiendra -1, valeur spéciale indiquant un défaut de page
  CMP R2, #-1         
  BNE finPrepareAccesMemoire      ; Pas de faute de page, donc on peut sauter directement à l'accès en RAM
  
  BL EvincerPage                  ; Évincer la page utilisée depuis le plus longtemps, et retourner dans R2 la position de cette trame
  BL ChargerPage                  ; Charger la page voulue à cet endroit
  
finPrepareAccesMemoire
  ; À cet endroit on sait que la page est présente en RAM, dans la trame R2
  ; et que l'offset par rapport au début de le page est R1
  LDR R3, =MaRAM                  ; Debut de la RAM
  ADD R3, R3, R2, LSL #5          ; On additionne 32 * l'index de page
  ADD R3, R3, R1
  
  BL AjusteCompteursAcces         ; On met à jour les compteurs nous permettant de déterminer quelle page évincer
  POP {R0-R2,LR}
  BX LR

;###############################################################################

LoadVirtuel
  ; Paramètres d'entrée :
  ; - R8 : Adresse VIRTUELLE à charger
  ;
  ; Valeurs de sortie :
  ; - R9 : Valeur (sur 4 octets) de la donnée à cette adresse virtuelle
  ;
  ; Description :
  ; Effectue l'équivalent de LDR R9, [R8], où R8 contient une adresse virtuelle
  ; 
  ; Suppositions nécessaires :
  ; On charge toujours 4 octets
  ;
  PUSH {LR}
  BL PrepareAccesMemoire
  LDR R9, [R3]                    ; On va chercher la valeur
  POP {LR}
  BX LR

;###############################################################################
  
StoreVirtuel
  ; Paramètres d'entrée :
  ; - R8 : Adresse VIRTUELLE à laquelle écrire
  ; - R9 : Valeur à y écrire
  ;
  ; Valeurs de sortie :
  ; - Aucune
  ;
  ; Description :
  ; Effectue l'équivalent de STR R9, [R8], où R8 est une adresse virtuelle
  ; 
  ; Suppositions nécessaires :
  ; On écrit toujours sur 4 octets
  ;
  PUSH {LR}
  BL PrepareAccesMemoire
  STR R9, [R3]                    ; On stocke la valeur
  POP {LR}
  BX LR

;###############################################################################

main
; Initialisation de la pile
  LDR SP, =MaPile
  ADD SP, SP, #200
  
  
; DÉBUT DES TESTS
; LES LIGNES SUIVANTES SONT DES PETITS PROGRAMMES QUI TESTENT CHAQUE FONCTION
; VOUS DEVEZ VOUS ASSURER VOUS-MÊMES DE LA RÉUSSITE DE CES TESTS
; CHAQUE TEST NECESSITE QUE LES TESTS PRÉCÉDENTS SOIENT FONCTIONNELS
; Les commentaires commençant par >>> indiquent le résultat qui devrait être obtenu APRES L'EXECUTION DE CETTE LIGNE
  
  ; Test de DecomposeAdresse
  MOV R8, #196
  BL DecomposeAdresse   ; >>> R0 = 6, R1 = 4
  MOV R8, #0
  BL DecomposeAdresse   ; >>> R0 = 0, R1 = 0
  MOV R8, #88
  BL DecomposeAdresse   ; >>> R0 = 2, R1 = 24
  
  ; Test de TrouverPage
  LDR R0, =tableDePages
  MOV R1, #1
  MOV R2, #0
  MOV R3, #-1
  MOV R4, #-1
  MOV R5, #-1
  MOV R6, #2
  STMIA R0, {R1-R6} 
  MOV R0, #0
  BL TrouverPage        ; >>> R2 = 1
  MOV R0, #1
  BL TrouverPage        ; >>> R2 = 0
  MOV R0, #5
  BL TrouverPage        ; >>> R2 = 2
  MOV R0, #4
  BL TrouverPage        ; >>> R2 = -1 (ou 0xFFFFFFFF)
  MOV R2, #5
  MOV R0, #2
  BL TrouverPage        ; >>> R2 = -1 (ou 0xFFFFFFFF)
  MOV R1, #-1
  MOV R2, #-1
  MOV R3, #-1
  MOV R4, #-1
  MOV R5, #-1
  MOV R6, #-1
  LDR R0, =tableDePages
  STMIA R0, {R1-R6} 
  
  ; Test de TrouverValeurMaximum
  LDR R0, =tableAcces
  MOV R1, #5
  MOV R2, #0
  MOV R3, #6
  STMIA R0, {R1-R3}
  BL TrouverValeurMaximum       ; >>> R2 = 2
  MOV R3, #4
  STMIA R0, {R1-R3}
  BL TrouverValeurMaximum       ; >>> R2 = 0
  
  ; Test de CopieMemoire
  LDR R10, =MonDisqueDur
  MOV R1, #-1
  MOV R2, #0
boucleTestCopieMemoire
  STR R1, [R10, R2]
  ADD R2, R2, #4
  CMP R2, #32
  BNE boucleTestCopieMemoire
  ADD R11, R10, #128
  MOV R12, #32
  BL CopieMemoire               ; >>> Les adresses allant de 0x100160 à 0x10017F devraient toutes valoir 0xFF
  
  ; Test de ChargerPage (CopieMemoire DOIT fonctionner correctement avant de faire ce test)
  MOV R0, #4
  MOV R2, #2
  BL ChargerPage                ; >>> Les adresses allant de 0x100040 à 0x10005F devraient toutes valoir 0xFF
                                ; >>> La valeur à l'adresse 0x14 devrait être 2
                                ; >>> La valeur à l'adresse 0x5C devrait être 4
  MOV R0, #0
  MOV R2, #0
  BL ChargerPage                ; >>> Les adresses allant de 0x100000 à 0x10001F devraient toutes valoir 0xFF
                                ; >>> La valeur à l'adresse 0x04 devrait être 0
                                ; >>> La valeur à l'adresse 0x54 devrait être 0
  
  
  ; Test de EvincerPage (TrouverValeurMaximum, CopieMemoire et ChargerPage DOIVENT fonctionner correctement avant de faire ce test)
  ; Les deux appels précédents à ChargerPage doivent avoir été exécutés sans erreur!
  LDR R0, =MaRAM
  MOV R1, #0xAA
  STR R1, [R0]
  STR R1, [R0, #8]
  STR R1, [R0, #12]
  LDR R0, =tableAcces
  MOV R1, #5
  MOV R2, #3
  MOV R3, #4
  STMIA R0, {R1-R3}
  BL EvincerPage                ; >>> R2 = 0 et la valeur aux adresses 0x1000E0, 0x1000E8 et 0x1000EC devrait être 0xAA
                                ; >>> La valeur à l'adresse 0x04 devrait être -1 (0xFFFFFFFF)
  LDR R0, =MaRAM
  ADD R0, R0, #64
  MOV R1, #0xBB
  STR R1, [R0]
  STR R1, [R0, #20]
  STR R1, [R0, #28]
  LDR R0, =tableAcces
  MOV R1, #0
  MOV R2, #7
  MOV R3, #9
  STMIA R0, {R1-R3}
  BL EvincerPage                ; >>> R2 = 2 et la valeur aux adresses 0x100160, 0x100174 et 0x10017C devrait être 0xBB
                                ; >>> La valeur à l'adresse 0x14 devrait être -1 (0xFFFFFFFF)
  
  
  ; Tests globaux. Ces tests sont plus complets que les précédents, car ils
  ; utilisent TOUTES vos fonctions (directement ou indirectement). Il est donc
  ; important de vous assurez que vos fonctions sont correctes individuellement
  ; avant de procéder aux tests suivants.
  
  ; Initialisation de la table de pages
  LDR R0, =tableDePages                 
  MOV R1, #-1
  MOV R2, #0
boucleInitTablePages
  STR R1, [R0, R2]
  ADD R2, R2, #4
  CMP R2, #80
  BNE boucleInitTablePages
  
  ; Initialisation de la table de pages inversée
  LDR R0, =tableDePagesInverse
  MOV R1, #-1
  MOV R2, #-1
  MOV R3, #-1
  STMIA R0, {R1-R3}
  
  ; Initialisation des compteurs
  LDR R0, =tableAcces
  MOV R1, #0
  MOV R2, #0
  MOV R3, #0
  STMIA R0, {R1-R3}
  
  ; Initialisation de la mémoire virtuelle
  ; Chaque page mémoire se voit assigner la valeur correspondant à son index + 1
  ; Par exemple, la page 0 est remplie de 1, la page 1 est remplie de 2, et
  ; ainsi de suite
  MOV R0, #1
  LDR R2, =MonDisqueDur
b1
  MOV R1, #32
b2
  STR R0, [R2], #4
  SUBS R1, R1, #4
  BNE b2
  ADD R0, R0, #1
  CMP R0, #21
  BNE b1
  
  ; Tests
  MOV R8, #0x00
  BL LoadVirtuel        ; >>> R9 = 1
  MOV R8, #0x20         
  BL LoadVirtuel        ; >>> R9 = 2
  MOV R8, #0x04         
  BL LoadVirtuel        ; >>> R9 = 1
  MOV R8, #0x3C         
  BL LoadVirtuel        ; >>> R9 = 2
  
  MOV R8, #0x08
  MOV R9, #0xAA
  BL StoreVirtuel
  MOV R9, #0xBB
  BL LoadVirtuel        ; >>> R9 = 0xAA
  MOV R8, #0x40
  BL LoadVirtuel        ; >>> R9 = 0x03
  MOV R8, #0x20C
  MOV R9, #0xCC
  BL StoreVirtuel
  MOV R8, #0xA8
  BL LoadVirtuel        ; >>> R9 = 0x06 et l'adresse 0x1000E8 devrait contenir 0xAA
  MOV R8, #0x08
  BL LoadVirtuel        ; >>> R9 = 0xAA
  MOV R8, #0x110
  BL LoadVirtuel        ; >>> R9 = 9 et l'adresse 0x1002EC devrait contenir 0xCC
  MOV R8, #0x20C
  BL LoadVirtuel        ; >>> R9 = 0xCC
  

  ; FIN DES TESTS
  B main
        


  SECTION `.noinit`:DATA(2)

MaRAM               DS8    96             ; Espace simulant la RAM (96 octets)
EspaceVide          DS32   32             ; Espace vide (seulement utilisé pour faciliter la séparation visuelle entre la RAM et le disque)
MonDisqueDur        DS8    640            ; Espace simulant le disque dur (640 octets)
Canari              DS32   1              ; Espace non initialisé (laissé volontairement vide)
MaPile              DS32   50             ; Utilisé pour sauvegarder les registres, à la discrétion du programmeur

        END
