Hindu Code in Microchip

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

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

    Do not think that I'm trying to bend a racially correct line - I didn’t have experience specifically with Indians, but I know for sure that there are enough of them among our people (don’t believe it, type “95” in Google ), but the concept of “Hindu code” appeared long ago and entrenched quite firmly, although you will not find it in the politically correct Wikipedia (but googol knows about it for sure).
    Hindu code (not Indian or Native American) is a slang common name for extremely low-quality program code that uses simple but vicious copy-paste principles.
    Why Hindu?
    According to rumors in India, for some time, there has been a practice of evaluating the productivity of a programmer based on the amount of code written. The more code, the more the programmer works, and therefore, his salary is higher. Nimble Indians quickly figured out how to trick unskilled customers.
    Useful note from kaladhara
    A resident of India is Indian, and a Hindu is a follower of any direction of Hinduism. Thus, even the Chukchi of advanced years, professing Shaivism (and probably writing in C ++) is an Indian.

    So, if you want to learn how to program like this in a microchip, follow these simple tips ...

    0. More code - more profit!

    The most important thing to remember when hiring when you get a job in a microchip is: "They still pay for lines of code!". Therefore, by any means increase the volume of source texts. The advice is general, so without examples, include fantasy.

    1. Classics of the genre

    The classic of the Hindu movie code genre has been unshakable since its inception. To warm up, try to guess what is hidden behind this piece of code contained in the file “MDD File System \ SD-SPI.c” on line 1042:

    if(localCounter != 0x0000)
            SPIBUF = 0xFF;
            if((--localCounter) == 0x0000)
            *localPointer = (BYTE)SPIBUF;
        *localPointer = (BYTE)SPIBUF;  

    do not rush to look in the ANSWER - it's simple:
    // в данном контекте (если значение счетчика на выходе не важно) сойдет и такое
    while (localCounter--)
    	SPIBUF = 0xFF;
    	while (!SPISTAT_RBF);
    	*localPointer++ = SPIBUF;
    // а это полный аналог, имеющий на выходе то же значение счетчика
    // правда на одну строчку длиннее - чуть менее эффектно
    while (localCounter)
    	SPIBUF = 0xFF;
    	while (!SPISTAT_RBF);
    	*localPointer++ = SPIBUF;

    2. Copy-paste

    In the absence of imagination, copy-paste is also suitable, although according to rumors many employers check the code for copy-paste, the microchip is apparently not one of them. Remember, never use macros to cut the dough with the Hindu code - they evil and ugly reduce the code. For example, a piece repeating twenty times in the MDD File System \ FSIO.c file:

    ctrl + v
    if (utfModeFileName)
    	i = *utf16path;
    	i = *temppath;

    In general, I can not stand copy-paste in programming. If there is a desire to copy something, I immediately think about where and what to rewrite. And if there is a need for the same piece of code that cannot be wrapped in a function, then instead of blunt copying, you can make a macro out of it, which will be both more readable and easier to edit in the future:

    simple and clear replacement for the piece above
    #define getnextpathchar() ( utfModeFileName ? *++utf16path : *++temppath ) // где-то в начале
    	i = getnextpathchar(); // там где надо

    A ratio of 10: 1 in favor of the first option, and given twenty repetitions in absolute terms, this is several hundred rupees!

    3. Line code

    Using cycles is evil. A linear program runs much faster without wasting time on condition statements and transitions, and contains more lines of code.

    feel free to do so
    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;

    Initialization of structures should be byte-by-bye, no need to write simple initializers like:

    const somestruct mystruct = {"Field1", 2, 4, 8 .... };

    do not hesitate to do so, memset for losers, and this is real money
    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. Inventing a bike or money out of spaces

    The idea of ​​the FileObjectCopy function in the file “MDD File System \ FSIO.c” on line 6065 led me to another thought, I suspect that if they had more different structures then other SomeObjectCopy would appear

    function itself
    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];

    Description charms with simplicity:
    The FileObjectCopy function will make an exacy copy of a specified FSFILE object.
    If "exacy" == "exact" as follows from the code, then this is a profitable replacement for direct assignment of structures - a standard operation in ANSI C, and made by the compiler, it should be faster and more compact since hardware FSR / INDF registers are used. Memcpy (d, s, sizeof (s)) is suitable for different objects and it also works quickly, in any case, its assembler implementation.

    unprofitable version of FileObjectCopy, disassembled version from HITech C18
    ; *FileObject1 = *FileObject2; // Одна строчка на С
    ; Загружаем индирект регистры
    MOVLW FileObject1 >> 8
    MOVLW FileObject1
    MOVLW FileObject2 >> 8
    MOVLW FileObject2
    ; Копируем
    MOVLW sizeof(*FileObject)
    MOVFF POSTINC0, POSTINC1 ; Три команды процессора на скпированный байт
    BRA loop

    Well, there are still little things for inflating the code, with which you can add a couple - three lines, such as:

    int FSerror (void)
        return FSerrno;

    Even if it is solely to make the read-only variable, then such a macro is quite enough for the compiler to swear where necessary:

    #define FSerror() ( FSerrno )

    5. Comments with fanaticism

    Comment on everything except the most unintelligible pieces (see Example 1.) If you have not yet reached full enlightenment and two or three functions have accidentally remained in your Hindu program - create a “function description template”, include smart section words in the section "Description" list again everything that was written above, but expanded. Especially the effect of multiplying lines of code is manifested with functions like “FSerror ()” from the example above.

    even an empty hat looks significant
        void func (void)
        Does a hard work
        This function should not be called by the user
      Return Values:
      Side Effects:
        This function will do , with  input parameter....
        Optimize code later

    6. Use features of architecture

    All that has been written above is general advice for beginners on the path to enlightenment, applicable to any program, in almost any language. But real Shiva fans use every opportunity to create chaos. Given the curvature of the Harvard architecture of PIC controllers, the real gurus of the Hindu code will discover an unbelievable number of possibilities for using specific directives and other features of the curvature of the C implementation in compilers.

    Write the code in such a way that it cannot even compile under different versions of compilers, and use all the specific #pragma. In this case, each function will be present in versions for at least two compilers and three to four PIC architectures, for a total of up to 8 times the code increase.

    It will help you to double the amount of code once again that the RAM and ROM pointers in the compilers for PIC are different, that is, “char *” cannot be converted explicitly or implicitly to “const char *” in high tech or “const rom char *” in microchip . Which, in general, does not cause any problems at all in high-tech, since void, far and const pointers can address all memory and apply to both ROM and RAM. But in the microchip implementation of si, this can lead to the creation of two functions: one working with ROM, and the second with RAM - pure profit. You should never be content with one function that works with RAM (and, if necessary, loads constants from ROM there).

    And finally, always use inline assembler even in cases when your code is much longer and slower than what the compiler does from a normal C program. Assembler looks cool and most will not suspect the poor folly that was applied when it was created, and they will also assume that the program is written as one of the best possible methods.

    Instead of a conclusion

    Trying to make the microchip
    miracle work (their site still rose) I spent a little more time than the one for which I wrote my implementation of working with SD and ported the file system taken here , which I warn you about - more accurately: “glitch inside”.

    Also popular now: