Понадобилось быстро подключить SD-карточку к микроконтроллеру, и задача казалась простецкой — добрый микрочип предлагает библиотеки для всего чего угодно (ах, поставить линк на библиотеки — не судьба), но после первого взгляда на их код, волосы на голове начали шевелиться.
Те кто общался с саппортом микрочипа, наверное замечал что зачастую попадает на индийский департамент конторы, и все-бы ничего если бы не подозрение что весь микрочип разом переехал в Бомбей и набрал индийских бездомных школьников для написания своих библиотек.

Не подумайте, что я сейчас пытаюсь гнуть расово верную линию — не имел опыта общения конкретно с индусами, но точно знаю что среди наших их тоже достаточно (не верите — наберите «95» в гугле), но понятие «индусского кода» появилось давно и закрепилось довольно прочно, хотя вы и не найдете его в политкорректной википедии (но гугол о нем точно знает).
Итак, если вы хотите научиться программировать так как это делают в микрочипе следуйте следующим простым советам…
Самое важное, что надо запомнитьнанимаясь получив работу в микрочипе: «Они-таки платят за строки кода!». Поэтому любыми способами увеличивайте объемы исходных текстов. Совет общий, так что без примеров, включайте фантазию.
Классика жанра индусскогокино кода незыблема со времен его появления, для разминки попробуйте угадать что скрывается за этим куском кода, содержащемся в файле «MDD File System\SD-SPI.c» на строчке 1042:
В отсутствии фантазии подойдет и копи-пейст, хотя по слухам многие работодатели проверяют код на копипаст, микрочип видимо не из их числа. Запомните, для срубания бабла индусским кодом никогда не используйте макросы — они зло и безобразно уменьшают код. В пример кусок, повторяющийся раз двадцать в файле «MDD File System\FSIO.c»:
Вообще не терплю копипаст в программировании. Если возникло желание что-то скопировать то сразу задумываюсь где и что надо переписывать. И уж если есть потребность в одинаковом куске кода, который не может быть завернут в функцию то вместо тупого копирования можно сделать из него макрос, что будет и читабельнее и легче правиться в дальнейшем:
Соотношение 10:1 в пользу первого варианта, а с учетом двадцатикратного повторения в абсолютных величинах это несколько сот рупий!
Использование циклов — зло. Линейная программа работает значительно быстрее, не тратя времени на операторы условий и переходы, и содержит больше строк кода.
Инициализация структур должна быть побайтной, не надо писать простые инициализаторы типа:
На очередную мысль меня навела идея функции FileObjectCopy в файле «MDD File System\FSIO.c» на строчке 6065, подозреваю что если бы у них было больше разных структур то появились бы и другие SomeObjectCopy
Описание очаровывает простотой:
Ну есть еще мелочи для раздувания кода, которыми можно добавить пару — тройку строк, типа:
Даже если это исключительно для того чтобы сделать переменную read-only то такого макроса вполне достаточно, чтобы компилятор выругался где надо:
Комментируйте все подряд, кроме самых не очевидных кусков (см пример 1.) Если вы еще не достигли полного просветления и в вашей индусской программе случайно осталось две-три функции — создайте «шаблон описания функции», включите туда умные слова-разделы, в разделе «Description» перечислите еще раз все что было написано выше, но развернуто. Особенно эффект умножения строк кода проявляется с функциями типа «FSerror()» из примера выше.
Все что было написано выше — общие советы для новичков на пути просветления, применимые к любой программе, практически на любом языке. Но настоящие поклонники Шивы используют все возможности для создания хаоса. Учитывая кучерявость гарвардской архитектуры PIC контроллеров, настоящие гуру индусского кода откроют для себя невообразное число возможностей использования специфических директив и прочих особенностей кривизны реализации си в компиляторах.
Пишите код таким образом, чтобы он даже не мог компилироваться под разными версиями компиляторов, и используйте все специфические #pragma. В этом случае каждая функция будет присутствовать в версиях как минимум для двух компиляторов и трех-четырех архитектур PIC, итого до 8 крат увеличения кода.
Еще раз удвоить количество кода вам поможет то, что указатели RAM и ROM в компиляторах под PIC разные, то есть «char*» не может быть преобразован явно или неявно к «const char*» в хайтеке или «const rom char*» в микрочипе. Что вобщем-то проблем в хайтеке не вызывает совсем, так как void, far и const указатели могут адресовать всю память и применяться как к ROM так и RAM. Но в микрочиповской реализации си это может привести к созданию двух функций: одной работающей с ROM, а второй с RAM — чистый профит. Никогда не следует довольствоваться одной функцией, работающей с оперативной памятью (а при необходимости загружающей туда константы из ROM).
И последнее, всегда используйте инлайн-ассемблер даже в случаях когда ваш код значительно длиннее и медленнее чем то, что делает компилятор из нормальной си программы. Ассемблер выглядит круто и большинство не заподозрят какое скудоумие было приложено при его создании, а также будут считать что программа написана одним из оптимальнейших из возможных методов.
Попытавшись заставить заработать микрочиповское чудо (их сайт все-таки поднялся) я потратил чуть больше времени чем то, за которое написал свою реализацию работы с SD и портировал файловую систему, взятую здесь, о чем и вас предупреждаю — аккуратнее: «glitch inside».
Те кто общался с саппортом микрочипа, наверное замечал что зачастую попадает на индийский департамент конторы, и все-бы ничего если бы не подозрение что весь микрочип разом переехал в Бомбей и набрал индийских бездомных школьников для написания своих библиотек.

