XC8: .asm to C ( i.e. Lowering One's Expectations)

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
These are the addresses the four variables are being assigned when the error is thrown:

WS_paint@bitcnt 00DD
WS_paint@red 00DC
WS_paint@green 00DB
WS_paint@blue 0178

So, blue is in a different bank than the other three in this build.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
To prevent bank splitting, I believe I can group the vars in a struct:

C:
typedef struct
{
   uint8_t bitcnt;
   uint8_t red;
   uint8_t green;
   uint8_t blue;
   
} ws_ledasm_t;

ws_ledasm_t regs;
WS_paint@regs 0148

Then I believe I can reference everything via an offset:

bitcnt = WS_paint@regs
red = WS_paint@regs + 1
green = WS_paint@regs + 2
blue = WS_paint@regs + 3

Working on it...
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
Yes, this is 1/2 the solution.

The other half is that I need to use BANKMASK in each line that accesses the variables, since the new structure is located in bank 1 instead of the original bank 0, and xc8 seems to throw that error whenever working outside bank 0 without the BANKMASK:

C:
typedef struct
{
   uint8_t bitcnt;
   uint8_t green;
   uint8_t red;
   uint8_t blue;

} ws_ledasm_t;

void WS_paint(void)
{
    uint8_t lednum;

    static ws_ledasm_t regs;

    if (!LEDPOWER)
        return;

    for (lednum=0; lednum < NUM_TRICOLOR_LEDS; lednum++)
    {
        regs.red    = ws_ledbar[lednum].color.red;
        regs.green  = ws_ledbar[lednum].color.green;
        regs.blue   = ws_ledbar[lednum].color.blue;

        regs.bitcnt = 24;
    
        asm("BANKSEL    WS_paint@regs");
        asm("dllp1:");
        asm("bcf"  ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");   //  **Only need to disable ints for high of pulse
        asm("bsf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start high  --   0.0ns   (1) / 625.0ns
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                              //shift blue  --  62.5ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+2))",1,1");                              //shift red   -- 125.0ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+1))",1,1");                              //shift green -- 187.5ns   (1)
        asm("bc         llbith");                                                           //high long   -- 250.0ns   (1)
        asm("llbitl:");
        asm("bcf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start low   -- 312.5ns   (1) / 625.0ns / 0.0
        asm("bra        llwait0");                                                          //            --  62.5ns   (0)
        asm("llbith:");
        asm("bra        $+2");                                                              //            -- 375.0ns   (1)
        asm("bra        llbitl");                                                           //            -- 500.0ns   (1)
        asm("llwait0:");
        asm("bsf" ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");    //  **can allow ints immediately after low 
        asm("btfss" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 187.5ns   (0)
        asm("bcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 250.0ns   (0)
        asm("btfsc" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 312.5ns   (0)
        asm("bsf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 375.0ns   (0)
        asm("decfsz" ___mkstr(BANKMASK(WS_paint@regs))",1,1");                              //              -- 437.5ns   (0)
        asm("bra        dllp1");                                                            //            -- 500.0ns   (0)
    }
}
I also had to change the order of the struct, as I forgot the WS2813 data goes out GRB, not RGB (because they just had to be different, didn't they?).

The final test will be in the morning (I don't have my hardware here at home).

The resulting inline .asm is just downright nasty. Completely unreadable for anyone who didn't write it to begin with.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
@nsaspook, is the order (and contiguousness) of the bytes in the structure guaranteed by the C standard? If not, I'll probably have to pack the bytes in a uint32_t instead (for portability....ha!).
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
@nsaspook, is the order (and contiguousness) of the bytes in the structure guaranteed by the C standard? If not, I'll probably have to pack the bytes in a uint32_t instead (for portability....ha!).
I found this:

http://www.catb.org/esr/structure-packing/

I assume this is telling me that, since they're all single bytes, they will be contiguous and in order, regardless of platform. Correct?
 

WBahn

Joined Mar 31, 2012
32,925
@nsaspook, is the order (and contiguousness) of the bytes in the structure guaranteed by the C standard? If not, I'll probably have to pack the bytes in a uint32_t instead (for portability....ha!).
Yes and no.

The order is guaranteed, but not the contiguousness. The compiler may place padding bytes between structure members in order to align them onto preferred boundaries, and it might add padding to the end in order to align each element in an array of structures onto those boundaries.

But padding is not required and your particular compiler is free to do it or not, possibly giving you some control over it.
 

nsaspook

Joined Aug 27, 2009
16,341
I found this:

http://www.catb.org/esr/structure-packing/

I assume this is telling me that, since they're all single bytes, they will be contiguous and in order, regardless of platform. Correct?
As per @WBahn contiguous is not a given but is likely as a default (for 8-bit hardware, native and likely most efficient) on hardware without byte alignment restrictions but shouldn't be trusted, as even if it's possible, the compiler might default to a alignment speed optimization resulting in padding.
https://developerhelp.microchip.com.../pic-mcu-specific-features/structure-padding/

1 struct {
2 unsigned char a;
3 long c;
4 unsigned char b;
5 long d;
6 } myStruct;
For comparison, even though the MPLAB® XC8, XC16, and XC32 C compilers all use a 1-byte-wide char and a 4-byte-wide long type, the size of the last structure shown is 10 bytes when compiling with XC8, 12 bytes when compiling with XC16, and 16 bytes when compiling with XC32. The differences are purely a result of padding between the structure members.
 

WBahn

Joined Mar 31, 2012
32,925
I found this:

http://www.catb.org/esr/structure-packing/

I assume this is telling me that, since they're all single bytes, they will be contiguous and in order, regardless of platform. Correct?
Probably, but not guaranteed. All the standard dictates is, "Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type."

The compiler is free to put each byte at a 32-bit boundary (or whatever boundary it deems appropriate).

If you are working with 8-bit MCU's, there will likely be no padding at all.

But notice that the key here is that the alignment is "implementation-defined" -- this means that you should be able to track down in the documentation how your compiler defined it.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
The struct works.

So does this:

C:
void WS_paint(void)
{
    uint8_t lednum;

    static uint32_t regs;
    
    if (!LEDPOWER)
        return;

    for (lednum=0; lednum < NUM_TRICOLOR_LEDS; lednum++)
    {
        *(((uint8_t *)&regs)+0) = 24;
        *(((uint8_t *)&regs)+1) = ws_ledbar[lednum].color.green;
        *(((uint8_t *)&regs)+2) = ws_ledbar[lednum].color.red;
        *(((uint8_t *)&regs)+3) = ws_ledbar[lednum].color.blue;

        asm("BANKSEL    WS_paint@regs");
        asm("dllp1:");
        asm("bcf"  ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");   //  **Only need to disable ints for high of pulse
        asm("bsf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start high  --   0.0ns   (1) / 625.0ns
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                              //shift blue  --  62.5ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+2))",1,1");                              //shift red   -- 125.0ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+1))",1,1");                              //shift green -- 187.5ns   (1)
        asm("bc         llbith");                                                           //high long   -- 250.0ns   (1)
        asm("llbitl:");
        asm("bcf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start low   -- 312.5ns   (1) / 625.0ns / 0.0
        asm("bra        llwait0");                                                          //            --  62.5ns   (0)
        asm("llbith:");
        asm("bra        $+2");                                                              //            -- 375.0ns   (1)
        asm("bra        llbitl");                                                           //            -- 500.0ns   (1)
        asm("llwait0:");
        asm("bsf" ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");    //  **can allow ints immediately after low
        asm("btfss" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 187.5ns   (0)
        asm("bcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 250.0ns   (0)
        asm("btfsc" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 312.5ns   (0)
        asm("bsf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 375.0ns   (0)
        asm("decfsz" ___mkstr(BANKMASK(WS_paint@regs))",1,1");                              //              -- 437.5ns   (0)
        asm("bra        dllp1");                                                            //            -- 500.0ns   (0)
    }
}
I assume, based on the discussion above, this is a better "portable" solution. Right?

(I use the word "portable" loosely...)
 

nsaspook

Joined Aug 27, 2009
16,341
The struct works.

So does this:

C:
void WS_paint(void)
{
    uint8_t lednum;

    static uint32_t regs;
 
    if (!LEDPOWER)
        return;

    for (lednum=0; lednum < NUM_TRICOLOR_LEDS; lednum++)
    {
        *(((uint8_t *)&regs)+0) = 24;
        *(((uint8_t *)&regs)+1) = ws_ledbar[lednum].color.green;
        *(((uint8_t *)&regs)+2) = ws_ledbar[lednum].color.red;
        *(((uint8_t *)&regs)+3) = ws_ledbar[lednum].color.blue;

        asm("BANKSEL    WS_paint@regs");
        asm("dllp1:");
        asm("bcf"  ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");   //  **Only need to disable ints for high of pulse
        asm("bsf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start high  --   0.0ns   (1) / 625.0ns
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                              //shift blue  --  62.5ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+2))",1,1");                              //shift red   -- 125.0ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+1))",1,1");                              //shift green -- 187.5ns   (1)
        asm("bc         llbith");                                                           //high long   -- 250.0ns   (1)
        asm("llbitl:");
        asm("bcf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start low   -- 312.5ns   (1) / 625.0ns / 0.0
        asm("bra        llwait0");                                                          //            --  62.5ns   (0)
        asm("llbith:");
        asm("bra        $+2");                                                              //            -- 375.0ns   (1)
        asm("bra        llbitl");                                                           //            -- 500.0ns   (1)
        asm("llwait0:");
        asm("bsf" ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");    //  **can allow ints immediately after low
        asm("btfss" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 187.5ns   (0)
        asm("bcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 250.0ns   (0)
        asm("btfsc" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 312.5ns   (0)
        asm("bsf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 375.0ns   (0)
        asm("decfsz" ___mkstr(BANKMASK(WS_paint@regs))",1,1");                              //              -- 437.5ns   (0)
        asm("bra        dllp1");                                                            //            -- 500.0ns   (0)
    }
}
I assume, based on the discussion above, this is a better "portable" solution. Right?

(I use the word "portable" loosely...)
1715869850479.png
Better? If it works, it's gold.

Magical Code Maestro
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,334
Wake-up from that dream, it's holding you back.
Holding me back? If I didn't know .asm as well as I do:

1. I would not be able to complete this project with this hardware
2. I would not have been able to solve this immediate problem.

I'd say I am advancing because my dream.
 

ApacheKid

Joined Jan 12, 2015
1,762
Holding me back? If I didn't know .asm as well as I do:

1. I would not be able to complete this project with this hardware
2. I would not have been able to solve this immediate problem.

I'd say I am advancing because my dream.
Speaking as an experienced programming team lead, I can see that you are having productivity issues, the way you are tackling some of this stuff is probably costing in ways you aren't appreciating.
 

ApacheKid

Joined Jan 12, 2015
1,762
The struct works.

So does this:

C:
void WS_paint(void)
{
    uint8_t lednum;

    static uint32_t regs;
 
    if (!LEDPOWER)
        return;

    for (lednum=0; lednum < NUM_TRICOLOR_LEDS; lednum++)
    {
        *(((uint8_t *)&regs)+0) = 24;
        *(((uint8_t *)&regs)+1) = ws_ledbar[lednum].color.green;
        *(((uint8_t *)&regs)+2) = ws_ledbar[lednum].color.red;
        *(((uint8_t *)&regs)+3) = ws_ledbar[lednum].color.blue;

        asm("BANKSEL    WS_paint@regs");
        asm("dllp1:");
        asm("bcf"  ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");   //  **Only need to disable ints for high of pulse
        asm("bsf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start high  --   0.0ns   (1) / 625.0ns
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                              //shift blue  --  62.5ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+2))",1,1");                              //shift red   -- 125.0ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+1))",1,1");                              //shift green -- 187.5ns   (1)
        asm("bc         llbith");                                                           //high long   -- 250.0ns   (1)
        asm("llbitl:");
        asm("bcf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start low   -- 312.5ns   (1) / 625.0ns / 0.0
        asm("bra        llwait0");                                                          //            --  62.5ns   (0)
        asm("llbith:");
        asm("bra        $+2");                                                              //            -- 375.0ns   (1)
        asm("bra        llbitl");                                                           //            -- 500.0ns   (1)
        asm("llwait0:");
        asm("bsf" ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");    //  **can allow ints immediately after low
        asm("btfss" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 187.5ns   (0)
        asm("bcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 250.0ns   (0)
        asm("btfsc" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 312.5ns   (0)
        asm("bsf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 375.0ns   (0)
        asm("decfsz" ___mkstr(BANKMASK(WS_paint@regs))",1,1");                              //              -- 437.5ns   (0)
        asm("bra        dllp1");                                                            //            -- 500.0ns   (0)
    }
}
I assume, based on the discussion above, this is a better "portable" solution. Right?

(I use the word "portable" loosely...)
I'd consider a tiny simplification too: (see "reg_ptr")

C:
void WS_paint(void)
{
    uint8_t lednum;

    static uint32_t regs;
    uint8_t * reg_ptr = &(regs);
  
    if (!LEDPOWER)
        return;

    for (lednum=0; lednum < NUM_TRICOLOR_LEDS; lednum++)
    {
        *(reg_ptr[0]) = 24;
        *(reg_ptr[1]) = ws_ledbar[lednum].color.green;
        *(reg_ptr[2]) = ws_ledbar[lednum].color.red;
        *(reg_ptr[3]) = ws_ledbar[lednum].color.blue;

        asm("BANKSEL    WS_paint@regs");
        asm("dllp1:");
        asm("bcf"  ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");   //  **Only need to disable ints for high of pulse
        asm("bsf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start high  --   0.0ns   (1) / 625.0ns
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                              //shift blue  --  62.5ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+2))",1,1");                              //shift red   -- 125.0ns   (1)
        asm("rlcf" ___mkstr(BANKMASK(WS_paint@regs+1))",1,1");                              //shift green -- 187.5ns   (1)
        asm("bc         llbith");                                                           //high long   -- 250.0ns   (1)
        asm("llbitl:");
        asm("bcf"  ___mkstr(BANKMASK(LATD)) ",5,0");                                        //start low   -- 312.5ns   (1) / 625.0ns / 0.0
        asm("bra        llwait0");                                                          //            --  62.5ns   (0)
        asm("llbith:");
        asm("bra        $+2");                                                              //            -- 375.0ns   (1)
        asm("bra        llbitl");                                                           //            -- 500.0ns   (1)
        asm("llwait0:");
        asm("bsf" ___mkstr(BANKMASK(INTCON0)) "," ___mkstr(_INTCON0_GIE_POSITION) ",0");    //  **can allow ints immediately after low
        asm("btfss" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 187.5ns   (0)
        asm("bcf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 250.0ns   (0)
        asm("btfsc" ___mkstr(BANKMASK(STATUS)) "," ___mkstr(_STATUS_C_POSITION) ",0");      //            -- 312.5ns   (0)
        asm("bsf" ___mkstr(BANKMASK(WS_paint@regs+3))",1,1");                               //            -- 375.0ns   (0)
        asm("decfsz" ___mkstr(BANKMASK(WS_paint@regs))",1,1");                              //              -- 437.5ns   (0)
        asm("bra        dllp1");                                                            //            -- 500.0ns   (0)
    }
}
I'd also be tempted to isolate that assembler code into a dedicated function (that contains ONLY assembler) and refer to that from the loop code, saves anyone having to look at C/asm mixed together. and makes the asm block potentially reusable...it might even get inlined and so there's be no actual call/return stack usage involved...
 

nsaspook

Joined Aug 27, 2009
16,341
Holding me back? If I didn't know .asm as well as I do:

1. I would not be able to complete this project with this hardware
2. I would not have been able to solve this immediate problem.

I'd say I am advancing because my dream.
Sure, if you only want to code in a narrow 8-bit universe.
 
Top