0

I have this code in ThreadCreate():

int tmpPSW = newThID << 3;
__asm
    PUSH A
    PUSH _tmpPSW
__endasm;

This results in:

?ASlink-Warning-Undefined Global '_tmpPSW' referenced by module 'cooperative'

I don't get why. tmpPSW is clearly defined, but sdcc complains. What am I doing wrong here? Is there other way to push C variable in sdcc inline assembly?

Also this is probably relevant. The .asm file generated contains allocation info:

;------------------------------------------------------------
;Allocation info for local variables in function 'ThreadCreate'
;------------------------------------------------------------
;fp                        Allocated to registers 
;newThMask                 Allocated to registers r6 r7 
;newThID                   Allocated to registers r5 
;startSP                   Allocated to registers r3 r4 
;tmp                       Allocated to registers 
;tmpPSW                    Allocated to registers 
;------------------------------------------------------------

Does this mean I run out of registers? If so how should I mitigate this?


EDIT:

Source of ThreadCreate():

// ThreadID is typedef&#039;ed as char
ThreadID ThreadCreate(FunctionPtr fp) {
    if (activeTh == 0b1111)
        return -1;

    // i.e. get rightmost bit 0 in bitmask
    // https://stackoverflow.com/a/42747608/6306190
    int newThMask = ~activeTh & (activeTh + 1);

    activeTh |= newThMask;

    ThreadID newThID = 0;
    while (newThMask >>= 1) { newThID++; }
    int startSP = (newThID ^ (1UL << 2)) << 4;

    int tmp = SP;
    SP = startSP;

    int tmpPSW = newThID << 3;
    __asm
        PUSH DPL          ;; push _fp (argument passed in as DPTR in SDCC)
        PUSH DPH          ;; push _fp
        MOV A, #0
        PUSH A            ;; ACC
        PUSH A            ;; B
        PUSH A            ;; DPL
        PUSH A            ;; DPH
        PUSH _tmpPSW      ;; PSW
    __endasm;

    savedSP[newThID] = SP;
    SP = tmp;
    return newThID;
}

Generated assembly of ThreadCreate():

;------------------------------------------------------------
;Allocation info for local variables in function &#039;ThreadCreate&#039;
;------------------------------------------------------------
;fp                        Allocated to registers 
;newThMask                 Allocated to registers r6 r7 
;newThID                   Allocated to registers r5 
;startSP                   Allocated to registers r3 r4 
;tmp                       Allocated to registers 
;tmpPSW                    Allocated to registers 
;------------------------------------------------------------
;   cooperative.c:104: ThreadID ThreadCreate(FunctionPtr fp) {
;   -----------------------------------------
;    function ThreadCreate
;   -----------------------------------------
_ThreadCreate:
;   cooperative.c:110: if (activeTh == 0b1111)
    mov a,#0x0f
    cjne    a,_activeTh,00121$
    clr a
    cjne    a,(_activeTh + 1),00121$
    sjmp    00122$
00121$:
    sjmp    00102$
00122$:
;   cooperative.c:111: return -1;
    mov dpl,#0xff
    ret
00102$:
;   cooperative.c:119: int newThMask = ~activeTh & (activeTh + 1);
    mov a,_activeTh
    cpl a
    mov r6,a
    mov a,(_activeTh + 1)
    cpl a
    mov r7,a
    mov a,#0x01
    add a,_activeTh
    mov r4,a
    clr a
    addc    a,(_activeTh + 1)
    mov r5,a
    mov a,r4
    anl ar6,a
    mov a,r5
    anl ar7,a
;   cooperative.c:157: activeTh |= newThMask;
    mov a,r6
    orl _activeTh,a
    mov a,r7
    orl (_activeTh + 1),a
;   cooperative.c:160: while (newThMask >>= 1) { newThID++; }
    mov r5,#0x00
00103$:
    mov ar3,r6
    mov a,r7
    mov c,acc.7
    rrc a
    xch a,r3
    rrc a
    xch a,r3
    mov r4,a
    mov ar6,r3
    mov ar7,r4
    mov a,r3
    orl a,r4
    jz  00105$
    inc r5
    sjmp    00103$
00105$:
;   cooperative.c:161: int startSP = (newThID ^ (1UL << 2)) << 4;
    mov ar3,r5
    mov r4,#0x00
    mov r6,#0x00
    xrl ar3,#0x04
    mov a,r4
    swap    a
    anl a,#0xf0
    xch a,r3
    swap    a
    xch a,r3
    xrl a,r3
    xch a,r3
    anl a,#0xf0
    xch a,r3
    xrl a,r3
    mov r4,a
;   cooperative.c:163: int tmp = SP;
    mov r7,_SP
;   cooperative.c:164: SP = startSP;
    mov _SP,r3
;   cooperative.c:176: __endasm;
    PUSH    DPL ;; push _fp (argument passed in as DPTR in 390)
    PUSH    DPH ;; push _fp
    MOV A, #0
    PUSH    A ;; ACC
    PUSH    A ;; B
    PUSH    A ;; DPL
    PUSH    A ;; DPH
    PUSH    _tmpPSW ;; PSW
;   cooperative.c:178: savedSP[newThID] = SP;
    mov a,r5
    add a,r5
    add a,#_savedSP
    mov r1,a
    mov r4,_SP
    mov r6,#0x00
    mov @r1,ar4
    inc r1
    mov @r1,ar6
;   cooperative.c:179: SP = tmp;
    mov _SP,r7
;   cooperative.c:180: return newThID;
    mov dpl,r5
;   cooperative.c:181: }
    ret
