Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Указатель во флэш и IAR
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Makki
Доброго всем времени суток!
С недавних пор начал разбираться с компилятором IAR, до этого писал на ассемблере. Начал простенький проектик с графическим дисплеем. Пытаюсь вывести на экран строку, расположенную в памяти программ. Строка, конечно, выводится, отображается как надо, но вот какими методами...
В общем, есть у меня функция:
Код
void lcd_write_string(char __flash * string)
{
  char i= * string++;
  while(i--!=0)
  {
    lcd_write_character(*string++);
  }
}


Принимает она указатель на flash, где переменная где у меня выглядит следующим образом:
char __flash string[]={5,'A','B','C','D','E'};
5 - число символов в строке, дальше символы.

При компиляции всего этого начинаются для меня непонятные вещи:
Код
@00000057: lcd_write_string
46:       void lcd_write_string(char __flash * string)
47:       {
+00000057:   940E00D2    CALL      0x000000D2     Call subroutine;тут компилятор сохраняет в программный стек регистры, хотя это и не нужно, ну да ладно
+00000059:   01C8        MOVW      R24,R16        Copy register pair;тут получили указатель, переданный через пару регистров R16:R17
48:         char i= * string++;
+0000005A:   01FC        MOVW      R30,R24        Copy register pair;но почему было сразу их не занести в ZH:ZL???
+0000005B:   91A4        LPM       R26,Z          Load program memory;если бы сразу занесли в ZH:ZL, можно бы было сделать LPM R26,Z+
+0000005C:   9601        ADIW      R24,0x01       Add immediate to word;тогда эта строка лишняя
+0000005D:   C004        RJMP      PC+0x0005      Relative jump;прыг +5
51:           lcd_write_character(*string++);
+0000005E:   01FC        MOVW      R30,R24        Copy register pair;опять переносим в Z указатель
+0000005F:   9104        LPM       R16,Z          Load program memory
+00000060:   DFE9        RCALL     PC-0x0016      Relative call subroutine;собственно, запись символа
+00000061:   9601        ADIW      R24,0x01       Add immediate to word;и почему-то увеличиваем указатель уже после выполнения функции
49:         while(i--!=0)
+00000062:   2F0A        MOV       R16,R26        Copy register;вот зачем дальше вся эта чехарда, когда можно просто проверить R26???
+00000063:   2FA0        MOV       R26,R16        Copy register
+00000064:   95AA        DEC       R26            Decrement
+00000065:   2300        TST       R16            Test for Zero or Minus
+00000066:   F7B9        BRNE      PC-0x08        Branch if not equal;прыг на -8
53:       }
+00000067:   E0E3        LDI       R30,0x03       Load immediate
+00000068:   940C00D6    JMP       0x000000D6     Jump


Итак, вопрос: это такая работа компилятора, или это я чего-то не знаю? Как напрямую в регистр ZH:ZL скормить указатель и работать именно с этими регистрами? Нет, я могу то же самое написать на ассемблере, и ассемблерную библиотеку прикрутить, но зачем тогда мне компилятор?
Chameleon
Оптимизацию включать не пробовали?

Вы как-то странно строки инициализируете. Вы каждый раз вручную будете вычислять длину строки?

Почему нельзя записать

char __flash string[]="ABCDE";

И потом проверять ноль в конце строки? Ноль сам компилятор добавит.
oll
Для двухстрочного LCD (взято с AVR Freaks) делал так:
CODE
#define LCD_Puts_FLASH(s) {static __flash unsigned char str[] = s; LCD_Putsf(str);}
......
void LCD_Putsf(unsigned char __flash *str)
{
unsigned char temp;

while (temp=*str++)
LCD_Data(temp, 1);
}
использование:
LCD_Puts_FLASH("Start...");
Makki
Цитата(Chameleon @ Dec 26 2011, 00:19) *
Оптимизацию включать не пробовали?
Вы как-то странно строки инициализируете. Вы каждый раз вручную будете вычислять длину строки?
Почему нельзя записать
char __flash string[]="ABCDE";
И потом проверять ноль в конце строки? Ноль сам компилятор добавит.

