Autor Thema: Port 0x60 leeren - Tastaturtreiber  (Gelesen 13232 mal)

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« 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. :)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 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?
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #2 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.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 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.
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #4 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.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 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. */
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #6 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);
    }
}

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 06. May 2012, 17:55 »
Funktioniert die Tastatur denn überhaupt auf echter Hardware?
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 06. May 2012, 18:00 »
Ja, ohne Probleme. Das Problem ist ja auch, dass man zu früh auf Enter drücken kann. ;)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #9 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
    }
  }
}
Dieser Text wird unter jedem Beitrag angezeigt.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #10 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;
}
« Letzte Änderung: 07. May 2012, 22:06 von üäpöol »

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #11 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

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #12 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?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #13 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?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 08. May 2012, 11:12 »
Zitat
if (ignore_keypress == 1) return 0;
und wenn ignore_keypress 1 ist, oder?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 08. May 2012, 12:00 »
Ja, aber das ist ja bei der while-Schleife nicht der Fall.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #16 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!

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #17 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

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #18 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.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #19 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

 

Einloggen