|
|
  |
Злополучная функция what_day(), Вычисление дня недели |
|
|
|
Jun 7 2008, 23:21
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 8 2008, 00:48)  Тогда выкладывайте HEX-файл...Может файл линковки неправильный. Тоже выложите посмотреть. Выкладываю файлы линковки, list, map и hex варианта с глюком. В настройках проекта Configure system using dialogы (not in XCL) стоит галка. В функции what_day() , которую выкладывал ранее, закралась маленькая ошибка, не имеющая никакого отношения к глюку - после else нужно убрать пару фигурных скобок.
Прикрепленные файлы
gluk.rar ( 76.3 килобайт )
Кол-во скачиваний: 40
|
|
|
|
|
Jun 8 2008, 09:49
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Так и не понял косяк. Со стеком не может быть проблемы точно. В RSTACK требуется всего 2 вызова, то есть 4 байта. В CSTACK тоже минимум - 6 байт. Портит регистры R0,R1,R17-R21,R30,R31 (не восстанавливаются в прологе/эпилоге). Посмотрел получше. R16-R21 можно портить, в них передаются параметры и возвращается результат. Ну а R0,R1,R30,R31 кажется полюбому можно портить. Кстати, CSTACK - это похоже стек данных, а RSTACK - вызовов.
Кажется глюки вообще не от этой злополучной процедуры.
PS. Не всё она правильно считает. 1 января 2000 года = 6 (суббота). 1 января 2004 неправильно посчитала. 1 января 2008 правильно = 2 (вторник). Результат похоже не нужно увеличивать на 1.
Сообщение отредактировал GetSmart - Jun 8 2008, 10:27
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Jun 8 2008, 19:29
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 8 2008, 12:49)  Кажется глюки вообще не от этой злополучной процедуры. Когда не остается версий, остается одно - валить все на компилятор  Я смирился с глюком. Вышел из положения как говорил - вместо вызова функции вставил само тело. Что от этого изменилось - не понятно. Работает (пусть пока) - ну и ладно. Как сказал один умный человек (Билл Гейтс кажется), компьютер (микроконтроллер, компилятор, ...) настолько сложная штука, что не понятно, каким образом это все работает  Цитата(GetSmart @ Jun 8 2008, 12:49)  Не всё она правильно считает. 1 января 2000 года = 6 (суббота). 1 января 2004 неправильно посчитала. 1 января 2008 правильно = 2 (вторник). Результат похоже не нужно увеличивать на 1. Действительно, 1 января 2004 неправильно посчитало. Алгоритм переписал с Turbo Pascal
. А единицу нужно прибавить, потому-что у DS1338 Sunday=1, Monday=2 ,... и т.д.
|
|
|
|
|
Jun 8 2008, 19:36
|

Местный
  
Группа: Участник
Сообщений: 355
Регистрация: 27-03-07
Из: Україна, Чуднів
Пользователь №: 26 530

