реклама на сайте
подробности

 
 
> Таблица переходов вместо Switch, Keil Си
AndreyS
сообщение Mar 23 2010, 15:28
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Добрый день.

Если я не первый раз поднимаю эту тему, то заранее извиняюсь (я не смог придумать поисковику удобоваримое слово для поиска. На void* или таблица вываливается куча страниц).

Хочу заменить конструкцию Switch case на таблицу переходов и все это на языке Си.

Как вызвать процедуру по адресу я знаю, вопрос как бы мне заполнить таблицу с 256 значениями.
На асме я это реализовывал так:
Сначала создавал массив адресов на обработчик default, а затем в другом инклюднике прописывал (переопределял по вычисленным адресам определенные обработчики) Вот так:
Код
COM_SN_WRITE     EQU    42h                    ; Команда записи SN
    ORG TABEL_COM+(COM_SN_WRITE*2)              ; тут умножаю на 2, так как храню адрес из 2-х байт, в Keil Си он 3-х байтовый
    DW SEND_COM_SN_WRITE                               ; Ну а тут собственно сам обработчик (процедура). Тут ее адрес


SEND_COM_SN_WRITE:
       RET


TABEL_COM это адрес таблицы
COM_SN_WRITE номер команды
SEND_COM_SN_WRITE адрес обработчика

Как бы мне теперь так же сделать на Си в Keil?

PS Работу switch в Keil я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
aaarrr
сообщение Mar 23 2010, 15:43
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Определите массив указателей на функции, например, массив из 10 указателей на функции вида int func(int, int) будет выглядеть так:
Код
int (*zzz[10])(int, int);
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Mar 23 2010, 16:30
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Код
typedef void (*VECTORS)();

const VECTORS function[stQty] =
{
  f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7,  f8,  f9,
  f10, f11, f12, f13, f14, f15, f16, f17, f18, f19,
  f20, f21, f22, f23, f24, f25, f26, f27, f28, f29,
  f30, f31, f32, f33, f34, f35, f36, f37, f38, f39,
  f40, f41, f42, f43, f44, f45, f46, f47, f48, f49,
  f50, f51, f52, f53, f54, f55, f56, f57, f58, f59,
  f60, f61, f62, f63, f64, f65, f66, f67, f68, f69,
  f70, f71, f72, f73, f74, f75, f76, f77, f78, f79,
  f80, f81, f82, f83, f84, f85, f86, f87, f88, f89,
  f90, f91, f92, f93, f94, f95, f96, f97, f98, f99,
  f100,f101,f102,f103
   };


Цитата(AndreyS @ Mar 23 2010, 19:28) *
...PS Работу switch в Keil я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).

Оптимизация включена?


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Mar 23 2010, 19:10
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(Dog Pawlowa @ Mar 23 2010, 19:30) *
[code]typedef void (*VECTORS)();

const VECTORS function[stQty]={указатели}

Оптимизация включена?

спасибо большое за ответ. Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Вот например таблица изначально заполнена адресом на один обработчик заглушку. Теперь я создал например обработчик 10-го позиционного номера таблицы. И хочу что бы компилятор в таблице заменил адрес 10 ячейки этой таблицы. На асме я это регулировал оргом, тут с _at_ такая штука не проходит. а нужно мне это для дополнения обработчиков. Ну всего 256 комбинаций (у меня байт проверяется), из них 200 определено. И вот хочу еще 2 определить, что бы не искать позицию в таблице, я хотел что бы компилятор (по моему указанию) высчитал позицию адреса в таблице и туда этот адрес положил. А как вызвать функию по адресу из этой таблицы я знаю, спасибо.

Цитата(Dog Pawlowa @ Mar 23 2010, 19:30) *
Оптимизация включена?

