Arduino / compilers on uC

I've heard the kids here opine compilers are so good these days that assembly makes no sense.

There's lots to be said for high-level languages. But I disassembled some Arduino code--here's the output to write a port pin (a built-in function):

00001006 : 1006: 90 e0 ldi r25, 0x00 ; 0 1008: fc 01 movw r30, r24 100a: e3 52 subi r30, 0x23 ; 35 100c: fd 4f sbci r31, 0xFD ; 253 100e: 34 91 lpm r19, Z 1010: fc 01 movw r30, r24 1012: e7 53 subi r30, 0x37 ; 55 1014: fd 4f sbci r31, 0xFD ; 253 1016: 24 91 lpm r18, Z 1018: fc 01 movw r30, r24 101a: eb 54 subi r30, 0x4B ; 75 101c: fd 4f sbci r31, 0xFD ; 253 101e: e4 91 lpm r30, Z 1020: ee 23 and r30, r30 1022: 09 f4 brne .+2 ; 0x1026 1024: 3b c0 rjmp .+118 ; 0x109c 1026: 33 23 and r19, r19 1028: 39 f1 breq .+78 ; 0x1078 102a: 33 30 cpi r19, 0x03 ; 3 102c: 91 f0 breq .+36 ; 0x1052 102e: 38 f4 brcc .+14 ; 0x103e 1030: 31 30 cpi r19, 0x01 ; 1 1032: a9 f0 breq .+42 ; 0x105e 1034: 32 30 cpi r19, 0x02 ; 2 1036: 01 f5 brne .+64 ; 0x1078 1038: 84 b5 in r24, 0x24 ; 36 103a: 8f 7d andi r24, 0xDF ; 223 103c: 12 c0 rjmp .+36 ; 0x1062 103e: 37 30 cpi r19, 0x07 ; 7 1040: 91 f0 breq .+36 ; 0x1066 1042: 38 30 cpi r19, 0x08 ; 8 1044: a1 f0 breq .+40 ; 0x106e 1046: 34 30 cpi r19, 0x04 ; 4 1048: b9 f4 brne .+46 ; 0x1078 104a: 80 91 80 00 lds r24, 0x0080 ; 0x800080 104e: 8f 7d andi r24, 0xDF ; 223 1050: 03 c0 rjmp .+6 ; 0x1058 1052: 80 91 80 00 lds r24, 0x0080 ; 0x800080 1056: 8f 77 andi r24, 0x7F ; 127 1058: 80 93 80 00 sts 0x0080, r24 ; 0x800080 105c: 0d c0 rjmp .+26 ; 0x1078 105e: 84 b5 in r24, 0x24 ; 36 1060: 8f 77 andi r24, 0x7F ; 127 1062: 84 bd out 0x24, r24 ; 36 1064: 09 c0 rjmp .+18 ; 0x1078 1066: 80 91 b0 00 lds r24, 0x00B0 ; 0x8000b0 106a: 8f 77 andi r24, 0x7F ; 127 106c: 03 c0 rjmp .+6 ; 0x1074 106e: 80 91 b0 00 lds r24, 0x00B0 ; 0x8000b0 1072: 8f 7d andi r24, 0xDF ; 223 1074: 80 93 b0 00 sts 0x00B0, r24 ; 0x8000b0 1078: f0 e0 ldi r31, 0x00 ; 0 107a: ee 0f add r30, r30 107c: ff 1f adc r31, r31 107e: e5 55 subi r30, 0x55 ; 85 1080: fd 4f sbci r31, 0xFD ; 253 1082: a5 91 lpm r26, Z+ 1084: b4 91 lpm r27, Z 1086: 8f b7 in r24, 0x3f ; 63 1088: f8 94 cli 108a: ec 91 ld r30, X 108c: 61 11 cpse r22, r1 108e: 03 c0 rjmp .+6 ; 0x1096 1090: 20 95 com r18 1092: 2e 23 and r18, r30 1094: 01 c0 rjmp .+2 ; 0x1098 1096: 2e 2b or r18, r30 1098: 2c 93 st X, r18 109a: 8f bf out 0x3f, r24 ; 63 109c: 08 95 ret

