I used a naked "8-pin Arduino" in my Vetinari clock, because that was the quick way to get an undemanding result.
But when making something that required very low power consumption, I simply wrote C to peek/poke the atmega328 registers. That avoided me having to work out how Arduino libraries might be frustrating me.That's exactly why ATmega32u4 is one of my favourite 8-bitters. It has native full-speed USB (12 Mbit/s, can do 1 Mbyte/sec even over USB Serial), but still is completely programmable without any HAL or similar, on bare metal, using nothing but avr-gcc (and avr-libc, if you really want), in (freestanding) C or C++.I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.
An extremely common pattern for functions is to return a value or error. Obviously, with scalar return types, when you return a single scalar, some values must be reserved for error codes. (That can get messy, like it once did in the Linux kernel, where certain filesystem drivers could not handle reads over 231 bytes (due to widespread use of int), and returned negative values. Now, the Linux kernel limits all single read() and write() syscalls to 231 less one page automagically.) Having a separate value and status returns is sometimes more robust, even if the status return is just a single "OK"/"Error" flag.
However, SysV ABI on x86-64 (LP64, so ints are 32-bit, long and pointers are 64-bit) as used on Linux, macOS, FreeBSD, and others, already supports this (by using rax and rdx registers for 128-bit non-FP return values), so code like
typedef struct { long x, y } ipair;
lpair somefunc(long x, long y) { return (lpair){ .x = x, .y = y }; }
compiles to
somefunc:
mov rax, rdi
mov rdx, rsi
ret
because rdi and rsi are the first two (non-FP) parameters, and the pair of return values are returned in rax and rdx.
This suffices for my needs, although it is slightly odd-looking at first, wrapping return values into structs. It would be nicer to have syntactic sugar for this and not require struct use, but this works too.
Because this is architecture ABI, any compiler for this architecture can use this. Other ABIs, however, need to do annoying "emulation" by e.g. passing a pointer to the additional return values, similar to how larger (struct-type) result values are passed. A compiler can obviously use its own function call ABI internally, but it does have to have some kind of foreign function interface* for bindings to binary libraries (if used on hosted environments/full OSes, and not just bare metal embedded). It might be useful to use a rarely used register for the status/error return, so that it is not so often clobbered by ordinary code.
Simply put, I wasn't hankering so much for syntactic sugar, but for more sensible ABIs like SysV on x86-64. Brucehoult put me straight on this, albeit indirectly.
* Part of the reason I like using Python for graphical user interfaces is that it has a very nice interface to calling native code, without needing any native code support; using pure Python code for the interface. Certain libraries, like glib's gobject introspection for C, provide introspection information (.gi or gir files) that means full interfaces can be constructed without any bespoke code (machine or Python), using the introspection information only. It also means that when the library and corresponding introspection file is updated, the new features are immediately available in Python also. Very useful.
Because native machine code is dynamically loaded for access from Python, this also provides a perfect mechanism for license separation. You can keep the user interface open source, modifiable by end users, but keep all Sekret Sauce proprietary code in your binary-only dynamic library. In my experience, this provides the optimal balance between user access and proprietary requirements when going full open source isn't viable, e.g. for commercial reasons.
if (TryGetRemoteSystemStatus(system_id, var out status))
{
// do stuff.
}
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.How did you establish that? what evidence supports this claim? Here's a post by an engineer that proves you wrong, I quote:
I note that one of the desires there was "give easy access to the flags". I pointed out that many ISAs don't even *have* flags.Yep. In particular, you helped me realize I did not actually desire access to flags, just multiple function result values, to fulfill my needs.Yes. Multiple return values are very desirable. Returning structures can substitute, but it's notationally annoying.
Most modern ABIs allow returning two word-size or one double sized value. With register based ABIs, usually in the same registers as used for the first two arguments. x86_64 is a weird exception with the first argument passed in RDI but the result in RAX.
But why can't you pass the same number of results as arguments? Function return is semantically no different to function call -- it is "calling the continuation". It would clearly be very very easy to enable this in modern ABIs that pass up to 6 or 8 arguments in registers -- it's simply relaxing a restriction. Figuring out where to put return values that overflow into RAM is harder. If the caller knows how many results are to be returned then it can allocate stack space for max(arguments, results). Support an unbounded number of return values would be a lot harder. But it's also hard to see a reason to support it. VARARGS seems like mostly a hack to support printf and friends.
So, what specifically did you want to say about language grammar, extensibility, features that could be helpful to MCU developers?There is very little (if anything) about language grammar that could be useful to MCU developers.How did you establish that? what evidence supports this claim? Here's a post by an engineer that proves you wrong, I quote:Ehh, what?
What has that post by that engineer have to do with language grammar? They're talking about conditional builds, which is solved by any toolchain that generates linkable object files by selecting which object files to compile and include in the final image.
ELF object files support per-symbol sections, regardless of the language you use, which allows the linker to select which symbols (usually functions) to include based on which symbols may be referenced. It means that only features you actually use are included in the linked target, and this is heavily used in current embedded toolchains, which basically all use ELF object files. You do not even have to deselect the features you do not need; all you need to do is make sure all you might need is available (via whatever include or import mechanism your language uses), and use the ones you want. Only the features you use, and the features they refer to, will get included in the final linked object.
The way we established "language grammar isn't important to embedded developers" is by being, and working with embedded developers, who use whatever tools fit the task at hand best. Grammar is just flavour. I've never heard of any successful developer choosing their programming language or environment based on the grammar.
I have seen many who did choose their programming language based on grammar or because it was the only language they could use, fail. I've talked with, and continuously talk to, and occasionally help, many developers, both in embedded and application development. Grammar has never been more than a flavour on top, as in "I find it ugly" or "it looks neat"; never at the core of any discussion related to solving a problem.
I'm not sure if I have talked shop in such details with the commonly suggested 1000 persons sufficient for a random opinion poll, and the selection definitely isn't random, but it is a significant number, well over a hundred at minimum. I also do not shy away from antagonistic discussions, so I often disagree with others –– often to find out the underlying reasons for their opinions, because opinions themselves aren't useful, but the reasons underlying the opinions are. (For one, opinions cannot be compared in any rational manner, but the reasons for those opinions can usually be rationally examined and compared.)
You can pass and return structures in C, which makes your life much easier. Yes the downside is that you need 1/ to define the structure first and 2/ to access its members. Sure a bit more syntax overhead. Interesting to note that very few developers ever use that feature.
I have mixed "feelings" about returning multiple values though. A few reasons for this, but one of them being the "implicit" order. Note that this is the exact same issue with function parameters. Ada fixes it by using parameter designators, although those are optional. But at least function parameters have an identifier. Many languages handle returning multiple values using some kind of tuples - so no identifier per se associated with each value returned inside the tuple, only some guaranteed order.
So please don't present personal anecdotal perceptions as being anything more than that, this is why you'll sometimes see "IMHO" inside some of my posts.
concentrate on my questions.
When I last looked at the problem, c1986, it had to be portable across machines, the language was much simpler and any given compiler targeted, at best, a small subset of machines.
My comment was based on erroneously ignoring the changes.since then. Mea culpa.
I had implemented a 32 bit floating point package on a 6800 a decade earlier, in a medium level language. Without checking, I would have used the carry flag, which doesn't exist in C
So please don't present personal anecdotal perceptions as being anything more than that, this is why you'll sometimes see "IMHO" inside some of my posts.
Ah, I see. When you post someones personal anecdotal perceptions, they are 'proof'.
When I describe my own experiences discussing these technical details with well over a hundred other embedded developers, they're just 'perceptions', and not indicative of anything.
You, sir, are full of shit.
I'm out. I'm not ready to try to help another electrodacus see the reality from their own preconceptions, sorry.
It would have been convenient in that case, but different processors have very different semantics for their carry flag.
For all I know there may be processors without a carry flag; I no longer bother to keep up with processor ISAs.
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
For all I know there may be processors without a carry flag; I no longer bother to keep up with processor ISAs.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
If Alpha had crossed my mind, I would have presumed it didn't, since that went to all extremes to excise everything they could. (And, IIRC, excised byte addressing - oops).
I'm not sure what the other currently interesting architecture (the Mill) does.
MIPS
DEC Alpha
Intel IA64
RISC-V
Probably others I can't think of right now.
also, subtract with borrow (68k) vs subtract with carry (ppc)
At least the following use subtract with carry: IBM System/360, 6502, PIC, MSP430, ARM, PowerPC
At least the following use subtract with carry: IBM System/360, 6502, PIC, MSP430, ARM, PowerPC
Don't know about the others but although the 6502 instruction is called SBC (subtract with carry) my R6500 programming manual states it is with borrow.
You have to set the carry before the first subtract to indicate no borrow.
If Alpha had crossed my mind, I would have presumed it didn't, since that went to all extremes to excise everything they could. (And, IIRC, excised byte addressing - oops).
Alpha always had byte addressing. What it doesn't have, for the first half dozen models (until 199's "EV56" 21164A), was byte (and 16 bit) load and store instructions. It wasn't a raw speed thing -- Alpha was often the fastest CPU in the world during that time, including on software doing a lot of byte operations.
The problem was multiprocessor systems and software that assumes byte accesses are atomic.
QuoteI'm not sure what the other currently interesting architecture (the Mill) does.
No globals flags, of course :-) Each value on the belt has its own metadata.
In http://millcomputing.com/topic/metadata/#post-419 Ivan wrote:
"It would certainly be possible to have a carry metabit in each byte, and we considered it. After all, there is already a NaR bit in each byte. However, we could not find enough usage for such a design to justify the cost."
That was 2014. Things could have changed. I don't know.
I make the following bold claim: No clean sheet ISA designed for high performance since 1990 has had a carry flag, or global flags register.
Note 1) IBM RS/6000->PowerPC->POWER is exactly 1990. It has 8 flags registers.
Note 2) ARM Aarch64 is not a clean sheet design. While it makes quite a lot of changes from Aarch32, it needs to share an execution pipeline with 32 bit code, with both running optimally, at least for the first 10 years or so.
Around the same time I was skeptical about the approach of the HP PA-RISC successor,ItanicItanium. At that time any tiny change to the implementation required someone to re-hand-optimise inner loops; the compiler was supposed to solve that issue, but never quite managed it. In addition, just as CPU power consumption was becoming the limiting factor, the Itanic strategy was to waste power doing lots of speculative execution.
The other strategy exhibiting that phenomenon was the TI9900's registers being in memory. Great for fast context changes, but somewhat limiting otherwise.
concentrate on my questions..
We are not here at your beck and call...
Now you need to read (not skim) the points, and do the work...
Your position is pessimistic, discouraging, not open minded, this is nothing to do with your undoubted credentials either, it is you, your hostile tone. This is just a discussion and you're frothing and fuming.
The only frothing is coming at me, not from me.
I see others I respect have now reached and expressed the same conclusion as I did. To their credit, they perhaps gave the benefit of the doubt for longer, though not forever.
That's exactly why ATmega32u4 is one of my favourite 8-bitters. It has native full-speed USB (12 Mbit/s, can do 1 Mbyte/sec even over USB Serial), but still is completely programmable without any HAL or similar, on bare metal, using nothing but avr-gcc (and avr-libc, if you really want), in (freestanding) C or C++.
uint8_t buffer[] = {0xac, 0xdc, 0xab, 0xba, 0xcd};
NRF_UARTE0->TXD.PTR = (uint32_t)buffer;
NRF_UARTE0->TXD.MAXCNT = sizeof buffer;
NRF_UARTE0->TASKS_STARTTX = 1;
while(!NRF_UARTE0->EVENTS_ENDTX)
;
I think the unimportance of details in grammar can be demonstrated with a simple example:
Consider the achievements of human kind. We have built pyramids, airplanes, went to the Moon and whatnot. All of this is possible thanks to human being able to communicate with others, with a natural language.
Yet, such great achievements have been made in countless of different cultures, with different languages, with very different grammars and syntax.
It's the process itself, behind the grammar and syntax, which dictates the result. A surgeon can discuss with their colleagues in English, Chinese or Finnish just fine, and as long as they know the language, they absolutely do not care, it simply does not matter to them.
Same can be said about programming languages, for example C and Ada have very different looking grammar and syntax but if you learn both, it's confusing only in the very beginning. You learn the grammar and syntax quickly.
That's why it's more important to start from what the language can do and how, then the grammar will follow.
So of course we can discuss what the language can do, go ahead I've been asking for some nine pages now! For example I recently said that I think the language should support runtime access to metadata like string capacity/lengths, arrays dimensions and bounds and so on. The C language is - IMHO - rather poor in that area, so lets address that.
#define ARRAY_LEN
unsigned char array[ARRAY_LEN] = {1, 2, 3, 4};
for(int i=0; i<ARRAY_LEN; i++)
...
uint8_t array[] = {1, 2, 3, 4}; // automagic array length!
for(int i=0; i<NUM_ELEM(array); i++) // despite being automagic, length is still known, at compile time! Googling the NUM_ELEM helper macro is left as an excercise for reader. Add to utils.h or something
...