оптимхизация включена (какой уровень сейчас не скажу да это и не важно). У кейла в факе на сайте по этому поводу сказано что начиная 7 версии свитчь сам сворачивается в таблицу в зависимости отусловий (от кода). Я стал анализировать. Он таблицу создает сразу от 6 кейсов, если кейсы стоят последовательно (т.е. Сравниваемое значение в каждом кейсе отличается от предыдущего на 1). Если их перетасовать, то таблица появляетя уже если кейсов более 10. Но это все фигня. Я зашел в их функию ccall за которой в коде лежит таблица, и посмотрел что она делает. Оказалось что 3-х байтовая таблица это не просто адрес. А 2 байта адрес и значение кейса. И фукция производит в цикле поиск значенния первого байта адреса равного 0. Это означает для нее что вся таблица перебрана и совпадения нет. И тут находится адрес обработчика дефаулт значения. Если первый байт в таблице не равен 0, то тогда сравнивается 3-й байт в таблице (в нем лежит кейс значение). Если он совпал с входящим числом (switch), то выполняется переход на адрес (1 и 2 ячейки таблицы). Иначе ищет дальше. Нахрена такая таблица я не понял. Компактно - да, быстро - нет.


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Mar 23 2010, 19:25
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Цитата(AndreyS @ Mar 23 2010, 22:51) *
Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции.

Ну пишете в произвольком порядке и все, вот как я: wink.gif

Код
/ basic functions:
STATE ( Restart            ,    "Restart"          ,  "Neustart"         ,    0        )
STATE ( SelfTest           ,    "Self-test"        ,  "Selbsttest"       , CLR+STN    )
STATE ( Waiting            ,    "Ready"            ,  "Bereit"           ,    0        )
STATE ( AirDeleting        ,    "Air out "         ,  "Entl"uuml"ften"   , CLR+STN    )


Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов.
Только не занимайтесь "ручной" линковкой "а-ля ассемблер".

Цитата(baralgin @ Mar 23 2010, 23:18) *
Изменить в таблице(массив указателей) адрес функции не сложно. ..
mas_func[10] = &new_handler;

Если таким образом программируется машина состояний, но таблицу незачем располагать в памяти данных.
Я понял, что задача автора сделать удобной инициализацию массива. Или?


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Mar 23 2010, 19:59
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(Dog Pawlowa @ Mar 23 2010, 22:25) *
Ну пишете в произвольком порядке и все, вот как я: wink.gif

Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов.
Только не занимайтесь "ручной" линковкой "а-ля ассемблер".

Я понял, что задача автора сделать удобной инициализацию массива. Или?

