2018-09-28 20:35:51 +02:00
|
|
|
|
/*******************************************************************************/
|
|
|
|
|
/* COS2000 - Compatible Operating System - LGPL v3 - Hord<72> Nicolas */
|
|
|
|
|
/* */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
#include "interrupts.h"
|
2007-04-02 16:34:19 +02:00
|
|
|
|
#include "types.h"
|
|
|
|
|
#include "asm.h"
|
|
|
|
|
#include "memory.h"
|
|
|
|
|
#include "keyboard.h"
|
2018-08-30 01:07:28 +02:00
|
|
|
|
#include "vga.h"
|
2007-04-02 16:34:19 +02:00
|
|
|
|
#include "video.h"
|
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static u8 bufferscan[256] = { 0 };
|
|
|
|
|
static u8 bufferascii[256] = { 0 };
|
2007-04-02 16:34:19 +02:00
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static u8 ptrscan = 0;
|
|
|
|
|
static u8 ptrascii = 0;
|
|
|
|
|
static u16 kbdstatus, breakcode;
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/* Tables clavier */
|
|
|
|
|
|
|
|
|
|
static const u8 set1_normal[] = {
|
|
|
|
|
0, 0x1B, '&', '<EFBFBD>', '\"', '\'', '(', '-',
|
|
|
|
|
'<EFBFBD>', '_', '<EFBFBD>', '<EFBFBD>', ')', '=', '\b', '\t',
|
|
|
|
|
'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
|
|
|
|
|
'o', 'p', '^', '$', '\r', 0, 'q', 's',
|
|
|
|
|
'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
|
|
|
|
|
'<EFBFBD>', '<EFBFBD>', 0, '*', 'w', 'x', 'c', 'v',
|
|
|
|
|
'b', 'n', ',', ';', ':', '!', 0, '*',
|
|
|
|
|
0, ' ', 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '7',
|
|
|
|
|
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
|
|
|
'2', '3', '0', '.', 0, 0, '<', 0,
|
|
|
|
|
0
|
2007-04-02 16:34:19 +02:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static const u8 set1_shift[] = {
|
|
|
|
|
0, 0x1B, '1', '2', '3', '4', '5', '6',
|
|
|
|
|
'7', '8', '9', '0', '<EFBFBD>', '+', '\b', '\t',
|
|
|
|
|
'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
|
|
|
|
|
'O', 'P', '<EFBFBD>', '<EFBFBD>', '\r', 0, 'Q', 'S',
|
|
|
|
|
'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
|
|
|
|
|
'%', 0, 0, '<EFBFBD>', 'W', 'X', 'C', 'V',
|
|
|
|
|
'B', 'N', '?', '.', '/', '<EFBFBD>', 0, '*',
|
|
|
|
|
0, ' ', 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '7',
|
|
|
|
|
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
|
|
|
'2', '3', '0', '.', 0, 0, '>', 0,
|
|
|
|
|
0
|
2007-04-02 16:34:19 +02:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static const u8 set1_alt[] = {
|
|
|
|
|
0, 0x1B, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, '\r', 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '*',
|
|
|
|
|
0, ' ', 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '7',
|
|
|
|
|
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
|
|
|
'2', '3', '0', '.', 0, 0, 0, 0,
|
|
|
|
|
0
|
|
|
|
|
};
|
2007-04-02 16:34:19 +02:00
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static const u8 set1_altgr[] = {
|
|
|
|
|
0, 0x1B, 0, '~', '#', '{', '[', '|',
|
|
|
|
|
'`', '\\', '^', '@', ']', '}', '\b', '\t',
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, '<EFBFBD>', '\r', 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '*',
|
|
|
|
|
0, ' ', 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '7',
|
|
|
|
|
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
|
|
|
'2', '3', '0', '.', 0, 0, 0, 0,
|
|
|
|
|
0
|
2007-04-02 16:34:19 +02:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
static const u8 set1_ctrl[] = {
|
|
|
|
|
0, 0x1B, 0, 0, 0, 0, 0x1B, 0,
|
|
|
|
|
0, 0x1C, 0, 0, 0x1D, 0, 0, 0x1F,
|
|
|
|
|
0x01, 0x1A, 0x05, 0x012, 0x14, 0x19, 0x15, 0x09,
|
|
|
|
|
0x0F, 0x10, 0x1E, 0, 0, 0, 0x11, 0x13,
|
|
|
|
|
0x04, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D,
|
|
|
|
|
0, 0, 0, 0, 0x17, 0x18, 0x03, 0x16,
|
|
|
|
|
0x02, 0x0E, 0, 0, 0, 0, 0, '*',
|
|
|
|
|
0, ' ', 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, '7',
|
|
|
|
|
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
|
|
|
'2', '3', '0', '.', 0, 0, 0, 0,
|
|
|
|
|
0
|
2007-04-02 16:34:19 +02:00
|
|
|
|
};
|
2018-08-17 16:46:56 +02:00
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
2018-08-21 08:26:35 +02:00
|
|
|
|
/* Attend une chaine de caract<63>re de taille max */
|
|
|
|
|
|
2018-09-27 17:47:27 +02:00
|
|
|
|
u8 *getstring(u8 * temp)
|
|
|
|
|
{
|
|
|
|
|
u8 maxwidth = strlen(temp);
|
|
|
|
|
u8 *pointer = temp;
|
|
|
|
|
u8 ascii = 0;
|
|
|
|
|
while (ascii != '\r') {
|
|
|
|
|
ascii = waitascii();
|
|
|
|
|
if (ascii == '\b' && pointer > temp) {
|
|
|
|
|
pointer--;
|
|
|
|
|
putchar(ascii);
|
|
|
|
|
} else if (ascii > 31 && pointer <= temp + 80) {
|
|
|
|
|
*pointer++ = ascii;
|
|
|
|
|
putchar(ascii);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*pointer = '\000';
|
|
|
|
|
return temp;
|
2018-08-21 08:26:35 +02:00
|
|
|
|
}
|
2018-09-27 17:47:27 +02:00
|
|
|
|
|
2018-08-21 08:26:35 +02:00
|
|
|
|
/******************************************************************************/
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/* Fonction qui attend l'appuie d'une touche g<>n<EFBFBD>rant un code ASCII puis le retourne */
|
2007-04-02 16:34:19 +02:00
|
|
|
|
|
2018-09-28 20:35:51 +02:00
|
|
|
|
u8 waitascii(void)
|
2018-08-17 16:46:56 +02:00
|
|
|
|
{
|
|
|
|
|
u8 oldptrascii = ptrascii;
|
|
|
|
|
while ((oldptrascii == ptrascii)) ;
|
|
|
|
|
return bufferascii[ptrascii];
|
2007-04-02 16:34:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/******************************************************************************/
|
|
|
|
|
/* Envoi d'une commande vers le contr<74>leur de clavier */
|
2007-04-02 16:34:19 +02:00
|
|
|
|
|
|
|
|
|
void outkbd(u8 port, u8 data)
|
|
|
|
|
{
|
|
|
|
|
u32 timeout;
|
|
|
|
|
u8 state;
|
|
|
|
|
|
|
|
|
|
/* timeout */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
for (timeout = 500000L; timeout != 0; timeout--) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
state = inb(0x64);
|
|
|
|
|
/* vide le buffer du 8042 */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if ((state & 0x02) == 0)
|
|
|
|
|
break;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
}
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (timeout != 0)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
outb(port, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/* Redemarre l'ordinateur */
|
|
|
|
|
|
2018-09-28 20:35:51 +02:00
|
|
|
|
void reboot(void)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
{
|
|
|
|
|
u8 temp;
|
|
|
|
|
cli();
|
|
|
|
|
/* vide le 8042 */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
do {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
temp = inb(0x64);
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if ((temp & 0x01) != 0) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
(void)inb(0x60);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-08-17 16:46:56 +02:00
|
|
|
|
} while ((temp & 0x02) != 0);
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* active le reset CPU */
|
|
|
|
|
outb(0x64, 0xFE);
|
2018-08-17 16:46:56 +02:00
|
|
|
|
while (1)
|
|
|
|
|
/* boucle infinie */ ;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/* Converti un scancode vers une code ASCII */
|
|
|
|
|
|
2007-04-02 16:34:19 +02:00
|
|
|
|
unsigned convert(u32 keypressed)
|
|
|
|
|
{
|
2018-08-17 16:46:56 +02:00
|
|
|
|
u8 temp, key, lastscan;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* garde le dernier pointeur du buffer scan */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
lastscan = ptrscan;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* incr<63>mente le pointeur est assigne au buffer le dernier scancode */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
ptrscan++;
|
|
|
|
|
if (ptrscan == 255)
|
|
|
|
|
ptrscan == 0;
|
|
|
|
|
bufferscan[ptrscan] = keypressed;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* break key (touche relach<63>) ? */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (keypressed >= 0x80)
|
|
|
|
|
breakcode = 1;
|
|
|
|
|
key = (keypressed & 0x7F);
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* Mise a jour des flags lors du relachement de touches de controle */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (breakcode) {
|
|
|
|
|
if (key == SCAN_ALT) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus &= ~STATUS_ALT;
|
|
|
|
|
/* si ALT GR (E01D) alors activer aussi control */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (bufferscan[lastscan] == 0xE0)
|
|
|
|
|
kbdstatus &= ~STATUS_CTRL;
|
|
|
|
|
} else if (key == SCAN_CTRL)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus &= ~STATUS_CTRL;
|
2018-08-17 16:46:56 +02:00
|
|
|
|
else if (key == SCAN_LEFTSHIFT || key == SCAN_RIGHTSHIFT)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus &= ~STATUS_SHIFT;
|
|
|
|
|
breakcode = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* Mise a jour des flags lors de l'appuie de touches de controle */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (key == SCAN_ALT) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus |= STATUS_ALT;
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/* si ALT GR (E01D) alors desactiver aussi control */
|
|
|
|
|
if (bufferscan[lastscan] == 0xE0)
|
|
|
|
|
kbdstatus |= STATUS_CTRL;
|
|
|
|
|
return 0;
|
2018-09-27 17:47:27 +02:00
|
|
|
|
} else if (key == SCAN_CTRL) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus |= STATUS_CTRL;
|
|
|
|
|
return 0;
|
2018-09-27 17:47:27 +02:00
|
|
|
|
} else if (key == SCAN_LEFTSHIFT || key == SCAN_RIGHTSHIFT) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus |= STATUS_SHIFT;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-08-17 16:46:56 +02:00
|
|
|
|
|
2018-08-30 01:07:28 +02:00
|
|
|
|
else if ((key >= SCAN_F1) && (key <= SCAN_F8)) {
|
2018-08-17 16:46:56 +02:00
|
|
|
|
changevc(key - SCAN_F1);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 01:07:28 +02:00
|
|
|
|
else if (key == SCAN_F9) {
|
|
|
|
|
dump_regs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (key == SCAN_F10) {
|
|
|
|
|
nextvmode();
|
|
|
|
|
}
|
|
|
|
|
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* Scroll Lock, Num Lock, and Caps Lock mise a jour des leds */
|
2018-08-30 01:07:28 +02:00
|
|
|
|
else if (key == SCAN_SCROLLLOCK) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus ^= STATUS_SCRL;
|
|
|
|
|
goto LEDS;
|
2018-09-27 17:47:27 +02:00
|
|
|
|
} else if (key == SCAN_NUMLOCK) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus ^= STATUS_NUM;
|
|
|
|
|
goto LEDS;
|
2018-09-27 17:47:27 +02:00
|
|
|
|
} else if (key == SCAN_CAPSLOCK) {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
kbdstatus ^= STATUS_CAPS;
|
2018-08-17 16:46:56 +02:00
|
|
|
|
LEDS:
|
|
|
|
|
outkbd(0x60, 0xED); /* "mise a jour des LEDS */
|
|
|
|
|
temp = 0;
|
|
|
|
|
if (kbdstatus & STATUS_SCRL)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
temp |= 1;
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (kbdstatus & STATUS_NUM)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
temp |= 2;
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (kbdstatus & STATUS_CAPS)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
temp |= 4;
|
|
|
|
|
outkbd(0x60, temp); /* 3 bits de poids faible pour les LEDs */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-09-17 13:14:27 +02:00
|
|
|
|
/* Appuie de CRTL + ALT + SUPR ? */
|
2018-09-17 13:18:05 +02:00
|
|
|
|
if ((kbdstatus & STATUS_CTRL) && (kbdstatus & STATUS_ALT) &&
|
2018-09-17 13:14:27 +02:00
|
|
|
|
(key == 73)) {
|
|
|
|
|
print("redemarrage du systeme");
|
|
|
|
|
reboot();
|
|
|
|
|
}
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* est ce un code etendu */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if ((bufferscan[lastscan] == 0xE0)
|
|
|
|
|
|| ((kbdstatus & STATUS_NUM) && (key >= 0x47) && (key <= 0x53)
|
|
|
|
|
&& (key != 0x4A) && (key != 0x4e)))
|
|
|
|
|
/* exceptions */
|
|
|
|
|
{
|
|
|
|
|
/* '/' (E035) */
|
|
|
|
|
if (key == 0x35)
|
|
|
|
|
return '/';
|
|
|
|
|
/* '\r' (E01C) 2<>me enter num<75>rique */
|
|
|
|
|
if (key == 0x1C)
|
|
|
|
|
return '\r';
|
|
|
|
|
/* 0x11 (E048) device control 1) */
|
|
|
|
|
if (key == 0x48)
|
|
|
|
|
return 0x11;
|
|
|
|
|
/* 0x12 (E050) device control 2) */
|
|
|
|
|
if (key == 0x50)
|
|
|
|
|
return 0x12;
|
|
|
|
|
/* 0x13 (E04b) device control 3) */
|
|
|
|
|
if (key == 0x4b)
|
|
|
|
|
return 0x13;
|
|
|
|
|
/* 0x14 (E04d) device control 4) */
|
|
|
|
|
if (key == 0x4d)
|
|
|
|
|
return 0x14;
|
|
|
|
|
/* 0x02 (E049) start of text) */
|
|
|
|
|
if (key == 0x49)
|
|
|
|
|
return 0x2;
|
|
|
|
|
/* 0x03 (E051) end of text) */
|
|
|
|
|
if (key == 0x51)
|
|
|
|
|
return 0x3;
|
|
|
|
|
/* 0x10 (E047) Line feed) */
|
|
|
|
|
if (key == 0x47)
|
|
|
|
|
return '\n';
|
|
|
|
|
/* 0x1A (E052) Substitution) */
|
|
|
|
|
if (key == 0x52)
|
|
|
|
|
return 0x1A;
|
|
|
|
|
/* 0x18 (E053) Cancel) */
|
|
|
|
|
if (key == 0x53)
|
|
|
|
|
return 0x18;
|
|
|
|
|
/* 0x19 (E04f) End of medium) */
|
|
|
|
|
if (key == 0x4f)
|
|
|
|
|
return 0x19;
|
|
|
|
|
return 0x00;
|
|
|
|
|
} else {
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* detecte les SCANCODES invalides */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (key >= sizeof(set1_normal) / sizeof(set1_normal[0]))
|
|
|
|
|
return 0;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* converti le scancode en code ASCII en fonction du statut*/
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (kbdstatus & STATUS_SHIFT || kbdstatus & STATUS_CAPS)
|
|
|
|
|
temp = set1_shift[key];
|
|
|
|
|
else if ((kbdstatus & STATUS_ALT) && (kbdstatus & STATUS_CTRL))
|
|
|
|
|
temp = set1_altgr[key];
|
|
|
|
|
else if (kbdstatus & STATUS_CTRL)
|
|
|
|
|
temp = set1_ctrl[key];
|
|
|
|
|
else if (kbdstatus & STATUS_ALT)
|
|
|
|
|
temp = set1_alt[key];
|
|
|
|
|
else
|
|
|
|
|
temp = set1_normal[key];
|
|
|
|
|
}
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* si scancode non reconnu fin de fonction */
|
2018-08-17 16:46:56 +02:00
|
|
|
|
if (temp == 0)
|
|
|
|
|
return temp;
|
2007-04-02 16:34:19 +02:00
|
|
|
|
/* Renvoie le Code ascii */
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
2018-08-17 16:46:56 +02:00
|
|
|
|
/* Handler d'interruption IRQ 1 pour le clavier */
|
|
|
|
|
|
2018-09-28 20:35:51 +02:00
|
|
|
|
void keyboard(void)
|
2007-04-02 16:34:19 +02:00
|
|
|
|
{
|
2018-08-17 16:46:56 +02:00
|
|
|
|
cli();
|
|
|
|
|
pushf();
|
|
|
|
|
pushad();
|
|
|
|
|
u8 scancode, ascii;
|
|
|
|
|
cli();
|
|
|
|
|
while ((inb(0x64) & 1) == 0) ;
|
|
|
|
|
scancode = inb(0x60);
|
|
|
|
|
ascii = convert(scancode);
|
|
|
|
|
if (ascii != 0) {
|
|
|
|
|
ptrascii++;
|
|
|
|
|
if (ptrascii == 255)
|
|
|
|
|
ptrascii == 0;
|
|
|
|
|
bufferascii[ptrascii] = ascii;
|
|
|
|
|
}
|
|
|
|
|
irqendmaster();
|
|
|
|
|
popad();
|
|
|
|
|
popf();
|
2018-08-17 11:17:24 +02:00
|
|
|
|
sti();
|
2018-08-17 16:46:56 +02:00
|
|
|
|
asm("addl $0x01C, %esp;");
|
|
|
|
|
iret();
|
2007-04-02 16:34:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|