Module Mips

module Mips: sig .. end
Bibliothèque pour l'écriture de programmes MIPS


Le module Mips permet l'écriture de code MIPS dans du code OCaml, sans utiliser un préprocesseur. Un exemple complet est donné ci-dessous, dans la section exemple.

Un descriptif détaillé des instructions peut être trouvé dans l'appendice A de: 'Computer Organization and Design: The Hardware/Software Interface.' (Hennessy & Patterson, Elsevier). Le pdf de l'appendice A est disponible à cette URL:

http://pages.cs.wisc.edu/~larus/HP_AppA.pdf

type 'a asm 
type abstrait pour représenter du code assembleur. Le paramètre 'a est utilisé comme type fantôme.
type text = [ `text ] asm 
type représentant du code assembleur se trouvant dans la zone de texte
type data = [ `data ] asm 
type représentant du code assembleur se trouvant dans la zone de données
type program = {
   text : [ `text ] asm;
   data : [ `data ] asm;
}
un programme est constitué d'une zone de texte et d'une zone de donnée
val print_program : Format.formatter -> program -> unit
print_program fmt p imprime le code du programme p dans le formatter fmt
type 'a register 
Type abstrait pour les registres et constantes pour chacun des registres. La variable de type 'a est un type fantôme représentant encodant le fait que le registre est un registre général (type word) ou un registre du co-processeur flottant (type double).
type word = [ `word ] 
type double = [ `double ] 
val v0 : word register
val v1 : word register
val a0 : word register
val a1 : word register
val a2 : word register
val a3 : word register
val t0 : word register
val t1 : word register
val t2 : word register
val t3 : word register
val s0 : word register
val s1 : word register
val ra : word register
val sp : word register
val fp : word register
val gp : word register
val zero : word register
Constantes représentant les registres généraux
val f0 : double register
val f2 : double register
val f4 : double register
val f6 : double register
val f8 : double register
val f10 : double register
val f12 : double register
constantes représentant les registres du co-processeur 1 (calculs flottants). seul les registres pairs permettent de manipuler des doubles
type label = string 
Les étiquettes d'addresses sont des chaines de caractères
type 'a operand 
val oreg : word register operand
val oi : int operand
val oi32 : int32 operand

type abstrait pour représenter la dernière opérande d'une expression arithmétique ainsi que 3 constantes (soit un registre, soit un entier, soit un entier 32 bits)

Opérations arithmétiques



Opérations arithmétiques sur les entiers


val li : word register -> int -> text
val li32 : word register -> int32 -> text

Chargement des constantes entières
val abs : word register -> word register -> text
abs r1 r2 stocke dans r1 la valeur absolue de r2
val neg : word register -> word register -> text
neg r1 r2 stocke dans r1 l'opposé de r2
val add : word register ->
word register -> 'a operand -> 'a -> text
val sub : word register ->
word register -> 'a operand -> 'a -> text
val mul : word register ->
word register -> 'a operand -> 'a -> text
val div : word register ->
word register -> 'a operand -> 'a -> text
val rem : word register ->
word register -> 'a operand -> 'a -> text
Les 5 opérations arithmétique de base: add rdst rsrc1 ospec o stocke dans rdst le résultat de l'opération entre rsrc1 et o. La constante ospec spécifie si o est un immédiat, immédiat sur 32 bits ou un registre. Exemple:

add v0 v1 oreg v2

div v0 v1 oi 424

sub t0 a0 oi32 2147483647l


Opérations arithmétiques sur les flottants double précision


val cvt_d_w : double register -> double register -> 'a asm
cvt_d_w f1 f2 convertit l'entier placé dans le registre f2 en flottant double précision et place le résultat dans f1
val cvt_w_d : double register -> double register -> 'a asm
cvt_w_d f1 f2 convertit le flottant double précision placé dans le registre f2 en entier et place le résultat dans f1
val abs_d : double register -> double register -> text
abs_d f1 f2 stocke dans f1 la valeur absolue de f2
val neg_d : double register -> double register -> text
neg_d f1 f2 stocke dans f1 l'opposé de f2
val add_d : double register ->
double register -> double register -> text
val sub_d : double register ->
double register -> double register -> text
val mul_d : double register ->
double register -> double register -> text
val div_d : double register ->
double register -> double register -> text
Les 4 opérations arithmétique de base sur les nombres flottants. ces opérations ne travaillent que sur des registres (flottants).

