225 | | {{{#!protected ------------------------------------------------------------------------------------ |
226 | | ''''''''''''''' |
227 | | - |
228 | | ''''''''''''''' |
229 | | }}} |
230 | | 1. inclusion de code assembleur en C? |
231 | | {{{#!protected ------------------------------------------------------------------------------------ |
232 | | ''''''''''''''' |
233 | | - |
234 | | ''''''''''''''' |
235 | | }}} |
236 | | 1. appel de code assembleur depuis le code C? |
| 225 | Dans le fichier `harch.c` : |
| 226 | {{{#!c |
| 227 | 12 struct tty_s { |
| 228 | 13 int write; // tty's output address |
| 229 | 14 int status; // tty's status address something to read if not null) |
| 230 | 15 int read; // tty's input address |
| 231 | 16 int unused; // unused address |
| 232 | 17 }; |
| 233 | 18 |
| 234 | 19 extern volatile struct tty_s __tty_regs_map[NTTYS]; |
| 235 | }}} |
| 236 | À quoi servent les mots clés `extern` et `volatile` ?\\Si NTTYS est une macro dont la valeur est 2, quelle est l'adresse en mémoire `__tty_regs_map[1].read` ? |
| 237 | {{{#!protected ------------------------------------------------------------------------------------ |
| 238 | ''''''''''''''' |
| 239 | - `extern` : informe le compilateur que la variable définie existe ailleurs. Grâce à son type, le compilateur sait s'en servir. |
| 240 | - `volatile` : informe le compilateur que la variable peut changer de valeur toute seule et que donc il doit toujours accéder en mémoire à chaque fois que le programme le demande. Il ne peut donc pas optimiser les accès mémoire en utilisant les registres. |
| 241 | - `__tty_regs_map` est un tableau à 2 cases (puisque `NTTYS`=2). Chaque case est une structure de 4 entiers, donc `0x10` octets. `read` est le troisième champs, c'est le troisième entier de la structure, donc en `+8` par rapport au début.\\En conséquence `__tty_regs_map[1].read` est en `0xd0200018` |
| 242 | ''''''''''''''' |
| 243 | }}} |
| 244 | 1. Certaines parties du noyau sont en assembleur, il y au moins les toutes premières instructions du code de boot et l'entrée dans le noyau après l'exécution d'un syscall. Dans ce dernier cas, le gestionnaire de syscall écrit en assembleur a besoin d'appeler une fonction écrite en langage C. Si le gestionnaire de syscall connait l'adresse de la fonction C qu'il veut appeler et qu'il la place dans un registre, par exemple `$2`, il suffit qu'il exécuter l'instruction `jal $2`. Que doivent contenir les registres `$4` à `$7` et comment doit-être la pile? |
| 245 | {{{#!protected ------------------------------------------------------------------------------------ |
| 246 | ''''''''''''''' |
| 247 | - |
| 248 | ''''''''''''''' |
| 249 | }}} |
| 250 | 1. Vous avez appris à écrire des programmes assembleur, mais parfois il est plus simple, voire il est nécessaire de mélanger le code C et le code assembleur. Dans l'exemple ci-dessous, nous voyons comment la fonction `kinit()` procède pour entrer dans la fonction placée à l'adresse `__crt0` définie dans le fichier `kernel/ld`. Combien vaut cette adresse? Dans quelle section se trouve-t-elle? Quelle fonction est à cette adresses? Pourquoi doit-on écrire ce code en assembleur? |
| 251 | {{{#!c |
| 252 | 9 void kinit (void) |
| 253 | 10 { |
| 254 | 11 kprintf (0, banner); |
| 255 | 12 __asm__ volatile ( // this code allows to exit the kernel to go to user code |
| 256 | 13 "la $26, __crt0 \n" // get first address of user cod e |
| 257 | 14 "mtc0 $26, $14 \n" // put it in c0_EPC |
| 258 | 15 "li $26, 0b00010010 \n" // next status [UM,0,ERL,EXL,IE] |
| 259 | 16 "mtc0 $26, $12 \n" // UM <- 1, IE <- 0, EXL <- 1 |
| 260 | 17 "la $29, __data_end \n" // define new user stack pointer |
| 261 | 18 "eret \n"); // j EPC and EXL <- 0 |
| 262 | 19 } |
| 263 | }}} |