Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: üäpöol am 06. May 2012, 16:44

Titel: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 16:44
Hallo,
ich habe einen kleinen Tastaturtreiber mit Port 0x60 geschrieben, der auch wunderbar funktioniert. Nun ist es aber so, dass der Port die gedrückte Taste immer speichert. Ich möchte erst prüfen, ob nach einer bestimmten Stelle eine bzw. welche Taste gedrückt wurde. Dazu habe ich folgende Funktion geschrieben:
void reset_keyboard() {
outb(0x60, inb(0x60)^inb(0x60));
}
Dieser Code funktioniert im Gegensatz zu
void reset_keyboard() {
outb(0x60, 0);
}
zumindest in der VirtualBox, aber nicht mit echter Hardware.
Mir ist nicht ganz klar, was an der Funktion falsch ist.
Ich bedanke mich schon einmal vorträglich für jede Hilfestellung. :)
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 06. May 2012, 16:52
Ich bin mir nicht sicher, ob ich genau verstehe was du damit meinst, dass der Port die gedrückte Taste speichert. Meinst du damit, dass immer noch der selbe Wert drin steht, nachdem du den Port einmal ausgelesen hast, und du erwartest, dass der Port jetzt leer sein müsste?
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 16:54
Ich meine damit nur, dass ich nicht kontrollieren kann, was der Port die Taste einliest. Ich kann nur dem Port "leeren", wenn ich überprüfen will, was danach gedrückt wurde.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 06. May 2012, 17:03
Ich versteh immer noch nicht, was du vorhast.

Also der Puffer im KBC kann so verwendet werden, dass du zuerst Port 0x64 ausliest, und das untereste Bit prüfst. Wenn es gesetzt ist, steht ein Wert in Port 0x60 zum Auslesen bereit. Ein Reset ist da nicht nötig.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 17:11
Ich werde mal konkreter. Ich habe eine while Schleife, in der Töne abgespielt werden. Danach wird der Benutzer gefragt, ob er die Töne noch einmal hören möchte. Man muss auf Enter drücken, damit es nochmal abgespielt wird. Wenn eine andere Taste gedrückt wird, wird die while Schleife beendet. Es ist allerdings von entscheidender Bedeutung, dass man erst, wenn die Töne abgespielt wurden, auf Enter drückt. Im Moment kann man aber auch während die Töne abgespielt werden auf Enter drücken, um schon zuvor zu sagen, dass das Programm beendet werden soll. Deshalb muss der Port nach dem Abspielen der Töne resettet werden, um es danach zu prüfen. Ich hoffe, es wurde jetzt klarer.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 06. May 2012, 17:16
Ich glaube ich verstehe das jetzt. Nachdem du die Töne abgespielt hast, musst du den Tastaturpuffer leeren. Das geht wie in meinem letzten Post beschrieben, indem du solange von Port 0x60 liest, wie Bit 0 in Port 0x64 gesetzt ist. Damit sind alle Tasten verworfen, die er während des Abspielens gedrückt hat. Danach sagst du dem Nutzer, dass er eine Taste drücken soll, und kannst wieder von Port 0x60 lesen, um diese Taste zu bekommen.

    /* Tastaturpuffer leeren */
    while (inb(0x64) & 0x1) {
        inb(0x60);
    }

    /* Hier auf Eingabe warten. */
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 17:37
Ja, das funktioniert leider auch nur im EMU, aber nicht mit echter Hardware.
Hier mal ein bisschen Code:
while(I_had_enough == 0)
{
reset_keyboard();
PlayMusic();
reset_keyboard();
print("Drücke Enter, um die Töne nochmal zu hören", 15*160+12, 0x0E);
print("oder eine andere Taste, wenn du genug hattest.", 16*160+12, 0x0E);
while(keyboard_handler(0) == 0);
if (keyboard_handler(0) == '\n') {
I_had_enough=0;
}
else {
I_had_enough=1;
}
reset_keyboard();
}
und
void reset_keyboard() {
while (inb(0x64) & 0x1)
{
        inb(0x60);
    }
}
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 06. May 2012, 17:55
Funktioniert die Tastatur denn überhaupt auf echter Hardware?
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 18:00
Ja, ohne Probleme. Das Problem ist ja auch, dass man zu früh auf Enter drücken kann. ;)
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 06. May 2012, 18:34
Was macht dann deine keyboard_handler-Funktion? Passt das Verhalten der Funktion dazu, dass du sie mindestens zweimal aufrufst, wenn du nur einen Tastendruck haben willst? Behandeltst du make und break-Codes korrekt? Hast du schon mal versucht den Rückgabewert der Funktion in einer Variable zu speichern, und diese dann mit '\n' zu vergleichen?

