Перейти к содержанию
    

В ИАРе для МСП430 есть такая классная фича - квалификатор _even_in_range_(range) для аргументов switch().

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

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

 

Так вот, как бы такую штуку провернуть в GCC? На форуме mspgcc были какие-то наброски на асме, но это как-то некрасиво.

типа этого

Хотелось бы на чистом си, типа макроса чтоли...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотелось бы на чистом си, типа макроса чтоли...
Просьба на чистом С просить только то, что и у IAR делается на чистом С :rolleyes:

 

Так пойдёт?

unsigned char foo(unsigned char sel)
{
    // !!! проедполагается, что sel  чётно И sizeof(void*)==2

    // для простоты изложения для AVR в ОЗУ, 
    static const void *sw[] = { &&la, &&lb, &&lc };

    void *target = *(void**)((unsigned char*)sw + sel);
    
    goto *target;

    la:
        return 5;
    lb:
        return 8;
    lc:
        return 11;
}

msp-gcc под рукой нет, avr-gcc:

foo:
    mov r30,r24
    ldi r31,lo8(0)
    subi r30,lo8(-(sw.1469))
    sbci r31,hi8(-(sw.1469))
    ld __tmp_reg__,Z+
    ld r31,Z
    mov r30,__tmp_reg__
    ijmp
.L2:
    ldi r24,lo8(5)
    ldi r25,hi8(5)
    ret
.L4:
    ldi r24,lo8(8)
    ldi r25,hi8(8)
    ret
.L5:
    ldi r24,lo8(11)
    ldi r25,hi8(11)
    ret

Для MSP, по идее, будет несколько хуже, чем совсем красивое добавление прямо к PC, но всё же лучше стандартного switch

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так пойдёт?

Для MSP, по идее, будет несколько хуже, чем совсем красивое добавление прямо к PC, но всё же лучше стандартного switch

Да, примерно в этом роде.

Только бы вот ещё завернуть ещё как-то покрасивше...

Но даже в таком виде красивее, чем на асме.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, короче, вот что получилось.

#define switch_even_in_range(index, ... )   \
            static const unsigned int *sw[] = {__VA_ARGS__};\
            unsigned int *target = *(void**)((unsigned int*)sw + index);\
            goto *target;

index - переключатель, далее следует записать все метки кейсов.

interrupt (TIMERB1_VECTOR) Timerb_ccr1(void)
{    
  switch_even_in_range(TBIV, &&def, &&l2, &&l4, &&l6, &&l8, &&l10, &&l12 )
  {
    l2:
        pxTimerB1ccr1expired();
    l4:
        pxTimerB1ccr2expired();
    l6:
        (void)pxMBPortCBTimerExpired();
    l8:
        pxTimerB1ccr4expired();
    l10:
        pxTimerB1ccr5expired();
    l12:
        pxTimerB1ccr6expired();

    def:
  }
}

Ничё так, гламурненько :)

 

А вот выход:

00002eb0 <Timerb_ccr1>:
   2eb0:	0f 12       	push	r15		;
   2eb2:	0e 12       	push	r14		;
   2eb4:	0d 12       	push	r13		;
   2eb6:	0c 12       	push	r12		;
   2eb8:	05 12       	push	r5		;
   2eba:	04 12       	push	r4		;
   2ebc:	05 41       	mov	r1,	r5	;
   2ebe:	35 50 0e 00 	add	#14,	r5	;#0x000e
   2ec2:	21 83       	decd	r1		;
   2ec4:	04 41       	mov	r1,	r4	;
   2ec6:	1f 42 1e 01 	mov	&0x011e,r15	;0x011e
   2eca:	0f 5f       	rla	r15		;
   2ecc:	3f 50 04 02 	add	#516,	r15	;#0x0204
   2ed0:	a4 4f 00 00 	mov	@r15,	0(r4)	;
   2ed4:	20 44       	br	@r4		;
   2ed6:	1f 42 ca 03 	mov	&0x03ca,r15	;0x03ca
   2eda:	8f 12       	call	r15		;
   2edc:	1f 42 a8 03 	mov	&0x03a8,r15	;0x03a8
   2ee0:	8f 12       	call	r15		;
   2ee2:	1f 42 aa 03 	mov	&0x03aa,r15	;0x03aa
   2ee6:	8f 12       	call	r15		;
   2ee8:	1f 42 c6 03 	mov	&0x03c6,r15	;0x03c6
   2eec:	8f 12       	call	r15		;
   2eee:	1f 42 c4 03 	mov	&0x03c4,r15	;0x03c4
   2ef2:	8f 12       	call	r15		;
   2ef4:	1f 42 c2 03 	mov	&0x03c2,r15	;0x03c2
   2ef8:	8f 12       	call	r15		;
   2efa:	21 53       	incd	r1		;
   2efc:	34 41       	pop	r4		;
   2efe:	35 41       	pop	r5		;
   2f00:	3c 41       	pop	r12		;
   2f02:	3d 41       	pop	r13		;
   2f04:	3e 41       	pop	r14		;
   2f06:	3f 41       	pop	r15		;
   2f08:	00 13       	reti			

ссылка по указателю на функцию в прерывании это конечно изврат, но речь сейчас не об этом.

В целом вроде бы всё удалось.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, короче, вот что получилось.

#define switch_even_in_range(index, ... )   \
            static const unsigned int *sw[] = {__VA_ARGS__};\
            unsigned int *target = *(void**)((unsigned int*)sw + index);\
            goto *target;

Симпатишненько.

Только вроде бы ж надо

= *(void**)((unsigned char*)sw + index);
, на входе чётное число, смещение в таблице в байтах.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

"раз пошла такая пьянка"

(Cлабонервным не смотреть, тут, как и в protothreads, не просто используется goto, а ещё и внутрь блока оператора цикла. Правда, "одноразового", как в ATOMIC_BLOCK или привычного в макросах do { } while(0) )

#define switch_even_in_range(index, ... )   \
        static const void *sw[] = {__VA_ARGS__};\
        void *target = *(void**)((unsigned char*)sw + index);\
        goto *target; \
        for(int s_e_i_r_I = 0; s_e_i_r_I == 0; s_e_i_r_I = 1)