Opérations logiques


val clz : word register -> word register -> text
val and_ : word register ->
word register -> word register -> text
val or_ : word register ->
word register -> word register -> text
val not_ : word register -> word register -> text
Opérations de manipulation de bits. "et" bit à bit, "ou" bit à bit, "non" bit à bit et clz (count leading zero)

Comparaisons



Comparaisons d'entiers


val seq : word register ->
word register -> word register -> text
val sge : word register ->
word register -> word register -> text
val sgt : word register ->
word register -> word register -> text
val sle : word register ->
word register -> word register -> text
val slt : word register ->
word register -> word register -> text
val sne : word register ->
word register -> word register -> text
conditionnelles sop ra rb rc met ra à 1 si rb op rc et à 0 dans le cas contraire (eq : ==, ge : >=, gt : >, le : <=, lt : <=, ne : !=)
val c_eq_d : double register -> double register -> text

Comparaisons de nombres flottants


val c_le_d : double register -> double register -> text
val c_lt_d : double register -> double register -> text

conditionnelles s_op_d fa fb met le registre de condition du co-processeur flottant à 1 si ra op rb et à 0 dans le cas contraire (eq : ==, le : <=, lt : <=). Les instructions bclf et bclt permettent de tester le registre de condition.

Sauts


val b : label -> text
saut inconditionnel
val beq : word register -> word register -> label -> text
val bne : word register -> word register -> label -> text
val bge : word register -> word register -> label -> text
val bgt : word register -> word register -> label -> text
val ble : word register -> word register -> label -> text
val blt : word register -> word register -> label -> text
bop ra rb label branche vers le label label si ra op rb
val beqz : word register -> label -> text
val bnez : word register -> label -> text
val bgez : word register -> label -> text
val bgtz : word register -> label -> text
val blez : word register -> label -> text
val bltz : word register -> label -> text
bopz ra rb label branche vers le label label si ra op 0
val bc1f : label -> text
bc1f label saute vers le label label si le registre de condition du co-processeur flottant vaut 0
val bc1t : label -> text
bc1f label saute vers le label label si le registre de condition du co-processeur flottant vaut 1
val jr : word register -> text
jr r Continue l'exécution à l'adresse spécifiée dans le registre r
val jal : label -> text
jal l Continue l'exécution à l'adresse spécifiée par le label l, sauve l'adresse de retour dans $ra.
val jalr : word register -> text
jalr r Continue l'exécution à l'adresse spécifiée par le registre r, sauve l'adresse de retour dans $ra.
type 'a address 

Lecture / écriture en mémoire

type abstrait pour représenter des adresses