60 lines.

Assembly takes one or two lines, and is proportionally faster.

Meanwhile, I just spent three days tracking down a compiler error-- the compiler insisted (0xff & 1) = 254. It was no fun tracking down, and ultimately took a workaround.

Cute system, horrible documentation. I'm afraid it sets a lot of kids off on the wrong foot, hacking instead of thinking.

YMMV.

Cheers, James Arthur

Reply to
dagmargoodboat
Loading thread data ...

Yes, it's horrid, but the assembler version doesn't do the same thing. The digitalWrite function looks up a pin mapping table in flash in order to create an artificial portability across Arduino versions. Accessing flash is awkward because it's Harvard arch. It's a stupid thing to want to do - it could be done at compile time using C++ inlines and templates - but the compiler does a fairly decent job of it.

I don't believe you. Show us your code and your compiler version. I bet you did (0xFF & ~1).

Clifford Heath.

Reply to
Clifford Heath

O.O

Michael

Reply to
mrdarrett

Den tirsdag den 7. februar 2017 kl. 00.43.55 UTC+1 skrev snipped-for-privacy@yahoo.com:

that is to say the least comparing apples to oranges, the build in function handles all the checks if it is valid pin, possibly controlled with a timer etc. so it work on multiple platforms without you needing to know much about the underlaying HW

you can write directly to PORTD on the AVR if you like

Reply to
Lasse Langwadt Christensen

The built-in functions "digitalWrite" and "digitalRead" are known to be slow. They're designed for ease of use and abstraction, not speed. If one desperately needs speed you do it the "old fashioned way."

"Premature optimization is the root of all evil."

What was the error and workaround? There isn't any "Arduino Compiler", it's a front-end to avr-gcc with some abstraction libraries. Something that low level sounds like it must be a compiler error with gcc.

BTW the numerical representation of using bitwise operators when one of the arguments is a signed integer is "implementation defined" in C/C++.

Better than doing nothing, which if the only option were to write everything in AVR assembly is certainly what they'd be doing...;-)

Reply to
bitrex

Something that low level would have to be an error in gcc's own handling of bitwise operators, which seems...unlikely

Reply to
bitrex

Maybe you are using the wrong language.

formatting link

--

Rick C
Reply to
rickman

It's pretty messy to document, but the gist is that I wanted to toggle a boolean flag with the statement

flg = flg ? false : true;

where 'true' == 1, 'false' == 0, Arduino-defined.

It didn't work--the variable was assigned either 0xfe, or 0xff.

Boolean on the AVR is an unsigned byte, flg was initialized to 0xff, from blank EEPROM (as expected, prior to storing user parameters).

When simple statements didn't work as expected, I added a bunch of print statements to diagnose the problem (below, with output).

You can see that, with 'boolean' type, output lines 6a., 6b., 7a., 7b. do not give the expected results.

If I change 11. w = (U.dat.flg) ? true : false; to 11. w = (U.dat.flg) ? false : true;

and w=0xff, line 13. reports that (w & 0x1) == 254.

The upshot was that the compiler generates different code for a 'boolean' and a 'uint8_t', even though they're both supposed to be unsigned char.

Either type should've been fine for my purposes. Both should've worked. I tried various gimmicks like casts, forcing conversions, and assigning to int or word types to try forcing the compiler to treat values differently, to no avail.