unsigned moo(unsigned sel)
{
    unsigned result;
    switch_even_in_range(sel, &&la, &&lb, &&lc) {
    la:
        result = 2;
        break;
    lb:
        result = 1;
        break;
    lc:
        result = 11;
    }
    return result;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

"раз пошла такая пьянка"

(Cлабонервным не смотреть, тут, как и в protothreads, не просто используется goto, а ещё и внутрь блока оператора цикла.

Чё-то я не всосал...

Размер увеличился, смысл - неясен...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Немного отвлеченный вопрос: а откуда берется этот самый index? Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чё-то я не всосал...

Размер увеличился, смысл - неясен...

Где размер увеличился? Ни на грамм, переменная s_e_i_r_I не создаётся вообще, этот for - это "обёртка" в духе do { } while(0);, не генерирующая кода (при уровне оптимизации, отличающемся от полного отсутствия)

moo:
    mov r31,r25
    mov r30,r24
    subi r30,lo8(-(sw.1367))
    sbci r31,hi8(-(sw.1367))
    ld __tmp_reg__,Z+
    ld r31,Z
    mov r30,__tmp_reg__
    ijmp
.L8:
    ldi r24,lo8(2)
    ldi r25,hi8(2)
    ret
.L10:
    ldi r24,lo8(1)
    ldi r25,hi8(1)
    ret
.L11:
    ldi r24,lo8(11)
    ldi r25,hi8(11)
    ret

Зато можно писать break;

 

Немного отвлеченный вопрос: а откуда берется этот самый index?
Ну, например, из аппаратного регистра TBIV (TimerBInterruptVector, если я правильно расшифровал). Там именно индекс.

 

Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...
Конечно проще!

Например, так http://electronix.ru/forum/index.php?showt...mp;#entry520130

Но тут другой случай.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Немного отвлеченный вопрос: а откуда берется этот самый index? Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...

index - это переключатель свитча

Данный подход выгоден, когда есть переключатель на большое количество индексируемых состояний.

Например, стэйт машин или обработчик кодов команд (параметров, etc)

То есть пишем switch(comand_code<<1,[список меток]) и максимально быстро перескакиваем на обработчик нужной команды

 

Где размер увеличился? Ни на грамм, переменная s_e_i_r_I не создаётся вообще, этот for - это "обёртка" в духе do { } while(0);, не генерирующая кода (при уровне оптимизации, отличающемся от полного отсутствия)

 

Зато можно писать break;

А, теперь понятно...

У меня почему-то вставляет цикл.

Причём объявление for(int s_e_i_r_I = 0; s_e_i_r_I == 0; s_e_i_r_I = 1) не прокатывает,

пишет "TimerB\TimerB.c|168|`for' loop initial declaration used outside C99 mode|"

Приходится объявление int s_e_i_r_I вытаскивать наружу.

Как бы эту гадость победить, давно хочу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

`for' loop initial declaration used outside C99 mode|"
Дык -std=c99

Или я чего-то не понял?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

typedef enum { func0=0 , func1 , .... , func_count } func_index ;

 

typedef void (*FPRT)(void) ;

 

inline void Func0(void)

{

/* код 0 */

}

 

inline void Func1(void)

{

/* код 1 */

}

 

 

 

FPRT func_table[fuc_count ] = {Func0,Func1,.....} ; // таблица адресов функий

 

далее в коде просто в нужном месте вместо

switch (arg)

{}

вызвать

(func_table[arg]) () ;

 

я всегда так делаю - добавлять обработчики и их индексы удобно и быстро. работает это тоже быстро

вопрос тока как это для MSP сгенерится, я армами пользуюсь, про MSP ничего сказать не могу

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дык -std=c99

Или я чего-то не понял?

Я так понимаю, что данная инициализация FOR не соответствует (по мнению компилятора) стандарту С99.

Щас вот как раз качаю С99, надо ж когда-то начинать обращаться к первоисточникам...

 

Вот, нарыл. Что и требовалось доказать.

6.8.5 Iteration statements

Syntax

iteration-statement:

while ( expression ) statement

do statement while ( expression );

for ( expressionopt ; expressionopt ; expressionopt ) statement

for ( declaration expressionopt ; expressionopt ) statement

Чё-то компилер глюкавит...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я так понимаю, что данная инициализация FOR не соответствует (по мнению компилятора) стандарту С99.
Как раз соответствует. Просто у вас поддержка C99 не включена.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как раз соответствует. Просто у вас поддержка C99 не включена.

CFLAGS = -mmcu=$(MCU) -c $(OPT) $(DEBUG) -std=c99

Вроде подключил, или нет?

 

Секундочку, чё-то я сам не пойму, куда чего подключил...

Ёжкин кот! У меня галка стояла на автомэйке!

А я тут чего-то исследую, оптимизацию типа меняю...

 

Ну вот, таки подключил...

Вообще чудеса пошли - ругается на всё из io.h

 

Особенно невзлюбил sfrb и sfrw

Точнее вот что:

#if defined(__MSP430_HAS_PORT1__) || defined(__MSP430_HAS_PORT1_R__)

__MSP430_EXTERN__ struct port_full_t port1 asm("0x0020");

#endif

Пишет "c:\mspgcc\msp430\include\msp430\iostructures.h|136|syntax error before "asm"|"

 

:maniac:

И дался мне этот долбаный for с евоным с99... есть же ещё do{}while...

Хотя нет, в макрос не подставишь...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...