the busybee
  • 10,755
  • 3
  • 13
  • 30
johan
  • 1,664
  • 2
  • 14
  • 30
  • From the time I worked with sdcc, it didn't support accessing local variable from inside assembly. You needed to assign to global variable, and then use it in assembly. – KamilCuk Jan 12 '20 at 13:57
  • 1
    What others lines are in the generated assembly file with `tmpPWS`? -- Note that `int` is 16 bit on 8051, you'd rather use `uint8_t`. -- There is no instruction `PUSH A` in 8051. You need to `PUSH ACC`. – the busybee Jan 12 '20 at 15:25
  • @thebusybee [source](https://www.paste.org/102281) and [asm](https://www.paste.org/102282) of `ThreadCreate()`. Thanks for the reminders! I totally forgot those idiosyncrasies. – johan Jan 12 '20 at 16:11
  • Next time, please [edit] your question instead of putting code somewhere else. – the busybee Jan 12 '20 at 16:57

1 Answers1

1

The compiler optimized your variable away, because it was never used from the view of the compiler. Compare the generated assembly with the source to see this.

You might try other options. Because I don't have SDCC installed, I can just suggest:

  • Make the variable volatile.
  • Make the variable static, since one flavor of PUSH works with an address in the internal RAM.
  • Because inline assembly is heavily depending on the surrounding code and the compiler, you could also use newThID in the assembly part, and do the shift there.

Note 1: The generated assembly demonstrates how slow machine code gets if you use int without thinking. Limit your variables to the smallest appropriate data type.

Note 2: Don't make the variable global. static does what you want without exposing the variable globally: Place it in RAM so it can be accessed.

the busybee
  • 10,755
  • 3
  • 13
  • 30
  • Why would you even expect `int tmpPSW` in automatic storage to have an asm symbol name, even if it didn't get optimized away? e.g. a `volatile int` in automatic storage? (Or does 8051 not have a callstack usable for automatic storage, leading to compilers using static memory for C automatic storage in non-recursive / re-entrant functions? I don't use 8051 either). It seems the actual answer you're suggesting is to put it in static storage, e.g. as a `static` local. Or better just do it in asm. Those make sense to me. – Peter Cordes Jan 12 '20 at 20:04
  • Since there is no addressing mode like `SP + offset` (not even any ` + offset` for RAM), commonly automatic variables are in registers or overlaid RAM. Both can be accessed for `PUSH`. -- The stack is really small if the built-in SP is used, only some tens of bytes. Compilers tend to avoid this stack for automatic variables. If you don't add a compiler-specific keyword, functions are commonly not re-entrant. – the busybee Jan 12 '20 at 20:53
  • 1
    Ok, that's what I thought I remembered from [Why do C to Z80 compilers produce poor code?](//retrocomputing.stackexchange.com/q/6095). So really my point is that a local scope `int tmpPSW` basically can't have an asm symbol name of `_tmpPSW` because the same name could be used in multiple functions. Unless the compiler rewrites your inline asm to match... Of course, that reasoning applies to a locally-scoped `static int` as well; two functions could each have `static int tmpPSW;`, or even two scopes within the same function; gcc/clang use the func name as part of asm names for static locals. – Peter Cordes Jan 12 '20 at 20:58
  • Some assemblers are quite smart and use a kind of local scope in each function. I don't know about SDCC, though. – the busybee Jan 13 '20 at 06:42
  • How does that work? For assemblers like NASM, function-scoped labels have special names, prefixed by a `.`. If just `_tmpPSW` worked for accessing a static local, that would imply that you'd need special syntax for accessing a *global* symbol, wouldn't it? Or are you guessing that `_` is the local-label prefix for some 8051 assembler? Highly doubtful since it's a valid part of a C identifier. – Peter Cordes Jan 13 '20 at 06:46
  • Apparently SDCC doesn't have it, as the generated assembly shows. Other assemblers have pseudo mnemonics like `local `. – the busybee Jan 13 '20 at 06:48
  • Ah I see, thanks. Yeah that could have made it work. – Peter Cordes Jan 13 '20 at 06:49