Не подумайте, что я сейчас пытаюсь гнуть расово верную линию — не имел опыта общения конкретно с индусами, но точно знаю что среди наших их тоже достаточно (не верите — наберите «95» в гугле), но понятие «индусского кода» появилось давно и закрепилось довольно прочно, хотя вы и не найдете его в политкорректной википедии (но гугол о нем точно знает).
Индусский код (не индийский или индейский) — жаргонное нарицательное название для программного кода крайне низкого качества, использующего простые, но порочные принципы «copy-paste».
Почему именно индусский?
По слухам в Индии с некоторых времен существует практика оценки производительности труда программиста на основе количества написанного кода. Чем больше кода, тем больше программист работает, и, следовательно, выше его оклад. Шустрые индусы быстро сообразили, как обманывать неквалифицированных заказчиков.
Полезное замечание от kaladhara
Житель Индии — индиец, а индус — это последователь любого направления индуизма. Таким образом даже чукча преклонных годов, исповедующий шиваизм (и, вероятно пишуший на с++) — индус.
Итак, если вы хотите научиться программировать так как это делают в микрочипе следуйте следующим простым советам…
0. Больше кода — больше профит!
Самое важное, что надо запомнить
1. Классика жанра
Классика жанра индусского
if(localCounter != 0x0000) { localPointer--; while(1) { SPIBUF = 0xFF; localPointer++; if((--localCounter) == 0x0000) { break; } while(!SPISTAT_RBF); *localPointer = (BYTE)SPIBUF; } while(!SPISTAT_RBF); *localPointer = (BYTE)SPIBUF; }
не спешите заглядывать в ОТВЕТ - это просто:
// в данном контекте (если значение счетчика на выходе не важно) сойдет и такое while (localCounter--) { SPIBUF = 0xFF; while (!SPISTAT_RBF); *localPointer++ = SPIBUF; } // а это полный аналог, имеющий на выходе то же значение счетчика // правда на одну строчку длиннее - чуть менее эффектно while (localCounter) { localCounter--; SPIBUF = 0xFF; while (!SPISTAT_RBF); *localPointer++ = SPIBUF; }
2. Копипаст
В отсутствии фантазии подойдет и копи-пейст, хотя по слухам многие работодатели проверяют код на копипаст, микрочип видимо не из их числа. Запомните, для срубания бабла индусским кодом никогда не используйте макросы — они зло и безобразно уменьшают код. В пример кусок, повторяющийся раз двадцать в файле «MDD File System\FSIO.c»:
ctrl+v
if (utfModeFileName) { utf16path++; i = *utf16path; } else { temppath++; i = *temppath; }
Вообще не терплю копипаст в программировании. Если возникло желание что-то скопировать то сразу задумываюсь где и что надо переписывать. И уж если есть потребность в одинаковом куске кода, который не может быть завернут в функцию то вместо тупого копирования можно сделать из него макрос, что будет и читабельнее и легче правиться в дальнейшем:
простая и понятная замена куску выше
#define getnextpathchar() ( utfModeFileName ? *++utf16path : *++temppath ) // где-то в начале ... i = getnextpathchar(); // там где надо
Соотношение 10:1 в пользу первого варианта, а с учетом двадцатикратного повторения в абсолютных величинах это несколько сот рупий!
3. Линейный код
Использование циклов — зло. Линейная программа работает значительно быстрее, не тратя времени на операторы условий и переходы, и содержит больше строк кода.
не стесняйтесь делать так
fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[1]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[0]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[5]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[4]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[3]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[2]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[1]; fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[0]; tempShift.byte.LB = lfnObject.LFN_Part1[8]; tempShift.byte.HB = lfnObject.LFN_Part1[9]; fileFoundString[fileFoundLfnIndex--] = tempShift.Val; tempShift.byte.LB = lfnObject.LFN_Part1[6]; tempShift.byte.HB = lfnObject.LFN_Part1[7]; fileFoundString[fileFoundLfnIndex--] = tempShift.Val; tempShift.byte.LB = lfnObject.LFN_Part1[4]; tempShift.byte.HB = lfnObject.LFN_Part1[5]; fileFoundString[fileFoundLfnIndex--] = tempShift.Val; tempShift.byte.LB = lfnObject.LFN_Part1[2]; tempShift.byte.HB = lfnObject.LFN_Part1[3]; fileFoundString[fileFoundLfnIndex--] = tempShift.Val; tempShift.byte.LB = lfnObject.LFN_Part1[0]; tempShift.byte.HB = lfnObject.LFN_Part1[1]; fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
Инициализация структур должна быть побайтной, не надо писать простые инициализаторы типа:
const somestruct mystruct = {"Field1", 2, 4, 8 .... };
не стесняйтесь делать и так, memset для лузеров, а это живые деньги
gDataBuffer[0] = 0xEB; //Jump instruction gDataBuffer[1] = 0x3C; gDataBuffer[2] = 0x90; gDataBuffer[3] = 'M'; //OEM Name "MCHP FAT" gDataBuffer[4] = 'C'; gDataBuffer[5] = 'H'; gDataBuffer[6] = 'P'; gDataBuffer[7] = ' '; gDataBuffer[8] = 'F'; gDataBuffer[9] = 'A'; gDataBuffer[10] = 'T'; gDataBuffer[11] = 0x00; //Sector size gDataBuffer[12] = 0x02; gDataBuffer[13] = disk->SecPerClus; //Sectors per cluster gDataBuffer[14] = 0x20; //Reserved sector count gDataBuffer[15] = 0x00; disk->fat = 0x20 + disk->firsts; gDataBuffer[16] = 0x02; //number of FATs gDataBuffer[17] = 0x00; //Max number of root directory entries - 512 files allowed gDataBuffer[18] = 0x00; gDataBuffer[19] = 0x00; //total sectors gDataBuffer[20] = 0x00; gDataBuffer[21] = 0xF8; //Media Descriptor gDataBuffer[22] = 0x00; //Sectors per FAT gDataBuffer[23] = 0x00; gDataBuffer[24] = 0x3F; //Sectors per track gDataBuffer[25] = 0x00; gDataBuffer[26] = 0xFF; //Number of heads gDataBuffer[27] = 0x00; // Hidden sectors = sectors between the MBR and the boot sector gDataBuffer[28] = (BYTE)(disk->firsts & 0xFF); gDataBuffer[29] = (BYTE)((disk->firsts / 0x100) & 0xFF); gDataBuffer[30] = (BYTE)((disk->firsts / 0x10000) & 0xFF); gDataBuffer[31] = (BYTE)((disk->firsts / 0x1000000) & 0xFF); // Total Sectors = same as sectors in the partition from MBR gDataBuffer[32] = (BYTE)(secCount & 0xFF); gDataBuffer[33] = (BYTE)((secCount / 0x100) & 0xFF); gDataBuffer[34] = (BYTE)((secCount / 0x10000) & 0xFF); gDataBuffer[35] = (BYTE)((secCount / 0x1000000) & 0xFF); gDataBuffer[36] = fatsize & 0xFF; //Sectors per FAT gDataBuffer[37] = (fatsize >> 8) & 0xFF; gDataBuffer[38] = (fatsize >> 16) & 0xFF; gDataBuffer[39] = (fatsize >> 24) & 0xFF; gDataBuffer[40] = 0x00; //Active FAT gDataBuffer[41] = 0x00; gDataBuffer[42] = 0x00; //File System version gDataBuffer[43] = 0x00; gDataBuffer[44] = 0x02; //First cluster of the root directory gDataBuffer[45] = 0x00; gDataBuffer[46] = 0x00; gDataBuffer[47] = 0x00; gDataBuffer[48] = 0x01; //FSInfo gDataBuffer[49] = 0x00; gDataBuffer[50] = 0x00; //Backup Boot Sector gDataBuffer[51] = 0x00; gDataBuffer[52] = 0x00; //Reserved for future expansion gDataBuffer[53] = 0x00; gDataBuffer[54] = 0x00; gDataBuffer[55] = 0x00; gDataBuffer[56] = 0x00; gDataBuffer[57] = 0x00; gDataBuffer[58] = 0x00; gDataBuffer[59] = 0x00; gDataBuffer[60] = 0x00; gDataBuffer[61] = 0x00; gDataBuffer[62] = 0x00; gDataBuffer[63] = 0x00; gDataBuffer[64] = 0x00; // Physical drive number gDataBuffer[65] = 0x00; // Reserved (current head) gDataBuffer[66] = 0x29; // Signature code gDataBuffer[67] = (BYTE)(serialNumber & 0xFF); gDataBuffer[68] = (BYTE)((serialNumber / 0x100) & 0xFF); gDataBuffer[69] = (BYTE)((serialNumber / 0x10000) & 0xFF); gDataBuffer[70] = (BYTE)((serialNumber / 0x1000000) & 0xFF); gDataBuffer[82] = 'F'; gDataBuffer[83] = 'A'; gDataBuffer[84] = 'T'; gDataBuffer[85] = '3'; gDataBuffer[86] = '2'; gDataBuffer[87] = ' '; gDataBuffer[88] = ' '; gDataBuffer[89] = ' ';
4. Изобретаем велосипед или деньги из пробелов
На очередную мысль меня навела идея функции FileObjectCopy в файле «MDD File System\FSIO.c» на строчке 6065, подозреваю что если бы у них было больше разных структур то появились бы и другие SomeObjectCopy
сама функция
void FileObjectCopy(FILEOBJ foDest, FILEOBJ foSource) { int size; BYTE* dest; BYTE* source; int Index; dest = (BYTE*)foDest; source = (BYTE*)foSource; size = sizeof(FSFILE); for (Index = 0; Index < size; Index++) { dest[Index] = source[Index]; } }
Описание очаровывает простотой:
The FileObjectCopy function will make an exacy copy of a specified FSFILE object.Если «exacy» == «exact» как следует из кода, то это профитная замена прямого присвоения структур — стандартной операции в ANSI C, a сделанное компилятором, оно должно быть и быстрее и компактнее так как используются аппаратные FSR/INDF регистры. Для разных объектов подойдет memcpy(d, s, sizeof(s)) и работает он тоже быстро, во всяком случае его ассемблерная реализация.
не прибыльная версия FileObjectCopy, дизассемблированный вариант от HITech C18
; *FileObject1 = *FileObject2; // Одна строчка на С ; Загружаем индирект регистры MOVLW FileObject1 >> 8 MOVWF FSR1H MOVLW FileObject1 MOVWF FSR1L MOVLW FileObject2 >> 8 MOVWF FSR0H MOVLW FileObject2 MOVWF FSR0L ; Копируем MOVLW sizeof(*FileObject) loop: MOVFF POSTINC0, POSTINC1 ; Три команды процессора на скпированный байт DECFSZ WREG, F BRA loop
Ну есть еще мелочи для раздувания кода, которыми можно добавить пару — тройку строк, типа:
int FSerror (void) { return FSerrno; }
Даже если это исключительно для того чтобы сделать переменную read-only то такого макроса вполне достаточно, чтобы компилятор выругался где надо:
#define FSerror() ( FSerrno )
5. Комментарии с фанатизмом
Комментируйте все подряд, кроме самых не очевидных кусков (см пример 1.) Если вы еще не достигли полного просветления и в вашей индусской программе случайно осталось две-три функции — создайте «шаблон описания функции», включите туда умные слова-разделы, в разделе «Description» перечислите еще раз все что было написано выше, но развернуто. Особенно эффект умножения строк кода проявляется с функциями типа «FSerror()» из примера выше.
даже пустой такая шапка смотрится значимо
/************************************************************************** Function: void func (void) Summary: Does a hard work Conditions: This function should not be called by the user Input: None Return Values: None Side Effects: None Description: This function will do <a hard work>, with <none> input parameter.... Remarks: Optimize code later **************************************************************************/
6. Используйте особенности архитектуры
Все что было написано выше — общие советы для новичков на пути просветления, применимые к любой программе, практически на любом языке. Но настоящие поклонники Шивы используют все возможности для создания хаоса. Учитывая кучерявость гарвардской архитектуры PIC контроллеров, настоящие гуру индусского кода откроют для себя невообразное число возможностей использования специфических директив и прочих особенностей кривизны реализации си в компиляторах.
Пишите код таким образом, чтобы он даже не мог компилироваться под разными версиями компиляторов, и используйте все специфические #pragma. В этом случае каждая функция будет присутствовать в версиях как минимум для двух компиляторов и трех-четырех архитектур PIC, итого до 8 крат увеличения кода.
Еще раз удвоить количество кода вам поможет то, что указатели RAM и ROM в компиляторах под PIC разные, то есть «char*» не может быть преобразован явно или неявно к «const char*» в хайтеке или «const rom char*» в микрочипе. Что вобщем-то проблем в хайтеке не вызывает совсем, так как void, far и const указатели могут адресовать всю память и применяться как к ROM так и RAM. Но в микрочиповской реализации си это может привести к созданию двух функций: одной работающей с ROM, а второй с RAM — чистый профит. Никогда не следует довольствоваться одной функцией, работающей с оперативной памятью (а при необходимости загружающей туда константы из ROM).
И последнее, всегда используйте инлайн-ассемблер даже в случаях когда ваш код значительно длиннее и медленнее чем то, что делает компилятор из нормальной си программы. Ассемблер выглядит круто и большинство не заподозрят какое скудоумие было приложено при его создании, а также будут считать что программа написана одним из оптимальнейших из возможных методов.
Вместо заключения
Попытавшись заставить заработать микрочиповское чудо (их сайт все-таки поднялся) я потратил чуть больше времени чем то, за которое написал свою реализацию работы с SD и портировал файловую систему, взятую здесь, о чем и вас предупреждаю — аккуратнее: «glitch inside».
