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

ApacheKid

Joined Jan 12, 2015
1,609
@nsaspook,

I made a discovery today, dammit. Look at this code snippet:

C:
    //Send Data & checksum

    UART2_dataByte(
       -( 
            UART2_dataWord(vtemp)
          + UART2_dataWord(vbias)
          + UART2_dataWord(vduty)
          + UART2_dataWord(vbatt)
          + UART2_dataWord(PID_getTarget())
          + UART2_dataByte(PID_getLock())
          + UART2_dataByte(getGainIndex())
        )
    );
It took me the day today to realize that the functions (as part of a mathematical expression) are not necessarily executed in the order as written.

My checksum was calculated properly, but the datastream was all mixed up.

I should have realized this as I wrote it, but my subconscious fully expected the code to be executed as written.
I agree with you, in C# they had the good sense to enforce an evaluation order that is left to right. The C approach is inexcusable:

The compiler will evaluate them in any order, and may choose another order when the same expression is evaluated again.
That's about as unhelpful as one can be as a language designer and is almost guaranteed to generate bugs. They didn't even let you use some keyword (like say ordered) to force an evaluation order either, at least that would be there for you when you needed it; and people wanted to write an operating system with this insanity? Jeez.

It's completely safe of course when all the operands are side-effect free, but that too is a rare thing in a language like C. Far too much is left either undefined or to the whim of the implementor with C, sloppy, sloppy, sloppy.
 

nsaspook

Joined Aug 27, 2009
13,265
Simple, clean, and direct?

As a reminder, I'm writing in C, not .asm.