Source: 1. int b,c,i; 2. word w; 3. float v; 4. 5. Serial.print(F(" 1. 'true' = ")); Serial.print(true,HEX); Serial.print(F(" 2. 'false' = ")); Serial.println(false,HEX); 6. Serial.print(F(" 3. 'i = true' =")); Serial.print((i = true),HEX); Serial.print(F(" 4. 'w = false' =")); Serial.println((w = false),HEX); 7. Serial.print(F(" 5. Before toggle, U.dat.flg = ")); Serial.println(U.dat.flg, HEX); 8. 9. i = (int) U.dat.flg;

  1. Serial.print(F(" 6a. i = (int) U.dat.flg =")); Serial.println(i,HEX);
  2. w = (U.dat.flg) ? true : false;
  3. Serial.print(F(" 6b. After assignment by '?' to 'true' : 'false', w = ")); Serial.println(w,HEX);
  4. if (U.dat.flg) {
  5. w = true;
  6. } else {
  7. w = false;
  8. }
  9. Serial.print(F(" 6c. After if-then assignment to 'true' or 'false', w = ")); Serial.println(w,HEX);
  10. Serial.print(F(" 7a. i & 1 = ")); Serial.println((i & 1), HEX);
  11. Serial.print(F(" 7b. w & 1 = ")); Serial.println((w & (word)1), HEX);
  12. Serial.print(F(" 8a. (i & 0x1u) = ")); Serial.println((i & 0x1u), HEX);
  13. Serial.print(F(" 8b. (w & 0x1) = ")); Serial.println((w & 0x1), HEX);
  14. Serial.print(F(" 8c. (0xff & 0x1) = ")); Serial.println((0xff & 0x1), HEX);

=== Output with U.dat.flg declared 'boolean'=== 1. 'true' = 1 2. 'false' = 0 3. 'i = true' =1 4. 'w = false' =0 5. Before toggle, U.dat.flg = FF 6a. i = (int) U.dat.flg =FF

->---> 6b. After 'w = (U.dat.flg) ? true : false', w = FF

Reply to
dagmargoodboat

Yes, understood. But isn't that always the problem? The compiler's always generating for some general case, which the assembly programmer need not do.

It wasn't just the digitalWrite() -- lots of pushes and pops, subroutine calls to subroutine calls, etc. for my regular code.

The CPU's plenty fast enough and there are heaps of memory, so in practice it won't generally matter.

It's just a little distressing to see folks piling abstraction on abstraction on abstraction, with documentation that doesn't give parameter and return value types, 27^3 kinds of 'portable' integers, ready to all fall down.

Cheers, James Arthur

Reply to
dagmargoodboat

"boolean" is not an ISO standard C++ data type - use "bool."

Or include if you want to use "boolean" and it should behave as expected

Reply to
bitrex

See above.

I'm not sure it isn't Arduino's pre-processing--I'm not sure who defined the 'boolean' type. I plowed through the stdint.h and didn't see it.

But understand it's not my goal to be an Arduino expert, nor divine the inner workings of gcc. I'm trying to use it as supplied as a tool to do a small part of a big job, nothing more.

Don't underestimate people. Everyone I know got started writing assembly, or worse, toggling front panel switches.

Cheers, James Arthur

Reply to
dagmargoodboat

