Ralph, many thx, that really brought back lots of early coding memories, some of them good .... but in those days you used to be grateful when a loop went around and stopped roughly when it should. Keep up the great work.
Thanks so much for doing this! Having dabbled with a few port-reads/writes in an ISR for an RTI-driven app, I thought I "knew enough", but you've filled-in gaps in my knowledge that I didn't even know that I had! Like many of the commenters below, I have fond memories of low-level coding. In my case, it was to test the hardware that I designed in my first day-job, now 40 years ago. Over the years, I "climbed the stack" and transitioned into higher-level languages, but unless I could relate what the code was doing to the hardware, I always felt that there was something missing. While I wouldn't dream of using assembler for anything other than ISRs or interfacing directly with (time-critical) hardware, I now feel less daunted by the prospect, for which you have my sincere gratitude.
Great to hear! Lots to see on this channel, Michael, and remember I always include a pdf link that lists ALL my videos for quick searching: bit.ly/UA-camVideoList-RalphBacon (Thanks to long-time viewer Michael Vogel who prepares this)
Back in the dark days of computing, learnt to assembler program on a Science Of Cambridge Mk14. First challenge was to build the Mk14 kit and get it working. Had to write the assembler on paper, convert this to hex codes and then input the codes via a keypad. Program was lost when the power was turned off to the Mk14! Making alterations to the assembler was a real pain, so had to learn to be very patient.
"Program was lost when the power was turned off". Wow, Andrew, you surely are a glutton for punishment. But I guess you could remember all the opcodes after you'd done it a few times!
@@RalphBacon Hi Ralph, If I remember correctly, Science of Cambridge did produce a tape cassette interface so that programs could be stored and re-loaded. Buying the Mk14 (cost £39.99) broke my piggy bank at the time (being 18) so I was stuck with just keypad entry. A few years latter I managed to get hold of an Apple 2, which was a huge improvement as it came with a disk drive for program storage. I did carry on with assembler programming for the 6502.
That cassette interface probably ran at 300 (or 600) baud, like my UK101. Better than typing it in every time though! "...it was one of the most important British computers ever produced. Its success in finding a previously untapped market was not lost on either Sinclair or his employees, notably Chris Curry, soon to break away and establish Acorn. Without the MK 14, there probably would never have been a ZX81, Spectrum, BBC Micro or Archimedes, and the British computer scene would have been very different." www.computinghistory.org.uk
1 year after your upload, and just finding this, I'm now getting flashbacks to hand assembly on an 8080, then 6502 and then 8086. I need to get a better life! Long live NOP, always worth it for padding loops!
It was an interesting thing to do this, but in a sort of academic sense. It was just too hard to actually code a real project using assembler, although there are some die-hards who do just that! I can remember back in the 1980s converting 6502 assembler to Z80 assembler for a "digital clock". Amazingly, it worked!
Thankyou Mr Ralph for the interesting as always. I have also dipped my toes into assembler at one point got bitten by a assembler shark and haven’t been in there since. 😉. I see lots of people asking why use Assembler you have responded on various comments it’s for speed. It is indeed and also as per some of your other videos Arduino can be quite convoluted in the background. They make things easy (Thankyou Arduino) with things like digital write and read but it’s convoluted. And have to cater for a lot of things. The real place where I’ve seen assembler shine is when your hardware testers on the slow edge with regards to hardware and you have to write a driver that bit bangs and the timing have to absolutely spot on.
Over the years, I've received some funny looks after asking my programmer friends about assembly language. The basic gist of their sometimes rather snarky comments has been that it's simply not worth learning about. But as I enjoy understanding how things work, I greatly appreciate your taking the time to provide this brief example, which has been most instructive.
When I first worked with microcontrollers (I started with PIC chips) I insisted on writing an assembler program to flash a few LEDs so I could see what was going on "under the hood", so to speak. I got a few funny look too. It was laborious in the extreme but taught me lots, and I then felt confident in programming such chips in C++, and marvelling at how a single statement could cover about 100 assembler instructions. If I were teaching high school (or higher) μController programming, I'd ensure that each student wrote one LED-flashing (blink) program, just to get the experience.
@@RalphBacon Yes, I used to program PICs in assembly. Z80s, and MC68HC11s too. Eventually around 2006 I started programming newer PICs in C. Still do. Just starting to look at using the Arduino environment with ESP32s. I agree some knowledge of what is going on at lower levels close to the hardware is good. Also, that is much easier to do with the ATmega328P in the Uno and with old PICs than with newer more feature rich MCUs.
Ah the BBC Micro. I hankered after one but had to suffice with a UK101 Compukit to start with (yes, in kit form) followed by a real computer in the form of an Amstrad 1640. Sigh. Memories...
For every single processor/MCU family I've ever used, I have gone trough an assembly language and It's architecture first (I know it takes time), then later make C language programs. That way I always know why It behaves the way it does.
I wish I had the time (and motivation) to do this here (which I did on a couple of PIC chips, many years ago) but at least here we get a flavour of what can be done!
I just popped in from the future where I was watching Ralph's video on FPGA logic routines. The demo was... wait for it...a blinking LED. Seriously, as you know, a blink sketch is a nice, uniform way to demo some idea or concept and not have to explain the part that isn't the part you want to explain.
And I must get Back To The Future (part VIII) where I show how to build your own FPGA using those new fangled "replicators" that everyone now has. Those dratted Androids think they know best, however.
Excellent video, Ralph. Your description of the volatile attribute was the clearest I have ever read. You don't have to apologise for the flashing LED in this context. For other viewers: you have just seen the "hello world" for bit banging. Imagine that, instead of an LED, pin 5 was connected to a device that required an accurately controlled pulse to, say, initialise a display or simulate a 2nd I2C data connection, this is the way you would produce that output. Now, if you tried produce that pulse, or stream of pulses, using classic Arduino IDE coding, you would find that it wouldn't work or only worked intimately because the timing would be off. I advise those really interested to re-run the video and copy the code from both files (you will have to stop and start the video). Make sure you get ALL of the comments, there is a ton of info in there. Well done, Ralph.
The code is up in my GitHub, Michael, (link in the video description and below) so easy to download. And your description is spot on! github.com/RalphBacon/202-Assembler-for-Arduino
Thanks Ralph. I like your presentation style, especially your choice of background music. That brought back all kinds of memories: In 1968, learning how to program delays on a PDP8, the only way it could do timing. In ca 1980, programming a mask programmed Mostek MK3870 as a dynamically tunable tone decoder for 5-tone signalling (selcall) for two way radios using a software "delay line" that could generate a range of delays with, AFAICR, 1uS resolution. Every cycle counts!
the curate's egg - ahh, the memories flood back. Thanks for the nice intro to using asm with Arduino. Just what I need to bit-bang SPI receive on the Uno.
Assembler, still relevant today! A very basic intro to assembler. hopefully you can ramp that up with more interesting examples in future. I'm keen on learning how to control and use all types of Arduino ports as some public libraries are just too large... maybe write a game in assembler?
Hmm, sounds like a lot of work. A simpler application might be one of those coloured LEDs and sound memory game, where you have to press the correct buttons in the correct sequence to continue. Even then... what's wrong with C++?
Thanks again for great content! I don't believe I often need to go that deep but man it's hard to find and to get your head around when you suddenly need to.
Hi Ralph, Off the top, let me say how glad I am to see you back creating great content! I usually learn something interesting, and you almost always give me something to think about. Speaking of which, I'm afraid I have a few nits to pick with your assembler "delay_10ms" subroutine... First nit is that you didn't allow for the case of someone passing a zero count in r20. If that does happen, then the first decrement will set it to 0xFF, and your inner loop will then run 256 times, giving a total delay of about 2560mS -- or a bit over two and a half seconds. I know it's not an external routine, and you've got complete control over the values it's being called with, but still... (as a said, a nit). My second nit goes a bit deeper into your accuracy issues, and the delay calculations in your comments to the routine. From your video, you were obviously aware of the issue, but you got the math wrong in the comments. (Yes, I know you'd say "maths", but I'm Canadian so I don't add that extra "s"). At line 78 you say "At 16Mhz outer loop runs in: (loopCnt * 159,991) + 1 + 4 - 1 cycles = 159,995 cycles". Ignoring the fact that it takes the same number of cycles regardless of the clock frequency, the calculation is wrong. Yes, the inner loop does take 159,991 cycles, but the outer loop isn't run just once, it's run over and over again -- "loopCnt" times. So the setting of the inner loop counter (2 ldi instructions) at lines 65 & 66, the loop counter decrement (subi) at line 75, and the conditional branch (brne) at line 76 are all executed every time through the loop. Counting it out--- ldi r30 instruction : 1 cycle ldi r31 : 1 cycle (innerLoop, total) : 159,991 cycles subi r20 : 1 cycle brne delay_10ms : 2 cycles TOTAL: : 159,996 cycles -- all executed loopCnt times, less 1 cycle for the last brne execution where no branch is taken. So the correct calculation - with parentheses corrected - is: ((loopCnt * (159,991 + 1 + 4)) - 1) = (loopCnt * 159,996) - 1 Using your example of a 100mS delay in the comments from line 83 forward, the actual calculation would be (10 * 159,996) -1 +1[for the nop] +4[for the ret] = 1,599,960 +4 = 1,599,964 cycles, not 1,599,914 cycles. Still wrong, but very slightly less wrong. And BTW - the error could be adjusted to negligible with a few more no-operation instructions. Assuming the 16MHz clock, what you want is for the entire subroutine to take (loopcnt * 160,000) cycles. If you were to add 4 nop instructions to the outer loop, but outside of the inner loop, (i.e. after line 71 and before line 75), then your outer loop would take EXACTLY 160,000 cycles each time through. Do that as many times as loop count says, and you'd only be out by the overhead of the return process. Delete the nop for the "missing" cycle on line 80, and you'd be over by exactly 3 cycles no matter what delay was requested (well, except for zero - see my first nit). Offhand, I don't see a reasonable way to eliminate that last error, but I think a very small constant error of 3 cycles (less than 0.00025ms at 16MHz) sounds better than a variable (and obviously non-obvious) delay-dependant error. Of course you'll never be able to get 100% accuracy with this method. If for whatever reason your main routine needed the bit reset (LED off) to happen some exact number of cycles after you did the bit set (LED on) instruction, you'd need to count every cycle in between. That would have to include not only the instructions executed within the loop, but the ldi to set the delay count, and the rcall instruction to call the loop. But I see from the assembler manual (last - and nittiest possible - nit being picked here) that the rcall instruction takes 2, 3, or 4 cycles depending on which AVR processor you're running it on, so there's an inherent uncertainty of 2 cycles when calling a subroutine. Oh well. As you said, this was a demo - and a very fine demo at that! :)
Everything you say here (I read it in a Canadian accent, just to get into character) is true, and I'm glad you've corrected my rather dubious assembler! That's why I don't code in assembler. I have enough trouble with C++. But it's good that there is a nice bit of refactoring that could be done and make it more reliable, I'm sure others will find your comment and use the work you've done. I'm glad you let me off the hook in your final analysis and that I'm also happy you like my work generally, my assembler notwithstanding! Thanks for taking the time and trouble in working all this out, appreciated.
Z80 in the 1980s was my start in assembly, looking up in z80 user manual to find the Hex code and entering in to ram on my kit built Talking electronics computer, you can still buy the kit last I heard.
The most complicated programming I undertook was using the z80 computer to scan through the security codes of the old 1980s cordless phones by hacking into the handset security IC and using the signal led to pause the scan and save the security code in ram, lots of people had cordless home phones. I have to add that it was purely an exercise no call went beyond the test number which made the phone ring apon hanging up.
My C sucks and in my Diploma of electronics and comm eng i have been only familiarised with assembly on micropcontrollers and microp processors like 8085 and 8051 else i wouldn't have been able to make heads and toes from this video But i understood it really well its a personal achievement to actually understand a geeky video online... Really loved your explanation it was to the point and really amazing....... I am so dumb
I suspect you're not dumb at all, Ash, and I'm glad you could follow the video. Once you have done any assembler for any kind of chip you are already halfway in understanding the code for a new type of chip. I hope you enjoyed it!
erm. I know it's not the point, but your microsecond perfect blink is being looped by the compiled skeych - a bunch of extra code that you have very little control over and is adding unknown time to that second. Even without taking any interupts into account.
Hi Ralph, good video. In the spirit of teaching, Assembly is a low level programming language that gets assembled by the assembler. And then linked by the linker.
Strictly speaking, we're still using the avr-gcc compiler, right? (Which might internally switch to a dedicated assembler when it sees that ".S" file, I don't know). But always good to keep things straight.
@@RalphBacon The avr-gcc command is not really a compiler but rather a compiler *driver*. Its job is to call the compiler proper (cc1 for C, cc1plus for C++, none for assembly source), then the assembler, and finally the linker. And give those commands a significant list of options. You can run avr-gcc with the “-v” option if you want to see the commands it runs.
Cheers for that - I wrote a $h!t load of 6502 / 68000 / 8088 assembler back in the day for industrial electronic uses, how we could have used a friendly higher level langauge compiler back then! I was telling my Prof that C was the language of the future (according to Byte) but he would 'ave none of it, and so we toiled on with Assembler. But handy to know how to incorporate it into Arduino code, thanks for the overview.
Compilers are the enemy of *Freedom* Spend months looking for why volatile is not really volatile then now I need to do DSP Audio Special Effects in the manner of building up terrain image from Side Looking Radar with special emphasis on Dracula Commination's for my Stage Production and have it complete by 31 October 2020 for extraction of Beetle Juice ticket buyers • 4-Layer OS on P-5 with *NO MIDNIGHT PHONE CALLS FROM INVESTORS!!!!* ◘ handshake by D_H Key Exchange confirming Nonce conveyed by AF Intel Courier ~ dressed in Prada Linea Rossa PS 54IS 5AV5Z1 65 Gunmetal Man Rectangle Sunglasses
Folks the *absolute* funniest thing that ever happened to me is a lifetime IBM coder attempts to assist a musician whom states he has to have his production running in Java MIDI in 3 months TCU: Our agent says convincingly Pravda means Truth in Russian Intel Pull back medium Why have you betrayed us? B-Roll of Winged monkeys - VO "I’ll Make You Go Insane!" Those who betray us live so that we may teach our trainee's
for a sunday morning, THAT HAS BEEN MOST INFORMATIVE+INSPIRATIONAL, you have demonstrated to come from arduino sketch to call an assembler routine, how does on do the reverse, from assembler to arduino sketch ??? thanks in advance for reading and answering !!!
OMG you now want to call a function from the C++ from Assembler? I'm not even sure that it can be done (but no doubt someone has done it, probably by declaring the function as external and letting the linker find it). Now, why would you even want to do this? (Ich bin der Meinung dass du spinnst ein bisschen)?
you could probably force a function to be at a certain address, and then branch to that address from asm. if you want to pass values, just call it normally in C, and then dissassemble it. check what registers, or what order of your variables on the stack is. then you can go back and do it in asm. not sure if this is possible, I only know 65c816 assembly, which no one uses the C compiler for it.
Wow Ralph, pic assembly that takes me back probably mid/late 90's. Pic 16c55 one time programmable, had to use I think the JW variant that was UV erasable took forever to develop code for it having to put it through the UV eraser every time!
Great video. Although it did start a series of flash-backs to writing assembler for Motorola 6800 and 6809s. At the time there was no added work. Having to consider a 16 bit number as two 8 bit numbers was just the natural way. All this changed when we started writing C, under OS9. OH! There's another flash back to coding in "machine language" using the op codes printed on a folded card. Never got into microcoding.
The most stupid thing ever asked of me was to convert some Z80 assembler to 6502 assembler, and all it did was display a digital clock with large digits on a (dumb) screen!
Thanks for this video. An exact 10ms delay routine use always a “timer-ISR”, I miss the example for this in assembler for 100 usek or 1000 usek. A 10ms Counter can take the 100usek ISR for calculating if it was 100 times called. Mixing code in assembler with C++ code is a good way to solve timing problems. C++ compiler are not so good in optimizing code for cycle reduction.
"I used to write PIC assembler". The first step to recovery is admitting that you have a serious problem. I feel your pain. Once Arduino came along, my PIC programming days pretty much ended. I thought I recognized your name from the piclist. I used to post on there a lot, but it's been quite some time. PICs are great, but the risc assembler sets the standard for coding ugliness. AVR assembler is so much nicer.
I'm still in rehab, Tony, but I'm sure I will be cured of all PIC-related assembler any day now. Frankly, I find C/C++ so therapeutic that I will probably never (say never) use assembler seriously again!
Wow, the way it took hours of coding to make that LED blink, so almost precisely. It would be interesting to see a pro/con table in your mindset for Assembler usage/value vs A Higher level language. Personally, I was happy to leave assembler behind for such fancy things as Cobol and Fortran pre 1980. Even BASIC on my C64 made my heart thump in those days, now it's more about the fastest way to achieve my goal(within limitation) as I'm not Government employed or production orientated. A Canted view perhaps , but still a very interesting and well presented video, but like skydiving videos, I doubt I'll try it. 😉
Advantages of High Level Language (eg C++, BASIC, Python): 1. Easy to read, use and implement. 2. Easy to change. 3. Easy to understand. 4. Lots of examples on the 'net. Advantages to using Assembler: 1. Absolute speed if you can write code better than a compiler. 2. Lots of debugging time. 3. Lots of time to look for a decent High Level IDE. 'Nuff said?
Hey Ralph! In the asm part, you did not safe registers before used. This can work, depending what the gcc produced for the C part of your sketch, but it also can make things utterly fail. So it might be wise to safe registers (push) before using in asm, an restore (pop) them again when done in asm. At least for the registers you do use in the asm section. Best wishes from Berlin!
I was worried about this too, Sebastian, but the avr-gcc manual is clear on this: "The call-used or call-clobbered general purpose registers (GPRs) are registers that might be destroyed (clobbered) by a function call. "R18-R27, R30, R31 These GPRs are call clobbered. An ordinary function may use them *without restoring* the contents. Interrupt service routines (ISRs) must save and restore each register they use." So we are safe, thankfully! And Gruesse as Milton Keynes to you too! gcc.gnu.org/wiki/avr-gcc
@@RalphBacon Hey! Good to know that this is not going to cause issues. Btw. Do not forget to safe and restore status-flags within ISRs, since the interrupt could once in a while take place while inside e.g. a "if(x==y)...;" Where the ISR takes place right after the compare instruction, but before the e.g. "branch if Zero" asm instruction, in which case the status might be changed in the ISR and the branch after return from interrupt would never take place while it otherwise would had been, or vice versa would take place while it otherwise would not have. A very common trap in assembler for many, and extremly hard to detect by observation at runtime, since it only happens very rarely of course. Besides that, I hope your eyes are doing perfectly well and your little hairy friend has found a nice shady place to rest for the eternal mice hunting to come. All my best wishes! ps. a simple example for an asm ISR: __ISR_INT0: push r15 in r15, SREG ;; ;; some assembler stuff ;; more assembler stuff ;; out SREG, r15 pop r15 reti
Very true what you say there, Sebastian. I shall have to remember that about ISRs, not that I really intend to program in assembler. My eyes are doing "OK", laser in one of them last week, that has improved things too. Regarding Benny, he is resting in his sleeping cat run on our sideboard. And, yes, we still miss him like crazy. Thanks for your thoughts.
@@RalphBacon Oh, noes! :-) Oi! Don't be scared about Assembler! Using lots of Macros, it becomes as handy as C ... or in other words, the more "Macros" you develop over time, the more it becomes like a simplistic "C". Oi, give it a try! It's fun ... --- How about a video about Assembler Macros, correct ISRs in Assembler and the like perhaps? :-) You've got your first two Macros already for ISRs in some *.S project: .MACRO PUSH_FLAGS push r15 in r15, SREG .ENDMACRO .MACRO POP_FLAGS out SREG, r15 pop r15 .ENDMACRO Here is some new ones for your "MyMacros.inc" file: .MACRO INIT_SP ldi TL, LOW( @0 ) ldi TH, HIGH( @0 ) out SPL, TL out SPH, TH .ENDMACRO ;; load a register pair (first argument=@0) with ;; a 16 bit constant (second argument=@1) .MACRO LDIW ldi @0L, LOW( @1 ) ldi @0H, HIGH( @1 ) .ENDMACRO ;; add a 16 bit constant 16 to a register pair: ;; usage: "ADDIW , " .MACRO ADDIW subi @0L, LOW( -@1 ) sbci @0H, HIGH( -@1 ) .ENDMACRO Arduino style (init/loop) example (in Atmel Studio 7): .include "m328Pdef.inc" .include "MyMacros.inc" ;; define a register pair "T" where ;; r16=low part of "T" and ;; r17 is the high part of register pair "T" .def TL = r16 .def TH = r17 .org 0x0000 rjmp _init .org INT0addr ;; take a look in m328Pdef.inc for interrupt vector declarations rjmp __isr_int0 .org 0x200 _init: INIT_SP ( SRAM_START + SRAM_SIZE ) ;; init stack pointer ;; ;; some asm code ;; rjmp _loop __isr_int0: PUSH_FLAGS ;; safe flags ;; ;; some asm code ;; POP_FLAGS ;; restore flags reti _loop: ;; ;; ... some asm code ... ;; ;; load 16 bit constant into RegPair "T" LDIW T, 0x1020 ;; add 16 bit constant to RegPair "T" ADDIW T, 0x1234 ;; ;; ... some more asm code ... ;; rjmp _loop Glad to hear your eyes are doing better ... thumbs up for you and hopefully enjoy your NAAFI store "steak and onions pie" while performing some nifty asm! Hehe!
Thanks again! Actually, even I do that sometimes. Well, at my age I think "What did I say about x,y,z on this processor?". My video has the answer! It's not _what_ you know it's knowing _where to look_ that's important!
Hi again, my Friend ! Assembler language reminds me when I first started programming those PICs. It evolved, for sure, but you lot me at « Assembler » ! Hahahaha !! (Just joking !). I guess, with old age and memory starting to fail a bit on the sides, one trends to keep it as simple as it gets with Arduino programmation. LOL. Once again, I like your way of explaining things. It makes it simple to understand and and I appreciate it very much. Have a great evening and stay healthy !
I could have actually used Timer0 but the code just got too complex for beginners to follow. As Mike says, it was just a demo, not really for "production" code. But I like the idea of TimerCalc so out of all the hits this is one that writes Arduino code: www.arduinoslovakia.eu/application/timer-calculator
04:59:22.998 -> That's a blink:241 04:59:24.002 -> That's a blink:244 04:59:25.006 -> That's a blink:247 04:59:26.002 -> That's a blink:250 04:59:27.022 -> That's a blink:253 Notice it is off a little assuming the goal was to make it once per second. Which variable do I vary in the routine to change that?
I can't say off hand (and I'm not looking at that assembler code again, sorry) but the goal of the video was not to make an LED flash every second (that was a hypothetical use case) but to actually run the assembler from within the Arduino sketch _and_ get the result. If you're super interested in getting it exact, note that there are two loops running in the assembler and that determines the delay - but it will always be slightly off as the first loop takes a few cycles longer... or shorter, I can't remember now but I gave up on improving it after that!
Note that the loop() includes a Serial.print command, which takes some time, and the assembly code is only called again after the print. Also, there may be some arduino overhead between each call to loop().
I wish AVR's had something like the DWT counter in Cortex processors. Really nifty little thing for figuring out exactly how long something took without the need for cycle stealing interrupts :)
For cycle counting, I usually set Timer 1 to run in 16-bit mode at the full CPU speed, take a reading of TCNT1 right before and right after the thing I wan to time, and compute the difference. The overhead of this method is typically about 4 CPU cycles.
Hi Ralph, SBI PINB,5 will toggle PORTB, PIN5. Call it twice to flash the LED. No need to SBI and CBI PORTB in your blink function. Saves typing! ;-) Cheers, Norm.
@@RalphBacon You don't need to be one! It's in the data sheet. The designers used the PORT register for input pins to enable pull-ups because PORT isn't needed for input, and used the PIN register to toggle output pins, because PIN wasn't needed for output pins. This, and much more can be found in "Arduino Software Internals" from Apress. (Yes, unashamed plug, I wrote it! There's even a bit of assembly code in there too, but not much.) Take care. Cheers, Norm.
I had a sneak peak at your book on Amazon. When you pick up a book (even virtually) they either grab you (me) or leave you (me) cold. This book grabbed me. I was hooked from the moment I saw that *sbi* was a C++ macro. I know, I'm weird. Ordered. Will review in due course and potentially share with my viewers. And stop being so self deprecating, I know that's a British thing to do but still! I'm quite envious of you that you have published works.
You don't need the “volatile” qualifier, as the counter is not modified in the background: it is modified by a function your C++ code calls explicitly. The compiler assumes that any external function can potentially have side effects, such as modifying externally visible global variables. You would need to declare the function with __attribute__((pure)) in order for the compiler to assume it has no side effects. Only then would you need the “volatile” qualifier. And you don't need __attribute__((used)) either: the compiler can only optimize-out a global variable if it has internal linkage, i.e. the “static” qualifier. It cannot optimize-out a global variable with external linkage. The linker can, but it also can see that the counter is used by the assembly code.
This is the sort of information I had searched for, Edgar, but could not find any original verification. Have you got any links to citations where this has been documented by avr-gcc? I did wonder whether the compiler/linker was "clever" enough to tie things together, including the registers that are clobbered.
@@RalphBacon Re “Have you got any links”: Unfortunately not, as I acquired by knowledge through many years of practice and browsing many sources. My favorite sources are cppreference.com and stackoverflow.com (through Google) for the C and C++ languages, and gcc.gnu.org/onlinedocs/ for the gcc specifics, like the attributes. But these are not tutorial material. Re “I did wonder whether the compiler/linker was ‘clever’ enough to tie things together, including the registers that are clobbered.”: It is not: it doesn't try to analyze the assembly you write. It just assumes that the functions you write (in assembly or any other language) only clobber the registers they are allowed to clobber as per the ABI: r0, r18-r27, r30 and r31. See gcc.gnu.org/wiki/avr-gcc
great video as usual. Calling assembler prices is a bit of a stretch. The crystals is not that prices, it can still be a few hertz of but we do know how many clock cycles a given instruction takes. It can bring up a discussion on the difference between precision and accuracy and "what is necessary". Let's not open that can of worms. With assembler everything is just more complex and you really do need to plan ahead. Variables, registers, addresses and such do need some constitution. The higher the level of program language, the easier to is to handle those issues, in most cases you don't even think about it. As i remember, the "word" size is one of the earliest measures, earlier than byte. It had an odd size, by today's standard, I think it was 14 or 15 bit long.
A crystal is very precise, unlike a ceramic resonator, which is why we use them. The few I've ever measured were within a few Hz of the specified frequency. On that basis I guess this is accurate (and precise) enough for a demo!
@@RalphBacon The serial and timer interrupts from the sketch overhead will cause your timer loops to halt long enough to service the interrupts before continuing. You could avoid this by clearing (cli) and re-enabling (sei) interrupts; but then the serial communications and Arduino delays will not work correctly. To get truly correct millisecond and second scale timing, you need to use hardware timers and hardware interrupts like Arduino has done.
Yes, I'm aware of that but the assembler code to use a timer would have confused my viewers! This might not be totally accurate but at least everyone can follow the code.
@@RalphBacon Correctly we have do disable *ALL* interrupts and push the stack such as to save all RTOS relevant flags and registers which I guess what Byron Watkins might be how to do it · P-5 and up has an rdtsc instruction, which returns the processor time stamp then code could run something then T-1 minus T-0 and store that to some auxiliary register that is not used or something ~ I bought one of these intending to use it for control but just hard-wired a pull-down relay >> Apparently from what I remember it has a countdown timer with would drop in to some interrupt handler but how to save and restore state on an NMI becomes confounding
Excellent! WTG Ralph - BTW, What is exactly the IDE you are using in this video? As far as the NEW Arduino IDE I can say that it takes a very long time to start-up - don't know really why it doe that? BUT I was thinking to move to a better Arduino IDE based alternative - I would be happy to know what is the one you are using? As always, Thanks for the videos!
Remember that the _new_ Arduino PRO IDE is still very much in Alpha mode; it should run slicker when in Beta mode and certainly when released. I'm using Eclipse Sloeber (has the Arduino plugin), works very well but is not being developed any further.
I know this is an example on how to use assembler. But just to nitpick on you saying "blink exactly every second": this sketch does not do it, assembler or not. You still have Arduino's standard timer interrupt and the whole (invisible) Arduino outer loop (which calls the sketch's "loop()") "using up" clocks. But you are definitely a little bit closer to "exactly 1 second", because you replaced the overhead heavy digitalWrite with an assembler instruction ;) .
Well, my 10mS delay does run in exactly 10mS if the crystal frequency is correct (and my past measurements of crystals indicate they are very, very precise). Or at least it takes 160,000 cycles! The loop as a whole gets more inaccurate the bigger the delay and I didn't disable the Timer0 interrupt so it's all a bit of a near miss, really. Close, but no cigar!
I probably used the Eclipse Sloeber version, but it's no longer being actively developed or supported. The new Arduino IDE is based on Eclipse though so that should be much better when released. Or do what I did and use PlatformIO with Visual Code. eclipse.baeyens.it/
Great stuff, thanks. Got it as a UA-cam recommendation. So far I've managed to avoid assembler even though I've been doing audio-related stuff, timing obviously important. Couple of questions maybe you can help with, save me trawling: I'm using PlatformIO (on VSCode, on Ubuntu). Are there any differences I should be aware of for ASM in this environment? The other thing - obviously there will be significant differences when targeting different boards (ESP32, Arduino Due), but in practice, ballparky, how similar will the ASM be? (I've only glanced at the instruction sets - eek!)
It's the compiler/assembler that determines which instructions you must use, Danny. If you use the (Arduino-standard) avr-gcc (that calls the avr-as) then you must use those opcodes. If you use the Atmel compiler/assembler then it's (slightly) different. I don't know which one your PlatformIO environment uses; it might depend on which language(s) you have installed in VS. There are _considerable differences_ between writing assembler for the 8-bit Atmel 328P vs the 32-bit ESP32 (Tensilica Xtensa) processor. May the force be with you!
@@RalphBacon thank you. Enough said. I suppose I was thinking some kind of commonality - any Arduino-ish thing same C code works in Arduino as very different chips. Not naive, no no no. I would recommend the PlatformIO thing, when I was working with Arduino IDE, everything wound up in the same file. Still scared of doing from command line
Would this general calling setup and assembler syntax style work in the Arduino IDE for ARM-based Arduinos like the MKR series? Obviously the specific instructions and registers and I/O would need to be the ARM ones, but other than that is the overall principle the same? Thanks for the great video. Flashbacks for me to PDP11/10 assembler in the 1970s and Z80 in the 80s.
Well, you have to get the code into the microcontroller in the first place; if the Arduino IDE supports it then there is no reason this should not work in the way you describe.
@@RalphBacon Thanks, yes the Arduino IDE supports the ARM processor Arduinos so no problem there. It took some work to find the exact correct assembler directives and syntax for the Cortex-M0 Thumb instructions, but everything you showed with regards to defining the external routines and variables and passing them and setting up the assembler labels to match worked perfectly. Thanks for that.
Great video, thank you for explaining to me the uint16_t I have seen this in other people's code and have been frightened of it as the internet doesn't help
Hi, mi name is Julian from argentina, I have found intresting this helpfully info because I am try to build a floppy emulator for my tandy 1000and assembler is the best option for the r/w realtime routines. thanks to share!
Well, Julian, only you can determine whether Assembler or C++ works best for a floppy disk emulator - I'd be inclined to try C++ first, as it is about as quick as assembler (especially if you don't use the Arduino-speak language) and much easier to code! Best greetings to Argentina!
@@RalphBacon intresting answer. The first step is to make a read only with a few tracks disk stored in the arduino rom memory. So i can test the timings in c++ first. Thanks you!
No, the micro runs at the full 16MHz, Raymond. You can set the bootloader flags (fuses) to divide by 8 but that's about it. The datasheet for the ATMega328P specifically states that you can get "Up to 16 MIPS throughput at 16MHz" but that's assuming all the instructions you are running are single-clock cycle instructions!
Hi., Very informative video. I am assembly language programmer and I have assembly codes written in AVR studio. I would like to include those routines in arduino IDE. If not please let me know where I can find Arduino Assembler. Thanks
The Arduino Assembler is part of the AVR-GCC Compiler. It can be invoked separately if required, but usually Assembler is called from within C when it is required.
How do you ensure that the registers you use are not used by the compiler generated functions? Does the compiler guarantee registers it won't touch? I enjoy seeing the assembler stuff. I was on the design team for the Magnavox/Philips Odyssey w, and the cartridges were usually 2k ROMs, programmed in Intel 8048 assembler. I did three or four of them.
An interesting question, Mike, and one for which I searched for the answer long before I did the video. I could not find any sensible answer, so I _assume_ that because the whole thing is being compiled at the same time, any registers that are clobbered by my assembler sketch will be "noticed" by the compiler and dealt with accordingly. If I had written this "in-line" in the sketch as one big string (note to others, don't _ever_ do that, it's horrendous) you have to then list the clobbered registers so that the compiler knows as it does not ever assemble your code - it just leaves it "as-is". That's my 10 cents / 2-penneth worth on this - anybody else have any *evidence* on how this works?
@@RalphBacon Perhaps just put some of the register values as output in a test sketch? Run a sketch that sets pins high, calls register values from the assembler, prints them, sets pins lows, rinse and repeat, do the same for assigning various types of variable, increments them, conditionals, etc. I don't see why this wouldn't work so long as the assembly code never changes the value of a register. Run the test before coding any sketches to make sure there are no conflicts?
@@RalphBacon It's really about the misleading title of your video. Assembly language has always been available for embedded processors, when I started coding I didn't even have the luxury of a compiler/assembler (I had no access to a university main frame) and had to hand write machine code. Writing time critical code components in assembler was still sometimes required when the C compilers came along. The video title suggested to me that you had something new. @Ralph S Bacon and I were both successfully click-baited.
C++ only mangles function/member names, not variable names. That's why you need the extern "C" for blink() and start(). I think the use of asm("counter") is an unnecessary concept for beginners.
Doesn't the asm("counter") ensure that the compiler/assembler knows that the sketch variable "myCounter" is referred to as "counter" in the assembler code? Otherwise how does it tie the two together?
Yes, I understand that, but I was trying to make a point here that you _can_ use different names. What if the 3rd party assembler developer had called it "x6fgh" - I'm guessing you might want a friendlier name than that in your sketch?
I finally got my first Arduino 2 months ago (20 years late, thought they were expensive and was 'afraid' of the AVR suite a had seen then...) I was still using a very old PC with Win98 because I could poke directly to the LPT & COM ports and control my home build interfaces, winXP does not allow that... so I stuck on QBasic and VB6. I am used to walk in the (some what) dark corridors of programming (API's) but was not familiar with C (+-#@ wse) at least I thought so. (POV-Ray 'script' is a variant of C...) Most of my programming is copy/paste (Syntax Terror...) This video Is very clear to me, perhaps I am more advanced than I think ?
You must work somewhere as a programmer I'm guessing in C/C++ and you're used to using the debugging mode where asm code is referenced as you step through the code. If I could I'd be doing all my coding in a professional grade enterprise IDE where you only have to specify a target platform.
I've definitely been "spoiled" by having a "proper" debugging environment made available for me - but that's the way it's always been in my professional (coding) life. That said, the new line-by-line, single-stepping debugging features in the Arduino IDE 2.0 look promising (for newer chips) and something I'll be exploring in more detail.
All in all it gives me a little idea of why: 𝘷𝘰𝘪𝘥 𝘭𝘰𝘰𝘱() { 𝘥𝘪𝘨𝘪𝘵𝘢𝘭𝘞𝘳𝘪𝘵𝘦(𝘓𝘌𝘋_𝘉𝘜𝘐𝘓𝘛𝘐𝘕, !𝘥𝘪𝘨𝘪𝘵𝘢𝘭𝘙𝘦𝘢𝘥(𝘓𝘌𝘋_𝘉𝘜𝘐𝘓𝘛𝘐𝘕)); 𝘥𝘦𝘭𝘢𝘺(1000); } Compiles to more bytes (960) than the origin (924). Somethings is happening under the skin :-)
Actually, LOTS is happening "under the skin", as you put it, or perhaps, behind the scenes. Nothing wrong with that, if it makes millions of developers' lives easier but it it good to know that something is happening somewhere! All in all, I think Arduino-speak is pretty good, but you can't use it in a job; you will have to learn and use native C++.
@@RalphBacon C++ would be way overkill for me and when you mention making 'developers' lives easier', I've started to use Great Cow Basic for programming 8-bits. The code above actually derives from an even shorter code in Basic. You could try to take a look: gcbasic.sourceforge.net/Typesetter/index.php/Comming-from-another-programming-environment See, - That's how you make live easier :-)
Yes, I've seen Great Cow BASIC and it's very good for beginners and hobbyists alike. But as the Arduino environment is geared to C/C++ and I happen to know C (well, a variant) it seems an obvious decision for me to continue using that.
None, it's all part of the Arduino IDE AVR-GCC compiler. Although you can invoke the assembler part separately if you want, but I didn't really cover that.
Ralph, many thx, that really brought back lots of early coding memories, some of them good .... but in those days you used to be grateful when a loop went around and stopped roughly when it should. Keep up the great work.
Glad you enjoyed it!
Thanks so much for doing this! Having dabbled with a few port-reads/writes in an ISR for an RTI-driven app, I thought I "knew enough", but you've filled-in gaps in my knowledge that I didn't even know that I had! Like many of the commenters below, I have fond memories of low-level coding. In my case, it was to test the hardware that I designed in my first day-job, now 40 years ago. Over the years, I "climbed the stack" and transitioned into higher-level languages, but unless I could relate what the code was doing to the hardware, I always felt that there was something missing. While I wouldn't dream of using assembler for anything other than ISRs or interfacing directly with (time-critical) hardware, I now feel less daunted by the prospect, for which you have my sincere gratitude.
I'm glad it filled in some gaps for you, Gerallt! Like you, I have no desire to program in assembler any time soon.
Oh how I enjoy the addition of a little assembler to my day. It reminds me of over 30 years S360/S370 bit banging. Thanks Ralph.
Glad you enjoyed it, Phil!
Me too, exactly. CLC MVC all that stuff. oh and the ever confusing ED.
I'm a very rusty coder, just starting to get into arduino, and you, sir, are an excellent resource. Thank you.
Great to hear! Lots to see on this channel, Michael, and remember I always include a pdf link that lists ALL my videos for quick searching:
bit.ly/UA-camVideoList-RalphBacon
(Thanks to long-time viewer Michael Vogel who prepares this)
Back in the dark days of computing, learnt to assembler program on a Science Of Cambridge Mk14. First challenge was to build the Mk14 kit and get it working. Had to write the assembler on paper, convert this to hex codes and then input the codes via a keypad. Program was lost when the power was turned off to the Mk14! Making alterations to the assembler was a real pain, so had to learn to be very patient.
"Program was lost when the power was turned off". Wow, Andrew, you surely are a glutton for punishment. But I guess you could remember all the opcodes after you'd done it a few times!
@@RalphBacon Hi Ralph, If I remember correctly, Science of Cambridge did produce a tape cassette interface so that programs could be stored and re-loaded. Buying the Mk14 (cost £39.99) broke my piggy bank at the time (being 18) so I was stuck with just keypad entry. A few years latter I managed to get hold of an Apple 2, which was a huge improvement as it came with a disk drive for program storage. I did carry on with assembler programming for the 6502.
That cassette interface probably ran at 300 (or 600) baud, like my UK101. Better than typing it in every time though!
"...it was one of the most important British computers ever produced. Its success in finding a previously untapped market was not lost on either Sinclair or his employees, notably Chris Curry, soon to break away and establish Acorn. Without the MK 14, there probably would never have been a ZX81, Spectrum, BBC Micro or Archimedes, and the British computer scene would have been very different."
www.computinghistory.org.uk
1 year after your upload, and just finding this, I'm now getting flashbacks to hand assembly on an 8080, then 6502 and then 8086. I need to get a better life! Long live NOP, always worth it for padding loops!
It was an interesting thing to do this, but in a sort of academic sense. It was just too hard to actually code a real project using assembler, although there are some die-hards who do just that! I can remember back in the 1980s converting 6502 assembler to Z80 assembler for a "digital clock". Amazingly, it worked!
Thankyou Mr Ralph for the interesting as always. I have also dipped my toes into assembler at one point got bitten by a assembler shark and haven’t been in there since. 😉. I see lots of people asking why use Assembler you have responded on various comments it’s for speed. It is indeed and also as per some of your other videos Arduino can be quite convoluted in the background. They make things easy (Thankyou Arduino) with things like digital write and read but it’s convoluted. And have to cater for a lot of things. The real place where I’ve seen assembler shine is when your hardware testers on the slow edge with regards to hardware and you have to write a driver that bit bangs and the timing have to absolutely spot on.
Bit-banging is certainly one area where assembler wins, Rudolph.
Over the years, I've received some funny looks after asking my programmer friends about assembly language. The basic gist of their sometimes rather snarky comments has been that it's simply not worth learning about. But as I enjoy understanding how things work, I greatly appreciate your taking the time to provide this brief example, which has been most instructive.
When I first worked with microcontrollers (I started with PIC chips) I insisted on writing an assembler program to flash a few LEDs so I could see what was going on "under the hood", so to speak. I got a few funny look too.
It was laborious in the extreme but taught me lots, and I then felt confident in programming such chips in C++, and marvelling at how a single statement could cover about 100 assembler instructions.
If I were teaching high school (or higher) μController programming, I'd ensure that each student wrote one LED-flashing (blink) program, just to get the experience.
@@RalphBacon Yes, I used to program PICs in assembly. Z80s, and MC68HC11s too. Eventually around 2006 I started programming newer PICs in C. Still do. Just starting to look at using the Arduino environment with ESP32s. I agree some knowledge of what is going on at lower levels close to the hardware is good. Also, that is much easier to do with the ATmega328P in the Uno and with old PICs than with newer more feature rich MCUs.
Hi Ralph, this is interesting stuff, one of my fond memories of the BBC micro was the ability to freely mix BASIC and assembler...cheers.
Ah yes the good old BBC computer
Ah the BBC Micro. I hankered after one but had to suffice with a UK101 Compukit to start with (yes, in kit form) followed by a real computer in the form of an Amstrad 1640. Sigh. Memories...
I like it when people get into the roots of things and actually explain them.
Great video. Great lesson. Thanks.
Glad you liked it!
For every single processor/MCU family I've ever used, I have gone trough an assembly language and It's architecture first (I know it takes time), then later make C language programs. That way I always know why It behaves the way it does.
I wish I had the time (and motivation) to do this here (which I did on a couple of PIC chips, many years ago) but at least here we get a flavour of what can be done!
I just popped in from the future where I was watching Ralph's video on FPGA logic routines. The demo was... wait for it...a blinking LED. Seriously, as you know, a blink sketch is a nice, uniform way to demo some idea or concept and not have to explain the part that isn't the part you want to explain.
And I must get Back To The Future (part VIII) where I show how to build your own FPGA using those new fangled "replicators" that everyone now has. Those dratted Androids think they know best, however.
"Blink" is the "Hello World" of microcontrollers.
Excellent video, Ralph. Your description of the volatile attribute was the clearest I have ever read. You don't have to apologise for the flashing LED in this context. For other viewers: you have just seen the "hello world" for bit banging. Imagine that, instead of an LED, pin 5 was connected to a device that required an accurately controlled pulse to, say, initialise a display or simulate a 2nd I2C data connection, this is the way you would produce that output. Now, if you tried produce that pulse, or stream of pulses, using classic Arduino IDE coding, you would find that it wouldn't work or only worked intimately because the timing would be off. I advise those really interested to re-run the video and copy the code from both files (you will have to stop and start the video). Make sure you get ALL of the comments, there is a ton of info in there. Well done, Ralph.
The code is up in my GitHub, Michael, (link in the video description and below) so easy to download. And your description is spot on!
github.com/RalphBacon/202-Assembler-for-Arduino
Thanks Ralph. I like your presentation style, especially your choice of background music.
That brought back all kinds of memories: In 1968, learning how to program delays on a PDP8, the only way it could do timing. In ca 1980, programming a mask programmed Mostek MK3870 as a dynamically tunable tone decoder for 5-tone signalling (selcall) for two way radios using a software "delay line" that could generate a range of delays with, AFAICR, 1uS resolution. Every cycle counts!
Glad you enjoyed it, David!
You are a great teacher!
Thank you! 😃
the curate's egg - ahh, the memories flood back. Thanks for the nice intro to using asm with Arduino. Just what I need to bit-bang SPI receive on the Uno.
Glad to help!
Assembler, still relevant today! A very basic intro to assembler. hopefully you can ramp that up with more interesting examples in future. I'm keen on learning how to control and use all types of Arduino ports as some public libraries are just too large... maybe write a game in assembler?
Hmm, sounds like a lot of work. A simpler application might be one of those coloured LEDs and sound memory game, where you have to press the correct buttons in the correct sequence to continue. Even then... what's wrong with C++?
Thanks again for great content! I don't believe I often need to go that deep but man it's hard to find and to get your head around when you suddenly need to.
Well said! Always useful background knowledge.
Dear sir,
Thanks. Your lesson is very clearly structured. I understood everything. Your speaking style is very warm and easily understood.
You are most welcome!
Hi Ralph,
Off the top, let me say how glad I am to see you back creating great content! I usually learn something interesting, and you almost always give me something to think about. Speaking of which, I'm afraid I have a few nits to pick with your assembler "delay_10ms" subroutine...
First nit is that you didn't allow for the case of someone passing a zero count in r20. If that does happen, then the first decrement will set it to 0xFF, and your inner loop will then run 256 times, giving a total delay of about 2560mS -- or a bit over two and a half seconds. I know it's not an external routine, and you've got complete control over the values it's being called with, but still... (as a said, a nit).
My second nit goes a bit deeper into your accuracy issues, and the delay calculations in your comments to the routine. From your video, you were obviously aware of the issue, but you got the math wrong in the comments. (Yes, I know you'd say "maths", but I'm Canadian so I don't add that extra "s").
At line 78 you say "At 16Mhz outer loop runs in: (loopCnt * 159,991) + 1 + 4 - 1 cycles = 159,995 cycles". Ignoring the fact that it takes the same number of cycles regardless of the clock frequency, the calculation is wrong. Yes, the inner loop does take 159,991 cycles, but the outer loop isn't run just once, it's run over and over again -- "loopCnt" times. So the setting of the inner loop counter (2 ldi instructions) at lines 65 & 66, the loop counter decrement (subi) at line 75, and the conditional branch (brne) at line 76 are all executed every time through the loop.
Counting it out---
ldi r30 instruction : 1 cycle
ldi r31 : 1 cycle
(innerLoop, total) : 159,991 cycles
subi r20 : 1 cycle
brne delay_10ms : 2 cycles
TOTAL: : 159,996 cycles -- all executed loopCnt times,
less 1 cycle for the last brne execution where no branch is taken.
So the correct calculation - with parentheses corrected - is: ((loopCnt * (159,991 + 1 + 4)) - 1)
= (loopCnt * 159,996) - 1
Using your example of a 100mS delay in the comments from line 83 forward, the actual calculation would be (10 * 159,996) -1 +1[for the nop] +4[for the ret] = 1,599,960 +4 = 1,599,964 cycles, not 1,599,914 cycles. Still wrong, but very slightly less wrong.
And BTW - the error could be adjusted to negligible with a few more no-operation instructions. Assuming the 16MHz clock, what you want is for the entire subroutine to take (loopcnt * 160,000) cycles. If you were to add 4 nop instructions to the outer loop, but outside of the inner loop, (i.e. after line 71 and before line 75), then your outer loop would take EXACTLY 160,000 cycles each time through. Do that as many times as loop count says, and you'd only be out by the overhead of the return process. Delete the nop for the "missing" cycle on line 80, and you'd be over by exactly 3 cycles no matter what delay was requested (well, except for zero - see my first nit). Offhand, I don't see a reasonable way to eliminate that last error, but I think a very small constant error of 3 cycles (less than 0.00025ms at 16MHz) sounds better than a variable (and obviously non-obvious) delay-dependant error.
Of course you'll never be able to get 100% accuracy with this method. If for whatever reason your main routine needed the bit reset (LED off) to happen some exact number of cycles after you did the bit set (LED on) instruction, you'd need to count every cycle in between. That would have to include not only the instructions executed within the loop, but the ldi to set the delay count, and the rcall instruction to call the loop. But I see from the assembler manual (last - and nittiest possible - nit being picked here) that the rcall instruction takes 2, 3, or 4 cycles depending on which AVR processor you're running it on, so there's an inherent uncertainty of 2 cycles when calling a subroutine.
Oh well. As you said, this was a demo - and a very fine demo at that! :)
Everything you say here (I read it in a Canadian accent, just to get into character) is true, and I'm glad you've corrected my rather dubious assembler!
That's why I don't code in assembler. I have enough trouble with C++.
But it's good that there is a nice bit of refactoring that could be done and make it more reliable, I'm sure others will find your comment and use the work you've done.
I'm glad you let me off the hook in your final analysis and that I'm also happy you like my work generally, my assembler notwithstanding!
Thanks for taking the time and trouble in working all this out, appreciated.
I've tried a lot, thank you.
Glad to hear that!
Great video you sort of lost me I think just after "And welcome back" :-)
Sorry! It was only a very quick peek at Assembler, and as I said, the last for a while.
Oh snap! It's getting intense boys!
Just wait until next week, that is what I call intense!
This was GREAT!!! Everybody should be introduced to Assembler / Macro code, I cut my teeth on VAX11/32. Well done, clear and concise.
Nice of you to say, even though the 10mS delay wasn't quite as accurate as it might have been I think it got the message across!
Z80 in the 1980s was my start in assembly, looking up in z80 user manual to find the Hex code and entering in to ram on my kit built Talking electronics computer, you can still buy the kit last I heard.
But only if you're a computer masochist, I heard!
Hey, that was my start too......until i could afford the BBC Micro years later.
The most complicated programming I undertook was using the z80 computer to scan through the security codes of the old 1980s cordless phones by hacking into the handset security IC and using the signal led to pause the scan and save the security code in ram, lots of people had cordless home phones. I have to add that it was purely an exercise no call went beyond the test number which made the phone ring apon hanging up.
"...it was purely an exercise..." I understand you've been involved in a car accident that wasn't your fault. Hmm.
@@RalphBacon Lol......
My C sucks and in my Diploma of electronics and comm eng i have been only familiarised with assembly on micropcontrollers and microp processors like 8085 and 8051 else i wouldn't have been able to make heads and toes from this video
But i understood it really well its a personal achievement to actually understand a geeky video online...
Really loved your explanation it was to the point and really amazing.......
I am so dumb
I suspect you're not dumb at all, Ash, and I'm glad you could follow the video. Once you have done any assembler for any kind of chip you are already halfway in understanding the code for a new type of chip. I hope you enjoyed it!
erm. I know it's not the point, but your microsecond perfect blink is being looped by the compiled skeych - a bunch of extra code that you have very little control over and is adding unknown time to that second. Even without taking any interupts into account.
Indeed, but it's a demo. What's a few nanoseconds amongst friends (rhetorical)?
Hi Ralph, good video. In the spirit of teaching, Assembly is a low level programming language that gets assembled by the assembler. And then linked by the linker.
Strictly speaking, we're still using the avr-gcc compiler, right? (Which might internally switch to a dedicated assembler when it sees that ".S" file, I don't know). But always good to keep things straight.
@@RalphBacon The avr-gcc command is not really a compiler but rather a compiler *driver*. Its job is to call the compiler proper (cc1 for C, cc1plus for C++, none for assembly source), then the assembler, and finally the linker. And give those commands a significant list of options. You can run avr-gcc with the “-v” option if you want to see the commands it runs.
Thank you, perfect!
You're welcome!
Really interesting - thanks Ralph! Did some PIC assembler years ago - we are so spoiled these days :)
Very cool! I wrote a PIC assembler program last time in about 2012. Painful and tedious. Thank goodness for compilers.
Lovely work Ralph. Very good explanation! 👍🙂
Thank you! Cheers!
Cheers for that - I wrote a $h!t load of 6502 / 68000 / 8088 assembler back in the day for industrial electronic uses, how we could have used a friendly higher level langauge compiler back then! I was telling my Prof that C was the language of the future (according to Byte) but he would 'ave none of it, and so we toiled on with Assembler. But handy to know how to incorporate it into Arduino code, thanks for the overview.
Compilers are the enemy of *Freedom*
Spend months looking for why volatile is not really volatile then now I need to do DSP Audio Special Effects in the manner of building up terrain image from Side Looking Radar with special emphasis on Dracula Commination's for my Stage Production and have it complete by 31 October 2020 for extraction of Beetle Juice ticket buyers
•
4-Layer OS on P-5 with *NO MIDNIGHT PHONE CALLS FROM INVESTORS!!!!*
◘
handshake by D_H Key Exchange confirming Nonce conveyed by AF Intel Courier ~ dressed in Prada Linea Rossa PS 54IS 5AV5Z1 65 Gunmetal Man Rectangle Sunglasses
Folks the *absolute* funniest thing that ever happened to me is a lifetime IBM coder attempts to assist a musician whom states he has to have his production running in Java MIDI in 3 months
TCU: Our agent says convincingly Pravda means Truth in Russian Intel
Pull back medium
Why have you betrayed us?
B-Roll of Winged monkeys - VO "I’ll Make You Go Insane!"
Those who betray us live so that we may teach our trainee's
We want none of that new-fangled C language here, that's for sure. Oh. Hang on...
for a sunday morning, THAT HAS BEEN MOST INFORMATIVE+INSPIRATIONAL, you have demonstrated to come from arduino sketch to call an assembler routine, how does on do the reverse, from assembler to arduino sketch ??? thanks in advance for reading and answering !!!
OMG you now want to call a function from the C++ from Assembler? I'm not even sure that it can be done (but no doubt someone has done it, probably by declaring the function as external and letting the linker find it). Now, why would you even want to do this? (Ich bin der Meinung dass du spinnst ein bisschen)?
you could probably force a function to be at a certain address, and then branch to that address from asm.
if you want to pass values, just call it normally in C, and then dissassemble it. check what registers, or what order of your variables on the stack is.
then you can go back and do it in asm.
not sure if this is possible, I only know 65c816 assembly, which no one uses the C compiler for it.
or better yet, create a pointer variable like my/counter, which points to the method, and jump to that.
Wow Ralph, pic assembly that takes me back probably mid/late 90's. Pic 16c55 one time programmable, had to use I think the JW variant that was UV erasable took forever to develop code for it having to put it through the UV eraser every time!
I never used a UV eraser, thank goodness, as with the amount of erasing I'd have to do I'd still be there now.
Great stuff indeed. Got me inspired to try some things on the ATtiny13A... Thank you Ralph!
Great to hear!
Thank you so much for this awesome tutorial. Assembly can be very amazing.
You're very welcome!
Great video. Although it did start a series of flash-backs to writing assembler for Motorola 6800 and 6809s. At the time there was no added work. Having to consider a 16 bit number as two 8 bit numbers was just the natural way. All this changed when we started writing C, under OS9.
OH! There's another flash back to coding in "machine language" using the op codes printed on a folded card. Never got into microcoding.
The most stupid thing ever asked of me was to convert some Z80 assembler to 6502 assembler, and all it did was display a digital clock with large digits on a (dumb) screen!
Thanks for this video. An exact 10ms delay routine use always a “timer-ISR”, I miss the example for this in assembler for 100 usek or 1000 usek.
A 10ms Counter can take the 100usek ISR for calculating if it was 100 times called. Mixing code in assembler with C++ code is a good way to solve timing problems. C++ compiler are not so good in optimizing code for cycle reduction.
Great tip! thanks, Paul.
"I used to write PIC assembler". The first step to recovery is admitting that you have a serious problem. I feel your pain. Once Arduino came along, my PIC programming days pretty much ended. I thought I recognized your name from the piclist. I used to post on there a lot, but it's been quite some time. PICs are great, but the risc assembler sets the standard for coding ugliness. AVR assembler is so much nicer.
I'm still in rehab, Tony, but I'm sure I will be cured of all PIC-related assembler any day now. Frankly, I find C/C++ so therapeutic that I will probably never (say never) use assembler seriously again!
Wow, the way it took hours of coding to make that LED blink, so almost precisely.
It would be interesting to see a pro/con table in your mindset for Assembler usage/value vs A Higher level language. Personally, I was happy to leave assembler behind for such fancy things as Cobol and Fortran pre 1980. Even BASIC on my C64 made my heart thump in those days, now it's more about the fastest way to achieve my goal(within limitation) as I'm not Government employed or production orientated.
A Canted view perhaps , but still a very interesting and well presented video, but like skydiving videos, I doubt I'll try it.
😉
Advantages of High Level Language (eg C++, BASIC, Python):
1. Easy to read, use and implement.
2. Easy to change.
3. Easy to understand.
4. Lots of examples on the 'net.
Advantages to using Assembler:
1. Absolute speed if you can write code better than a compiler.
2. Lots of debugging time.
3. Lots of time to look for a decent High Level IDE.
'Nuff said?
Hey Ralph! In the asm part, you did not safe registers before used. This can work, depending what the gcc produced for the C part of your sketch, but it also can make things utterly fail. So it might be wise to safe registers (push) before using in asm, an restore (pop) them again when done in asm. At least for the registers you do use in the asm section. Best wishes from Berlin!
I was worried about this too, Sebastian, but the avr-gcc manual is clear on this:
"The call-used or call-clobbered general purpose registers (GPRs) are registers that might be destroyed (clobbered) by a function call.
"R18-R27, R30, R31
These GPRs are call clobbered. An ordinary function may use them *without restoring* the contents. Interrupt service routines (ISRs) must save and restore each register they use."
So we are safe, thankfully! And Gruesse as Milton Keynes to you too!
gcc.gnu.org/wiki/avr-gcc
@@RalphBacon Hey! Good to know that this is not going to cause issues.
Btw. Do not forget to safe and restore status-flags within ISRs, since the interrupt could once in a while take place while inside e.g. a "if(x==y)...;" Where the ISR takes place right after the compare instruction, but before the e.g. "branch if Zero" asm instruction, in which case the status might be changed in the ISR and the branch after return from interrupt would never take place while it otherwise would had been, or vice versa would take place while it otherwise would not have. A very common trap in assembler for many, and extremly hard to detect by observation at runtime, since it only happens very rarely of course.
Besides that, I hope your eyes are doing perfectly well and your little hairy friend has found a nice shady place to rest for the eternal mice hunting to come.
All my best wishes!
ps. a simple example for an asm ISR:
__ISR_INT0:
push r15
in r15, SREG
;;
;; some assembler stuff
;; more assembler stuff
;;
out SREG, r15
pop r15
reti
Very true what you say there, Sebastian. I shall have to remember that about ISRs, not that I really intend to program in assembler.
My eyes are doing "OK", laser in one of them last week, that has improved things too. Regarding Benny, he is resting in his sleeping cat run on our sideboard. And, yes, we still miss him like crazy. Thanks for your thoughts.
@@RalphBacon Oh, noes! :-)
Oi! Don't be scared about Assembler! Using lots of Macros, it becomes as handy as C ... or in other words, the more "Macros" you develop over time, the more it becomes like a simplistic "C".
Oi, give it a try! It's fun ... --- How about a video about Assembler Macros, correct ISRs in Assembler and the like perhaps? :-)
You've got your first two Macros already for ISRs in some *.S project:
.MACRO PUSH_FLAGS
push r15
in r15, SREG
.ENDMACRO
.MACRO POP_FLAGS
out SREG, r15
pop r15
.ENDMACRO
Here is some new ones for your "MyMacros.inc" file:
.MACRO INIT_SP
ldi TL, LOW( @0 )
ldi TH, HIGH( @0 )
out SPL, TL
out SPH, TH
.ENDMACRO
;; load a register pair (first argument=@0) with
;; a 16 bit constant (second argument=@1)
.MACRO LDIW
ldi @0L, LOW( @1 )
ldi @0H, HIGH( @1 )
.ENDMACRO
;; add a 16 bit constant 16 to a register pair:
;; usage: "ADDIW , "
.MACRO ADDIW
subi @0L, LOW( -@1 )
sbci @0H, HIGH( -@1 )
.ENDMACRO
Arduino style (init/loop) example (in Atmel Studio 7):
.include "m328Pdef.inc"
.include "MyMacros.inc"
;; define a register pair "T" where
;; r16=low part of "T" and
;; r17 is the high part of register pair "T"
.def TL = r16
.def TH = r17
.org 0x0000
rjmp _init
.org INT0addr ;; take a look in m328Pdef.inc for interrupt vector declarations
rjmp __isr_int0
.org 0x200
_init:
INIT_SP ( SRAM_START + SRAM_SIZE ) ;; init stack pointer
;;
;; some asm code
;;
rjmp _loop
__isr_int0:
PUSH_FLAGS ;; safe flags
;;
;; some asm code
;;
POP_FLAGS ;; restore flags
reti
_loop:
;;
;; ... some asm code ...
;;
;; load 16 bit constant into RegPair "T"
LDIW T, 0x1020
;; add 16 bit constant to RegPair "T"
ADDIW T, 0x1234
;;
;; ... some more asm code ...
;;
rjmp _loop
Glad to hear your eyes are doing better ... thumbs up for you and hopefully enjoy your NAAFI store "steak and onions pie" while performing some nifty asm! Hehe!
What IDE are you using? It looks like Eclipse. I have not seen videos showing Eclipse used for Arduino boards.
Yes, it's Eclipse Sloeber version (ie with the Arduino plugin).
@@RalphBacon Thanks for the tip
back again :) re-watched a great reference video :)
Thanks again! Actually, even I do that sometimes. Well, at my age I think "What did I say about x,y,z on this processor?". My video has the answer! It's not _what_ you know it's knowing _where to look_ that's important!
@@RalphBacon spot on im 50+ and the memory is not like it used to be lol :)
Hi again, my Friend !
Assembler language reminds me when I first started programming those PICs. It evolved, for sure, but you lot me at « Assembler » ! Hahahaha !! (Just joking !). I guess, with old age and memory starting to fail a bit on the sides, one trends to keep it as simple as it gets with Arduino programmation. LOL.
Once again, I like your way of explaining things. It makes it simple to understand and and I appreciate it very much.
Have a great evening and stay healthy !
I'm just trying to keep those grey cells in condition, Daniel. Make them work!
Hi Ralph, if you want to calculate accurate timing for Arduino timers try, RTM TimerCalc . It also produces the assembly code for Arduino.
He was just trying to demonstrate working assembler code. He said at the beginning that there were better ways to do the timing.
I could have actually used Timer0 but the code just got too complex for beginners to follow. As Mike says, it was just a demo, not really for "production" code. But I like the idea of TimerCalc so out of all the hits this is one that writes Arduino code:
www.arduinoslovakia.eu/application/timer-calculator
brill video jusy what i was looking for top stuff :) thank you
Great to hear!
you did it again...great content. I will play with this.My cat says "Bacon Bacon Bacon!!"
I already love your cat!
04:59:22.998 -> That's a blink:241
04:59:24.002 -> That's a blink:244
04:59:25.006 -> That's a blink:247
04:59:26.002 -> That's a blink:250
04:59:27.022 -> That's a blink:253
Notice it is off a little assuming the goal was to make it once per second. Which variable do I vary in the routine to change that?
I can't say off hand (and I'm not looking at that assembler code again, sorry) but the goal of the video was not to make an LED flash every second (that was a hypothetical use case) but to actually run the assembler from within the Arduino sketch _and_ get the result.
If you're super interested in getting it exact, note that there are two loops running in the assembler and that determines the delay - but it will always be slightly off as the first loop takes a few cycles longer... or shorter, I can't remember now but I gave up on improving it after that!
Note that the loop() includes a Serial.print command, which takes some time, and the assembly code is only called again after the print. Also, there may be some arduino overhead between each call to loop().
I wish AVR's had something like the DWT counter in Cortex processors. Really nifty little thing for figuring out exactly how long something took without the need for cycle stealing interrupts :)
The good old Debug and Watch Trace? You'd still have to disable interrupts though, right?
For cycle counting, I usually set Timer 1 to run in 16-bit mode at the full CPU speed, take a reading of TCNT1 right before and right after the thing I wan to time, and compute the difference. The overhead of this method is typically about 4 CPU cycles.
Very interesting, thanks for sharing!
Thanks for watching!
Hi Ralph,
SBI PINB,5 will toggle PORTB, PIN5.
Call it twice to flash the LED. No need to SBI and CBI PORTB in your blink function.
Saves typing! ;-)
Cheers,
Norm.
Thanks, Norm, always good to know these things; as you can tell I'm not an assembler programmer!
@@RalphBacon You don't need to be one! It's in the data sheet. The designers used the PORT register for input pins to enable pull-ups because PORT isn't needed for input, and used the PIN register to toggle output pins, because PIN wasn't needed for output pins.
This, and much more can be found in "Arduino Software Internals" from Apress. (Yes, unashamed plug, I wrote it! There's even a bit of assembly code in there too, but not much.)
Take care.
Cheers,
Norm.
Ah ha! So you _are_ indeed the expert. I might even have a sneak peak at that book of yours...
@@RalphBacon Ha! "Ex" as in "unwanted", "spurt" as in "drip under pressure"! That's me. ;-)
I had a sneak peak at your book on Amazon.
When you pick up a book (even virtually) they either grab you (me) or leave you (me) cold. This book grabbed me. I was hooked from the moment I saw that *sbi* was a C++ macro. I know, I'm weird. Ordered. Will review in due course and potentially share with my viewers. And stop being so self deprecating, I know that's a British thing to do but still! I'm quite envious of you that you have published works.
You don't need the “volatile” qualifier, as the counter is not modified in the background: it is modified by a function your C++ code calls explicitly. The compiler assumes that any external function can potentially have side effects, such as modifying externally visible global variables. You would need to declare the function with __attribute__((pure)) in order for the compiler to assume it has no side effects. Only then would you need the “volatile” qualifier.
And you don't need __attribute__((used)) either: the compiler can only optimize-out a global variable if it has internal linkage, i.e. the “static” qualifier. It cannot optimize-out a global variable with external linkage. The linker can, but it also can see that the counter is used by the assembly code.
This is the sort of information I had searched for, Edgar, but could not find any original verification. Have you got any links to citations where this has been documented by avr-gcc? I did wonder whether the compiler/linker was "clever" enough to tie things together, including the registers that are clobbered.
@@RalphBacon Re “Have you got any links”: Unfortunately not, as I acquired by knowledge through many years of practice and browsing many sources. My favorite sources are cppreference.com and stackoverflow.com (through Google) for the C and C++ languages, and gcc.gnu.org/onlinedocs/ for the gcc specifics, like the attributes. But these are not tutorial material.
Re “I did wonder whether the compiler/linker was ‘clever’ enough to tie things together, including the registers that are clobbered.”: It is not: it doesn't try to analyze the assembly you write. It just assumes that the functions you write (in assembly or any other language) only clobber the registers they are allowed to clobber as per the ABI: r0, r18-r27, r30 and r31. See gcc.gnu.org/wiki/avr-gcc
great video as usual.
Calling assembler prices is a bit of a stretch. The crystals is not that prices, it can still be a few hertz of but we do know how many clock cycles a given instruction takes.
It can bring up a discussion on the difference between precision and accuracy and "what is necessary". Let's not open that can of worms.
With assembler everything is just more complex and you really do need to plan ahead. Variables, registers, addresses and such do need some constitution.
The higher the level of program language, the easier to is to handle those issues, in most cases you don't even think about it.
As i remember, the "word" size is one of the earliest measures, earlier than byte. It had an odd size, by today's standard, I think it was 14 or 15 bit long.
A crystal is very precise, unlike a ceramic resonator, which is why we use them. The few I've ever measured were within a few Hz of the specified frequency. On that basis I guess this is accurate (and precise) enough for a demo!
The Serial communications will affect your delay.
Yes, it will, as will the entire wrapper sketch, but at least it's a fixed delay. Don't forget, it's just a demo!
@@RalphBacon The serial and timer interrupts from the sketch overhead will cause your timer loops to halt long enough to service the interrupts before continuing. You could avoid this by clearing (cli) and re-enabling (sei) interrupts; but then the serial communications and Arduino delays will not work correctly. To get truly correct millisecond and second scale timing, you need to use hardware timers and hardware interrupts like Arduino has done.
Yes, I'm aware of that but the assembler code to use a timer would have confused my viewers! This might not be totally accurate but at least everyone can follow the code.
@@RalphBacon
Correctly we have do disable *ALL* interrupts and push the stack such as to save all RTOS relevant flags and registers which I guess what Byron Watkins might be how to do it · P-5 and up has an rdtsc instruction, which returns the processor time stamp then code could run something then T-1 minus T-0 and store that to some auxiliary register that is not used or something ~ I bought one of these intending to use it for control but just hard-wired a pull-down relay >> Apparently from what I remember it has a countdown timer with would drop in to some interrupt handler but how to save and restore state on an NMI becomes confounding
Excellent! WTG Ralph - BTW, What is exactly the IDE you are using in this video? As far as the NEW Arduino IDE I can say that it takes a very long time to start-up - don't know really why it doe that? BUT I was thinking to move to a better Arduino IDE based alternative - I would be happy to know what is the one you are using? As always, Thanks for the videos!
Remember that the _new_ Arduino PRO IDE is still very much in Alpha mode; it should run slicker when in Beta mode and certainly when released. I'm using Eclipse Sloeber (has the Arduino plugin), works very well but is not being developed any further.
I know this is an example on how to use assembler. But just to nitpick on you saying "blink exactly every second": this sketch does not do it, assembler or not. You still have Arduino's standard timer interrupt and the whole (invisible) Arduino outer loop (which calls the sketch's "loop()") "using up" clocks. But you are definitely a little bit closer to "exactly 1 second", because you replaced the overhead heavy digitalWrite with an assembler instruction ;) .
you don't need to tell all that knowing that Arduino 99% timers uses ceramic crystal and how bad they are for precision.
Well, my 10mS delay does run in exactly 10mS if the crystal frequency is correct (and my past measurements of crystals indicate they are very, very precise). Or at least it takes 160,000 cycles! The loop as a whole gets more inaccurate the bigger the delay and I didn't disable the Timer0 interrupt so it's all a bit of a near miss, really. Close, but no cigar!
which ide were you using in this excellent video?
I probably used the Eclipse Sloeber version, but it's no longer being actively developed or supported. The new Arduino IDE is based on Eclipse though so that should be much better when released. Or do what I did and use PlatformIO with Visual Code.
eclipse.baeyens.it/
Thank you for a great video
Glad you enjoyed it
Very informative video SIR
Thanks and welcome!
Great video. 😃
Thanks for the visit, Henk. My viewers are getting younger and younger 😜
@@RalphBacon I'm 76. 😂 (still young!)
Great stuff, thanks. Got it as a UA-cam recommendation. So far I've managed to avoid assembler even though I've been doing audio-related stuff, timing obviously important.
Couple of questions maybe you can help with, save me trawling: I'm using PlatformIO (on VSCode, on Ubuntu). Are there any differences I should be aware of for ASM in this environment? The other thing - obviously there will be significant differences when targeting different boards (ESP32, Arduino Due), but in practice, ballparky, how similar will the ASM be? (I've only glanced at the instruction sets - eek!)
It's the compiler/assembler that determines which instructions you must use, Danny. If you use the (Arduino-standard) avr-gcc (that calls the avr-as) then you must use those opcodes. If you use the Atmel compiler/assembler then it's (slightly) different. I don't know which one your PlatformIO environment uses; it might depend on which language(s) you have installed in VS. There are _considerable differences_ between writing assembler for the 8-bit Atmel 328P vs the 32-bit ESP32 (Tensilica Xtensa) processor. May the force be with you!
@@RalphBacon thank you. Enough said. I suppose I was thinking some kind of commonality - any Arduino-ish thing same C code works in Arduino as very different chips. Not naive, no no no.
I would recommend the PlatformIO thing, when I was working with Arduino IDE, everything wound up in the same file. Still scared of doing from command line
Would this general calling setup and assembler syntax style work in the Arduino IDE for ARM-based Arduinos like the MKR series? Obviously the specific instructions and registers and I/O would need to be the ARM ones, but other than that is the overall principle the same?
Thanks for the great video. Flashbacks for me to PDP11/10 assembler in the 1970s and Z80 in the 80s.
Well, you have to get the code into the microcontroller in the first place; if the Arduino IDE supports it then there is no reason this should not work in the way you describe.
@@RalphBacon Thanks, yes the Arduino IDE supports the ARM processor Arduinos so no problem there. It took some work to find the exact correct assembler directives and syntax for the Cortex-M0 Thumb instructions, but everything you showed with regards to defining the external routines and variables and passing them and setting up the assembler labels to match worked perfectly. Thanks for that.
Great video, thank you for explaining to me the uint16_t I have seen this in other people's code and have been frightened of it as the internet doesn't help
You're welcome!
Hi, mi name is Julian from argentina, I have found intresting this helpfully info because I am try to build a floppy emulator for my tandy 1000and assembler is the best option for the r/w realtime routines. thanks to share!
Well, Julian, only you can determine whether Assembler or C++ works best for a floppy disk emulator - I'd be inclined to try C++ first, as it is about as quick as assembler (especially if you don't use the Arduino-speak language) and much easier to code! Best greetings to Argentina!
@@RalphBacon intresting answer. The first step is to make a read only with a few tracks disk stored in the arduino rom memory. So i can test the timings in c++ first. Thanks you!
Doesn't the clock circuit in the micro divide the crystal freq in half?
No, the micro runs at the full 16MHz, Raymond. You can set the bootloader flags (fuses) to divide by 8 but that's about it. The datasheet for the ATMega328P specifically states that you can get "Up to 16 MIPS throughput at 16MHz" but that's assuming all the instructions you are running are single-clock cycle instructions!
good video.
Thanks for the visit!
Hi., Very informative video. I am assembly language programmer and I have assembly codes written in AVR studio. I would like to include those routines in arduino IDE. If not please let me know where I can find Arduino Assembler. Thanks
The Arduino Assembler is part of the AVR-GCC Compiler. It can be invoked separately if required, but usually Assembler is called from within C when it is required.
@@RalphBacon
Thank you
How do you ensure that the registers you use are not used by the compiler generated functions? Does the compiler guarantee registers it won't touch?
I enjoy seeing the assembler stuff. I was on the design team for the Magnavox/Philips Odyssey w, and the cartridges were usually 2k ROMs, programmed in Intel 8048 assembler. I did three or four of them.
An interesting question, Mike, and one for which I searched for the answer long before I did the video. I could not find any sensible answer, so I _assume_ that because the whole thing is being compiled at the same time, any registers that are clobbered by my assembler sketch will be "noticed" by the compiler and dealt with accordingly.
If I had written this "in-line" in the sketch as one big string (note to others, don't _ever_ do that, it's horrendous) you have to then list the clobbered registers so that the compiler knows as it does not ever assemble your code - it just leaves it "as-is".
That's my 10 cents / 2-penneth worth on this - anybody else have any *evidence* on how this works?
@@RalphBacon Perhaps just put some of the register values as output in a test sketch? Run a sketch that sets pins high, calls register values from the assembler, prints them, sets pins lows, rinse and repeat, do the same for assigning various types of variable, increments them, conditionals, etc. I don't see why this wouldn't work so long as the assembly code never changes the value of a register. Run the test before coding any sketches to make sure there are no conflicts?
Michael, you've just volunteered for this. Good Luck.
@@RalphBacon Thanks. To be honest, I couldn't think of a better project to learn about both Arduino "c" and AVR assembly.
This is defined by the ABI, which is documented in the GCC Wiki: gcc.gnu.org/wiki/avr-gcc
Meh! I thought you were going to present an assembly compiler running inside an arduino.
Me too :)
C'mon guys, why would I write a compiler/assembler inside an Arduino when there's a mature avr-gcc compiler available?!
@@RalphBacon It's really about the misleading title of your video. Assembly language has always been available for embedded processors, when I started coding I didn't even have the luxury of a compiler/assembler (I had no access to a university main frame) and had to hand write machine code. Writing time critical code components in assembler was still sometimes required when the C compilers came along. The video title suggested to me that you had something new. @Ralph S Bacon and I were both successfully click-baited.
Anyone know how I would shift a bit over a certain amount of times using multiple led's?
To shift a bit we would do > to right shift it).
Interesting... I generally die a little inside every time I have to do work in assembly lol.
Ha! As I said, it's just soooo long-winded. And error-prone.
is there any way to get the Arduino IDE to make an assembler listing of the entire sketch?
You can put the hex file through a disassembler but it won't represent anything that a human being would write.
@@RalphBacon right, it wouldn't have the labels.
I suppose it could, but they would be meaningless relatively and take a lot of work to deconstruct.
So I was looking forward to reading all of those comments in the code, but I can't seem to find a github link to download it...
Oops. The link is now in the video description and here:
github.com/RalphBacon/202-Assembler-for-Arduino
Thanks! Downloaded...
C++ only mangles function/member names, not variable names. That's why you need the extern "C" for blink() and start(). I think the use of asm("counter") is an unnecessary concept for beginners.
Doesn't the asm("counter") ensure that the compiler/assembler knows that the sketch variable "myCounter" is referred to as "counter" in the assembler code? Otherwise how does it tie the two together?
@@RalphBacon Why use different variable names in the first place? You can use "myCounter" in the C++ code and in the asm. Easy peasy.
Yes, I understand that, but I was trying to make a point here that you _can_ use different names. What if the 3rd party assembler developer had called it "x6fgh" - I'm guessing you might want a friendlier name than that in your sketch?
I finally got my first Arduino 2 months ago (20 years late, thought they were expensive and was 'afraid' of the AVR suite a had seen then...)
I was still using a very old PC with Win98 because I could poke directly to the LPT & COM ports and control my home build interfaces, winXP does not allow that... so I stuck on QBasic and VB6. I am used to walk in the (some what) dark corridors of programming (API's) but was not familiar with C (+-#@ wse) at least I thought so. (POV-Ray 'script' is a variant of C...) Most of my programming is copy/paste (Syntax Terror...)
This video Is very clear to me, perhaps I am more advanced than I think ?
i know you know best (i am talking to compiler ..not to my wife).::::-----)))
Always good to clarify!
Flashing LEDs Oh the Excitment....
I know, I could hardly contain myself as I built this.
You must work somewhere as a programmer I'm guessing in C/C++ and you're used to using the debugging mode where asm code is referenced as you step through the code.
If I could I'd be doing all my coding in a professional grade enterprise IDE where you only have to specify a target platform.
I've definitely been "spoiled" by having a "proper" debugging environment made available for me - but that's the way it's always been in my professional (coding) life.
That said, the new line-by-line, single-stepping debugging features in the Arduino IDE 2.0 look promising (for newer chips) and something I'll be exploring in more detail.
All in all it gives me a little idea of why:
𝘷𝘰𝘪𝘥 𝘭𝘰𝘰𝘱() {
𝘥𝘪𝘨𝘪𝘵𝘢𝘭𝘞𝘳𝘪𝘵𝘦(𝘓𝘌𝘋_𝘉𝘜𝘐𝘓𝘛𝘐𝘕, !𝘥𝘪𝘨𝘪𝘵𝘢𝘭𝘙𝘦𝘢𝘥(𝘓𝘌𝘋_𝘉𝘜𝘐𝘓𝘛𝘐𝘕));
𝘥𝘦𝘭𝘢𝘺(1000);
}
Compiles to more bytes (960) than the origin (924).
Somethings is happening under the skin :-)
Actually, LOTS is happening "under the skin", as you put it, or perhaps, behind the scenes. Nothing wrong with that, if it makes millions of developers' lives easier but it it good to know that something is happening somewhere!
All in all, I think Arduino-speak is pretty good, but you can't use it in a job; you will have to learn and use native C++.
@@RalphBacon C++ would be way overkill for me and when you mention making 'developers' lives easier', I've started to use Great Cow Basic for programming 8-bits.
The code above actually derives from an even shorter code in Basic.
You could try to take a look:
gcbasic.sourceforge.net/Typesetter/index.php/Comming-from-another-programming-environment
See, - That's how you make live easier :-)
Yes, I've seen Great Cow BASIC and it's very good for beginners and hobbyists alike. But as the Arduino environment is geared to C/C++ and I happen to know C (well, a variant) it seems an obvious decision for me to continue using that.
What assembler do you use here?
None, it's all part of the Arduino IDE AVR-GCC compiler. Although you can invoke the assembler part separately if you want, but I didn't really cover that.
@@RalphBacon GUI is not familiar to me.
Everyone in the world knows the Arduino IDE, very simple (too simple, one might say) but it does allow assembler language:
www.arduino.cc/en/software
You would use gcc-avr plus associated libraries.
__asm__ __volatile__(
"addb $1, %0
"
"adcb $0, %1
"
"ret
"
:"=m"(COUNTER_LO),"=m"(COUNTER_HI)
: "m"(COUNTER_LO), "m"(COUNTER_HI)
:
);
Writing inline assembler like this was something I was keen to avoid at all costs!
are you related to Francis Bacon
Yes, he was my great grandfather. Obviously.
@@RalphBacon you would be over 300 years old.
You mother use Assembler 😂😂😂
Highly unlikely, unless it was assembling the strange cake mixing contraption!
Knowing she's a CS student and when she became one, yea she probably touched on it once or twice.
Assembler is for old sad gays😂😂😂😂
And straight people too, sad or otherwise? 🫨😮😆