|
Цитата(alux @ Jun 8 2008, 03:21)  Выкладываю файлы линковки, list, map и hex варианта с глюком. В настройках проекта Configure system using dialogы (not in XCL) стоит галка. В функции what_day() , которую выкладывал ранее, закралась маленькая ошибка, не имеющая никакого отношения к глюку - после else нужно убрать пару фигурных скобок. Если я ещё не надоел, то в общем есть предложение переменную day сделать беззнаковой итежер ну и тоже самое возвращаемое значение беззнаковый итежер функции what_day() . при вычислении остатка от деления исспользуется макро US_DIVMOD_L02 - файл стандартный библиотеки ИАР I02.s90. Там происходит вот такое 'short' (i.e. 16 bit) unsigned division and modulo Может поэтому результат возвращается не совсем корректно через беззнаковый чар хотя его размера и достаточно?
--------------------
нельзя недооценивать предсказуемость глупости
|
|
|
|
|
Jun 9 2008, 08:18
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(sKWO @ Jun 8 2008, 22:36)  Если я ещё не надоел, то в общем есть предложение переменную day сделать беззнаковой итежер ну и тоже самое возвращаемое значение беззнаковый итежер функции what_day() . Там же вычисления проводятся с int. К unsigned char day приводится лишь результат. Ну проверил на всякий случай... Не в этом дело. Цитата(GetSmart @ Jun 9 2008, 00:53)  Ещё как вариант, нужно сравнить два листинга с процедурой в которой наблюдаются глюки при вызове what_day(), припоминаю что это было в меню. Первый листинг скомпилированный с вызовом процедуры what_day(), в котором наблюдаются глюки. Второй листинг без этой процедуры и без глюков. Листинги чего? Меню? Дело в том, что программа состоит из десятков файлов. Тем более это все работает под управлением ОС... Вкратце это выглядит так: обработка клавиатуры и получение скан-кода вынесено в высокоприоритетный процесс. Действие, необходимое при нажатии кнопки, зависит от текущего режима: если MainMenu- то навигация по пунктам меню, если EditValue - то редактирование параметров, и т.д.... Код OS_PROCESS void TKeyScan::Exec() //TProc1 { for(;;) { PCInt3.Wait(); Sleep(20); // Задержка 10*2=20мсек для устранения дребезга контактов scan_key(); if(key_code.scan & KEY_PRESSED) { key_code.scan &= ~KEY_PRESSED; // Clear MSB of scan_code (key_pressed) switch(key_code.scan) { case UP: //k_right CurrentMode->Up(); break; case DOWN: //k_left CurrentMode->Down(); break; case LEFT: //k_esc CurrentMode->Left(); break; case RIGHT: //k_enter CurrentMode->Right(); break; default: // 0...9 CurrentMode->Numeric(); break; } } } } PS. Функцию вычисления дня недели заменил на предыдущую (Сергея Фролова). Она вычисляет правильно. По крайней мере мне не удалось найти "неправилные" дни. Причем вызов ее не приводит к глюку. Но стоит добавить что-нибудь, например, проверку валидности данных: Код if((date>=1)&&(date<=31)&&(month>=1)&&(month<=12)&&(year>=1996)&&(year<=2100)) или хотя бы просто : Код i2c_write(year-2000); ... сразу вылазит глюк...
|
|
|
|
|
Jun 11 2008, 15:03
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Этот глюк выпил достаточно моей крови...  Решил досконально разобраться с этой проблемой. И выяснил следующее: каким-то образом портится скан-код нажатой клавиши. За основу программы keymatrix взял апнот Atmel avr243. Проблема исчезает, если убрать #define ALTKEYS (см. avr243). В программе используется следующая структура данных: Код union _key_code { unsigned int complete; // Access all 16 bits struct { union { unsigned char flags; // Access flag status only struct { unsigned char altKey0 : 1; // Access the flags separately unsigned char altKey1 : 1; unsigned char altKey2 : 1; unsigned char altKey3 : 1; unsigned char lckKey0 : 1; unsigned char lckKey1 : 1; unsigned char lckKey2 : 1; }; }; union { unsigned char scan; // Access scancode struct { unsigned char col : 3; // Access column of keypress unsigned char row : 3; // Access row of keypress unsigned char unused : 2; }; }; }; }; extern volatile union _key_code key_code; // Scan result structure extern volatile unsigned char key_altState; // Current alternation flags Так как в отдельный момент времени в памяти может находиться только один из указанных при объявлении объединения типов, то при вызове key_processAltKeys(), в которой осуществляется доступ к переменной key_altState, портится key_code.scan нажатой кнопки со всеми вытекающими последствиями. Т.е. изначально проблема была не в функции what_day(), а в драйвере клавиатуры. Внешне это выглядело довольно странно: скан-код портился выборочно при определенных условиях и на завершающей стадии проекта. Осталось придумать, как решить эту проблему.  PS. Насколько я понимаю, этого не должно быть. Ведь эти переменные относятся к разным union, которые являются членами общей структуры, которая в свою очередь является членом объединения. Или я ошибаюсь?
Прикрепленные файлы
AVR243.zip ( 7.83 килобайт )
Кол-во скачиваний: 36
|
|
|
|
|
Jun 11 2008, 23:15
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(alux) Так как в отдельный момент времени в памяти может находиться только один из указанных при объявлении объединения типов, то при вызове key_processAltKeys(), в которой осуществляется доступ к переменной key_altState, портится key_code.scan нажатой кнопки со всеми вытекающими последствиями. Вряд ли key_code.scan портится в key_processAltKeys(). В объявлении union _key_code тоже вроде бы всё "чисто". Клавиатура сканируется на двух полных портах C и D и получается 6-битный скан-код. Если на этих портах ещё что-нибудь висит, хотя бы один пин используется не для клавиатуры, то будут глюки. Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу. Запись в PORTA сначала ~key_altState, а потом сразу же кода символа не очень понятна. Если там светодиоды, то на них будет отображаться именно код символа. Ещё одна маловероятная идея. В коде: Код /*** Global variables ***/ volatile unsigned char key_altState; // Holding current alternation flags volatile union _key_code key_code; // Scan result structure Поменяйте местами две этих переменных. Объявляйте key_code перед объявлением key_altState. Может что изменится. А вообще, если какая-нить часть проги портит память с этими переменными, то может в этом дело.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Jun 12 2008, 06:30
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 12 2008, 02:15)  Клавиатура сканируется на двух полных портах C и D и получается 6-битный скан-код. Если на этих портах ещё что-нибудь висит, хотя бы один пин используется не для клавиатуры, то будут глюки. Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу. Да, выводы мк использованы все. Для клавиатуры пришлось разнести строки и столбцы в разные порты. Не понял на счет глюка, я же прерывания настроил именно на эти выводы, к которым подключена клавиатура: Код // Ports #define KEYMATRIX_COL_PORT PORTB // Command Output Register #define KEYMATRIX_COL_DDR DDRB // Data Direction Register for KeymatrixPort #define KEYMATRIX_COL_PIN PINB // PIN Register for KeymatrixPort
#define KEYMATRIX_ROW_PORT PORTD // Command Output Register #define KEYMATRIX_ROW_DDR DDRD // Data Direction Register for KeymatrixPort #define KEYMATRIX_ROW_PIN PIND // PIN Register for KeymatrixPort
// pins PCINT28..31->PCI3 #define ENABLE_PCINT3 {PCIFR |= (1<<PCIF3); PCICR |= (1<<PCIE3);}// Clear interrupt status flag, Enable pin change interrupt PCINT3 #define DISABLE_PCINT3 PCICR &= ~(1<<PCIE3) // Disable pin change interrupt PCINT3 .......................... void key_Init(void) { /* Init global variables */ key_altState = 0; // Clear alternation flags key_code.complete = 0; // Clear scan result
/* Init ports */ KEYMATRIX_ROW_DDR &= 0x0f; // Set row lines to input KEYMATRIX_ROW_PORT |= 0xf0; // Pull row lines high KEYMATRIX_COL_DDR |= 0x0f; // Set column lines to output KEYMATRIX_COL_PORT &= 0xf0; // Drive all column lines low /* Enable external interrupt */ // PCIFR |= (1<<PCIF3); //Очистить флаги внешних прерываний PCMSK3 = (1<<PCINT31)|(1<<PCINT30)|(1<<PCINT29)|(1<<PCINT28); //Настроить прерывания на выводах PD4...PD7 ENABLE_PCINT3; // Enable pin change interrupt PCINT3 } Цитата(GetSmart @ Jun 12 2008, 02:15)  Запись в PORTA сначала ~key_altState, а потом сразу же кода символа не очень понятна. Если там светодиоды, то на них будет отображаться именно код символа. Это только для демонстрации скан-кода. В моей программе этого нет. Цитата(GetSmart @ Jun 12 2008, 02:15)  Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу. Скан-код тоже 5-битный - от 0 до 31. Матрица 4х4. Цитата(GetSmart @ Jun 12 2008, 02:15)  Вряд ли key_code.scan портится в key_processAltKeys(). На начальном этапе проекта (до применения ОС) тоже долго не мог понять почему портился скан-код. Тогда случайно обнаружил, что проблема была связана с key_processAltKeys(). Уже не помню, что я тогда изменил (кажется я копировал скан-код в отдельную переменную). Сейчас история повторяется. Действительно, проблема осталась! Сначала после удаления #define ALTKEYS проблема исчезла, о чем я поспешил сообщить. Но после изменения/добавления пунктов меню - снова появилась! Это меня уже достало.
|
|
|
|
|
Jun 12 2008, 11:10
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(alux) Не понял на счет глюка, я же прерывания настроил именно на эти выводы, к которым подключена клавиатура: Вне зависимости от того, на что настроены прерывания, в процедуре переполнения таймера 0 формируется 6-битный скан-код. И если на портах C и D помимо клавиатуры что-то подключено (хоть на вход, хоть на выход) скан-коды будут "левые". Особенно, если для клавиатуры на одном порте используется старшая половина полубайта: Код /* Init ports */ KEYMATRIX_ROW_DDR &= 0x0f; // Set row lines to input KEYMATRIX_ROW_PORT |= 0xf0; // Pull row lines high Как я понял, здесь на битах 4..7 висит клавиатура, а на младших что-то ещё. Прерывание по таймеру сканирует биты от младшего к старшему и найдя еденичный бит в младших битах перестанет дальше сканировать. Даже когда для клавиатуры используются две младших половинки в двух разных портах, то может произойти прерывание по отжатию кнопки, при этом на старших битах порта столбцов будет посторонний нулевой сигнал, то он засчитается за нажатую клавишу. Чтобы всех этих проблем не происходило нужно подправить в файле keymatrix.c процедуру сканирования портов клавиатуры timer0OVFISR().
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Jun 12 2008, 11:31
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 12 2008, 14:10)  Чтобы всех этих проблем не происходило нужно подправить в файле keymatrix.c процедуру сканирования портов клавиатуры timer0OVFISR(). Ну это само собой разумеется. Конечно, я переписал этот keymatrix.c для моего случая: Код //############################################################################# // Вычисление скан-кода нажатой кнопки //_____________________________________________________________________________ void scan_key(void) { TCritSect cs; /* Local variables */ unsigned char lineResult; // Resulting column and row lines unsigned char tempScan; // Temporary scancode
/* Find row of keypress */ lineResult = KEYMATRIX_ROW_PIN&0xf0; // Get row lines if(lineResult != 0xf0) // Any row lines low ? { /* Invert port directions */ KEYMATRIX_COL_PORT |= 0x0f; // Drive all col lines high KEYMATRIX_COL_DDR &= 0xf0; // Set col lines to input, already pulled up KEYMATRIX_ROW_PORT &= 0x0f; // Disable pull-up on row lines KEYMATRIX_ROW_DDR |= 0xf0; // Set row lines to output, already driven low
tempScan = 0; // Init temp scan code while(lineResult & 0x10) // Loop while row line high { lineResult >>= 1; // Next row line into LSB tempScan += 8; // Increment row part of scancode } /* Find col of keypress */ lineResult = KEYMATRIX_COL_PIN&0x0f; // Get col lines
/* Set original port directions */ KEYMATRIX_ROW_PORT |= 0xf0; // Drive all row lines high KEYMATRIX_ROW_DDR &= 0x0f; // Set row lines to input, already pulled up, KEYMATRIX_COL_PORT &= 0xf0; // Disable pull-up on col lines KEYMATRIX_COL_DDR |= 0x0f; // Set col lines to output, alreay driven low
if(lineResult != 0x0f) // Any col lines low ? { while(lineResult & 0x01) // Loop while col line high { lineResult >>= 1; // Next col line into LSB tempScan ++; // Increment col part of scancode } /* Process scancode */ tempScan |= KEY_PRESSED; // Set MSB of scan_code (key pressed) key_code.scan = tempScan; // Save scancode
#ifdef ALTKEYS key_processAltKeys(); // Process alternation keys if implemented #endif } else key_code.scan = 0; // Indicate no keys pressed 0x00 } else key_code.scan = 0; // Indicate no keys pressed 0x00 /* Prepare external interrupt */ ENABLE_PCINT3; // Reenable pin change interrupt } Эта функция вызывается не в прерывании таймера, а в процессе OS_PROCESS void TKeyScan::Exec() с использованием флагов сообщений ОС. Код я приводил в предыдущем посте. PS. Для эксперимента заменил key_code.scan на обычную глобальную volatile unsigned char scan. Не помогло. Все равно портится те же 4 скан-кода кнопок.  Т.е. проблема не в использовании union. Версий пока больше нет... PS2. Все! Наконецто локализовал проблему. Надеюсь в последний раз  . Скан-код портит функция convertKey(); Код char __flash characters[4][32] = { { '1', '2', '3', 0, 0, 0, 0, 0, // No alternation '4', '5', '6', 0, 0, 0, 0, 0, '7', '8', '9', 0, 0, 0, 0, 0, '.', '0', '#', 0, 0, 0, 0, 0 },
{ 'a', 'd', 'g', 0, 'а', 'г', 'є', 'ъ', // First character 'j', 'm', 'p', 0, 'и', 'й', 'м', 'ы', 's', 'v', 'y', 0, 'п', 'т', 'х', 0, '*', '*', '#', 0, 'ш', 'ю', 0, 0 },
{ 'b', 'e', 'h', 0, 'б', 'д', 'ж', 0, // Second character 'k', 'n', 'q', 0, 'і', 'к', 'н', 0, 't', 'w', 'z', 0, 'р', 'у', 'ц', 0, '*', '?', '#', 0, 'щ', 'я', 0, 0 },
{ 'c', 'f', 'i', 0, 'в', 'е', 'з', 0, // Third character 'l', 'o', 'r', 0, 'ї', 'л', 'о', 0, 'u', 'x', '-', 0, 'с', 'ф', 'ч', 0, '*', '!', '#', 0, 'ь', 0, 0, 0 } };
/*** Convert scancode to character ***/ char convertKey(void) { char tempChar;
if(key_code.altKey0) // First char ? tempChar = characters[1][key_code.scan+4]; else if(key_code.altKey1) // Second char ? tempChar = characters[2][key_code.scan+4]; else if(key_code.altKey2) // Third char ? tempChar = characters[3][key_code.scan+4]; else // No alternation ? tempChar = characters[0][key_code.scan];
if(key_code.altKey3) // Uppercase if caps lock tempChar = toupper(tempChar); return tempChar; } Если заменить многомерный массив одномерным, то все нормально. А почему?
Сообщение отредактировал alux - Jun 13 2008, 08:15
|
|
|
|
|
Jun 13 2008, 08:04
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
...И вновь продолжается бой. Ребята, извините, пожалуйста, если достал уже этой проблемой. Напомню, что глюк проявляет себя в том, что портит скан-код четырех кнопок. Причем одинаково: кнопка "3" вместо скан-кода 2 дает 1, кнопка "ESC" вместо скан-кода 3 дает 1, кнопка "6" вместо скан-кода 10 дает 9, кнопка "UP" вместо скан-кода 11 дает 10. Может эта информация как-то наведет на мысли. Глюк появляется при входе в пункт меню ("Редактирование"). Вроде нашел причину: в функции convertKey() если заменить многомерный массив одномерным, то глюк пропадает. Ну, бог с ним, с многомерным массивом. Но следующее выходит за рамки моего понимания: если раскомментировать #define ALTKEYS , т.е. вызвать функцию void key_processAltKeys() , в которой просто XORятся биты флага, снова появляется этот злосчастный глюк. Код #ifdef ALTKEYS
void key_processAltKeys() { if (flags & ALTKEY0) flags ^= ALTKEY0; else if(flags & ALTKEY1) flags ^= ALTKEY1; else if(flags & ALTKEY2) flags ^= ALTKEY2; else if(flags & ALTKEY3) flags ^= ALTKEY3; } #endif Причем, для чистоты эксперимента переменные scan (скан-код) и flags сделал глобальными volatile unsigned char. Функцию scan_key() приводил в предыдущем посте. Воздействие ОС на глюк исключается,- функция вызывается в критической секции (TCritSect cs)
|
|
|
|
|
Jun 13 2008, 08:57
|

Местный
  
Группа: Участник
Сообщений: 355
Регистрация: 27-03-07
Из: Україна, Чуднів
Пользователь №: 26 530

|
Цитата(alux @ Jun 13 2008, 12:04)  Код else if(flags & ALTKEY1) flags ^= ALTKEY1; else if(flags & ALTKEY2) flags ^= ALTKEY2; Так бегло alux а зачем инвертировать флаг в флаговом регистре ничего при этом не делая и не проверяя сканкод Выдержка из оригинала Код if ( key_code.scan == ALTKEY0 ) key_altState ^= (1<<0); else if( key_code.scan == ALTKEY1 ) key_altState ^= (1<<1); else if( key_code.scan == ALTKEY2 ) key_altState ^= (1<<2); else if( key_code.scan == ALTKEY3 ) key_altState ^= (1<<3); else if( key_code.scan == LCKKEY0 ) key_altState ^= (1<<4); else if( key_code.scan == LCKKEY1 ) key_altState ^= (1<<5); else if( key_code.scan == LCKKEY2 ) key_altState ^= (1<<6); else // Not alternation key ? { key_altState &= ALTLOCKMASK; // Clear one-shot key flags } ALTKEY как у Вас определены, може всё завязано на битовых сдвигах?
--------------------
нельзя недооценивать предсказуемость глупости
|
|
|
|
|
Jun 13 2008, 09:39
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Код // Alternation key code #define ALTKEY 26 // '#' on test keypad
#define ALT0 0x01 #define ALT1 0x02 #define ALT2 0x04 #define ALT3 0x08 Проблема возникает если просто : Код void key_processAltKeys() { if(scan == ALTKEY) flags ^= ALT0; } Что здесь не так? Не вижу криминала. PS. Меня не покидает чувство, что виноват все-таки компилятор (v.5.10A). Где-то на форуме проскакивало сообщение, что компилятор 5.10 не правильно определяет CSTACK >= 0xC0. Уже качаю IAR v.5.11B.
|
|
|
|
|
Jun 13 2008, 17:42
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(alux @ Jun 13 2008, 14:04)  Код #ifdef ALTKEYS
void key_processAltKeys() { if (flags & ALTKEY0) flags ^= ALTKEY0; else if(flags & ALTKEY1) flags ^= ALTKEY1; else if(flags & ALTKEY2) flags ^= ALTKEY2; else if(flags & ALTKEY3) flags ^= ALTKEY3; } #endif Это что за ерунда такая? Раньше эта процедура сравнивала скан код нажатой клавиши с кодами альтернативных клавиш и устанавливала бит в key_altState. Даже если key_altState теперь называется flags, то ерунда тут. Исключающее ИЛИ с кодом клавиши? Зачем? В flags лежат коды клавиш? ЗЫ. Больше не буду искать ошибки, когда на обозрение выкладываются какие-то левые исходники, а потом оказывается что в реале что-то где-то поменяно и вообще хз что скомпилено.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|