Looks like whatever this issue is (I didn't read the entire thread) was noted and fixed in a more recent release:

formatting link

AVR assembly is a lot more obtuse than say, PDP-8 assembly. The ISA was supposedly designed to allow optimal compilation of C code, after all...

Reply to
bitrex

Thanks for trying anyway... though you don't show the generated assembly code, but didn't provide clean enough code for me to do it either.

Hint: use "gcc -S -fverbose-asm" to get the assembly code into .s with the original C code in comments.

But see below...

I suspect that the EEPROM is operating in "write more zeroes" mode. Do you get the same problem where the target is not EEPROM?

Clifford Heath.

Reply to
Clifford Heath

"bool" is what I started off with, gives the same result as "boolean." I switched to "boolean," since that was listed in Arduino's reference page, and, having made it part of their environment, one would hope they've made full provision for it.

formatting link

When that didn't work, I also quit trusting the values of 'true' and 'false', which is why I started specifying numerical values, all to no effect.

This is a lot of abstraction -- it's a byte, nothing more.

Cheers, James Arthur

Reply to
dagmargoodboat

No, that's not the problem. The problem is that digitalWrite takes a *logical* pin number, which is meant to map to the Arduino footprint for a range of different Arduino types, even though those bring a different port pin to that logical pin.

If you had written (not checked, but like):

*((uint8_t*)PORTD) = 0; or *((uint8_t*)PORTD) |= 0x1;

Then you would get the appropriate minimal I/O instructions from any recent Arduino gcc compiler.

Use of subroutine calls is your prerogative, neither gcc nor asm can really circumvent those decisions... however...

Did you use a high optimisation level? gcc is pretty good at inlining code if and when it can see that's possible.

The Arduino APIs are far from well-designed, for the most part. But thats not the fault of gcc nor of compiles in general.

Clifford Heath.

Reply to
Clifford Heath

The "clean" code was a simple assignment. All the rest of the mess is a pile of debugging print statements, using the awkward functions provided for that purpose.

The generated code is even messier, disassembled from the .elf file.

Booleans, and bools work fine elsewhere in the program, substituting for one another, doing math, logic, comparisons, and accessing this same problematic variable. AFAICT it's only this one area in my code--and only a few statements--where 'bool' and 'boolean' misbehave.

Can't. Tried. The Arduino IDE won't let you. It compiles and links, including includes and header files madly, doing God knows what, then deletes the evidence. It took a miner's hat and a gas lamp to disassemble what I did.

I tried running gcc from the command line, but I couldn't ever make it happy.

That's not the case. I loaded 'U.dat.flg' from EEPROM, which initializes U.dat.flg to 0xff. All operations thereafter are in RAM.

Perhaps the 'boolean' type never anticipated a variable being assigned an illegal value (neither 1 nor 0) in the first place. Dunno.

A snip of the disassembled code follows.

The two cases use different print() calls, indicating the compiler regards the expression results as different types, even though they ought to all be unsigned bytes of whatever type you'd like to call 'em.

Note that the assignment w = (U.dat.flg) ? true : false;

generates different code in the two cases, and that different Serial.print() routines are called. It might be that one of those print calls used in the 'boolean' case trashes R1 where its counterpart in the 'uint8_t' case doesn't. I don't know.

Like I said before, abstractions piled on abstractions--the compiler's not protecting me, it's in the way. But it'll all be sweet once it's done, and easy for the customer to maintain.

Cheers, James Arthur

============= 'boolean' (does not work) ================== Serial.print(F(" 5. Before toggle, U.dat.flg = ")); 203e: 86 e4 ldi r24, 0x46 ; 70 2040: 9c e0 ldi r25, 0x0C ; 12 2042: 0e 94 6c 0b call Serial.print(F(""))

Serial.println(U.dat.flg, HEX); 2046: 80 91 94 01 lds r24, U.dat.flg ; 0x800194 204a: 60 e1 ldi r22, 0x10 ; 16 204c: 70 e0 ldi r23, 0x00 ; 0 204e: 90 e0 ldi r25, 0x00 ; 0 2050: 0e 94 5c 0c call 0x18b8 ; 0x18b8

i = (int) U.dat.flg; 2054: c0 90 94 01 lds r12, U.dat.flg ; 0x800194 2058: d1 2c mov r13, r1

Serial.print(F(" 6a. i = (int) U.dat.flg =")); 205a: 8d e0 ldi r24, 0x0D ; 13 205c: 9c e0 ldi r25, 0x0C ; 12 205e: 0e 94 6c 0b call Serial.print(F(""))

Serial.println(i,HEX); 2062: 60 e1 ldi r22, 0x10 ; 16 2064: 70 e0 ldi r23, 0x00 ; 0 2066: c6 01 movw r24, r12 2068: 0e 94 5c 0c call 0x18b8 ; 0x18b8

w = (U.dat.flg) ? true : false; 206c: e0 90 94 01 lds r14, U.dat.flg ; 0x800194 2070: f1 2c mov r15, r1

Serial.print(F(" 6b. After 'w = (U.dat.flg) ? true : false', w = ")); 2072: 8d eb ldi r24, 0xBD ; 189 2074: 9b e0 ldi r25, 0x0B ; 11 2076: 0e 94 6c 0b call Serial.print(F("string"))

Serial.println(w,HEX); 207a: c7 01 movw r24, r14 207c: 0e 94 47 0e call Serial.println(word R25:R24,HEX)

============= uint8_t (works) ================== Same as above, except U.dat.flg is defined as 'uint8_t', and longer text in the print statements has moved the addresses down slightly. Setting this version to back 'boolean' recreates the error, though--the error is executable-position independent.

Serial.print(F(" 5. Before toggle, U.dat.flg = ")); 206e: 86 e4 ldi r24, 0x46 ; 70 2070: 9c e0 ldi r25, 0x0C ; 12 2072: 0e 94 6b 0b call Serial.print(F(""))

Serial.println(U.dat.flg, HEX); 2076: 60 e1 ldi r22, 0x10 ; 16 2078: 70 e0 ldi r23, 0x00 ; 0 207a: 80 91 94 01 lds r24, 0x0194 ; 0x800194 207e: 0e 94 19 0c call 0x1832 ; 0x1832

i = (int) U.dat.flg; 2082: 10 91 94 01 lds r17, 0x0194 ; 0x800194 2086: c1 2e mov r12, r17 2088: d1 2c mov r13, r1

Serial.print(F(" 6a. i = (int) U.dat.flg =")); 208a: 8d e0 ldi r24, 0x0D ; 13 208c: 9c e0 ldi r25, 0x0C ; 12 208e: 0e 94 6b 0b call Serial.print(F(""))

Serial.println(i,HEX); 2092: 60 e1 ldi r22, 0x10 ; 16 2094: 70 e0 ldi r23, 0x00 ; 0 2096: c6 01 movw r24, r12 2098: 0e 94 4d 0e call 0x1c9a ; 0x1c9a

w = (U.dat.flg) ? true : false; 209c: ee 24 eor r14, r14 209e: e3 94 inc r14 20a0: f1 2c mov r15, r1 20a2: 80 91 94 01 lds r24, 0x0194 ; 0x800194 20a6: 81 11 cpse r24, r1 20a8: 02 c0 rjmp .+4 ; 0x20ae

Serial.print(F(" 6b. After 'w = (U.dat.flg) ? true : false', w = ")); 20aa: e1 2c mov r14, r1 20ac: f1 2c mov r15, r1 20ae: 8d eb ldi r24, 0xBD ; 189 20b0: 9b e0 ldi r25, 0x0B ; 11 20b2: 0e 94 6b 0b call Serial.print(F(""))

Serial.println(w,HEX); 20b6: c7 01 movw r24, r14 20b8: 0e 94 60 0e call 0x1cc0 ; 0x1cc0

Reply to
dagmargoodboat

You're not understanding--I'm fully aware that I can go so far as to embed assembly if I choose.

The fact that the compiler is jumping though hoops to map virtual pin numbers to physical ports and pins is exactly my point: it's solving problems that a human programmer wouldn't have to. I'd just read-modify-write the port with the appropriate mask.

BUT, what the naive users have and use, is layers and layers abstracted from the hardware doing the actual work.

Not so--it's using all sorts of calls to functions within functions as part of its standard environment. Thank heaven I'm not short on stack space!

I can't control it. The IDE controls the build process from start to finish.

It might've been easier for me to skip the Arduino IDE and write straight gcc, or even bare assembly, but the Arduino environment is a lot less intimidating for the customer.

It's cute, and has gotten a lot of hobbyists started. Lots of enthusiasm. It's the 'encouraging hacking' aspect--poking, prodding, and fiddling instead of planning--that's unfortunate. It teaches the next generation of programmers to be sloppy.

Of course not.

Cheers, James Arthur

Reply to
dagmargoodboat

This have any effect?

#ifdef __cplusplus typedef bool boolean; #else typedef uint8_t boolean; #define false 0 #define true !false #endif

The text of the bug report said the Arduino libraries futzed around with the boolean definitions to support C code in the core or somethin'

Reply to
bitrex

I saw that they've been having problems with this, and had tried to fix it in the past. My version's only a month or two old though, so, there's a decent chance the damn thing's just plain broken.

It's super simple, I think. The VAX was *complicated*, and some of the other big machines quirky and weird; the AVR's actually pretty clean.

Cheers, James Arthur (who once programmed a breakpointing debugger that single-stepped through EPROM & talked RS-232 through two port pins, in ~160 lines of 8051 assembly, taking up 1/3rd of a 1k processor and 6 of its 64 bytes of RAM.)

Reply to
dagmargoodboat

Oops, make that ~320 lines ... I just counted 8 screens of 40 lines each.

Cheers, James Arthur

Reply to
dagmargoodboat

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.