consoledevФорум

PSP - подводные камни

#0
1:50, 6 июня 2006

Предлагаю в этой теме рассказывать о тех неурядицах, с которыми приходится столкнуться PSP-неофиту во время первых опытов. Начну.

Внешние файлы с ресурсами на флешке
Полчаса ушло на то, чтобы понять, почему не открывается файл на флешке. Оказывается, далеко не всякое расширение файла подходит. Мой файл имел расширение .bmp и поэтому не открывался ни под каким предлогом. Пришлось сменить расширение на .bin.

#1
2:05, 6 июня 2006

Чтение файлов
Чтение надо всегда организовывать (слово-то какое)  в aligned storage. То есть, например, фокус со стеком типа такого не пройдет:

t_bitmapfileheader bmfh;
sceIoRead(fp, &bmfh, 40); // - здесь получим bus-error

Вместо этого, надо так:

static t_bitmapfileheader bmfh __attribute((aligned(64)));
sceIoRead(fp, &bmfh, 40);

Или динамически выделять память под структуру с помощью memalign, не забывая потом освободить с free.

#2
2:32, 6 июня 2006

Little and Big endian
Сразу нужно иметь в виду, что архитектура main cpu  PSP подразумевает "little endian" порядок следования битов в числах. В то время, как на привычном x86 это может быть и "big endian", и "little endian". Хоть в компиляторе CW и есть "возможность" выбора endianness, но она не работает.

#3
0:15, 7 июня 2006

Структуры, классы, портирование кода и ресурсов с PC
Вот известная вам структура - заголовок BMP-файла:

typedef struct { // 54 bytes unpadded
  u_int16 bfType;
  u_int32 bfSize;
  u_int16 bfReserved1;
  u_int16 bfReserved2;
  u_int32 bfOffBits;
. . .
} t_bitmapheader __attribute__((packed)); 

Ею воспользоваться не получится, потому что поля автоматически выравниваются. Чтобы посмотреть, как именно они выравниваются, можно сделать так:

snprintf(s, 64, "bfType=%u bfSize=%u bfReserved1=%u bfReserved2=%u bfOffBits=%u", 
  (char*)&pBMH->bfType-(char*)pBMH,
  (char*)&pBMH->bfSize-(char*)pBMH,
  (char*)&pBMH->bfReserved1-(char*)pBMH,
  (char*)&pBMH->bfReserved2-(char*)pBMH,
  (char*)&pBMH->bfOffBits-(char*)pBMH
);
sceGuDebugPrint(8, 96, 0x0000ff00, s);

То, есть, получим bfType=0, bfSize=4, bfReserved1=8, bfReserved2=10, bfOffBits=12. Иначе сказать, всё выравнивается либо по размеру элементов, либо по заданным индивидуальным значеням для элементов, либо по заданному значению для структур (structure align). Подробнее об этом:

<dDIMA> Как формируется структура. Сейчас поясню.
<dDIMA> Мы начинаем складывать в структуру поля. Сначала offset = 0.
<dDIMA> Берем очередное поле. Выясняем, какого оно размера. Если его sizeof() равен 1, то его можно класть прямо так
<dDIMA> Если sizeof() равен двойке, то надо проверить текущий offset и сделать его кратным 2
<dDIMA> Если sizeof() равен четверке, то надо проверить текущий offset и сделать его кратным 4
<dDIMA> И так далее.
<Padawan> спасибо
<dDIMA> Если мембырь класса имеет явно заданный align(), то offset выравнивается до указаного align
<dDIMA> Если мембырь имеет размер больше, чем "structure align" (для PC, например, это 8), то используется structure align
<dDIMA> Наконец, в конце структуры добиваются байтики для того, чтобы sizeof() структуры стал "хорошим"
<Padawan> кратным structure align value?
<dDIMA> таким, что бы если ты работаешь с массивом этих структур, не произошло bus error
<Padawan> понятно. зависит от архитектуры значит.
<dDIMA> Если структура состоит только из однобайтовых полей - ничего добавлять не надо
<dDIMA> Если есть int16 - размер структуры должен быть четным
<dDIMA> *итоговый размер структуры
<dDIMA> Если int32 - то sizeof() структуры должен делиться на 4
<dDIMA> И так далее - либо до structure align, либо до размера фвно выровненных мемберов
<Padawan> ясно. определяется размером наибольшего элемента, либо structure align
<dDIMA> Для PS2 structure align = 16. Для PSP, подозреваю, сколько же

Поэтому при проектировании структур необходимо учитывать эти факты. А при переносе кода/ресурсов стоит внимательно оценивать существующие структуры, так как они по-просту могут быть непригодными, как например в моем случае с BMP.

С классами всё становится немного запутанным. С появлением наследования, а тем более множественного, от нашего взгляда всё дальше ускользают детали распределения памяти. Что часто приводит к неоправданным расходам.

<dDIMA> теперь можем поговорить о наследовании классов и выравнивании наследованных структур :)
<Padawan> ужос
<Padawan> лучше на С всё писать
<dDIMA> в том случае, если у тебя есть class A и class B : public class A, ситуация следующая
Правка: классы склеиваются в структуру, которая потом выравнивается
<dDIMA> Проблема только в том, если например class A имеет виртуальные функции
<dDIMA> Все известные мне реализацыи пишут адрес VTBL в первые 4 байта структуры.
<dDIMA> Поэтому потом начинается жопа
<dDIMA> Если не дай бог в классе есть мемберь, который требует выравнивания на 16, получается следующая хуйня
<Padawan> выравниться должно 
<dDIMA> class A: { virtual ~A(); var a; } // var - структура с выравниванием на 16
<dDIMA> Получаем следующую карту памяти: [vtbl] со смещением 0
<dDIMA> char padding[12]; // 12 байт выравнивания после vtbl
<dDIMA> var a; // сама переменная, которая идет со смещением на 16 байт
<dDIMA> множественное наследование смотреть будем? :-)
<Padawan> понятно убийство получается.
<Padawan> (памяти)
consoledevФорум

Тема в архиве.