while (1) {
  int k = keyboard_handler(0);
  if (k != 0) {
    if (k == '\n') {
       enter
    } else {
       andere taste
    }
  }
}
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 06. May 2012, 18:40
Das mit dem Enter und der anderen Taste funktioniert doch, aber dass Enter kann eben auch zu früh gedrückt werden und das muss ich verhindern. Wie gesagt, die keyboard_handler() funktioniert ohne Probleme.
Aber ich kann hier ja mal die Funktion posten:
char keyboard_handler(int show) {
char scancode=inb(0x60);
if (!(scancode & 0x80)) {
if (show == 1) print_char(zeichen[(short)scancode], 4*160+12, 0x02);
return zeichen[(short)scancode];
}
return 0;
}
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Svenska am 07. May 2012, 23:27
Mach eine globale Variable "volatile int ignore_keypress = 1" und in deinem Handler ein "if(ignore_keypress) return;" an den Anfang. Wenn du anfängst zu spielen, setzt du ignore_keypress auf 1, wenn du fertig bist, auf 0. Fertig.

Sauberer ist es, zwischen "Ende des Abspielvorgangs" und "Benutzeraufforderung" die von Jidder gepostete while-Schleife "Tastaturpuffer leeren" aufzurufen. Vielleicht ist es auch hilfreich, zwischenzeitlich ein bisschen Zeit vergehen zu lassen.

Gruß,
Svenska
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 08. May 2012, 08:33
Es ist mir ein Rätsel, warum auch das nicht funktioniert; Es wird einfach ignoriert. Ich poste hier mal den relevanten Code. Irgendwo muss ich einen Fehler gemacht haben:
keyboard.c
char keyboard_handler(int show) {
if (ignore_keypress == 1) return 0;
volatile char scancode=inb(0x60);
if (!(scancode & 0x80)) {
if (show == 1) print_char(kbdus[(short)scancode], 4*160+12, 0x02);
return kbdus[(short)scancode];
}
return 0;
}
main.c
while(I_had_enough == 0)
{
ignore_keypress=1;
PlayMusic();
print("Drücke Enter, um die Töne nochmal zu hören", 15*160+12, 0x0E);
print("oder eine andere Taste, wenn du genug hattest.", 16*160+12, 0x0E);
ignore_keypress=0;
while(keyboard_handler(0) == 0);
if (keyboard_handler(0) == '\n') {
I_had_enough=0;
}
else {
I_had_enough=1;
}
ignore_keypress=1;
}
ignore_keypress=1;
cls(15*160+12, 42);
print("Drücke 'r' um den PC neuzustarten..", 20*160+12, 0x04);
cls(16*160+12, 39);
ignore_keypress=0;
while (keyboard_handler(1) != 'r');
print("Restart...", 4, 0x07);
restart_pc();
Habe ich hier einen Fehler gemacht?
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: kevin am 08. May 2012, 10:01
Sicher, dass deine while-Bedingung korrekt ist? Kommt 0 nicht nur dann zurück, wenn es ein Breakcode oder eine unbekannte Taste war?
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 08. May 2012, 11:12
Zitat
if (ignore_keypress == 1) return 0;
und wenn ignore_keypress 1 ist, oder?
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: kevin am 08. May 2012, 12:00
Ja, aber das ist ja bei der while-Schleife nicht der Fall.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 08. May 2012, 12:45
Ah, sorry jetzt versteh ich's erst. Ich gehe mal davon aus, du meinst diese while Schleife
while(keyboard_handler(0) == 0);
Dass nur etwas passiert, wenn eine Taste gedrückt wurde funktioniert ja super, das Problem ist nur, dass ein zu frühes Enter auch akzeptert wird, also bevor der Text ausgegeben wird. Du hast aber trotzdem damit recht, dass diese Lösung nicht funktionieren kann, weil die Taste ja trotzdem im Port gespeichert bleibt. Ich muss den Port also auf 0 setzen, bevor ich abfrage. Aber das hat mit den bisherigen Lösungen aus irgendeinem Grund nur in VirtualBox funktioniert.
Ich bin weiterhin für jeden Vorschlag dankbar!
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Svenska am 08. May 2012, 13:41
Wie Jidder bereits geschrieben hatte, hast du einen Denkfehler in deiner keyboard_handle(). Du kannst zwar ständig aus Port 0x60 lesen, aber ob da auch was sinnvolles (neues) drinsteht, steht in Bit 1 in Port 0x64.

Sonst ignorierst du den Port 0x60 so lange, bis deine Schleife vorbei ist - und liest dann die zuletzt gedrückte Taste aus.

Gruß,
Svenska
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 08. May 2012, 14:16
Ah. OK, jetzt verstehe ich's. Jetzt funktioniert es auch, bis auf eine kleine Sache. Hier erstmal der neue Code:
char keyboard_handler(int show) {
volatile char scancode=inb(0x60);
if (!(scancode & 0x80) && inb(0x64) & 0x1) {
if (show == 1) print_char(kbdus[(short)scancode], 4*160+12, 0x02);
return kbdus[(short)scancode];
}
return 0;
}
Der Code hat nur zur Folge, dass es immer eine Verzögerung nach einem Tastendruck gibt, bis darauf reagiert wird. Manchmal muss ich sogar zweimal drücken. Ich habe das ignore_keypress=1 wieder zu reset_keyboard geändert.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Svenska am 08. May 2012, 19:01
Ah. OK, jetzt verstehe ich's.
Nein, tust du nicht.