ууу. С вашим кодом я поплыл, что такое state я не знаю :-(

я был не полностью откровенен. Keil в данном случае 51 ядро, массив конечно лежит в областе code.

Вы совершенно правы, что задача стоит в удобстве инициализации массива на стадии компиляции(или линковки это не важно, главное что не в процессе выполнения программы).

На асме то я как раз и не хотел делать (это у меня в старых проектах моих, написаных мной на асме). Пишу все время на си, посто потом смотрю что на асме получилось и оптимзирую запись на си, так что бы на асме красиво было (быстро). А со свичем не стал париться, но тут у меня кейс разросся и решил переделать свитчь на таблицу (таблица будет со временем переопределяться, дописываться будут новые обработчики). Вот и хотел написать макрос, который бы сам по числу проверки заносил адрес обработчика в общую таблицу в требуюмую ячейку с замещением в ней адреса дефолтового обработчика. Вот вопрос как?

Цитата(AHTOXA @ Mar 23 2010, 22:38) *
Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так:
Код
код проглотил

и эта функция попадает в нужное место.


во. Это то что мне нужно. Попробую покрутить в keilе 51.


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Mar 23 2010, 20:07
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Цитата(AndreyS @ Mar 23 2010, 23:59) *
С вашим кодом я поплыл, что такое state я не знаю :-(

Это просто мой макрос, определение которого меняется в программе несколько раз.
Не "грязный хак", но любители строгого С обычно стоят в сторонке. smile.gif


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Mar 23 2010, 20:41
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(Dog Pawlowa @ Mar 23 2010, 23:07) *
Это просто мой макрос, определение которого меняется в программе несколько раз.
Не "грязный хак", но любители строгого С обычно стоят в сторонке. smile.gif


ага. Значит читать мне надо про макросы?

Цитата(AHTOXA @ Mar 23 2010, 22:38) *
Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так:
...
и эта функция попадает в нужное место.

а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик.
Как бы привязать заполнение таблицы к номеру команды? И исходя из ее значения в соответствующую ячейку таблицы попадал адрес обработчика. Сменил я номер команды и адрес автоммтом лег в новую ячейку, а в старую опять вернулся адрес дефаулт обработчика.

Сообщение отредактировал rezident - Mar 24 2010, 01:39
Причина редактирования: Нарушение п.3.4 Правил форума.


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Mar 23 2010, 21:54
Сообщение #9


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(AndreyS @ Mar 24 2010, 01:41) *
а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик.
Как бы привязать заполнение таблицы к номеру команды?


Ну это уже мелочиsmile.gif

Код
#define DECLARE_COMMAND_HANDLER2(cmd_number) void f##cmd_number(void)
#define DECLARE_COMMAND_HANDLER(cmd_number) DECLARE_COMMAND_HANDLER2(cmd_number)

#define CMD_ENTER  10

DECLARE_COMMAND_HANDLER(CMD_ENTER)
{
  for (;;);
}


Код
DECLARE_COMMAND_HANDLER(CMD_ENTER)

развернётся в
Код
void f10(void)


При смене CMD_ENTER на 20 - станет
Код
void f20(void)
.


Цитата(Dog Pawlowa @ Mar 24 2010, 01:55) *
В простейшем виде пишете
#define f25 /*empty_function*/my_function_for_code_25


Это отличный вариант, пока все обработчики хранятся в одном файле. Но иногда (часто) нужно, чтоб модуль при подключении автоматически добавлял свой элемент в таблицу. В случае фиксированной таблицы (с известным заранее числом элементов, типа таблицы векторов прерываний) - можно применить вариант, который я привёл. А вот для произвольной таблицы - я так ничего путного и не придумал.
А как было бы здорово, если бы модуль (.c файл) мог при его добавлении в проект, скажем, автоматически добавить несколько своих терминальных команд, пару переменных в конфиг, етцsmile.gif
Придумалось только для плюсов, через смесь шаблонов и макросов. Да и то не всё там гладко, накладные расходы по ОЗУ (не всё во флеше), и работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Apr 6 2010, 03:05
Сообщение #10


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



Цитата(AHTOXA @ Mar 24 2010, 04:54) *
работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).

С этим можно побороться с помощью паттерна Singleton.

Код
//---------------------------------------------------------------------------
template<typename T> class singleton
{
public:
    static T& Instance() { static T instance; return instance; }
};
//---------------------------------------------------------------------------


Использование:

Код
class TSlon { ... };


TSlon& slon = singleton<TSlon>::Instance();

// далее идет использование слона (синтаксически как обычный объект):
// при создании ссылки внутри singleton<TSlon> будет
// статически создан и проинициализирован объект внутри Instance(), поэтому
// ссылку можно безопасно использовать

Есть небольшие накладняки на создание статического объекта, но они небольшие - соизмеримы с вызовом конструктора. Работает этот прием очень хорошо.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Apr 6 2010, 07:45
Сообщение #11


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(dxp @ Apr 6 2010, 09:05) *
Код
// при создании ссылки внутри singleton<TSlon> будет
// статически создан и проинициализирован объект внутри Instance(), поэтому
// ссылку можно безопасно использовать


А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления
Код
TSlon slon;
?
Имхо, без динамического создания объектов singleton совершенно бесполезен.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Apr 6 2010, 11:14
Сообщение #12


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



Цитата(AHTOXA @ Apr 6 2010, 14:45) *
А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления

Вот зрасьте! В этом же вся фишка синглтона. Когда какой-то объект нуждается в объекте TSlon, то он создает (использует) ссылку, возвращаемую функцией Instance() шаблона singleton. А ссылка эта ссылается на статический объект, создаваемый внутри фукнции-члена Instance(). Таким образом, получается следующая схема: при первом вызове Instance() будет создан и проинициализирован статический объект требуемого типа (в нашем примере TSlon), все последующие вызовы будут возвращать ссылку на уже существующий объект. И не важно, кто первый дернул Instance(), думать об этом не надо. В этом и избавление от зависимости от порядка вызова конструкторов.

Цитата(AHTOXA @ Apr 6 2010, 14:45) *
Код
TSlon slon;
?
Имхо, без динамического создания объектов singleton совершенно бесполезен.

Еще как полезен: объект создается как статический внутри статической же функции-члена Instance(). И живет там все время, пока программа работает. Т.е. по сути как обынчый глобальный объект, но спрятанный внутри функции и предоставляющий доступ по ссылке (что синтаксически ровно как доступ к обычному объекту). Нету тут ни работы с кучей, ни выделения памяти на рантайме. Выделение памяти и инициализация производятся комилятором на основе правил языка в части статических объектов внутри функций (которые есть даже в голом С).


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Apr 6 2010, 12:15
Сообщение #13


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(dxp @ Apr 6 2010, 17:14) *
Вот зрасьте! В этом же вся фишка синглтона.


Упс. Я был уверен, что разница между статическим и глобальным объектом только в области видимости.
А ты хочешь сказать, что для статических объектов конструкторы вызываются не в списке глобальных конструкторов, а при первом вызове функции, в которой он содержится? Если так, то это супер! Надо проверитьsmile.gif


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Apr 6 2010, 12:56
Сообщение #14


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



Цитата(AHTOXA @ Apr 6 2010, 19:15) *
А ты хочешь сказать, что для статических объектов конструкторы вызываются не в списке глобальных конструкторов, а при первом вызове функции, в которой он содержится? Если так, то это супер! Надо проверитьsmile.gif

Статический объект должен быть гарантировано проинициализирован до его использования. А где это делается - отдано на откуп реализации. Например:
Код
void f()
{
    int a = 10;
    ... // код функции
}

Реализация может быть разной. Самое простое - сунуть а в секцию, где помещаются глобальные переменные, и проинициализировать вместе с ними (IAR, помнится, так и делает) - разница тут будет только в области видимости. А можно тут вставить код, проверяющий первый вход в функцию, и вызывать инициализатор (это тот самый оверхед, про который я говорил, и который незначителен по сравнению с вызовом конструктора). Как делается реально в том или ином случае - это в ведении реализации (в случае с вызовом конструктора объекта пользовательского типа скорее всего делается по второму варианту). Главное, что поведение четко стандартизовано и это можно безопасно использовать.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Apr 6 2010, 17:57
Сообщение #15


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(dxp @ Apr 6 2010, 18:56) *
Главное, что поведение четко стандартизовано и это можно безопасно использовать.


Попробовал, получилось. Но далеко не сразу. Простая замена глобального объекта
Код
uart1_t uart1;

на ссылку:
Код
uart1_t& uart1 = singleton<uart1_t>::Instance();

Не прокатила. Потому что теперь вместо глобального объекта uart1 у нас получилась глобальная же ссылкаsmile.gif Порядок инициализации которой (на этот раз при помощи хитрого синглетона!) по прежнему не определён.
Пришлось везде, где используется uart1, заводить локальные переменные-ссылки
Код
uart1_t& localuart = singleton<uart1_t>::Instance();

и использовать их. Вот тогда всё заработало должным образомsmile.gif
Что касаемо накладных расходов, то они достаточно приличные:
Код
*с глобальным uart:
   text    data     bss     dec     hex filename
   4372       8    5792   10172    27bc ./exe/stm32-H103.elf
*с singleton<uart1_t>:
   text    data     bss     dec     hex filename
   4500       8    5800   10308    2844 ./exe/stm32-H103.elf

(Всё это gcc на stm32)

Я думаю, что для некоторых применений это хорошее решение, по крайней мере я намерен его применять.
(Или всё же new? smile.gif )


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Apr 7 2010, 03:51
Сообщение #16


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



Цитата(AHTOXA @ Apr 7 2010, 01:12) *
Пришлось везде, где используется uart1, заводить локальные переменные-ссылки
Код
uart1_t& localuart = singleton<uart1_t>::Instance();

и использовать их. Вот тогда всё заработало должным образомsmile.gif

Конечно, именно так и надо делать, в этом и смысл. Точнее, глобальную ссылку завести тоже не лишне, но ее использовать по ходу программы, а вот в конструкторах - да, только локальные ссылки.

Цитата(AHTOXA @ Apr 7 2010, 01:12) *
Я думаю, что для некоторых применений это хорошее решение, по крайней мере я намерен его применять.
(Или всё же new? smile.gif )

Для new надо свой быстрый и не подверженный фрагментации менеджер памяти сделать. Если только для вышеописанных целей, то это будет еще больше накладных. А вообще такой мененжер памяти местами очень полезен. Надо будет сделать, как руки дойдут.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- AndreyS   Таблица переходов вместо Switch   Mar 23 2010, 15:28
||- - Dog Pawlowa   Цитата(AndreyS @ Mar 24 2010, 00:14) ага....   Mar 23 2010, 20:55
||- - Dog Pawlowa   Цитата(AHTOXA @ Mar 24 2010, 01:54) Это о...   Mar 23 2010, 22:02
||- - RobFPGA   Приветствую! Цитата(AHTOXA @ Mar 24 201...   Apr 5 2010, 20:36
||- - AHTOXA   Цитата(dxp @ Apr 7 2010, 10:06) Для new н...   Apr 7 2010, 04:07
||- - dxp   Цитата(AHTOXA @ Apr 7 2010, 11:22) Если n...   Apr 7 2010, 06:24
||- - AHTOXA   Цитата(dxp @ Apr 7 2010, 12:39) А тогда с...   Apr 7 2010, 07:51
||- - dxp   Цитата(AHTOXA @ Apr 7 2010, 15:06) Смысл ...   Apr 7 2010, 13:35
||- - AHTOXA   А, это конечно. Просто наличие new позволяет даже ...   Apr 7 2010, 13:44
||- - dxp   Цитата(AHTOXA @ Apr 7 2010, 20:59) Кодif ...   Apr 8 2010, 03:28
|- - AHTOXA   Цитата(AndreyS @ Mar 24 2010, 00:10) Меня...   Mar 23 2010, 19:38
|- - sonycman   Цитата(AndreyS @ Mar 23 2010, 23:10) опти...   Mar 27 2010, 05:48
||- - AndreyS   Цитата(sonycman @ Mar 27 2010, 09:48) Как...   Mar 29 2010, 08:32
|- - defunct   Цитата(AndreyS @ Mar 23 2010, 22:10) Тепе...   Apr 4 2010, 23:56
|- - Dog Pawlowa   Цитата(defunct @ Apr 5 2010, 02:56) А что...   Apr 5 2010, 10:24
|- - defunct   Цитата(Dog Pawlowa @ Apr 5 2010, 13:24) Я...   Apr 6 2010, 01:40
|- - Dog Pawlowa   Цитата(defunct @ Apr 6 2010, 04:40) А для...   Apr 6 2010, 07:02
- - baralgin   Цитата(Dog Pawlowa @ Mar 23 2010, 18:30) ...   Mar 23 2010, 17:08
|- - Dog Pawlowa   Цитата(baralgin @ Mar 23 2010, 20:08) По ...   Mar 23 2010, 17:45
|- - zltigo   Цитата(baralgin @ Mar 23 2010, 20:08) По ...   Mar 23 2010, 17:58
- - baralgin   Dog Pawlowa, zltigo, Спасибо, буду знать, а то бол...   Mar 23 2010, 19:18


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 12:55
Рейтинг@Mail.ru


Страница сгенерированна за 0.0165 секунд с 7
ELECTRONIX ©2004-2016