Explained and finalized interrupts

This commit is contained in:
Mwa
2026-03-16 00:40:53 +01:00
parent c38bb22e88
commit 8cb41c61cd
2 changed files with 97 additions and 22 deletions

View File

@@ -11,14 +11,89 @@ syntaxe supplémentaire supportée:
- `D valeur`
rentre la donné brute égale à la valeur (immédiate ou addresse de label)
(pour mettre des données dans le fichier et pas du code executable)
- `eint` Active les interruptions
- `dint` Desactive les interruptions
- `swi` Active l'interruption swi
- `reti` Retourne depuis le handler d'interruption
- `umull` multiplication non signée usuelle
- `umulh` multiplication non signée, moitié haute du résultat 64 bits
- `smull` et `sumlh` versions signée de la multiplication
- `div` division non signée
- `mod` modulo non signé
# Simulateut
pour lancer la simulation:
# Simulateur
## pour lancer la simulation:
- depuis ce dossier
`cargo run --release -p simu fichierentre`
- depuis le sous dossier simu
`cargo run --release fichierentre`
Le mmio du clavier a les même scancode que la version python
Un horloge temps réèl a été ajoutée: lecture du nombre de millisecondes écoulés depuis la création de la simulation
a l'adresse 0x01200004 (clavier + 4)
Pour rajouter le support de multiplication/division: rajouter --features=div_mul
### instruction spéciale:
halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions
call 0 termine l'exécution du simulateur
## mmio:
- 0x01000000 à 0x0112c000 : Écran en lecture/écriture. un pixel par 32bits, de gauche a droite puis de bas en haut.
Format de pixel en 0BGR, 4 bits par couleur.
Passage au format RGBA (a pour alpha) avec la feature rgba
- 0x01200000 : Clavier (scancode) (lecture seule)
- 0x01200004 : Horloge (millisecondes écoulé depuis le début de la simulation, wrappe tout les 49 jours) (lecture seule)
- 0x01200008 : Boutons de la souris (OR des boutons préssés) Gauche = 1, Droit=2, Clic Molette = 3, Autres non testé (lecture seule)
- 0x0120000c : Position horizontale de la souris (en pixels, -1 si hors de l'écran) (lecture seule)
- 0x01200010 : Position verticale de la souris (en pixels, -1 si hors de l'écran) (lecture seule)
- 0x01201000 : Activation des Interupts de MMIO. Remis a zero par `dint`. Attention, Écrire ici sans activer les interruptions va bloquer les intéruptions et l'affichage (écriture seule)
## Interuptions:
Si les interruptions sont activées (avec eint), qu'il y a une raison de faire une interruption,
et qu'on n'est pas déjà dans une routine d'interruption de priorité >=,
alors le cpu effectue les actions suivantes:
Finis l'instruction en cours si c'est une interruption externe
si interruption a des arguments, les mets dans les registres par ordre croissant,
Mets PC a (priorité de l'intéruption)*4 (instruction n°priorité)
Cela veut dire que votre début de programme, si vous voulez utiliser les interruption, doit être une table de jump vers les handler correspondant
Pour sortir d'une interuption, utilisez `reti`
*Il FAUT sortir avec un reti*
*Les interuptions utilisent la stack, donc lorsque les interuptions sont activés, il est interdit de stocker des information au dessus du pointeur de stack.
de même, `reti` ne peut pas être appellé par une sous fonction appellée depuis l'interrupt handler*
Les intéruptions et leurs arguments, par priorité croissante:
### 0: Point d'entrée
C'est ici que le programme commence. Seul cas d'utilisation
### 1: MMIO
Doivent être activé par le MMIO a l'adresse 0x01201000, en y écrivant le OR des (1<<identifiant)
On toujours pour argument (r0) leur identifiant
Les mmio corespondant sont modifié avant d'entrer dans l'intéruption, et TOUT LES MMIO d'une interuption active sont bloqués tant qu'on n'est pas sorti
- 1 : Clavier
- 2 : Boutons de la souris
- 3 : Mouvements de la souris
- 4 : Écran (rien n'est modifié, attend la sortie de l'intéruption pour afficher une frame)
### 2: SWI
Pas d'argument
### 3: Division par zero
2 arguments: numéro de registre de sortie et valeur du dividende.
lors de l'appel a reti, attend que r0 n'ai pas été modifié et un retour dans r1.
la registre de destination prend la valeur retourné dans r1
### 4: Load/Store illégal
Un load store a été fait a une adresse non multiple de 4
3 aruments:
si c'est un load: 0, addresse, numéro de registre de retour
si c'est un store: 1, addresse, valeure a stocker
ne modifiez pas r0, et dans le cas des load, r2. (ou restaurez avant le reti)
Si c'est un load, le registre destination prendra la valeur de retour, attendue dans r1
### 5: Unsupported Opcode
Une division multiplication alors qu'elle ne sont pas activées
4 arguments:
indice de registre de retour, argument 1, argument 2, fonction
Même mode de retour que la division par zero
### 6: Instruction Illégale
Une instruction non légale, ou un load / store a une adresse non mappé.
1 argument: l'opcode de l'instruction associé
Il est fortement recommendé d'utiliser cette intéruption pour afficher du debug, puis de quitter le programme, mais il est possible
de continuer a l'instruction suivante à l'aide d'un `reti`

View File

@@ -214,7 +214,9 @@ impl TryFrom<u32> for Instruction {
| (1, 0b1011)
| (1, 0b1100)
| (1, 0b1101)
| (1, 0b1110) => return Err((InteruptKind::UnsupportedOpcode, rd, rx, op2)),
| (1, 0b1110) => {
return Err((InteruptKind::UnsupportedOpcode, rd, rx, op2, opcode));
}
(2, 0b0000) => Self::Store(rx, op2, rd),
(2, 0b0001) => Self::Load(rd, rx, op2),
(2, 0b0010) => Self::Push(op2),
@@ -232,13 +234,13 @@ impl TryFrom<u32> for Instruction {
(2, 0b1101) => Self::GetStack(rd),
(2, 0b1110) => Self::SetStack(op2),
(3, skip) => Self::Skip(rd.0, (skip as u8).into(), rx, op2),
_ => return Err((InteruptKind::IllegalOpcode, rd, rx, op2)),
_ => return Err((InteruptKind::IllegalOpcode, rd, rx, op2, opcode)),
}
}
})
}
type Error = (InteruptKind, Reg, Reg, Op2);
type Error = (InteruptKind, Reg, Reg, Op2, u32);
}
pub struct Computer {
@@ -396,7 +398,7 @@ impl Computer {
if !addr.is_multiple_of(4) {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[0, addr as u32, self[reg1]],
[1, addr as u32, self[reg1]],
);
return;
}
@@ -411,10 +413,7 @@ impl Computer {
(&SHARED.external_enabled_interupts)
.store(self[reg1], std::sync::atomic::Ordering::Relaxed);
} else {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[0, addr as u32, self[reg1]],
);
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]);
}
}
Instruction::Load(reg, reg1, op2) => {
@@ -446,10 +445,7 @@ impl Computer {
_ => unsafe { unreachable_unchecked() },
}
} else {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[1, addr as u32, reg.0 as u32],
);
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]);
return;
};
}
@@ -520,7 +516,12 @@ impl Computer {
ret_index = Some(self.regs[0]);
ret_value = self.regs[1];
}
InteruptKind::IllegalLoadStore => {}
InteruptKind::IllegalLoadStore => {
if self.regs[0] == 0 {
ret_value = self.regs[1];
ret_index = Some(self.regs[2]);
}
}
InteruptKind::IllegalOpcode => {}
}
}
@@ -573,12 +574,11 @@ impl Computer {
}
};
}
Err((kind, rx, ry, op2)) => {
Err((kind, rx, ry, op2, opcode)) => {
self.pc += 1;
match kind {
InteruptKind::UnsupportedOpcode => {
self.serve_interupt(kind, [rx.0.into(), self[ry], self.resolve(op2)])
}
InteruptKind::UnsupportedOpcode => self
.serve_interupt(kind, [rx.0.into(), self[ry], self.resolve(op2), opcode]),
InteruptKind::IllegalOpcode => self.serve_interupt(kind, [next_opcode]),
_ => unsafe { unreachable_unchecked() },
}