Du liest aus Port 0x60 und guckst danach in Port 0x64 nach, ob Port 0x60 gültige Daten enthält. Du solltest aber vorher nachschauen, weil du bereits durch das Lesen von Port 0x60 den Port 0x64 veränderst. Das ist Hardware. Da haben sogar Lesezugriffe (manchmal) Folgen.

Gruß,
Svenska
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 08. May 2012, 20:00
Hm... Das verwirrt mich ein bisschen. Ich habe aktuell folgenden Code:
char keyboard_handler(int show) {
volatile char scancode=port_in(0x60);
if (!(scancode & 0x80)) {
if (show == 1) print_char(kbdus[(short)scancode], 4*160+12, 0x02);
return kbdus[(short)scancode];
}
return 0;
}

void reset_keyboard() {
while (!(port_in(0x64) & 0x1));
}
Der Code in der main.c hat sich nicht verändert, nur dass ich das reset_keyboard ganz am Anfang der großen hile Schleife entfernt habe. Es funktioniert auch alles so weit perfekt; das einzige seltsame Problem ist noch, wenn man vor der großen while Schleife eine Taste drückt und danach Enter, führt das dazu, dass aus der Schleife gegangen wird und gleich abgefragt, wird, ob der Computer neugestartet werden soll. Ich habe den Eindruck, dass irgendetwas total schiefgeht.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 08. May 2012, 20:08
Warum verwendest du eigentlich keine Interrupts und abstrahierst die Tastatur als Queue (von Events)? Das ständige Rumgebastel an den I/O-Port-Funktionen führt nicht zum Ziel. Wenn du den Tastatur"treiber" von deiner Programmlogik mal etwas entkoppelst, kannst du den Code, den du schreibst sogar für andere Dinge wiederverwenden.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 09. May 2012, 08:47
Ja, das wäre wahrscheinlich einfacher. Soweit ich weiß, wird aber kein Interrupt ausgelöst, wenn eine Taste gedrückt wurde. Wie kann ich das denn programmieren? Gibt es dafür einen Artikel? Ich hab keinen gefunden.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: OS_3000 am 09. May 2012, 11:41
Natürlich kann auch ein Interrupt ausgelöst werden.
Ist im Wiki auch genauestens beschrieben:
http://www.lowlevel.eu/wiki/KBC#IRQ-Handler (http://www.lowlevel.eu/wiki/KBC#IRQ-Handler)
http://www.lowlevel.eu/wiki/IRQ#IRQ_Tabelle (http://www.lowlevel.eu/wiki/IRQ#IRQ_Tabelle)
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: Jidder am 09. May 2012, 20:02
Außerdem http://www.lowlevel.eu/wiki/Teil_5_-_Interrupts für allgemeine Interrupts im PM.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 10. May 2012, 16:16
OK. Vielen Dank, ich habe es jetzt über IRQ 1 gelöst. Funktioniert perfekt.
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 14. May 2012, 14:47
Ich habe nochmal eine ähnliche Frage. Ich wollte die Shifttaste programmieren:
void keyboard_handler(struct regs *null) {
volatile char scancode=inb(0x60);
if (!(scancode & 0x80) && scancode != 42 && scancode != 170) {
if (shift != 1) l_pressed_key=kbdus[(short)scancode];
else {
l_pressed_key=kbdus_caps[(short)scancode];
}
}
else {
if (scancode == 42) shift=1;
else if (scancode == 170) shift=0;
else {
print_char(scancode, 16, 0x07);
l_pressed_key=(char) 0;
}
}
}
Leider wird nur das Drücken der Shifttaste realisiert, ansonsten wird ein '¬' ausgegeben, was nach dieser Tabelle http://www.tcp-ip-info.de/tcp_ip_und_internet/ascii.htm (http://www.tcp-ip-info.de/tcp_ip_und_internet/ascii.htm) auch 170 sein sollte. Was habe ich schon wieder falsch verstanden? :D
Titel: Re: Port 0x60 leeren - Tastaturtreiber
Beitrag von: üäpöol am 14. May 2012, 21:05
Hat sich erledigt. Für alle, die dasselbe Problem haben:
void keyboard_handler(struct regs *null) {
volatile char scancode=inb(0x60);
if ((scancode & 0x80) == 0) {
if (shift != 1) l_pressed_key=kbdus[(short)scancode];
else {
l_pressed_key=kbdus_caps[(short)scancode];
}
if (scancode == 42) shift=1;
}
else {
scancode-=0x80;
if (scancode == 42) shift=0;
else {
l_pressed_key=(char) 0;
}
}
}
Das funktioniert bei mir. :)