Deutsches Tastaturlayout



  • Hi,

    ich habe auf Wunsch von Herrn Henkes angefangen, ein deutsches Tastaturlayout zu schreiben. Hier mein Anfang, einige deutsche Sachen sind bereits implementiert:

    (Keyboard_GER.h)

    #ifndef KEYBOARD_H
    #define KEYBOARD_H
    
    #define NULL 0
    #define ESC       27
    #define BACKSPACE '\b'
    #define TAB       '\t'
    #define ENTER     '\n'
    #define RETURN    '\r'
    #define NEWLINE   ENTER
    
    // Non-ASCII special scancodes // Esc in scancode is 1
    #define    KESC         1
    #define    KF1          0x80
    #define    KF2         (KF1 + 1)
    #define    KF3         (KF2 + 1)
    #define    KF4         (KF3 + 1)
    #define    KF5         (KF4 + 1)
    #define    KF6         (KF5 + 1)
    #define    KF7         (KF6 + 1)
    #define    KF8         (KF7 + 1)
    #define    KF9         (KF8 + 1)
    #define    KF10        (KF9 + 1)
    #define    KF11        (KF10 + 1)
    #define    KF12        (KF11 + 1)
    
    // Cursor Keys
    #define    KINS         0x90
    #define    KDEL        (KINS + 1)
    #define    KHOME       (KDEL + 1)
    #define    KEND        (KHOME + 1)
    #define    KPGUP       (KEND + 1)
    #define    KPGDN       (KPGUP + 1)
    #define    KLEFT       (KPGDN + 1)
    #define    KUP         (KLEFT + 1)
    #define    KDOWN       (KUP + 1)
    #define    KRIGHT      (KDOWN + 1)
    
    // "Meta" keys
    #define    KMETA_ALT     0x0200                                // Alt is pressed
    #define    KMETA_CTRL    0x0400                                // Ctrl is pressed
    #define    KMETA_SHIFT   0x0800                                // Shift is pressed
    #define    KMETA_ANY    (KMETA_ALT | KMETA_CTRL | KMETA_SHIFT)
    #define    KMETA_CAPS    0x1000                                // CapsLock is on
    #define    KMETA_NUM     0x2000                                // NumLock is on
    #define    KMETA_SCRL    0x4000                                // ScrollLock is on
    
    // Other keys
    #define    KPRNT    ( KRT + 1 )
    #define    KPAUSE   ( KPRNT + 1 )
    #define    KLWIN    ( KPAUSE + 1 )
    #define    KRWIN    ( KLWIN + 1 )
    #define    KMENU    ( KRWIN + 1 )
    
    #define    KRLEFT_CTRL        0x1D
    #define    KRRIGHT_CTRL       0x1D
    
    #define    KRLEFT_ALT         0x38
    #define    KRRIGHT_ALT        0x38
    
    #define    KRLEFT_SHIFT       0x2A
    #define    KRRIGHT_SHIFT      0x36
    
    #define    KRCAPS_LOCK        0x3A
    #define    KRSCROLL_LOCK      0x46
    #define    KRNUM_LOCK         0x45
    #define    KRDEL              0x53
    
    #define MAXKEYBUFFER 64       // max keyboard buffer
    
    // Keymaps: German
    
    // Non-Shifted scan codes to ASCII:
    static unsigned char asciiNonShift[] = {
    NULL, ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'ß', '´', BACKSPACE,
    TAB, 'q', 'w',   'e', 'r', 't', 'z', 'u', 'i', 'o', 'p',   'ü', '+', ENTER, 0,
    'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä', '#', 0, '<',
    'y', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0, 0, 0, ' ', 0,
    KF1, KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, 0, 0,
    KHOME, KUP, KPGUP,'-', KLEFT, '5', KRIGHT, '+', KEND, KDOWN, KPGDN, KINS, KDEL, 0, 0, 0, KF11, KF12 };
    
    // Shifted scan codes to ASCII:
    static unsigned char asciiShift[] = {
    NULL, ESC, '!', '"', '§', '$', '%', '&', '/', '(', ')', '=', '?', '`', BACKSPACE,
    TAB, 'Q', 'W',   'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P',   'Ü', '*', ENTER, 0,
    'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ö', 'Ä', '\'', 0, '>',
    'Y', 'X', 'C', 'V', 'B', 'N', 'M', ';', '.', '_', 0, 0, 0, ' ', 0,
    KF1,   KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, 0, 0,
    KHOME, KUP, KPGUP, '-', KLEFT, '5',   KRIGHT, '+', KEND, KDOWN, KPGDN, KINS, KDEL, 0, 0, 0, KF11, KF12 };
    
    #endif
    

    Problem:
    Nur das wichtigste geht (also Fragezeichen und Z mit Y vertauscht)
    Was soll ich tun?

    Cuervo


  • Mod

    Die Scancodes werden mittels Array in ASCII-Code umgewandelt.

    Das habe ich (siehe: http://www.viennacomputerproducts.com/downloads/documents.pdf) in einem gespeicherten Artikel (Auszüge) gefunden:

    ; the Key tables (for Europe keyboards)

    Key_Table_europe_ascii
    db
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'ß', '´', 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 'ü', '+', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä', '^', '#', 'y', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '-', '<'

    Key_Table_europe_ascii_shift
    db
    '!', '"', '§', '$', '%', '&', '/', '(', ')', '=', '?', '`', 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P', 'Ü', '*', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ö', 'Ä', '°', 27h, 'Y', 'X', 'C', 'V', 'B', 'N', 'M', ';', ':', '_', '>'

    Key_Table_europe_ascii_alt_gr
    db
    '²', '³', '{', '[', ']', '}', '\', '@', '€', '~', 'μ', '|'

    ; the Key tables (for American keyboards)

    Key_Table_american_ascii
    db
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '-', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 27h, '`', '\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\'

    Key_Table_american_ascii_shift
    db
    '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 'Q', 'W', 'E', 'R', 'T', 'Y', 'I', 'I', 'O', 'P', '{', '}', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', '|'

    Am besten die Header austauschen mittels #define GER oder #define US ... in os.h

    siehe auch die Abschnitte im bisherigen Tutorial:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId185043
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId50083
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId845287
    http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId681780

    Linux keyboard driver: http://www.linuxjournal.com/article/1080

    Ist kein triviales Thema, aber lohnenswert, weil man eine Menge über Keyboards und die damit zusammenhängende Technologie lernt. Die Tastatur ist bei einem einfachen OS die wichtigste Schnittstelle zum User.



  • @Cuervo: Ist jetzt zwar nicht so wichtig, aber bitte statt 'unsigned char' 'uint8_t' verwenden. Die Tabellen könnten auch 'static const' sein...


  • Mod

    statt 'unsigned char' 'uint8_t' verwenden

    Das tausche ich alles aus, wenn die os.h aktualisert wird auf die neuen typedefs.



  • Boah ey, ich glaub, das ist zu schwer...

    Hier aktueller Stand:

    #ifndef KEYBOARD_H
    #define KEYBOARD_H
    
    #define ESC       27
    #define BACKSPACE '\b'
    #define TAB       '\t'
    #define ENTER     '\n'
    #define RETURN    '\r'
    #define NEWLINE   ENTER
    
    // Non-ASCII special scancodes // Esc in scancode is 1
    #define    KESC         1
    #define    KF1          0x80
    #define    KF2         (KF1 + 1)
    #define    KF3         (KF2 + 1)
    #define    KF4         (KF3 + 1)
    #define    KF5         (KF4 + 1)
    #define    KF6         (KF5 + 1)
    #define    KF7         (KF6 + 1)
    #define    KF8         (KF7 + 1)
    #define    KF9         (KF8 + 1)
    #define    KF10        (KF9 + 1)
    #define    KF11        (KF10 + 1)
    #define    KF12        (KF11 + 1)
    
    // Cursor Keys
    #define    KINS         0x90
    #define    KDEL        (KINS + 1)
    #define    KHOME       (KDEL + 1)
    #define    KEND        (KHOME + 1)
    #define    KPGUP       (KEND + 1)
    #define    KPGDN       (KPGUP + 1)
    #define    KLEFT       (KPGDN + 1)
    #define    KUP         (KLEFT + 1)
    #define    KDOWN       (KUP + 1)
    #define    KRIGHT      (KDOWN + 1)
    
    // "Meta" keys
    #define    KMETA_ALT     0x0200                                // Alt is pressed
    #define    KMETA_CTRL    0x0400                                // Ctrl is pressed
    #define    KMETA_SHIFT   0x0800                                // Shift is pressed
    #define    KMETA_ANY    (KMETA_ALT | KMETA_CTRL | KMETA_SHIFT)
    #define    KMETA_CAPS    0x1000                                // CapsLock is on
    #define    KMETA_NUM     0x2000                                // NumLock is on
    #define    KMETA_SCRL    0x4000                                // ScrollLock is on
    
    // Other keys
    #define    KPRNT    ( KRT + 1 )
    #define    KPAUSE   ( KPRNT + 1 )
    #define    KLWIN    ( KPAUSE + 1 )
    #define    KRWIN    ( KLWIN + 1 )
    #define    KMENU    ( KRWIN + 1 )
    
    #define    KRLEFT_CTRL        0x1D
    #define    KRRIGHT_CTRL       0x1D
    
    #define    KRLEFT_ALT         0x38
    #define    KRRIGHT_ALT        0x38
    
    #define    KRLEFT_SHIFT       0x2A
    #define    KRRIGHT_SHIFT      0x36
    
    #define    KRCAPS_LOCK        0x3A
    #define    KRSCROLL_LOCK      0x46
    #define    KRNUM_LOCK         0x45
    #define    KRDEL              0x53
    
    #define MAXKEYBUFFER 64       // max keyboard buffer
    
    // Keymaps: German
    
    // Non-Shifted scan codes to ASCII:
    static unsigned char asciiNonShift[] = {
    0, ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0xE1, '´', BACKSPACE,
    TAB, 'q', 'w',   'e', 'r', 't', 'z', 'u', 'i', 'o', 'p',   0x81, '+', ENTER, 0,
    'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, 0x84, '^', 0, '#',
    'y', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0, 0, 0, ' ', 0,
    KF1, KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, 0, 0,
    KHOME, KUP, KPGUP,'-', KLEFT, '5', KRIGHT, '+', KEND, KDOWN, KPGDN, KINS, KDEL, 0, 0, 0, KF11, KF12 };
    
    // Shifted scan codes to ASCII:
    static unsigned char asciiShift[] = {
    0, ESC, '!', '"', 0x15, '$', '%', '&', '/', '(', ')', '=', '?', '`', BACKSPACE,
    TAB, 'Q', 'W',   'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P',   0x9A, '*', ENTER, 0,
    'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x99, 0x8E, 0xF8, 0, ' ',
    'Y', 'X', 'C', 'V', 'B', 'N', 'M', ';', ':', '_', 0, 0, 0, ' ', 0,
    KF1,   KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, 0, 0,
    KHOME, KUP, KPGUP, '-', KLEFT, '5',   KRIGHT, '+', KEND, KDOWN, KPGDN, KINS, KDEL, 0, 0, 0, KF11, KF12 };
    
    #endif
    

    Kein Unterschied, Buchstaben und ? gehen..-.-

    Cuervo


  • Mod

    Boah ey, ich glaub, das ist zu schwer...

    Nicht aufgeben. Es besteht kein Zeitdruck und wir haben ausreichend Expertise im Team. 🙂

    Ich werde mir die neuen Key Maps anschauen. Ist doch ein interessantes Thema, vor allem auch eine wichtige Schnittstelle. Tyndur hat deutsch hardverdrahtet, soweit ich weiß. 🙂



  • tyndur hat die deutsche Tastaturbelegung als Default, man kann aber zur Laufzeit zu beliebigen anderen Tastaturbelegungen wechseln (eine Datei für US-Layout ist dabei, andere müsste man erstmal anlegen).


  • Mod

    Danke für die korrekte Darstellung, habe das falsch aufgeschnappt. Da können wir sicher von euch "Honig saugen". 🙂

    Hier findet man ebenfalls das Thema Keyboard beschrieben:
    http://www.brokenthorn.com/Resources/OSDev19.html


  • Mod

    Habe es getestet, spricht noch kein "deutsch", nur qwertz bzw. QWERTZ.



  • Cuervo schrieb:

    Problem:
    Nur das wichtigste geht (also Fragezeichen und Z mit Y vertauscht)

    Das mag daran liegen, daß die "program.c" nicht neu kompiliert wurde. Dort ist noch ein "Filter" eingebaut:

    // program.c ver 110:
    
    // if (
    //      (input==32)                ||  // space key
    //      (input==45)                ||  // "-"   key
    //      (input==63)                ||  // "?"   key
    //     ((input>=65)&&(input<=90))  ||  // capital letters
    //     ((input>=97)&&(input<=122)) ||  // little letters
    //     ((input>=48)&&(input<=57))      // 0 ... 9
    //     )
    
     if ((input >= 0x20) && (input <= 0x7E))
    

    Ansonsten läuft es perfekt:

    $> rm -rf / <--
    
    Sorry, I do not know this command.
    
    $> _:;,.&#'(§$)# !!!111elf <--
    
    Sorry, I do not know this command.
    

    🙂


  • Mod

    Danke für diesen Hinweis! Wir verwenden die shell (program.elf) aus dem User-Space zum Testen. Directory: ...\Source\user\user_program_c\..
    program.elf wird dann nach ...\Source\user\init_rd_img\.. kopiert und in initrd.dat eingebunden.
    Diese Datei kopiert man nach ...\Source\kernel\..
    Auf diese Weise wird program.elf dann mittels incbin "initrd.dat" (im Assembler-Programm data.asm) im ELF-Format in den Kernel eingebunden.
    In ckernel.c wird diese Datei ausgelesen und im User Mode gestartet.


  • Mod

    äöüß ° § ' < > gehen noch nicht, und natürlich die AltGr. Sieht aber schon gut aus.



  • Das ist jetzt eine unangenehme Sache:

    ü == 0x81 ä == 0x84 ö == 0x94
    

    Als (signed) "char" sind sie kleiner 0. (Ev. Datentyp ändern). Nun muß man sehen, wo sie überall versehentlich "weggefiltert" werden, z.B.:

    if ((input >= 0x20) && (input <= 0x7E)) //  :open_mouth:
    

    Aber nachsehen kann man auch morgen noch. 🙂


  • Mod

    @ +gjm+ : Danke für die Unterstützung! Der Tipp mit der "shell" war wichtig. 🙂
    Weiß nicht mehr genau, warum ich da alles dicht gemacht habe, da gab's bestimmt "Störenfriede".



  • Abgesehen von der "program.c" gibt es nur noch in der "video.c" einen "Filter", der beseitigt werden sollte:

    // video.c (version 111)
    
    void putch(int8_t c)
    
    // else if(c >= ' ') // Läßt nur 0x20 bis 0x7F durch.
     else if(c != 0)     // Falls c == 0 gerät putch seltsamerweise in eine Endlosschleife!
    
    //  *pos = c | att;
      *pos = ((uint8_t) c) | att; // Vielleicht kennt jemand einen schöneren Cast hier weil Bit-Operationen
                                  // mit 'signed' meistens nicht das erwartete Ergebnis bringen.
                                  // Die Umlaute werden sonst z.B. mit 'falschen' Attributen angezeigt.
    

    Nun sollte der "keyboard_GER.h" nichts mehr im Wege stehen.
    Bis auf eine Kleinigkeit: Die Taste links vom "y" (<>|) hat bei mir den Scancode 0x56. 😕


  • Mod

    Danke für das Aufspüren dieser Stellen! Allerdings hat sich nun noch nichts geändert. Nun muss man wieder in den Tastaturtreiber und die Keymaps einsteigen.

    Die Taste links vom "y" (<>|) hat bei mir den Scancode 0x56

    Ja, das ist eine Taste, die aus der Reihe tanzt. Da hatten wir noch Nullen in der Keymap. Jetzt klappt das wie folgt:

    ... KDEL, 0, 0, '<', KF11 ...
    ... KDEL, 0, 0, '>', KF11 ...
    

    US-Keyboard-Layout: http://www.conjective.ch/kbdus/keymap.pdf
    Scancodes: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html#ss1.4
    ASCII/ANSI-Tabellen: http://www.torsten-horn.de/techdocs/ascii.htm
    Codepage 437: http://lowlevel.brainsware.org/wiki/index.php/Codepage_437 (äöüߧ kommen nicht als solche raus, da muss in putch(...) noch umgewandelt werden)

    Den Scancode kann man selbst mittels eines zusätzlichen printformat(...) in keyboard.c leicht verfolgen:

    uint8_t ScanToASCII()
    {
    	uint8_t retchar;	               // The character that returns the scan code to ASCII code
    	scan = FetchAndAnalyzeScancode();  // Grab scancode, and get the position of the shift key
    
        /// TEST
        printformat(" scan:%d ",scan); // <--------- hier!
        /// TEST
    
    	if( ShiftKeyDown )
    	    retchar = asciiShift[scan];	   // (Upper) Shift Codes
    	else
    		retchar = asciiNonShift[scan]; // (Lower) Non-Shift Codes
    
    	if( ( !(scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT) ) && ( KeyPressed == 1 ) ) //filter Shift Key and Key Release
    	    return retchar; // ASCII version
    	else
    	    return 0;
    }
    

  • Mod

    Geschafft! äöüß°§ <--- alles geht!
    Problem: In der userlib.h/c musste getchar() und putch() umgebaut werden von char auf unsigned char, damit die Werte >127 sauber durch kamen. 🙂


  • Mod

    Die Abfolge der Umwandlungen ist:

    Keyboard-Input ==> Scancode (Taste) + Shift(Non-Shift) ==> ASCII ==> Codepage437 ==> Monitor-Output

    Damit printformat("äöüߧ°ÄÖÜ") sauber auf dem Monitor ankommt, müssen die keymaps in ASCII geführt werden. Die Umwandlung nach codepage437 darf erst in putch(...) erfolgen. Ich habe dafür in video.c eine Funktion transferFromAsciiToCodepage437(...) eingeführt. Provisorischer Inhalt:

    static uint8_t transferFromAsciiToCodepage437(uint8_t ascii)
    {
        uint8_t c;
        if      ( ascii == 0xE4 ) c = 0x84; // ä
        else if ( ascii == 0xF6 ) c = 0x94; // ö
        else if ( ascii == 0xFC ) c = 0x81; // ü
        else if ( ascii == 0xDF ) c = 0xE1; // ß
        else if ( ascii == 0xA7 ) c = 0x15; // §
        else if ( ascii == 0xB0 ) c = 0xF8; // °
        else if ( ascii == 0xC4 ) c = 0x8E; // Ä
        else if ( ascii == 0xD6 ) c = 0x99; // Ö
        else if ( ascii == 0xDC ) c = 0x9A; // Ü
        else    { c = ascii;  } // to be checked for more deviations
    
        return c;
    }
    

    In putch(...):

    void putch(int8_t c)
    {
        uint8_t uc = transferFromAsciiToCodepage437((uint8_t)c); // no negative values
    

    Nun funktioniert die deutsche Tastatur sehr gut. Es fehlen nun nur noch die Verarbeitung von AltGr+Taste etc. im Keyboard-Treiber.

    Wer das mit prüfen möchte:
    http://www.henkessoft.de/OS_Dev/Downloads/111a.zip

    (Ich habe diese Version noch nicht nach SVN geschoben, da die shell noch überarbeitet werden muss)



  • Gute Arbeit! Bringt richtig Spaß zu testen. Folgendes könnte noch geändert werden:

    //--------------------------------------------
    // program.c (version 111a)
    
    // if(i<0xFF) // test-wise open
     if(i<70)   // sonst kann entry[80] überlaufen
    
    //--------------------------------------------
    // keyboard_GER.h (version 111a)
    
    static unsigned char asciiNonShift[] = {
    // ... '0', 0xDF, '´', BACKSPACE,
    ... '0', 0xDF, '\'', BACKSPACE, // Der Editor setzt für '´' einen falschen Wert.
    

    "AltGr" generiert auf "Druck" zwei Scancodes (0x60+0x38). Wenn man jetzt in der FetchAndAnalyzeScancode "AltGr" abfangen will, müsste man sich den letzten Scancode ständig merken:

    // Pseudocode
    
    if (inportb(0x64)&1 )
        cur_scan = inportb(0x60);   // 0x60: get scan code from the keyboard
    
    if (cur_scan == 0x38) {
    if (prv_scan == 0x60) {
     AltGrDown = 1; // dann AltGr gedrückt oder losgelassen :confused:
    }}
    
    prv_scan = cur_scan;
    

    Außerdem braucht sie dann in der ScanToASCII ihr eigenes "asciiAltGr[]"-Array. Möglicherweise kennt jemand eine elegantere Lösung? 🙂


  • Mod

    Gute Arbeit! Bringt richtig Spaß zu testen.

    Danke für das positive Feedback und für deine konstruktiven Anmerkungen. Das motiviert.

    // if(i<0xFF) // test-wise open
     if(i<70)   // sonst kann entry[80] überlaufen
    

    Klar! Das war ein Versehen, wird demnächst berichtigt. Befehlszeilen von nahezu einer Zeile reichen zur Zeit völlig aus.

    static unsigned char asciiNonShift[] = {
    // ... '0', 0xDF, '´', BACKSPACE,
    ... '0', 0xDF, '\'', BACKSPACE, // Der Editor setzt für '´' einen falschen Wert.
    

    Der Akzent führt ja zu einem Zeichen mit Akzent, also einem veränderten Zeichen. Das ist also eine Kombination aus zwei Scancodes, die zu einem dritten führen. So drücke ich zunächst auf die Akzent-Taste, dann auf y, wenn ich týndur schreibe, daraus ergibt sich dann das Zeichen ý. Schreibe ich aber "týndur´s Bedeutung", so ist der von die angesprochene Apostroph anstelle des Akzentes das bessere Zeichen: "týndur's Bedeutung", Das könnte man z.B. sofort korrigieren. Nicht ganz einfaches Thema.

    Bezüglich AltGr habe ich bisher noch kein separates Keymap gesehen, aber warum nicht, ist ja weitgehend identisch. Codepage437 hat aber kein Euro-Zeichen.


Anmelden zum Antworten