PIC Tutorial — Änderungen 11123 → 11405

Zeile 21: Zeile 21:
 
Zur Reinitialisierung müssen vier sogenannte ICWs (Initialization Control Words) geschickt werden, die ich nun im Einzelnen durchgehen werde.
 
Zur Reinitialisierung müssen vier sogenannte ICWs (Initialization Control Words) geschickt werden, die ich nun im Einzelnen durchgehen werde.
 
Beginnen wir mit den Portnummern:
 
Beginnen wir mit den Portnummern:
−
<c>
+
<syntaxhighlight lang="c">
 
#define PIC_MASTER_COMMAND 0x20
 
#define PIC_MASTER_COMMAND 0x20
 
#define PIC_MASTER_DATA 0x21
 
#define PIC_MASTER_DATA 0x21
Zeile 28: Zeile 28:
 
#define PIC_SLAVE_DATA 0xA1
 
#define PIC_SLAVE_DATA 0xA1
 
#define PIC_SLAVE_IMR 0xA1
 
#define PIC_SLAVE_IMR 0xA1
−
</c>
+
</syntaxhighlight>
 
Wie man sehen kann, gibt es zwei PICs, die üblicherweise über die IRQ-Leitung 2 miteinander kommunizieren, was man aber auch ändern kann (siehe unten). Befehle müssen allerdings zu jedem PIC gesendet werden. Das Bitmuster der Befehle kann [[PIC#Initialisierung|hier]] hier noch einmal im Einzelnen nachvollzogen werden.
 
Wie man sehen kann, gibt es zwei PICs, die üblicherweise über die IRQ-Leitung 2 miteinander kommunizieren, was man aber auch ändern kann (siehe unten). Befehle müssen allerdings zu jedem PIC gesendet werden. Das Bitmuster der Befehle kann [[PIC#Initialisierung|hier]] hier noch einmal im Einzelnen nachvollzogen werden.
  
 
Kommen wir also zu ICW Nummer Eins:
 
Kommen wir also zu ICW Nummer Eins:
  
−
<c>
+
<syntaxhighlight lang="c">
 
void pic_remap(int interrupt_num)
 
void pic_remap(int interrupt_num)
 
{
 
{
 
     outb(PIC_MASTER_COMMAND, 0x11);
 
     outb(PIC_MASTER_COMMAND, 0x11);
 
     outb(PIC_SLAVE_COMMAND, 0x11);
 
     outb(PIC_SLAVE_COMMAND, 0x11);
−
</c>
+
</syntaxhighlight>
 
0x11 bedeutet: Wir senden auch ICW4, es gibt zwei PICs und wir wollen Initialisieren.
 
0x11 bedeutet: Wir senden auch ICW4, es gibt zwei PICs und wir wollen Initialisieren.
  
 
Das nächste ICW ist das wichtigste, denn hiermit wird angegeben, auf welchen Eintrag in der IDT der IRQ 0 (für den Master PIC) und IRQ 8 (für den Slave) zeigt. Dieser Wert wird der Funktion als Parameter übergeben. Üblicherweise verwendet man 32, um direkt an die Exceptions anzuschließen. Aber natürlich dürfen auch andere Werte verwendet werden, solange diese durch 8 teilbar sind.
 
Das nächste ICW ist das wichtigste, denn hiermit wird angegeben, auf welchen Eintrag in der IDT der IRQ 0 (für den Master PIC) und IRQ 8 (für den Slave) zeigt. Dieser Wert wird der Funktion als Parameter übergeben. Üblicherweise verwendet man 32, um direkt an die Exceptions anzuschließen. Aber natürlich dürfen auch andere Werte verwendet werden, solange diese durch 8 teilbar sind.
  
−
<c>
+
<syntaxhighlight lang="c">
 
     outb(PIC_MASTER_DATA, interrupt_num);
 
     outb(PIC_MASTER_DATA, interrupt_num);
 
     outb(PIC_SLAVE_DATA, interrupt_num+8);
 
     outb(PIC_SLAVE_DATA, interrupt_num+8);
−
</c>
+
</syntaxhighlight>
  
 
Die Hälfte ist geschafft. Kommen wir zu ICW3. Mit diesem gibt man an, über welchen IRQ die beiden PICs miteinander kommunizieren, das ist IRQ 2.
 
Die Hälfte ist geschafft. Kommen wir zu ICW3. Mit diesem gibt man an, über welchen IRQ die beiden PICs miteinander kommunizieren, das ist IRQ 2.
  
−
<c>
+
<syntaxhighlight lang="c">
 
     outb(PIC_MASTER_DATA, 0x04);
 
     outb(PIC_MASTER_DATA, 0x04);
 
     outb(PIC_SLAVE_DATA, 2);
 
     outb(PIC_SLAVE_DATA, 2);
−
</c>
+
</syntaxhighlight>
 
Merkwürdigerweise übergibt man an den Slave direkt die IRQ-Nummer, dem Master übergibt man eine Bitmaske, in dem das betreffende Bit gesetzt ist.
 
Merkwürdigerweise übergibt man an den Slave direkt die IRQ-Nummer, dem Master übergibt man eine Bitmaske, in dem das betreffende Bit gesetzt ist.
  
 
Fast fertig, nur noch ICW4 fehlt. Es wird 0x01 an beide gesendet, um anzuzeigen, dass wir uns im 8086-Mode befinden (was auch sonst...).
 
Fast fertig, nur noch ICW4 fehlt. Es wird 0x01 an beide gesendet, um anzuzeigen, dass wir uns im 8086-Mode befinden (was auch sonst...).
  
−
<c>
+
<syntaxhighlight lang="c">
 
   outb(PIC_MASTER_DATA, 0x01);
 
   outb(PIC_MASTER_DATA, 0x01);
 
   outb(PIC_SLAVE_DATA, 0x01);
 
   outb(PIC_SLAVE_DATA, 0x01);
 
}
 
}
−
</c>
+
</syntaxhighlight>
  
 
=== IRQs maskieren ===
 
=== IRQs maskieren ===
 
Wie bereits erwähnt, können die PICs auch bestimmte IRQs blockieren. Dazu dienen die Interrupt Mask Register (IMR). In diesen liegt eine Bitmaske. Ist ein Bit gesetzt, so ist der entsprechende IRQ blockiert. Die Nummern der IRQs sind [[PIC#IRQ Tabelle|hier]] zu finden. Schreiben wir uns eine Funktion dazu:
 
Wie bereits erwähnt, können die PICs auch bestimmte IRQs blockieren. Dazu dienen die Interrupt Mask Register (IMR). In diesen liegt eine Bitmaske. Ist ein Bit gesetzt, so ist der entsprechende IRQ blockiert. Die Nummern der IRQs sind [[PIC#IRQ Tabelle|hier]] zu finden. Schreiben wir uns eine Funktion dazu:
−
<c>
+
<syntaxhighlight lang="c">
 
void pic_mask_irqs(uint16_t mask)
 
void pic_mask_irqs(uint16_t mask)
 
{
 
{
Zeile 72: Zeile 72:
 
   outb(PIC_SLAVE_IMR, (uint8_t) (mask>>8));
 
   outb(PIC_SLAVE_IMR, (uint8_t) (mask>>8));
 
}
 
}
−
</c>
+
</syntaxhighlight>
 
Simpel, oder? Nebenbei: Wenn das Interruptflag gelöscht ist (->cli), dann kommen sowieso keine IRQs an. Außerdem werden alle IRQs des Slaves blockiert, wenn im Master der IRQ 2 maskiert wird.
 
Simpel, oder? Nebenbei: Wenn das Interruptflag gelöscht ist (->cli), dann kommen sowieso keine IRQs an. Außerdem werden alle IRQs des Slaves blockiert, wenn im Master der IRQ 2 maskiert wird.
  
Zeile 83: Zeile 83:
 
Bei ISRs, die IRQs behandeln, gibt es allerdings noch eine Besonderheit: Sie müssen ein End Of Interrupt (EOI) Signal an den PIC senden. Andernfalls arbeitet der PIC so, als wären alle IRQs im IMR maskiert. Mit anderen Worten: Kein IRQ kommt mehr bei der CPU an.
 
Bei ISRs, die IRQs behandeln, gibt es allerdings noch eine Besonderheit: Sie müssen ein End Of Interrupt (EOI) Signal an den PIC senden. Andernfalls arbeitet der PIC so, als wären alle IRQs im IMR maskiert. Mit anderen Worten: Kein IRQ kommt mehr bei der CPU an.
 
Dabei ist zu beachten, dass bei IRQs über 7 an ''beide'' PICs ein EOI gesendet werden muss, da diese ja auch über einen IRQ kommunizieren:
 
Dabei ist zu beachten, dass bei IRQs über 7 an ''beide'' PICs ein EOI gesendet werden muss, da diese ja auch über einen IRQ kommunizieren:
−
<c>
+
<syntaxhighlight lang="c">
 
#define EOI 0x20
 
#define EOI 0x20
  
Zeile 92: Zeile 92:
 
     outb(PIC_SLAVE_COMMAND, EOI);
 
     outb(PIC_SLAVE_COMMAND, EOI);
 
}
 
}
−
</c>
+
</syntaxhighlight>
 
Es ist eine beliebte Fehlerquelle, das Senden des EOI zu vergessen!
 
Es ist eine beliebte Fehlerquelle, das Senden des EOI zu vergessen!
  
 
== Der Code im Gesamten ==
 
== Der Code im Gesamten ==
−
<c>
+
<syntaxhighlight lang="c">
 
#define PIC_MASTER_COMMAND 0x20
 
#define PIC_MASTER_COMMAND 0x20
 
#define PIC_MASTER_DATA 0x21
 
#define PIC_MASTER_DATA 0x21
Zeile 130: Zeile 130:
 
     outb(PIC_SLAVE_COMMAND, EOI);
 
     outb(PIC_SLAVE_COMMAND, EOI);
 
}
 
}
−
</c>
+
</syntaxhighlight>
 
[[Kategorie:Hardware]]
 
[[Kategorie:Hardware]]
 
[[Kategorie:Tutorials]]
 
[[Kategorie:Tutorials]]