val alab : label address
val areg : (int * 'a register) address
Les adresses sont soit données par un label, soit par une paire décalage, registre
val la : word register -> 'a address -> 'a -> text
la reg alab "foo" charge dans reg l'adresse du label "foo" la reg1 areg (x, reg2) charge dans reg1 l'adresse contenue dans reg2 décallée de x octets
val lbu : word register -> 'a address -> 'a -> text
charge l'octet à l'adresse donnée sans extension de signe (valeur entre 0 et 255)
val lb : word register -> 'a address -> 'a -> 'b asm
charge l'octet à l'adresse donnée avec extension de signe (valeur entre -128 et 127)
val lw : word register -> 'a address -> 'a -> text
charge l'entier 32bits à l'adresse donnée
val lwc1 : double register -> 'a address -> 'a -> text
charge le flottant simple précision à l'adresse donnée. Attention, ne pas utiliser.
val ldc1 : double register -> 'a address -> 'a -> text
charge le flottant double précision à l'adresse donnée.
val sb : word register -> 'a address -> 'a -> text
écrit les 8 bits de poid faible du registre donnée à l'adresse donnée
val sw : word register -> 'a address -> 'a -> text
écrit le contenu du registre à l'adresse donnée
val swc1 : double register -> 'a address -> 'a -> text
écrit le contenu du registre simple précision à l'adresse donnée. Attention, ne pas utiliser.
val sdc1 : double register -> 'a address -> 'a -> text
écrit le contenu du registre double précision à l'adresse donnée.
val move : word register -> word register -> text
move rd rs copie le registre source rs dans le registre destination rd
val mov_d : double register -> double register -> text
mov_d fd fs copie le registre source flottant fs dans le registre destination fd
val mfc1 : word register -> double register -> text
mfc1 r1 f1 copie le registre f1 du co-processeur flottant vers le registre général r1
val mtc1 : word register -> double register -> text
mfc1 r1 f1 copie le registre général r1 dans le registre f1 du co-processeur flottant.

Divers


val nop : [>  ] asm
val label : label -> [>  ] asm
un label. Peut se retrouver dans du text ou du data
val syscall : text
l'instruction syscall
val comment : string -> [>  ] asm
place un commentaire dans le code généré. Peut se retrouver dans du text ou du data
val align : int -> [>  ] asm
align n aligne le code suivant l'instruction sur 2^n octets
val space : int -> data
space n réserve n octets non initialisés dans la zone data
val asciiz : string -> data
place une constante chaîne de carctères (terminées par 0) dans a zone data
val word : int list -> data
place une liste de mots mémoires dans la zone data
val address : label list -> data
place une liste d'adresses (dénotées par des labels) dans la zone data
val double : float list -> data
place une liste de flottant double précision dans la zone data
val (@@) : ([< `data | `text ] as 'a) asm -> 'a asm -> 'a asm
concatène deux bouts de codes (soit text avec text, soit data avec data)
val (++) : ([< `data | `text ] as 'a) asm -> 'a asm -> 'a asm
alias pour @@
val set_stack_alignment : int -> unit
val get_stack_alignment : unit -> int
permettent de définir l'alignement de la pile. 8 octet par défaut. set_stack_alignment lève une exception si l'alignement n'est pas une puissance de 2.
val push : [< `double | `word ] register -> text
push r place le contenu de r au sommet de la pile. $sp pointe sur l'adresse de la dernière case occupée
val peek : [< `double | `word ] register -> text
peek r place la valeur en sommet de pile dans r sans dépiler
val pop : [< `double | `word ] register -> text
pop r place la valeur en sommet de pile dans r et dépile

Exemple



Le programme ci-dessous donné à gauche en pur MIPS et à droite en OCaml charge deux constantes, effectues quelques opérations arithétiques et affiche le résultat à l'écran

        .text                                                |  { text =
        main:                                                |        label "main"
         #charge 42 dans $a0 et 23 dans $a1                  |    @@  comment "charge 42 dans $a0 et 23 dans $a1"
         li $a0,  42                                         |    @@  li  a0 42
         li $a1,  23                                         |    @@  li  a1 23
         mul $a0, $a0, $a1                                   |    @@  mul a0 a0 oreg a1 (* on utilise oreg pour dire que la dernière
                                                             |                             operande est un registre *)
         #place le contenu de $a0 sur la pile                |    @@  comment "place le contenu de $a0 sur la pile"
         sub $sp, $sp, 4                                     |    @@  sub sp sp oi 4
         sw  $a0,  0($sp)                                    |    @@  sw a0 areg (0, sp)
                                                             |
         #appelle une routine d'affichage                    |    @@  comment "appelle la routine d'affichage"
         jal print_int                                       |    @@  jal "print_int"
                                                             |
         #termine                                            |    @@  comment "termine"
         li $v0, 10                                          |    @@  li v0 10
         syscall                                             |    @@  syscall
                                                             |
      print_int:                                             |    @@  label "print_int"
         lw $a0,  0($sp)                                     |    @@  lw a0 areg (0, sp)
         add $sp, $sp, 4                                     |    @@  add sp sp oi 4
         li $v0, 1                                           |    @@  li v0 1
         syscall                                             |    @@  syscall
         #affiche un retour chariot                          |    @@  comment "affiche un retour chariot"
         la $a0, newline                                     |    @@  la a0 alab "newline"
         li $v0, 4                                           |    @@  li v0  4
         syscall                                             |    @@  syscall
         jr $ra                                              |    @@  jr ra  ; (* fin du label text *)
                                                             |
        .data                                                |    data =
       newline:                                              |        label "newline"
        .asciiz  "\n"                                        |    @@  asciiz "\n" ;
                                                             |  } (* fin du record *)