Полная оптимизация по скорости добавила таки команду LPM R16,Z+, счетчик теперь хранится только в R26, но хранение указателя в регистрах R25:R24 осталось. Наверное, есть какой-нибудь способ, чтобы жестко привязаться к регистрам ZH:ZL, если это все же предусмотрено компилятором. Будем искать...
Код
@00000054: lcd_write_string
46:       void lcd_write_string(char __flash * string)
47:       {
+00000054:   93AA        ST        -Y,R26         Store indirect and predecrement
+00000055:   939A        ST        -Y,R25         Store indirect and predecrement
+00000056:   938A        ST        -Y,R24         Store indirect and predecrement
48:         char i= * string++;
+00000057:   01F8        MOVW      R30,R16        Copy register pair
+00000058:   91A5        LPM       R26,Z+         Load program memory and postincrement
+00000059:   01CF        MOVW      R24,R30        Copy register pair
+0000005A:   23AA        TST       R26            Test for Zero or Minus
+0000005B:   F039        BREQ      PC+0x08        Branch if equal
+0000005C:   95AA        DEC       R26            Decrement
51:           lcd_write_character(*string++);
+0000005D:   01FC        MOVW      R30,R24        Copy register pair
+0000005E:   9105        LPM       R16,Z+         Load program memory and postincrement
+0000005F:   01CF        MOVW      R24,R30        Copy register pair
+00000060:   DFE7        RCALL     PC-0x0018      Relative call subroutine
+00000061:   23AA        TST       R26            Test for Zero or Minus
+00000062:   F7C9        BRNE      PC-0x06        Branch if not equal
53:       }
+00000063:   9189        LD        R24,Y+         Load indirect and postincrement
+00000064:   9199        LD        R25,Y+         Load indirect and postincrement
+00000065:   91A9        LD        R26,Y+         Load indirect and postincrement
+00000066:   9508        RET                      Subroutine return

А по поводу хранения строки - это у меня после написания протокола modbus привычка осталась - значение, например, порта ввода-вывода любым может быть, и конец посылки по символу определять некорректно.
hd44780
Makki, не пытайтесь сравнивать компилятор Си и ассемблер.
Компиляторы с языков высокого уровня всегда делают "много лишнего", если сравнивать с ассемблером.
Такова природа вещей. Не нравится - пишите сами на асме.....
Makki
Нашел настройку компилятора --lock regs, а потом использовал
__no_init __regvar char __flash * pointer @ 14;
но она позволяет жестко выделить только регистры с R4 по R15...
Сергей Борщ
QUOTE (Makki @ Dec 26 2011, 09:54) *
Нашел настройку компилятора --lock regs
В заголовочном файле pgmspace.h встречается квалификатор функции __x_z. Он как раз и задает передачу указателей в регистровых парах. Помню, что в документации какой-то из версий он (и несколько подобных) был задокументирован. Поищите его в документации к вашей версии или просто поэкспериментируйте.
В интернете находится такая цитата из документации:
QUOTE
__x, __z __x_z, __z_x
The compiler defines the function type attributes __x, __z __x_z, and __z_x which are used by parts of the runtime library. These attributes can give very efficient code in a local perspective, but should be used with care as they change the calling convention and may have a negative effect on the size of the entire application.
Makki
Цитата(Сергей Борщ @ Dec 26 2011, 16:39) *
В заголовочном файле pgmspace.h встречается квалификатор функции __x_z.


__x The first pointer in the parameter list is placed in register X
__z The first pointer in the parameter list is placed in register Z
__x_z The first pointer in the parameter list is placed in register X and the second one in register Z
__z_x The first pointer in the parameter list is placed in register Z and the second one in register X

Почитал, попробовал, всё получилось, с одним маленьким исключением. Если в цикле функции, работающей с указателем ZH:ZL, есть вызов какой-либо другой функции, компилятор все равно сохраняет значение указателя во временные регистры. Поэтому для того, чтобы этого не было, все вызываемые функции пришлось "раскрыть". Странно, что компилятор не знает, что в вызываемых функциях ZH:ZL не изменяются...
CODE

//Запись символьной строки
__z void lcd_write_string(char __flash * string)
{
char i= * string++;
while(i--!=0)
{
SetBit(lcdctrl,rs);
__delay_cycles(3);
SetBit(lcdctrl,e);
lcddata=*string++;
__delay_cycles(3);
ClearBit(lcdctrl,e);
ClearBit(lcdctrl,rs);
__delay_cycles(4);
ddrlcddata=0x00;
lcddata=0x00;
SetBit(lcdctrl,rw);
SetBit(lcdctrl,e);
while((lcddatain&(1<<db7))!=0)
{
;
}
ClearBit(lcdctrl,e);
ClearBit(lcdctrl,rw);
lcddata=0x00;
ddrlcddata=0xff;
}
}

И, соответственно, результат компиляции. Тут всё работает так, как было бы написано мной на ассемблере, без оптимизации задержек, правда.
CODE

@0000004C: lcd_write_string
48: char i= * string++;
+0000004C: 9115 LPM R17,Z+ Load program memory and postincrement
+0000004D: 2311 TST R17 Test for Zero or Minus
+0000004E: F0D9 BREQ PC+0x1C Branch if equal
+0000004F: 951A DEC R17 Decrement
53: SetBit(lcdctrl,rs);
+00000050: 9A38 SBI 0x07,0 Set bit in I/O register
54: __delay_cycles(3);
+00000051: C000 RJMP PC+0x0001 Relative jump
+00000052: 0000 NOP No operation
55: SetBit(lcdctrl,e);
+00000053: 9A3A SBI 0x07,2 Set bit in I/O register
56: lcddata=*string++;
+00000054: 9105 LPM R16,Z+ Load program memory and postincrement
+00000055: BB05 OUT 0x15,R16 Out to I/O location
57: __delay_cycles(3);
+00000056: C000 RJMP PC+0x0001 Relative jump
+00000057: 0000 NOP No operation
58: ClearBit(lcdctrl,e);
+00000058: 983A CBI 0x07,2 Clear bit in I/O register
59: ClearBit(lcdctrl,rs);
+00000059: 9838 CBI 0x07,0 Clear bit in I/O register
60: __delay_cycles(4);
+0000005A: C000 RJMP PC+0x0001 Relative jump
+0000005B: C000 RJMP PC+0x0001 Relative jump
61: DDRC=0x00;
+0000005C: E000 LDI R16,0x00 Load immediate
+0000005D: BB04 OUT 0x14,R16 Out to I/O location
62: lcddata=0x00;
+0000005E: BB05 OUT 0x15,R16 Out to I/O location
63: SetBit(lcdctrl,rw);
+0000005F: 9A39 SBI 0x07,1 Set bit in I/O register
64: SetBit(lcdctrl,e);
+00000060: 9A3A SBI 0x07,2 Set bit in I/O register
65: while((lcddatain&(1<<db7))!=0)
+00000061: 999F SBIC 0x13,7 Skip if bit in I/O register cleared
+00000062: CFFE RJMP PC-0x0001 Relative jump
69: ClearBit(lcdctrl,e);
+00000063: 983A CBI 0x07,2 Clear bit in I/O register
70: ClearBit(lcdctrl,rw);
+00000064: 9839 CBI 0x07,1 Clear bit in I/O register
71: lcddata=0x00;
+00000065: BB05 OUT 0x15,R16 Out to I/O location
72: DDRC=0xff;
+00000066: EF0F SER R16 Set Register
+00000067: BB04 OUT 0x14,R16 Out to I/O location
+00000068: 2311 TST R17 Test for Zero or Minus
+00000069: F729 BRNE PC-0x1A Branch if not equal
74: }
+0000006A: 9508 RET Subroutine return
Сергей Борщ
QUOTE (Makki @ Dec 29 2011, 11:08) *
Если в цикле функции, работающей с указателем ZH:ZL, есть вызов какой-либо другой функции, компилятор все равно сохраняет значение указателя во временные регистры.
Да. читайте раздел документации про calling conventions.
QUOTE (Makki @ Dec 29 2011, 11:08) *
Поэтому для того, чтобы этого не было, все вызываемые функции пришлось "раскрыть".
Нет sm.gif Изучайте квалификатор static для функций, изучайте квалификатор inline.
QUOTE (Makki @ Dec 29 2011, 11:08) *
Странно, что компилятор не знает, что в вызываемых функциях ZH:ZL не изменяются...
Да, не знает. Потому что вызываемая функция может находиться в другой единице компиляции. Или может использоваться в другой единице компиляции. Если это не так, и функция объявлена в той же единице компиляции в которой и используется, используйте квалификатор static.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.