Everything is obfuscated in C. Nothing is clean, simple, or direct.
Says you but not me. My mental programming slate is not (I'm guessing from your comments) , programming task, mentally formalize task into ASM then translate to C. I don't have an intermediate step when I program in C (something I've been doing for a large chunk of my life).
 
Last edited:

nsaspook

Joined Aug 27, 2009
13,265
It's the only way I can get half the performance I expect by writing directly in .asm.

Someone on AAC long ago called this "C--".
True but what's possible with C are things like the Linux kernel and most of the basic libs of other programming languages so there is no intrinsic performance limit in using C. It's up to the programmer and that programmers skills in creating a task model that is optimized for C. What you experience is completely expected and eventually shall pass, my C programming grasshopper.
C on 8-bit controllers is a special case because the default expected standard for C is at least a 16-bit processor with classic Von Neumann architecture. So lots of things like integer promotion and mixed const/heap operations will kill performance unless you know how to motivate the compiler with your code to optimize critical operations..
 
Last edited:

ApacheKid

Joined Jan 12, 2015
1,609
Transcript from the first design meeting for the groundbreaking new computer language named 'Chaos', abbreviated to simply 'C'.

K - "Lets design a programming language where the term 'function' means the result isn't always a function of it's arguments".

R - "You mean they aren't really functions?"

K - "C'mon, don't be picky, this is the brave new world of 1970s computers. Now, let's also allow people to compose function as operands joined with operators, but allow the operands (which could be other 'functions') to be evaluated in an unpredictable order, should allow us write some really clever stuff".

R - "Well yeah, but doesn't that mean the same code could give different answers depending on the code used to compile the code?"

K - "Are you serious? so picky! we're designing a new kind of language here, one that rejects everything we've learned since the 1940s and embraces the brave new world, the counterculture revolution, you gonna be a stick in the mud? a square?"

And so began slow descent into chaos, where computers were everywhere but nothing really worked, everything was chaos.
 

ApacheKid

Joined Jan 12, 2015
1,609
True but what's possible with C are things like the Linux kernel and most of the basic libs of other programming languages so there is no intrinsic performance limit in using C. It's up to the programmer and that programmers skills in creating a task model that is optimized for C. What you experience is completely expected and eventually shall pass, my C programming grasshopper.
What characteristics of Linux are directly attributable to it being written in C? There's nothing in Linux, Unix, MacOS, Mach, Solaris etc, that is there because C was used to write them, nothing. Everything in these systems was invented prior to C being created. Processes, threads, stacks, virtual memory, device drivers, shared/mapped memory, loaders, linkers, locks, schedulers, exceptions (which C never even bothered with) all of these and more predate C.
 

nsaspook

Joined Aug 27, 2009
13,265
What characteristics of Linux are directly attributable to it being written in C? There's nothing in Linux, Unix, MacOS, Mach, Solaris etc, that is there because C was used to write them, nothing. Everything in these systems was invented prior to C being created. Processes, threads, stacks, virtual memory, device drivers, shared/mapped memory, loaders, linkers, locks, schedulers, exceptions (which C never even bothered with) all of these and more predate C.
You're asking a rhetorical question based on your personal biases. Answers doesn't matter as it's just a diatribe but what the hell.

The main characteristic is that it was written it C because a C compiler was available and C was up to the job. The greatest possible language is useless if the tools don't exist on the needed platform.
 

ApacheKid

Joined Jan 12, 2015
1,609
You're asking a rhetorical question based on your personal biases. Answers doesn't matter as it's just a diatribe but what the hell.

The main characteristic is that it was written it C because a C compiler was available and C was up to the job. The greatest possible language is useless if the tools don't exist on the needed platform.
Right, but you seemed to be implying that Linux was in some way, unusual, special, because it was written in C, perhaps I misconstrued. Even a cursory search will reveal though that Linux is a mess, a mishmash of incoherent, poorly "integrated" pieces, a system that was never designed in the sense we - engineers - understand and use that term. Why we'd entrust such a thing in critical applications I can't imagine, I doubt one could ship electronic medical equipment that relied on Linux, but who knows.

So although that is not because it was coded in C, it does nevertheless serve as a poor example for advocating the salient features of C.

I also wouldn't say I was personally biased against C, I've used it extensively in my own career and developed some very large systems software components in C, I've done lots with the language. But that doesn't mean I'll defend the indefensible and excuse the inexcusable, that is more likely to demonstrate bias, in my opinion.
 
Last edited:

nsaspook

Joined Aug 27, 2009
13,265
@nsaspook, I find it a bit perplexing that C has never known how many elements are encompassed by a type enum.

This seems like an elementary language feature, like sizeof().
Easy solution that I use is to include a LAST type identifier in the elements as the last item list for the number of elements.

C:
    enum iammeter_phase {
        PHASE_A,
        PHASE_B,
        PHASE_C,
        PHASE_LAST,
    };

    enum iammeter_id {
        IA_VOLTAGE,
        IA_CURRENT,
        IA_POWER,
        IA_IMPORT,
        IA_EXPORT,
        IA_FREQ,
        IA_PF,
        IA_LAST,
    };

    enum mqtt_vars {
        V_FCCM,
        V_FBEKW,
        V_FRUNT,
        V_FBAMPS,
        V_FBV,
        V_FLO,
        V_FSO,
        V_FACE,
        V_BEN,
        V_PWA,
        V_PAMPS,
        V_PVOLTS,
        V_FLAST,
        V_HDCSW,
        V_HACSW,
        V_HSHUT,
        V_HMODE,
        V_HCON0,
        V_HCON1,
        V_HCON2,
        V_HCON3,
        V_HCON4,
        V_HCON5,
        V_HCON6,
        V_HCON7,
        // add other data ranges here
        V_DVPV,
        V_DPPV,
        V_DPBAT,
        V_DVBAT,
        V_DCMPPT,
        V_DPMPPT,
        V_DGTI,
        V_DLAST,
    };

    enum sane_vars {
        S_FCCM,
        S_FBEKW,
        S_FRUNT,
        S_FBAMPS,
        S_FBV,
        S_FLO,
        S_FSO,
        S_FACE,
        S_BEN,
        S_PWA,
        S_PAMPS,
        S_PVOLTS,
        S_FLAST,
        S_HDCSW,
        S_HACSW,
        S_HSHUT,
        S_HMODE,
        // add other data ranges here
        S_DVPV,
        S_DPPV,
        S_DPBAT,
        S_DVBAT,
        S_DCMPPT,
        S_DGTI,
        S_DLAST,
    };

#define MAX_IM_VAR  IA_LAST*PHASE_LAST

#define L1_P    IA_POWER
#define L2_P    L1_P+IA_LAST
#define L3_P    L2_P+IA_LAST
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
Easy solution that I use is to include a LAST type identifier in the elements as the last item list for the number of elements.

C:
    enum iammeter_phase {
        PHASE_A,
        PHASE_B,
        PHASE_C,
        PHASE_LAST,
    };

    enum iammeter_id {
        IA_VOLTAGE,
        IA_CURRENT,
        IA_POWER,
        IA_IMPORT,
        IA_EXPORT,
        IA_FREQ,
        IA_PF,
        IA_LAST,
    };

    enum mqtt_vars {
        V_FCCM,
        V_FBEKW,
        V_FRUNT,
        V_FBAMPS,
        V_FBV,
        V_FLO,
        V_FSO,
        V_FACE,
        V_BEN,
        V_PWA,
        V_PAMPS,
        V_PVOLTS,
        V_FLAST,
        V_HDCSW,
        V_HACSW,
        V_HSHUT,
        V_HMODE,
        V_HCON0,
        V_HCON1,
        V_HCON2,
        V_HCON3,
        V_HCON4,
        V_HCON5,
        V_HCON6,
        V_HCON7,
        // add other data ranges here
        V_DVPV,
        V_DPPV,
        V_DPBAT,
        V_DVBAT,
        V_DCMPPT,
        V_DPMPPT,
        V_DGTI,
        V_DLAST,
    };

    enum sane_vars {
        S_FCCM,
        S_FBEKW,
        S_FRUNT,
        S_FBAMPS,
        S_FBV,
        S_FLO,
        S_FSO,
        S_FACE,
        S_BEN,
        S_PWA,
        S_PAMPS,
        S_PVOLTS,
        S_FLAST,
        S_HDCSW,
        S_HACSW,
        S_HSHUT,
        S_HMODE,
        // add other data ranges here
        S_DVPV,
        S_DPPV,
        S_DPBAT,
        S_DVBAT,
        S_DCMPPT,
        S_DGTI,
        S_DLAST,
    };

#define MAX_IM_VAR  IA_LAST*PHASE_LAST

#define L1_P    IA_POWER
#define L2_P    L1_P+IA_LAST
#define L3_P    L2_P+IA_LAST
But then an n element enum really has n+1 elements.

And some compilers (i.e xc8) will throw a warning* if you don't reference all the elements of an enum in a switch statement.

*This is a warning I don't want to suppress, as it ensures my state machine functions have full coverage.
 

WBahn

Joined Mar 31, 2012
30,048
But then an n element enum really has n+1 elements.

And some compilers (i.e xc8) will throw a warning* if you don't reference all the elements of an enum in a switch statement.

*This is a warning I don't want to suppress, as it ensures my state machine functions have full coverage.
Presumably, you are already dealing with the possibility of getting a value outside the range of valid enumerations, likely by trapping with with the default case. If so, you could just have the LAST case fall through into the default case.

Admittedly a kludge, but I would imagine something that is reasonably clean and systematic could be devised.

But there is a bigger problem with this approach, which is that it relies on the enumeration involved being numbered consecutively from 0. But that is commonly not the case in which the value associated with an enumeration label is meaningful and explicitly assigned. In that case, there is no automatic way to have the value of the LAST label be associated with the number of labels in the enumeration and, even if there were (or even if it is hand-coded), that value might already be assigned to another label in the enumeration.

Of course, all that means is that this approach has limitations, but that's true of most things. So you use it for the things it works for and you don't use it in situations in which it doesn't work as needed.
 

nsaspook

Joined Aug 27, 2009
13,265
But then an n element enum really has n+1 elements.

And some compilers (i.e xc8) will throw a warning* if you don't reference all the elements of an enum in a switch statement.

*This is a warning I don't want to suppress, as it ensures my state machine functions have full coverage.
That's what default is for. Switch is just a structured goto (implemented in several ways), so it has to know where to GOTO.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
That's what default is for.
Obviously.

But I use the warning as thus:

Suppose I have 20 different functions that behave differently depending on the current state. I add a new state to the enum, and need to add the behavior for that state to the 20 functions as an additional case to a switch statement.

A quick compile will warn me if I missed updating one or more functions (happened twice today!).

The alternative is multiple debug cycles trying to figure out where I missed a case, and ensuring I exercise all 20 functions in all possible states.
 

nsaspook

Joined Aug 27, 2009
13,265
Presumably, you are already dealing with the possibility of getting a value outside the range of valid enumerations, likely by trapping with with the default case. If so, you could just have the LAST case fall through into the default case.

Admittedly a kludge, but I would imagine something that is reasonably clean and systematic could be devised.

But there is a bigger problem with this approach, which is that it relies on the enumeration involved being numbered consecutively from 0. But that is commonly not the case in which the value associated with an enumeration label is meaningful and explicitly assigned. In that case, there is no automatic way to have the value of the LAST label be associated with the number of labels in the enumeration and, even if there were (or even if it is hand-coded), that value might already be assigned to another label in the enumeration.

Of course, all that means is that this approach has limitations, but that's true of most things. So you use it for the things it works for and you don't use it in situations in which it doesn't work as needed.
It's kludge that as you say, works with limitations. In C the initial enumerator always has the value zero and the value of each subsequent enumerator is one greater than the previous enumerator.
 

nsaspook

Joined Aug 27, 2009
13,265
Obviously.

But I use the warning as thus:

Suppose I have 20 different functions that behave differently depending on the current state. I add a new state to the enum, and need to add the behavior for that state to the 20 functions as an additional case to a switch statement.

A quick compile will warn me if I missed updating one or more functions (happened twice today!).

The alternative is multiple debug cycles trying to figure out where I missed a case, and ensuring I exercise all 20 functions in all possible states.
That's your software design structured programming choice. IMO, It has little to do with using enums as it's just a stupid integer identifier.
C:
enum APP_TIMERS {
    TMR_INTERNAL = 0, //Used in timers.c - do not remove or use elsewhere
    TMR_MBMASTER,
    TMR_MBTEST,
    TMR_SPIN,
    TMR_DERE,
    //
    //(Add timers here as needed)
    //
    TMR_COUNT //number of timers - always keep at end of enum!
};

/*
* runs in timer #4 interrupt from FM_io(void)
*/
void timer_ms_tick(const uint32_t status, const uintptr_t context)
{
    //Decrement each software timer
    for (uint16_t i = 0; i < TMR_COUNT; i++) {
        if (tickCount[i] != 0) {
            tickCount[i]--;
        }
    }
    /*
     * check for button presses
     */
    button_press_check();
}
 

Thread Starter

joeyd999

Joined Jun 6, 2011
5,283
It has little to do with using enums as it's just a stupid integer identifier.
It has everything to do with enums. It tells the compiler the range of accepted values a variable has. This give you some power to avoid some kinds of programming errors.

An integer doesn't do this.
 
Top