How to specify a newline char in MCS51 asm

BLRCalvin
2011-09-06
2013-03-12
  • BLRCalvin
    BLRCalvin
    2011-09-06

    Dear All,

    I am trying to insert a newline char in mcs51 assembly code (which is part of a c macro). However sdas8051(#6622 snapshot version) is giving error as "Error: <q> missing or improper operators, terminators, or delimiters"

    The macro is as below:

    #define CAN_pushAMRegs() while(CAN_ADCON & CAN_ADCON_BSY); \
            _asm \
            push SDCC_CAN_ADL \
            push SDCC_CAN_ADH \
            push SDCC_CAN_DATA0 \
            push SDCC_CAN_DATA1 \
            push SDCC_CAN_DATA2 \
            push SDCC_CAN_DATA3 \
            _endasm

    I want to include a \n after every push instruction. As per v3.0 usermanual I tried using '\n. However, the assembler is not printing a newline as desired. I request you to let me know how to do it.

    Thanks.

    Best Regards,
    Raghu.

     
  • Borut Ražem
    Borut Ražem
    2011-09-06

    You can't insert a newline by using the C preprocessor macro definition, but the following trick should solve your problem:

    #define CAN_pushAMRegs() while(CAN_ADCON & CAN_ADCON_BSY); \
            __asm push SDCC_CAN_ADL __endasm; \
            __asm push SDCC_CAN_ADH __endasm; \
            __asm push SDCC_CAN_DATA0 __endasm; \
            __asm push SDCC_CAN_DATA1 __endasm; \
            __asm push SDCC_CAN_DATA2 __endasm; \
            __asm push SDCC_CAN_DATA3 __endasm;

    Borut

     
  • Tormod Volden
    Tormod Volden
    2011-09-06

    Note that your trailing slashes get "eaten" by the preprocessor while it parses your macro. So the macro itself will just be one long line with all your things concatenated. Borat's suggestion will work because the semicolons separate statements that are on the same line. Maybe it will also work to use \n\ at the end of each line (maybe this was what the documentation meant). Can you please tell which section of the documentation you are referring to?

    In Borat's example you will have to add braces {} around the while loop contents, otherwise only the first "push" will be in the loop, as it constitutes a full C statement.

    And in all of above, there should be no semicolon after the while() conditional, otherwise the loop will be just the empty statement.

     
  • Borut Ražem
    Borut Ražem
    2011-09-06

    > Borat's suggestion will work because the semicolons separate statements that are on the same line.

    It is not a semicolon: each __asm / __endasm pair generates an additional newline in the assembly code.

    > Maybe it will also work to use \n\ at the end of each line

    No, it won't: the preprocessor doesn't convert \n to a newline, so the \n is passed literally to the compiler and in this case to the assembly code.

    > In Borat's example you will have to add braces {} around the while loop contents

    I didn't pay attention, but this was already wrong (if it is wrong at all) in the original example.

    P.S.: it seems that I have new nick name ;-)

    BorUt

     
  • Tormod Volden
    Tormod Volden
    2011-09-06

    lol, oops, sorry about the name mix-up :)  I thought the semicolon separates the __asm / __endasm C statements, from the C point of view at least. But what the assembler ends up with I do not know very well.

    In the original example, the one statement to the while loop (if the semicolon is omitted) is the one __asm/__endasm statement. Well, I /think/ the __asm/__endasm constitutes a C statement, otherwise I am awfully wrong.

     
  • Borut Ražem
    Borut Ražem
    2011-09-06

    > In the original example, the one statement to the while loop (if the semicolon is omitted) is the one _asm/__endasm statement.

    Correct. With "this was already wrong" I meant the semicolon after the while(). Curly brackets are required.

    > Well, I /think/ the __asm/__endasm constitutes a C statement, otherwise I am awfully wrong.

    Yes, __asm/__endasm constitutes a C statement.

    Borut

     
  • Maarten Brock
    Maarten Brock
    2011-09-06

    Both the semicolon after the while condition in the original post as well as the ones after the __endasm parts end a C statement and thus braces are necessary if multiple __asm/__endasm pairs are used. The O/P may have really meant to have no statement in the wait while busy loop.

    Using _asm (single underscore) is depreciated and on top of that not recognized by the preprocessor. So the preprocessor cannot replace the backslashes in the macro with "__endasm; __asm" pairs to solve your problem. If you use __asm (double underscore) it will. See also bug 1505956 why all this is necessary. And the regression test bug1505956.c which shows it works.
    BUG: https://sourceforge.net/tracker/index.php?func=detail&aid=1505956&group_id=599&atid=100599
    TEST: http://sdcc.svn.sourceforge.net/viewvc/sdcc/trunk/sdcc/support/regression/tests/bug1505956.c?revision=6841&view=markup

    There may still be a bug lurking here as the preprocessor probably doesn't emit braces either while it does break up the inline asm instructions into multiple C statements.

    Maarten

     
  • Maarten Brock
    Maarten Brock
    2011-09-06

    It appears that _asm and _endasm are not yet depreciated by the lexer when using -std-sdccXX. This should (will) change soon.

     
  • Maarten Brock
    Maarten Brock
    2011-09-06

    It also appears that _asm IS recognized by the preprocessor so the original code should work as is.

    And the missing braces bug is indeed there.

     
  • Borut Ražem
    Borut Ražem
    2011-09-07

    Maarten, can you give an example of missing braces bug?

    Borut

     
  • Maarten Brock
    Maarten Brock
    2011-09-07

    #define NOP __asm \
                        nop     \
                        nop     \
                __endasm
    while (busy) NOP;
    

    The preprocessor changes the code into:

    while (busy) __asm nop __endasm; __asm nop __endasm;
    

    And thus it will not do two NOP's in the loop body.

     
  • BLRCalvin
    BLRCalvin
    2011-09-07

    Dear All,

    Thanks for all the quick replies. Sorry for my late replies.

    @Borut, @Maarten:  Thanks for your advices.

    In the above example, the while loop is a busy loop, so no statements in the wait state (@Maarten : you got it right :-) )

    The following are my observations:

    The preprocessor seems to be recognizing the _asm. However not replacing backslash with  "__endasm; __asm";

    When __asm …. __endasm (double underscore) is used with the rest of the above example as is, it still does NOT replace backslash with "__endasm; __asm"; the reason is : there are some #defines inside the asm code (e.g.: SDCC_CAN_DATA0, SDCC_CAN_ADL etc., in push instruction). However, if I replace the SDCC_CAN_ADL, C_CAN_DATA0 etc., with literal addresses like 0xDB, 0xDC, it works FINE.

    Does it mean the preprocessor can be further improved to solve this? Kindly provide your thoughts on this.

    Thanks & Best Regards,
    Raghunath Lolur.

     
  • Maarten Brock
    Maarten Brock
    2011-09-07

    Why use defines for SDCC_CAN_ADL if you can also create a real symbol for it? Is it an sfr that is defined in a C header? Then you can use the C name with an extra underscore in assembly.

    __sfr __at(0xDB) CAN_ADL;
    __asm push _CAN_ADL __endasm;
    

    Still the preprocessor should not forget it is inside an __asm/__endasm pair when it encounters another macro.

     
  • Robas Teodor
    Robas Teodor
    2011-09-07

    Hello all,

    Just did some code porting and I have noticed that both compilers had a very convenient "__asm ()" function.
    So macros like:

    #define SOME_MACRO   \
        __asm("\tpush r16");      \
        __asm("\tst.w   3, r16");  \
    

    put no problems. Is there a reason for not having this in SDCC ?

     
  • Borut Ražem
    Borut Ražem
    2011-09-07

    > The preprocessor changes the code into:
    > while (busy) __asm nop __endasm; __asm nop __endasm;

    What a surprise! Obviously I forgot that Maarten implemented it!

    This changes almost everything that I wrote :-(

    The second surprise is a different behavior between single underscored _asm / _endasm and double underscored __asm / __endasm. Usage of single underscored variant is deprecated but not obsoleted so by my opinion it should work the same as the double underscored variant.

    Borut

     
  • Maarten Brock
    Maarten Brock
    2011-09-07

    Actually I think there is no difference currently. I just remembered (wrongfully) that only __asm was recognized. But I sure would not add code for already deprecated keywords to fix very long standing bugs. Unfortunately _asm is not yet marked deprecated.

     
  • Borut Ražem
    Borut Ražem
    2011-09-08

    Here is the content of my test case file t.c:

    int main(void)
    {
    #define NOP __asm \
      nop     \
      nop     \
      __endasm
      NOP;
    #define NOP1 _asm \
      nop     \
      nop     \
      _endasm
      NOP1;
      return 0;
    }
    

    $ sdcpp t.c generates (blank lines excluded):

    # 1 "t.c"
    # 1 "<built-in>"
    # 1 "<command line>"
    # 1 "t.c"
    int main(void)
    {
     __asm __endasm; __asm nop __endasm; __asm nop __endasm; __asm __endasm;
      _asm __endasm; __asm nop __endasm; __asm nop __endasm; __asm _endasm;
      return 0;
    }
    

    so _asm works the same as __asm. Yesterday I probably made a mistake while testing, sorry.

    $ sdcc -S t.c

    t.c:15: warning 197: keyword '_asm' is deprecated, use '__asm' instead
    t.c:15: warning 197: keyword '_endasm' is deprecated, use '__endasm' instead
    

    So single underscored _asm is deprecated by sdcc (not by sdcpp), which I think is OK.

    The implementation of __asm / __endasm generated by preprocessor for each line in macro definition introduced a new problem, since a single C statement in source file is by preprocessor devided in sveral C statements. I don't know if the surroundig curley braces sole this problem. Maybe the { .. } while(0) preprocessor trick is more appropriate?

    __asm("…"); seems not a bad idea. This approach doesn't require any special handling by cpp, which siplifies the life a lot. But this also means that using macros in the asm strings doesen't works since literal string are not preprocessed. Is this correct or I'm missing something?

    Probably both __asm("…") and __asm / __endasm methods can even co-exist if the grammer analyser is smart enough…

    Borut

     
  • Maarten Brock
    Maarten Brock
    2011-09-08

    I don't see how __asm(…) solves the problem unless it is restricted to one single mnemonic only. In that case you might as well use __asm … __endasm; with only one mnemonic. I have no objections against also supporting __asm(…) as others have already proposed in the past though.

    And we probably need the trick for a proper fix.

    When I tested _asm I did not get a warning. Maybe I did something wrong too. I'll test again later.

     
  • Borut Ražem
    Borut Ražem
    2011-09-08

    > I don't see how __asm(…) solves the problem unless it is restricted to one single mnemonic only.
    Something like __asm("nop\nnop") would also work since the newline is properly transferred to the assembler and the __asm(); is still a single C statement. But a new problem arises: what about the indentation of mnemonics?….

     
  • Maarten Brock
    Maarten Brock
    2011-09-08

    Ah, right. So __asm("…") needs the argument as a string. This precludes macro expansion, but apparently that doesn't work properly now either. And instead of the line continuation character (\) you need to use \n. Indentation is copied from the string then as long as you use spaces and no tabs.

    __asm(
    "    cpl     a       ; comment 1 \n"
    "    cpl     a       ; comment 2 \n"
    );
    

    Or, if you want to write unreadable and unmaintainable code:

    __asm("\tcpl\ta\t; comment 1\n\tcpl\ta\t; comment 2\n");
    
     
  • Borut Ražem
    Borut Ražem
    2011-09-08

    > This precludes macro expansion, but apparently that doesn't work properly now either.
    Macro expansion does work. INSTR is properly expanded to nop in the following example:

    #define INSTR nop
    __asm INSTR __endasm;
    

    In __asm(…); syntax the macro expansion can be done by stringifying the  mcaro value:

    #define STRINGIFY(x) #x
    #define S(x) STRINGIFY(x)
    #define LABEL 100
    asm("jmp    " S(LABEL));
    

    gcc used asm or __asm__ keywords. If we could use the same kewords (maybe only __asm__), so both __asm / __endasm and __asm__ (…); can coexist.

    For solving the indentation the GCC-Inline-Assembly-HOWTO document at http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html proposes the following coding style:

    __asm__ ("movl %eax, %ebx\n\t"
              "movl $56, %esi\n\t"
              "movl %ecx, $label(%edx,%ebx,$4)\n\t"
              "movb %ah, (%ebx)");
    
     
  • Maarten Brock
    Maarten Brock
    2011-09-14

    The bugs are fixed in SDCC revision #6852. The preprocessor can handle macros inside other macros now. And instead of '__endasm; __asm' the preprocessor now inserts '‡' (0x87) for line continuations inside an __asm / __endasm block. The code generator replaces those '‡' characters by newlines.

     
  • b-s-a
    b-s-a
    2012-03-13

    As you see asm in gcc permits passing values to and from assembler block:

    asm ("movl %1, %%eax; 
                  movl %%eax, %0;"
                 :"=r"(b)        /* output */
                 :"r"(a)         /* input */
                 :"%eax"         /* clobbered register */
                 ); 
    asm ("cld\n\t"
                 "rep\n\t"
                 "stosl"
                 : /* no output registers */
                 : "c" (count), "a" (fill_value), "D" (dest)
                 : "%ecx", "%edi" 
                 );
    

    This is VERY VERY required feature for this compiler. Because many operations can be done inline. For example memcpy for z80:

    __asm("ldir\n\t"
                 : /* no output registers */
                 : "hl" (src), "de" (dst), "bc" (size)
                 : "hl", "de", "bc");
    

    I suggest gcc-like syntax (with classic assembler - not AT&T):
    keyword:
       asm("<STRING>")
       asm("<STRING>" : "=<REG>"(<var>)  : "<REG>"(<var>)  : "<REG>" )

    where <STRING> - assembler text (can be empty)
    <REG> can be:
       "r" - any 8 bit register
       "rr" - any 16 bit register (or register pair)
       "<register name>" - name of standard for corresponding CPU register (for example: A, HL, R0…)
       "<register1 name>,<register2 name>" - for values greater than one register/pair can hold (for example: "d,e,h,l"(var) - will load to de:hl value of 32-bit variable var; d - MSB, l - LSB)

    Related issues:
    please add asm() pseudo function as alternate
    Register parameter passing
    PIC16: Inline assembly: accesssing of function arguments
    A new "class" of functions