diff --git a/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.md b/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec343b7f2b43af25321c8abb83eea9457570011e
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.md
@@ -0,0 +1,236 @@
+---
+title       : Programmation Système Avancée
+author      :
+     - "Professeur : Florent Gluck"
+date        : \today
+papersize   : A4
+geometry    : "left=2.5cm,right=2cm,top=1.5cm,bottom=2cm"
+colorlinks  : urlcolor
+fontsize    : 11pt
+lang        : fr-CH
+---
+
+# Lab3 - Gestion des interruptions, du timer et du clavier
+
+## Introduction
+
+Maintenant que vous avez un noyau capable de gérer l'affichage et l'adressage mémoire grâce à la pagination, nous désirons passer à l'étape suivante, à savoir la gestion des interruptions, du timer et du clavier. Les interruptions matérielles sont nécessaires pour piloter certains périphériques et les exceptions processeurs sont nécessaires pour détecter des erreurs et des violations de sécurité. Enfin, vous aurez besoin du timer pour compter le temps indépendamment de la fréquence d'horloge du CPU, et le clavier afin d'interagir avec l'utilisateur.
+
+## Objectif
+
+L'objectif de ce travail pratique est d'ajouter à votre noyau la gestion des interruptions matérielles et des exceptions processeur, ainsi que de gérer deux nouveaux périphériques\ : le timer et le clavier. Au terme de ce travail pratique, votre noyau sera capable de\ :
+
+- Gérer les interruptions matérielles en provenance des périphériques, en particulier le timer et le clavier.
+- Gérer les exceptions processeur afin de détecter des événements indésirables, tels que : faute de page, division par zéro, erreur de protection, etc.
+- Gérer le timer afin d’implémenter une base de temps indépendante de la vitesse du processeur.
+- Gérer le clavier afin de pouvoir lire les touches pressées et ainsi interagir avec l'utilisateur.
+
+Les différentes tâches à réaliser sont décrites dans les sections qui suivent, veuillez donc les lire attentivement.
+
+## Cahier des charges
+
+### Tâche 0 : familiarisation avec le code fourni
+
+Lisez et familiarisez-vous avec le code fourni (C et assembleur) afin que vous en compreniez la teneur et la logique.
+
+### Tâche 1 : gestion des interruptions matérielles et des exceptions processeur
+
+Le code noyau s'occupant de la gestion des interruptions (exceptions processeur et interruptions matérielles) se trouve dans le répertoire `kernel/interrupt`, dans les fichiers `idt_asm.s`, `idt.h`, `idt.c`, `irq.h` et `irq.c`.
+
+Une partie du code vous est déjà fourni, car celui-ci repose sur des fonctions écrites en assembleur. Le code assembleur est nécessaire car le C n'offre pas certaines instructions requises (p.ex. `lidt`, `iret`, etc.) et nous devons contrôler exactement ce qui est déposé sur la pile lors de l'écriture des ISR (*Interrupt Service Routine*).
+
+Dans le fichier `idt.c` vous pouvez trouver\ :
+
+- La structure `idt_entry_t`\ : cette structure défini un descripteur d'interruption (i.e. une entrée dans la table IDT).
+- La structure `idt_ptr_t`\ : cette structure défini un pointeur sur la table IDT.
+- La fonction `idt_build_entry`\ : cette fonction permet de créer un descripteur d'interruption, donc une entrée pour l'IDT.
+- La variable `idt_ptr`\ : ctte variable défini la structure permettant de pointer sur l'IDT\ ; elle est nécessaire au processeur pour charger l'IDT via la fonction assembleur `idt_load`.
+- La fonction `idt_init`\ : cette fonction initialise l'IDT\ ; elle met en place les ISR pour les exceptions et les interruptions matérielles\ ; elle s'occupe également de charger l'IDT.
+- La fonction `exception_handler`\ : ISR haut niveau pour les exceptions processeur.
+- La fonction `irq_handler`\ : similaire à la fonction `exception_handler` mais pour les interruptions matérielles.
+
+La partie bas niveau des ISR est implémentée en assembleur dans `idt_asm.s`. Dans ce fichier, vous pouvez trouver\ :
+
+- Deux exemples d'ISR bas niveau pour des exceptions processeur\ :
+  - `_exception_3` : exemple pour l'exception processeur 3 qui ne dépose pas de code d'erreur sur la pile\ ;
+  - `_exception_8` : exemple pour l'exception processeur 8 qui dépose un code d'erreur sur la pile.
+- Un exemple d'ISR bas niveau pour une interruption matérielle\ :
+  - `_irq_5` : exemple pour l'IRQ 5.
+
+- La fonction `exception_wrapper` appelée par chaque ISR bas niveau d'exception processeur (deuxième partie de l'ISR bas niveau)
+  - cette fonction appelle à son tour l'ISR d'exception haut niveau écrit en C. Cette fonction assembleur réalise les étapes suivantes (dans l'ordre)\ :
+
+	- sauvegarde le contexte du CPU\ ;
+	- appelle la partie haut niveau, en C, de l'ISR via l'appel à l'instruction `call exception_handler`\ ;
+	- restaure le contexte du CPU\ ;
+	- revient au code interrompu grâce à l'instruction `iret`.
+
+Dans le cas d'une requête d'interruption (IRQ), le wrapper `irq_wrapper` est appelé et c'est à vous de l'implémenter. Celui-ci devrait être quasiment identique au wrapper pour les exceptions, à la différence qu'il devra appeller un ISR C différent (celui gérant les IRQ).
+
+Pour les handlers d'IRQ bas niveau (en assembleur), il est nécessaire de placer sur la pile le numéro d’IRQ générée.
+
+Voici les points que vous devez ajouter\ :
+
+**1. Implémentation des ISR de bas niveau**
+
+Modifiez le code assembleur dans `idt_asm.s` afin d'implémenter\ :
+
+- Les ISR bas niveau pour les exceptions processeur 0 à 21\ ;
+- Les ISR bas niveau pour les interruptions matérielles 0 à 15\ ;
+- La fonction `irq_wrapper`, appelée par la la partie bas niveau des ISR pour les interruptions matérielles (inspriez vous de la fonction `exception_wrapper`).
+
+Veuillez noter que le code assembleur gérant les interruptions est passablement répétitif avec des parties de code très semblables pour chaque ISR. Le compilateur assembleur `nasm` possède un mécanisme de macro avec paramètres variables assez puissant et facile à utiliser. N’hésitez pas à l’utiliser si vous désirez factoriser votre code (partie optionnelle).
+
+**2. Création et chargement de la table des descripteurs d'interruptions (IDT)**
+
+Ceci permettra au noyau de gérer correctement les interruptions matérielles et les exceptions processeur.
+
+Dans la fonction `idt_init`, les points ci-dessous sont à réaliser\ :
+
+- Créez une table IDT avec 256 entrées (remplie de zéro initialement).
+- A l'aide de la fonction `idt_build_entry`, insérez, aux entrées 0 à 20 de l'IDT, les descripteurs d'interruption pour les 21 exceptions processeur\ ; chaque descripteur doit pointer vers l'ISR bas niveau correspondant (implémenté dans `idt_asm.s`)\ ; ces descripteurs doivent être de type `TYPE_INTERRUPT_GATE` (cf. fichier `descriptors.h`)\ ; à noter que le code des ISR est localisé dans le segment du kernel (constante `GDT_KERNEL_CODE_SELECTOR` définie dans `descriptors.h`).
+- Insérez, aux entrées 32 à 47 de l'IDT, les descripteurs d'interruptions pour les 16 interruptions matérielles\ ; chaque descripteur doit pointer vers l'ISR bas niveau correspondant (implémenté dans `idt_asm.s`)\ ; ces descripteurs doivent aussi être de type `TYPE_INTERRUPT_GATE`.
+- L'IDT doit ensuite être chargée par le CPU via la fonction assembleur `idt_load`.
+
+**3. Implémentation des ISR de haut niveau**
+
+Dans `idt.c`, vous devez implémenter les ISR pour les exceptions (fonction `exception_handler`) et les interruptions matérielles (fonction `irq_handler`). Chaque fonction déclare un argument de type `regs_t *regs`. Celui-ci représente le contexte processeur au moment de l'exécution de l'ISR. Le champs `number` de la structure `regs` indique le numéro d'exception/IRQ déposé sur la pile par la partie bas niveau de l'ISR (implémenté en assembleur).
+
+Au cas où une exception processeur est déclenchée, on désire simplement afficher un message d'erreur fatale indiquant le nom de l'exception reçue et son numéro, puis stopper l'exécution du noyau.
+
+Pour les interruptions matérielles, on désire gérer les interruptions en provenance du timer (IRQ0) et du clavier (IRQ1). Le comportement à implémenter pour ces ISR est expliqué dans les sections suivantes. Chaque périphérique (timer et clavier) doit enregistrer son *handler* avec la fonction `irq_install_handler` du module `irq`. Cet enregistrement est toutefois à réaliser dans le driver du périphérique concerné. Dans le code de l'ISR haut niveau, appelez simplement le *handler* enregistré pour le périphérique concerné (selon l'IRQ déclenchée) et n'oubliez pas de signaler le *End Of Interrupt* (EOI) sinon l'interruption ne sera plus jamais levée.
+
+Pour tester votre code, le plus simple est de commencer par afficher des messages dans vos *handlers* afin de vous assurer que ce sont bien les bonnes exceptions ou interruptions matérielles qui sont déclenchées.
+
+### Objectif 2 : gestion du timer
+
+Le code lié au timer se trouve dans les fichiers `timer.c` et `timer.h`. Vous devez implémenter les fonctions suivantes\ :
+
+1. `void timer_init(uint_t freq_hz)`. Cette fonction permet d'initialiser le timer à la fréquence choisie par l'utilisateur. A vous de décider comment gérer la fréquence passée en argument par l’utilisateur (le comportement doit être sensé). Cette fonction doit également installer le *handler* du timer pour qu'il pointe sur la fonction `timer_handler` (cf. point suivant).
+
+1. `static void timer_handler()`. Cette fonction est le *handler* du timer. Elle est exécutée par l'ISR de haut niveau dans `idt.c` chaque fois que le timer lève une interruption matérielle. Si la fonction d'initialisation du timer est implémentée correctement, ce *handler* doit être appelé exactement à la fréquence spécifiée par `timer_init`. A titre démonstratif, on désire que ce *handler* affiche quelque-chose de visuel et animé à l'écran. Vous êtes complètement libre de choisir quoi afficher, mais il est important que l'affichage change au cours du temps.
+
+1. `uint_t timer_get_ticks()`. Cette fonction retourne le nombre de ticks écoulés depuis le démarrage du système.
+
+1. `uint_t timer_get_freq()`. Cette fonction retourne la fréquence du timer.
+
+1. `void timer_sleep(uint_t ms)`. Cette fonction permet d'attendre le nombre de milli-secondes spécifiées via une attente active. Celle-ci est donc similaire à la fonction POSIX `sleep` sauf qu'elle réalise une attente active. Vérifiez que votre fonction se comporte correctement quelle que soit la fréquence de timer choisie (dans les limites du possible). Indice\ : réfléchissez à l'impact de la fréquence du timer sur le comportement de votre fonction.
+
+En ce qui concerne le premier point, vous devez installer le *handler* du timer afin que celui-ci soit appelé lorsque l'IRQ0 lève l'interruption matérielle correspondante (interruption 32). Voici un petit exemple illustrant le principe. Utilisez la fonction `irq_install_handler` du module `irq` pour installer la fonction *handler* (ici `my_fun`) de l'IRQ désirée (ici IRQ `x`)\ :
+
+\small
+```{.c}
+handler_t handler = { my_func, "some meaningful name" };
+irq_install_handler(x, handler);
+```
+\normalsize
+
+Veuillez noter que les fonctions C pour lire ou écrire dans des registres mappés en PMIO (ports) se trouvent dans le sous-répertoire `kernel/pmio`.
+
+<!--
+**Attention** à ne pas écrire de code utilisant des nombres flottants car cela posera problème dans les travaux pratiques futures traitant de la gestion des tâches utilisateur. Tous les calculs que vous effectuez doivent donc utiliser uniquement des nombres entiers.
+-->
+
+### Objectif 3 : gestion du clavier
+
+Le code lié au clavier se trouve dans `drivers/keyboard.c`.
+
+Vous devez y implémenter les trois fonctions ci-dessous\ :
+
+1. `void keyb_init()`. Cette fonction s'occupe "d'initialiser" le clavier c'est à dire d'installer l'ISR du clavier qui est la fonction `keyboard_handler()`. Vous pouvez également y placer tout code relatif au clavier qui nécessite d'être initialisé.
+
+1. `void keyb_handler()`. Cette fonction est le *handler* du clavier, donc la fonction appelée au moment où l'IRQ1 lève l'interruption matérielle correspondante. Chaque touche du clavier pressée ou relâchée génère une ou plusieurs requêtes d'interruptions IRQ1. Notez que tout comme pour le timer, l'ISR du clavier est appelé depuis le code gérant les interruptions matérielles dans `idt.c`.  Etant donné que les ISR masquent les interruptions matérielles, il est important que la routine `keyb_handler` soit courte et surtout non bloquante\ !
+Cette fonction doit respecter les point suivants\ :
+    - Ne pas être bloquante et s'exécuter rapidement.
+    - Ne réaliser aucun affichage, sauf l'affichage du message de buffer plein (cf. ci-dessous) qui est un comportement exceptionnel de déboggage.
+    - Le code de chaque touche **pressée** doit être stocké dans un buffer interne. Le code stocké doit utiliser la mapping Suisse français. Pensez à gérer les caractères imprimables ainsi que les caractères "spéciaux" non imprimables (p.ex. touches ESC, F1, F2, etc.).
+    - Par "touche pressée", on veut dire une touche qui renvoie un caractère ASCII (p.ex. A..Z, 0..9, backspace, enter, space, etc.) ou un code spécial (typiquement pour p.ex. ESC, F1, F2, etc.). Les touches Shift, Ctrl et Alt ne doivent donc pas être stockées dans le buffer interne. Elles sont utilisées pour faire de la "composition". Par exemple, Shift + 3 donne le caractère '*'.
+    - Votre routine doit être capable de gérer les caractères standards (alpha-numérique) ainsi que les touches *shift* (majuscules) pour produire les caractères supplémentaires (p.ex. !?+&%, etc.). Un petit challenge est de gérer les deux touches *shift* correctement.
+    - Si le buffer interne à votre driver clavier est plein, alors aucun caractère supplémentaire ne peut y être stocké. Dans ce cas, et à titre de déboggage, un message indiquant que le buffer est plein doit être affiché à l'écran par le noyau (par exemple en rouge). Des caractères pourront à nouveau être stockés dans le buffer qu'une fois les caractères du buffer consommés (lus).
+
+1. `int keyb_get_key()`. Cette fonction permet de lire une touche pressée au clavier. Attention\ : cette fonction ne doit jamais être bloquante ! Si aucun caractère n'est présent dans le buffer interne de votre driver, alors la fonction renvoie 0. Cela signifie qu'aucune touche n'a été pressée.
+
+Il est souhaitable que votre driver de clavier gère au moins les touches suivantes\ : 
+
+- alpha-numériques : alphabet (dont caractères accentués et ponctuation) + chiffres
+- alpha-numériques + shift : les caractères supplémentaires réalisés avec la touche shift
+- caractères "de contrôle" : entrée (renvoie `\n`), backspace (renvoie `\b`), tab (renvoie `\t`)
+- touche spéciale : ESC, F1 à F8
+
+Vous n'avez pas à gérer les touches qui retournent des make ou break codes de plus de 1 byte (flèches de direction, Ctrl droit, Home, etc.) étant donné la complexité supplémentaire que cela implique.
+
+Le mapping Suisse-français vous est fourni dans le fichier `keymaps/keymap_fr_CH.c`. Les fichiers liés au mapping clavier se trouvent dans le répertoire `keymaps` du noyau. La structure `keymap_t` du fichier `keymap.h` stock les mappings clavier pour les touches\ : normal, normal + shift, normal + ctrl et normal + alt. A noter que seul normal et normal + shift sont à gérer dans le cadre de ce labo.
+
+Enfin, voici quelques scancodes qui pourront s'avérer utiles\ :
+
+- Make code du shift gauche : 0x2A
+- Make code du shift droit : 0x36
+- Break code du shift gauche : 0xAA
+- Break code du shift droit : 0xB6
+
+### Objectif 4 : comportement du noyau
+
+Dans la fonction point d'entrée de votre noyau pensez à ajouter les points suivants\ :
+
+- démasquage des interruptions matérielles ;
+- initialisation du clavier ;
+- initialisation de la table IDT ;
+- initialisation du PIC ;
+- initialisation du timer (à savoir que la fréquence typique du timer dans les OS est de l'ordre de 50 à 200 Hz).
+
+**Attention** toutefois à réaliser ces opérations dans le bon ordre\ !
+
+Lors de l'initialisation de votre noyau au boot du système, indiquez le déroulement des phases d'initialisation présentées ci-dessus à l'aide de messages utiles et explicites affichés à l'écran.
+
+Votre noyau devra ensuite avoir le comportement suivant\ :
+
+- Si la touche F1 est pressée, alors il génère l'exception "divide by zero"\ ;
+- Si la touche F4 est pressée, alors il génère l'exception "general protection fault"\ ;
+- Si la touche F5 est pressée, alors il génère l'exception "page fault"\ ;
+- Si la touche F8 est pressée, alors le noyau attends 5 secondes\ ;
+- Si la touche ESC est pressée, alors le noyau stoppe l'exécution du CPU\ ;
+- Si toute autre touche est pressée, alors celle-ci est affichée (selon la mapping demandé).
+
+## Code source fourni
+
+Afin de vous aider dans votre tâche, quelques nouveaux fichiers sources vous sont fourni dans le répertoire `skeleton/yoctos` de ce travail pratique\ :
+
+\footnotesize
+```
+|-- common
+|   `-- keycodes.h
+`-- kernel
+    |-- drivers
+    |   |-- keyboard.c
+    |   |-- keyboard.h
+    |   |-- pic.c
+    |   |-- pic.h
+    |   |-- timer.c
+    |   `-- timer.h
+    |-- interrupt
+    |   |-- idt_asm.s
+    |   |-- idt.c
+    |   |-- idt.h
+    |   |-- irq.c
+    |   `-- irq.h
+    |-- keymaps
+    |   |-- keymap.c
+    |   |-- keymap_fr_CH.c
+    |   `-- keymap.h
+    `-- pmio
+        |-- pmio_asm.s
+        `-- pmio.h
+```
+\normalsize
+
+## Quelques mots sur l'encodage ASCII et ASCII étendu
+
+Cette section décrit brièvement l'encodage ASCII et les encodages ASCII étendus.
+
+La table de caractères ASCII (cf. `man ascii`) défini un encodage de chaque caractère sur 7 bits, ce qui donne un maximum de 128 caractères. Typiquement, de nombreux caractères ne sont pas présents dans la table ASCII, p.ex. les caractères accentués (é, è, û, ö, etc.) ce qui s'est vite avéré trop limitant. En conséquence, au fil du temps d'autres encodages sur davantage de bits, en général 8 bits, ont vu le jour. Souvent, ceux-ci restaient "compatible" ASCII, c'est-à-dire que les 7 premiers bits étaient identiques à l'encodage ASCII et, le ou les bits supplémentaires, étaient utilisés pour encoder des caractères supplémentaires, dits "étendus". Ces encodages furent appelés ASCII étendus. Un exemple d'un tel encodage est l'encodage 8 bits, ISO-8859-1. Un autre exemple, est UTF-8, qui est un encodage à longueur variable (1 à 4 bytes), mais qui n'est pas un encodage ASCII étendu, bien que les 7 premiers bits correspondent exactement à l'encodage ASCII pour des raisons de compatibilité.
+
+Le fichier `font.c` fourni au lab1 est une police de 256 caractères utilisant l'encodage ASCII étendu IBM PC Code Page 437. Les 128 premiers caractères sont donc les caractères ASCII et les 128 suivants sont "étendus". Vous pouvez trouver tous les caractères de cette table et les valeurs numériques associées ici\ : [http://www.ascii-codes.com/](http://www.ascii-codes.com/). Les implications de l'utilisation de cet encodage signifient que les caractères ASCII "étendus" (é, ü, etc.) qui se trouveraient dans des chaînes de caractères de votre code source ne seront pas affichés correctement avec votre fonction "printf" car l'encodage utilisé sur votre système Linux n'est probablement pas l'encodage IBM PC Code Page 437 (votre système utilise probablement l'encodage UTF-8).
+
+## Echéance
+
+Ce travail pratique est à terminer pour le 22 novembre.
diff --git a/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.pdf b/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..859b77a20b6dff827024376aad5cf5c89ed9e6eb
Binary files /dev/null and b/labs/lab3-interrupts_timer_keyboard/lab3-interrupts_timer_keyboard.pdf differ
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/common/keycodes.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/common/keycodes.h
new file mode 100644
index 0000000000000000000000000000000000000000..9522fd658b9dc68bcd9dad69a8c800d76a6e5939
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/common/keycodes.h
@@ -0,0 +1,47 @@
+#ifndef _KEYCODES_COMMON_H_
+#define _KEYCODES_COMMON_H_
+
+#define KEY_ESC         50000
+#define KEY_HOME        50005
+#define KEY_LEFT        50006
+#define KEY_RIGHT       50007
+#define KEY_UP          50008
+#define KEY_DOWN        50009
+#define KEY_END         50010
+#define KEY_PGUP        50011
+#define KEY_PGDOWN      50012
+#define KEY_INSERT      50013
+#define KEY_DEL         50014
+#define KEY_F1          50015
+#define KEY_F2          50016
+#define KEY_F3          50017
+#define KEY_F4          50018
+#define KEY_F5          50019
+#define KEY_F6          50020
+#define KEY_F7          50021
+#define KEY_F8          50022
+#define KEY_F9          50023
+#define KEY_F10         50024
+#define KEY_F11         50025
+#define KEY_F12         50026
+
+// Hexadecimal values of IBM PC CP437's characters
+// Full table here: http://www.ascii-codes.com/
+#define KEY_A_CIRCUMFLEX   0x83
+#define KEY_A_DIAERESIS    0x84
+#define KEY_A_GRAVE        0x85
+#define KEY_C_CEDILLA      0x87
+#define KEY_E_ACUTE        0x82
+#define KEY_E_CIRCUMFLEX   0x88
+#define KEY_E_DIAERESIS    0x89
+#define KEY_E_GRAVE        0x8A
+#define KEY_I_DIAERESIS    0x8B
+#define KEY_I_CIRCUMFLEX   0x8C
+#define KEY_O_DIAERESIS    0x94
+#define KEY_U_CIRCUMFLEX   0x96
+#define KEY_U_DIAERESIS    0x81
+#define KEY_PARAGRAPH      0x15
+#define KEY_POUND          0x9C
+#define KEY_DEGREE         0xF8
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.c
new file mode 100644
index 0000000000000000000000000000000000000000..db0f6640617da1adab437ddd4aa80a4a79511d76
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.c
@@ -0,0 +1,22 @@
+#include "common/types.h"
+#include "common/keycodes.h"
+#include "keymaps/keymap.h"
+#include "keyboard.h"
+
+// Keyboard data and status registers
+#define DATA_REG     0x60
+#define STATUS_REG   0x64
+
+extern keymap_t *keymap;
+
+static void keyboard_handler() {
+    // TODO
+}
+
+void keyb_init() {
+    // TODO
+}
+
+int keyb_get_key() {
+    // TODO
+}
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.h
new file mode 100644
index 0000000000000000000000000000000000000000..4122acb31e24767742b798644653aa66d194c2bf
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/keyboard.h
@@ -0,0 +1,10 @@
+#ifndef _KEYBOARD_H_
+#define _KEYBOARD_H_
+
+void keyb_init();
+
+// Returns the key that was pressed or 0 if no key is present in the internal buffer.
+// This function never blocks.
+int keyb_get_key();
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.c
new file mode 100644
index 0000000000000000000000000000000000000000..1154d92bb4faf60a70fc869d2a9c78bf05d3d9aa
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.c
@@ -0,0 +1,45 @@
+#include "pmio/pmio.h"
+#include "term.h"
+
+#define PIC1_CMD        0x20
+#define PIC1_DATA       0x21
+#define PIC2_CMD        0xA0
+#define PIC2_DATA       0xA1
+
+// End Of Interrupt (reactivate the specified PIC)
+#define PIC_EOI         0x20
+
+// More details here: http://wiki.osdev.org/8259_PIC
+void pic_init() {
+    // By default IRQs 0-7 (master PIC) are mapped to interrupts 0x8-0xF (8-15)
+    // and IRQs 8-15 (slave PIC) are mapped to interrupts 0x70-0x77 (112-119).
+    // In protected mode, this scheme conflicts with CPU exceptions wich are
+    // reserved by the CPU and mapped to interrupts 0 to 31.
+    // Consequently, we map IRQs 0-7 to interrupts 32-39 and IRQs 8-15 to interrupts 40-47.
+
+    // Restart both PICs
+    outb(PIC1_CMD, 0x11);
+    outb(PIC2_CMD, 0x11);
+
+    // Map IRQs 0-7 to interrupts 32-39
+    outb(PIC1_DATA, 32);
+
+    // Map IRQs 8-15 to interrupts 40-47
+    outb(PIC2_DATA, 40);
+
+    // Setup PICs cascading
+    outb(PIC1_DATA, 0x04);
+    outb(PIC2_DATA, 0x02);
+    outb(PIC1_DATA, 0x01);
+    outb(PIC2_DATA, 0x01);
+
+    term_puts("PIC initialized.\n");
+}
+
+void pic_eoi(int irq) {
+    // An EOI must also be sent to the slave for IRQs > 7
+    if (irq > 7)
+        outb(PIC2_CMD, PIC_EOI);
+    // Send an EOI to the master
+    outb(PIC1_CMD, PIC_EOI);
+}
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.h
new file mode 100644
index 0000000000000000000000000000000000000000..be4e1bc6876c1cc1967ef8e2255dd51a15dd2d72
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/pic.h
@@ -0,0 +1,10 @@
+#ifndef _PIC_H_
+#define _PIC_H_
+
+// Initialize both PICs by mapping IRQs 0-15 to interrupts 32-47.
+void pic_init();
+
+// Send an EOI to the PICs given which IRQ was handled.
+void pic_eoi(int irq);
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.c
new file mode 100644
index 0000000000000000000000000000000000000000..8caeb7d6a714b952932656e6c0f3031a650fb7c2
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.c
@@ -0,0 +1,30 @@
+#include "common/types.h"
+#include "timer.h"
+#include "interrupt/irq.h"
+
+#define PIT_COMMAND    0x43
+#define PIT_CHANNEL0   0x40
+#define PIT_CHANNEL1   0x41
+#define PIT_CHANNEL2   0x42
+
+// Timer interrupt handler
+// Update ticks count and display a logo (animated)
+static void timer_handler() {
+    // TODO
+}
+
+void timer_init(uint_t freq_hz) {
+    // TODO
+}
+
+uint_t timer_get_freq() {
+    // TODO
+}
+ 
+uint_t timer_get_ticks() {
+    // TODO
+}
+
+void timer_sleep(uint_t ms) {
+    // TODO
+}
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.h
new file mode 100644
index 0000000000000000000000000000000000000000..b257fef7c3e832d0138e39e038ac7039569e4687
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/drivers/timer.h
@@ -0,0 +1,18 @@
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+// Initializes the timer with the specified frequency in Hz.
+// Note that the effective frequency might be different than the desired one.
+// Use timer_get_freq() to obtain the effective frequency.
+void timer_init(uint_t freq_hz);
+
+// Returns the number of ticks since boot.
+uint_t timer_get_ticks();
+
+// Sleeps (actively) the specified time in milliseconds.
+void timer_sleep(uint_t ms);
+
+// Returns the timer frequency in Hz.
+uint_t timer_get_freq();
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.c
new file mode 100644
index 0000000000000000000000000000000000000000..0417420007543017c6e5e1adb149f2b2289448eb
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.c
@@ -0,0 +1,103 @@
+#include "common/types.h"
+#include "common/mem.h"
+#include "mem/gdt.h"
+#include "idt.h"
+#include "irq.h"
+#include "x86.h"
+#include "descriptors.h"
+
+#define INTERRUPT_COUNT   256
+
+// Processor exceptions are located in this range of entries in the IVT
+#define FIRST_EXCEPTION   0
+#define LAST_EXCEPTION    20
+#define EXCEPTION_COUNT   (LAST_EXCEPTION-FIRST_EXCEPTION+1)
+
+// Reprograms the PIC to relocate hardware interrupts starting at IVT entry 32:
+// IRQ0  -> Interrupt 32
+// IRQ1  -> Interrupt 33
+// ...
+// IRQ15 -> Interrupt 47
+#define IRQ_REMAP_OFFSET  32
+
+// Structure of an IDT descriptor. There are 3 types of descriptors:
+// task-gates, interrupt-gates, trap-gates.
+// See 5.11 of Intel 64 & IA32 architectures software developer's manual for more details.
+// For task gates, offset must be 0.
+typedef struct {
+    uint16_t offset15_0;   // only used by trap and interrupt gates
+    uint16_t selector;     // segment selector for trap and interrupt gates; TSS segment
+                           // selector for task gates
+    uint16_t reserved : 8;
+    uint16_t type : 5;
+    uint16_t dpl : 2;
+    uint16_t p : 1;
+    uint16_t offset31_16;  // only used by trap and interrupt gates
+} __attribute__((packed)) idt_entry_t;
+
+// CPU context used when saving/restoring context from an interrupt
+typedef struct {
+    uint32_t gs, fs, es, ds;
+    uint32_t ebp, edi, esi;
+    uint32_t edx, ecx, ebx, eax;
+    uint32_t number, error_code;
+    uint32_t eip, cs, eflags, esp, ss;
+} regs_t;
+
+// Structure describing a pointer to the IDT gate table.
+// This format is required by the lidt instruction.
+typedef struct {
+    uint16_t limit;   // Limit of the table (ie. its size)
+    uint32_t base;    // Address of the first entry
+} __attribute__((packed)) idt_ptr_t;
+
+// Gates table
+static idt_entry_t idt[INTERRUPT_COUNT];
+static idt_ptr_t   idt_ptr;
+
+// Loads the IDT specified in argument.
+// Defined in idt_asm.s
+extern void idt_load(idt_ptr_t *idt_ptr);
+
+// Builds and returns an IDT entry.
+// selector is the code segment selector to access the ISR
+// offset is the address of the ISR (for task gates, offset must be 0)
+// type indicates the IDT entry type
+// dpl is the privilege level required to call the associated ISR
+static idt_entry_t idt_build_entry(uint16_t selector, uint32_t offset, uint8_t type, uint8_t dpl) {
+    idt_entry_t entry;
+    entry.offset15_0 = offset & 0xffff;
+    entry.selector = selector;
+    entry.reserved = 0;
+    entry.type = type;
+    entry.dpl = dpl;
+    entry.p = 1;
+    entry.offset31_16 = (offset >> 16) & 0xffff;
+    return entry;
+}
+
+// Low-level ISR for processor exceptions.
+// These are defined in idt_asm.s
+extern void _exception3();
+extern void _exception8();
+
+// Low-level ISR for hardware interrupts.
+// These are defined in idt_asm.s
+extern void _irq5();
+
+// High-level ISR for processor exceptions.
+void exception_handler(regs_t *regs) {
+    // TODO
+}
+
+// High-level ISR for hardware interrupts.
+void irq_handler(regs_t *regs) {
+    // TODO
+}
+
+void idt_init() {
+    irq_init();
+
+    // Example of IDT entry for exception 3
+    idt[3] = idt_build_entry(GDT_KERNEL_CODE_SELECTOR, _exception3, TYPE_INTERRUPT_GATE, DPL_KERNEL);
+}
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.h
new file mode 100644
index 0000000000000000000000000000000000000000..351d18498f5839c20f7a979ec73d844129efe508
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt.h
@@ -0,0 +1,6 @@
+#ifndef _IDT_H_
+#define _IDT_H_
+
+extern void idt_init();
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt_asm.s b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt_asm.s
new file mode 100644
index 0000000000000000000000000000000000000000..12549d9501e8e067ea03aa260ad1a3238d5aa940
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/idt_asm.s
@@ -0,0 +1,94 @@
+%include "const.inc"
+
+; --------------------------------------------------
+; Make symbols below visible to the linker
+
+global idt_load
+
+global _exception3
+global _exception8
+
+global _irq5
+
+section .text                      ; start of the text (code) section
+align 4                            ; the code must be 4 byte aligned
+
+; --------------------------------------------------
+; Loads the IDT specified in argument.
+; void idt_load(idt_ptr_t *idt_ptr);
+idt_load:
+    mov     eax,[esp+4]  ; Get the pointer to the IDT, passed as a parameter. 
+    lidt    [eax]        ; Load the IDT pointer.
+    ret
+
+; --------------------------------------------------
+; First part of low level exception handlers
+
+_exception3:
+    push    3
+    jmp     exception_wrapper
+
+_exception8:
+    push    0  ; dummy error code
+    push    8
+    jmp     exception_wrapper
+
+; --------------------------------------------------
+; First part of low level hardware interrupt handlers
+
+_irq5:
+    push    0  ; dummy error code
+    push    5
+    jmp     irq_wrapper
+
+; --------------------------------------------------
+; Second part of low level exception handler
+; Calls the high level handler (C function)
+
+; High level handler for exceptions (C function) 
+extern exception_handler
+
+exception_wrapper:
+    ; Save all registers
+    push    eax
+    push    ebx
+    push    ecx
+    push    edx
+    push    esi
+    push    edi
+    push    ebp
+    push    ds
+    push    es
+    push    fs
+    push    gs
+
+    ; Load kernel data descriptor into all segments
+    mov     ax,GDT_KERNEL_DATA_SELECTOR
+    mov     ds,ax
+    mov     es,ax
+    mov     fs,ax
+    mov     gs,ax
+    
+    ; Pass the stack pointer (which gives the CPU context) to the C function
+    mov     eax,esp
+    push    eax    
+    call    exception_handler
+    pop     eax  ; only here to balance the "push eax" done before the call
+
+    ; Restore all registers
+    pop     gs
+    pop     fs
+    pop     es
+    pop     ds
+    pop     ebp
+    pop     edi
+    pop     esi
+    pop     edx
+    pop     ecx
+    pop     ebx
+    pop     eax
+    
+	; Fix the stack pointer due to the 2 push done before the call
+	; to exception_wrapper: error code and exception number
+    add     esp,8
+    iret
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..d701a6cc8818f146df0627ed9cf97bd65d9ef6fe
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.c
@@ -0,0 +1,19 @@
+#include "irq.h"
+#include "common/mem.h"
+
+#define IRQ_COUNT    (IRQ_LAST-IRQ_FIRST+1)
+
+static handler_t irq_handlers[IRQ_COUNT];
+
+void irq_init() {
+    memset(irq_handlers, 0, sizeof(irq_handlers));
+}
+
+void irq_install_handler(uint_t irq, handler_t handler) {
+    irq_handlers[irq] = handler;
+}
+
+handler_t *irq_get_handler(uint_t irq) {
+    return &irq_handlers[irq];
+}
+
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fdbdf91bdd7288cb38f2c07645d53286bf6ebc7
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/interrupt/irq.h
@@ -0,0 +1,26 @@
+#ifndef _IRQ_H_
+#define _IRQ_H_
+
+#include "common/types.h"
+
+#define IRQ_FIRST    0
+#define IRQ_LAST     15
+
+typedef struct {
+    void (*func)(void);  // pointer to handler function
+    char name[64];  // associate a name to the function (for debugging purposes)
+} handler_t;
+
+// Initializes the array of IRQ handlers.
+void irq_init();
+
+// Installs a handler for the given IRQ.
+// The irq parameter must be in the range [0,15] inclusive.
+void irq_install_handler(uint_t irq, handler_t handler);
+
+// Retrieves the handler for a given IRQ.
+// The irq parameter must be in the range [0,15] inclusive.
+// Returns NULL if there is no handler for the given IRQ.
+handler_t *irq_get_handler(uint_t irq);
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.c
new file mode 100644
index 0000000000000000000000000000000000000000..efdedc79a01a763659f494835d6edfe78f9b6366
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.c
@@ -0,0 +1,5 @@
+#include "keymap.h"
+
+extern keymap_t keymap_fr_CH;
+
+keymap_t *keymap = &keymap_fr_CH;
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.h
new file mode 100644
index 0000000000000000000000000000000000000000..af6a84c84a382b855ca728395f00d0c4577506dd
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap.h
@@ -0,0 +1,18 @@
+#ifndef _KEYMAP_H_
+#define _KEYMAP_H_
+
+#include "common/types.h"
+
+#define MAP_SIZE 128
+
+// Indicate the entry must be ignored
+#define KEY_IGNORE 0x0
+
+typedef struct {
+    uint16_t normal[MAP_SIZE];
+    uint16_t shift[MAP_SIZE];
+    uint16_t ctrl[MAP_SIZE];
+    uint16_t alt[MAP_SIZE];
+} keymap_t;
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap_fr_CH.c b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap_fr_CH.c
new file mode 100644
index 0000000000000000000000000000000000000000..0cc29aa76de6229fbd9eb3d5aa026d98fc3c34bb
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/keymaps/keymap_fr_CH.c
@@ -0,0 +1,194 @@
+#include "common/types.h"
+#include "common/keycodes.h"
+#include "keymap.h"
+
+keymap_t keymap_fr_CH = {
+    // Normal mapping
+    {
+        KEY_IGNORE,     // 0
+        KEY_ESC,        // 1    ESC
+        '1',            // 2
+        '2',            // 3
+        '3',            // 4
+        '4',            // 5
+        '5',            // 6
+        '6',            // 7
+        '7',            // 8
+        '8',            // 9
+        '9',            // 10
+        '0',            // 11
+        '\'',           // 12
+        '^',            // 13
+        '\b',           // 14   BACKSPACE
+        '\t',           // 15   TAB
+        'q',            // 16
+        'w',            // 17
+        'e',            // 18
+        'r',            // 19
+        't',            // 20
+        'z',            // 21
+        'u',            // 22
+        'i',            // 23
+        'o',            // 24
+        'p',            // 25
+        KEY_E_GRAVE,    // 26
+        '"',            // 27
+        '\n',           // 28  ENTER
+        KEY_IGNORE,     // 29  LEFT/RIGHT CTRL
+        'a',            // 30
+        's',            // 31
+        'd',            // 32
+        'f',            // 33
+        'g',            // 34
+        'h',            // 35
+        'j',            // 36
+        'k',            // 37
+        'l',            // 38
+        KEY_E_ACUTE,    // 39
+        KEY_A_GRAVE,    // 40
+        KEY_PARAGRAPH,  // 41
+        KEY_IGNORE,     // 42  LEFT SHIFT
+        '$',            // 43
+        'y',            // 44
+        'x',            // 45
+        'c',            // 46
+        'v',            // 47
+        'b',            // 48
+        'n',            // 49
+        'm',            // 50
+        ',',            // 51
+        '.',            // 52
+        '-',            // 53
+        KEY_IGNORE,     // 54   RIGHT SHIFT
+        KEY_IGNORE,     // 55   ?
+        KEY_IGNORE,     // 56   LEFT/RIGHT ALT
+        ' ',            // 57
+        KEY_IGNORE,     // 58   ?
+        KEY_F1,         // 59   F1
+        KEY_F2,         // 60   F2
+        KEY_F3,         // 61   F3
+        KEY_F4,         // 62   F4
+        KEY_F5,         // 63   F5
+        KEY_F6,         // 64   F6
+        KEY_F7,         // 65   F7
+        KEY_F8,         // 66   F8
+        KEY_F9,         // 67   F9
+        KEY_F10,        // 68   F10
+        KEY_IGNORE,     // 69   ?
+        KEY_IGNORE,     // 70   ?
+        KEY_HOME,       // 71   HOME
+        KEY_UP,         // 72   UP ARROW
+        KEY_PGUP,       // 73   PAGE UP
+        KEY_IGNORE,     // 74   ?
+        KEY_LEFT,       // 75   LEFT ARROW
+        KEY_IGNORE,     // 76   ?
+        KEY_RIGHT,      // 77   RIGHT ARROW
+        KEY_IGNORE,     // 78   ?
+        KEY_END,        // 79   END
+        KEY_DOWN,       // 80   DOWN ARROW
+        KEY_PGDOWN,     // 81   PAGE DOWN
+        KEY_INSERT,     // 82   INSERT
+        KEY_DEL,        // 83   DELETE
+        KEY_IGNORE,     // 84   ?
+        KEY_IGNORE,     // 85   ?
+        '<',            // 86
+        KEY_F11,        // 87   F11
+        KEY_F12,        // 88   F12
+    },
+    // SHIFT mapping
+    {
+        KEY_IGNORE,     // 0    ?
+        KEY_ESC,        // 1    ESC
+        '+',            // 2
+        '"',            // 3
+        '*',            // 4
+        KEY_C_CEDILLA,  // 5
+        '%',            // 6
+        '&',            // 7
+        '/',            // 8
+        '(',            // 9
+        ')',            // 10
+        '=',            // 11
+        '?',            // 12
+        '`',            // 13
+        '\b',           // 14   BACKSPACE
+        '\t',           // 15   TAB
+        'Q',            // 16
+        'W',            // 17
+        'E',            // 18
+        'R',            // 19
+        'T',            // 20
+        'Z',            // 21
+        'U',            // 22
+        'I',            // 23
+        'O',            // 24
+        'P',            // 25
+        KEY_U_DIAERESIS,// 26
+        '!',            // 27
+        '\n',           // 28  ENTER
+        KEY_IGNORE,     // 29  LEFT/RIGHT CTRL
+        'A',            // 30
+        'S',            // 31
+        'D',            // 32
+        'F',            // 33
+        'G',            // 34
+        'H',            // 35
+        'J',            // 36
+        'K',            // 37
+        'L',            // 38
+        KEY_O_DIAERESIS,// 39
+        KEY_A_DIAERESIS,// 40
+        KEY_DEGREE,     // 41
+        KEY_IGNORE,     // 42  LEFT SHIFT
+        KEY_POUND,      // 43
+        'Y',            // 44
+        'X',            // 45
+        'C',            // 46
+        'V',            // 47
+        'B',            // 48
+        'N',            // 49
+        'M',            // 50
+        ';',            // 51
+        ':',            // 52
+        '_',            // 53
+        KEY_IGNORE,     // 54   RIGHT SHIFT
+        KEY_IGNORE,     // 55   ?
+        KEY_IGNORE,     // 56   LEFT/RIGHT ALT
+        ' ',            // 57
+        KEY_IGNORE,     // 58   ?
+        KEY_F1,         // 59   F1
+        KEY_F2,         // 60   F2
+        KEY_F3,         // 61   F3
+        KEY_F4,         // 62   F4
+        KEY_F5,         // 63   F5
+        KEY_F6,         // 64   F6
+        KEY_F7,         // 65   F7
+        KEY_F8,         // 66   F8
+        KEY_F9,         // 67   F9
+        KEY_F10,        // 68   F10
+        KEY_IGNORE,     // 69   ?
+        KEY_IGNORE,     // 70   ?
+        KEY_HOME,       // 71   HOME
+        KEY_UP,         // 72   UP ARROW
+        KEY_PGUP,       // 73   PAGE UP
+        KEY_IGNORE,     // 74   ?
+        KEY_LEFT,       // 75   LEFT ARROW
+        KEY_IGNORE,     // 76   ?
+        KEY_RIGHT,      // 77   RIGHT ARROW
+        KEY_IGNORE,     // 78   ?
+        KEY_END,        // 79   END
+        KEY_DOWN,       // 80   DOWN ARROW
+        KEY_PGDOWN,     // 81   PAGE DOWN
+        KEY_INSERT,     // 82   INSERT
+        KEY_DEL,        // 83   DELETE
+        KEY_IGNORE,     // 84   ?
+        KEY_IGNORE,     // 85   ?
+        '>',            // 86
+        KEY_F11,        // 87   F11
+        KEY_F12,        // 88   F12
+    },
+    // CTRL mapping
+    {},
+    // ALT mapping
+    {}
+};
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio.h b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio.h
new file mode 100644
index 0000000000000000000000000000000000000000..63d09658a21f2d72fef089f3eed80b689ac2c300
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio.h
@@ -0,0 +1,16 @@
+#ifndef _PMIO_H_
+#define _PMIO_H_
+
+#include "common/types.h"
+
+// Write a 8-bit data to the specified port
+void outb(uint16_t port, uint8_t data);
+// Read a 8-bit data from the specified port
+uint8_t inb(uint16_t port);
+
+// Write a 16-bit data to the specified port
+void outw(uint16_t port, uint16_t data);
+// Read a 16-bit data from the specified port
+uint16_t inw(uint16_t port);
+
+#endif
diff --git a/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio_asm.s b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio_asm.s
new file mode 100644
index 0000000000000000000000000000000000000000..e6d7a2d4a07f5c6f11238820af279f4e4eedb427
--- /dev/null
+++ b/labs/lab3-interrupts_timer_keyboard/skeleton/yoctos/kernel/pmio/pmio_asm.s
@@ -0,0 +1,33 @@
+global outb
+global inb
+global outw
+global inw
+
+section .text    ; start of the text (code) section
+align 4          ; the code must be 4 byte aligned
+
+; void outb(uint16 port, uint8 data)
+outb:
+    mov     dx,word [esp+4]    ; port (2 bytes)
+    mov     al,byte [esp+8]    ; data (1 byte)
+    out     dx,al
+    ret
+
+; uint8 inb(uint16 port)
+inb:
+    mov     dx,word [esp+4]    ; port (2 bytes)
+    in      al,dx
+    ret
+
+; void outw(uint16 port, uint16 data)
+outw:
+    mov     dx,word [esp+4]    ; port (2 bytes)
+    mov     ax,word [esp+8]    ; data (2 bytes)
+    out     dx,ax
+    ret
+
+; uint16 inw(uint16 port)
+inw:
+    mov     dx,word [esp+4]    ; port (2 bytes)
+    in      ax,dx
+    ret