|
Таблица переходов вместо Switch, Keil Си |
|
|
|
Mar 23 2010, 15:28
|

Местный
  
Группа: Участник
Сообщений: 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 я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 16:30
|
Гуру
     
Группа: Свой
Сообщений: 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 я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу). Оптимизация включена?
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 23 2010, 17:08
|
Частый гость
 
Группа: Участник
Сообщений: 92
Регистрация: 23-12-08
Из: Кишинёв
Пользователь №: 42 680

|
Цитата(Dog Pawlowa @ Mar 23 2010, 18:30)  Оптимизация включена? По идее компилятор такой самодеятельностью заниматься не должен. Ведь для этого понадобится дополнительная статическая память. А так конечно, нужно разобраться что такое указатели на функции, и дальше наиболее оптимальные решения будут сами проситься.
|
|
|
|
|
Mar 23 2010, 17:58
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(baralgin @ Mar 23 2010, 20:08)  По идее компилятор такой самодеятельностью заниматься не должен. Вот, как мало Вы знаете о приличных компиляторах  , хотя я вообще-то давно не видел компиляторов, которые таким не занимаются (причем не огульно, а действительно выбирая компромис), если, конечно не гробить им явно оптимизацию.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 23 2010, 19:10
|

Местный
  
Группа: Участник
Сообщений: 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 ячейки таблицы). Иначе ищет дальше. Нахрена такая таблица я не понял. Компактно - да, быстро - нет.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 19:25
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(AndreyS @ Mar 23 2010, 22:51)  Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Ну пишете в произвольком порядке и все, вот как я:  Код / 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; Если таким образом программируется машина состояний, но таблицу незачем располагать в памяти данных. Я понял, что задача автора сделать удобной инициализацию массива. Или?
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 23 2010, 19:38
|

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

|
Цитата(AndreyS @ Mar 24 2010, 00:10)  Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Вот например таблица изначально заполнена адресом на один обработчик заглушку. Теперь я создал например обработчик 10-го позиционного номера таблицы. И хочу что бы компилятор в таблице заменил адрес 10 ячейки этой таблицы. Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так: Код // объявления всех нужных обработчиков #define WEAK __attribute__ ((weak)) void WEAK f00(void); void WEAK f01(void); ... void WEAK f255(void);
//таблица : void (* const Handlers[])(void) = { f00, f01, ... f255 }
// и заглушки по умолчанию: #pragma weak f00 = Default_Handler #pragma weak f01 = Default_Handler ... #pragma weak f255 = Default_Handler Теперь для объявления своего обработчика №25 просто пишем в любом c-файле Код void f25() { // } и эта функция попадает в нужное место. Но в кейле для этого приходится залезать в ассемблер  Код MACRO def_int_vector $HandlerName $HandlerName PROC EXPORT $HandlerName [WEAK] B . ENDP MEND
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Mar 23 2010, 19:59
|

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

|
Цитата(Dog Pawlowa @ Mar 23 2010, 22:25)  Ну пишете в произвольком порядке и все, вот как я:  Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов. Только не занимайтесь "ручной" линковкой "а-ля ассемблер". Я понял, что задача автора сделать удобной инициализацию массива. Или? ууу. С вашим кодом я поплыл, что такое state я не знаю :-( я был не полностью откровенен. Keil в данном случае 51 ядро, массив конечно лежит в областе code. Вы совершенно правы, что задача стоит в удобстве инициализации массива на стадии компиляции(или линковки это не важно, главное что не в процессе выполнения программы). На асме то я как раз и не хотел делать (это у меня в старых проектах моих, написаных мной на асме). Пишу все время на си, посто потом смотрю что на асме получилось и оптимзирую запись на си, так что бы на асме красиво было (быстро). А со свичем не стал париться, но тут у меня кейс разросся и решил переделать свитчь на таблицу (таблица будет со временем переопределяться, дописываться будут новые обработчики). Вот и хотел написать макрос, который бы сам по числу проверки заносил адрес обработчика в общую таблицу в требуюмую ячейку с замещением в ней адреса дефолтового обработчика. Вот вопрос как? Цитата(AHTOXA @ Mar 23 2010, 22:38)  Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так: Код код проглотил и эта функция попадает в нужное место. во. Это то что мне нужно. Попробую покрутить в keilе 51.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 20:41
|

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

|
Цитата(Dog Pawlowa @ Mar 23 2010, 23:07)  Это просто мой макрос, определение которого меняется в программе несколько раз. Не "грязный хак", но любители строгого С обычно стоят в сторонке.  ага. Значит читать мне надо про макросы? Цитата(AHTOXA @ Mar 23 2010, 22:38)  Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так: ... и эта функция попадает в нужное место. а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик. Как бы привязать заполнение таблицы к номеру команды? И исходя из ее значения в соответствующую ячейку таблицы попадал адрес обработчика. Сменил я номер команды и адрес автоммтом лег в новую ячейку, а в старую опять вернулся адрес дефаулт обработчика.
Сообщение отредактировал rezident - Mar 24 2010, 01:39
Причина редактирования: Нарушение п.3.4 Правил форума.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 20:55
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(AndreyS @ Mar 24 2010, 00:14)  ага. Значит читать мне надо про макросы? Желательно, только сразу не лезьте в дебри. Альтернатива предложению от АНТОХА: В простейшем виде пишете #define f25 /*empty_function*/my_function_for_code_25 Но Вы оперируете конкретными индексами, в данном случае 25. Это неудобно, но непонятно где привязка к конкретным номерам. Если только в Вашей программе, то нее нужно избавляться, как это сделано у меня. Но если индекс участвует в обмене с другим прибором, и языки программирования разные, то красиво трудно сделать. Цитата(AndreyS @ Mar 24 2010, 00:41)  Как бы привязать заполнение таблицы к номеру команды? Ладно, покажу, как это делается у меня, может другие более простой способ подскажут. Код enum { #define STATE( name, engl, germ, prop) st##name, #include STATES_HEADER #undef STATE stQty };
const VECTORS function[stQty] = { #define STATE( name, engl, germ, prop) f##name , #include STATES_HEADER #undef STATE };
Сообщение отредактировал rezident - Mar 24 2010, 01:40
Причина редактирования: Оформление цитаты исходника.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 23 2010, 21:54
|

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

|
Цитата(AndreyS @ Mar 24 2010, 01:41)  а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик. Как бы привязать заполнение таблицы к номеру команды? Ну это уже мелочи  Код #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 файл) мог при его добавлении в проект, скажем, автоматически добавить несколько своих терминальных команд, пару переменных в конфиг, етц  Придумалось только для плюсов, через смесь шаблонов и макросов. Да и то не всё там гладко, накладные расходы по ОЗУ (не всё во флеше), и работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|