the BlinkWithoutDelay in assembler only sample program found at https://rwf.co/dokuwiki/doku.php?id=smallcpus does not seem to execute in a manner that allows LED to be observed as actually blinking on my UNO R2. Can someone please help?
In case people want to take a look at the code without visiting an external website, here it is:
Then also create a file called ‘asmtest.S’ in the same directory as the (empty) .ino file:
#include "avr/io.h" #define yl r28 #define yh r29 .global setup setup: sbi _SFR_IO_ADDR(DDRB), DDB5 ; Bit 5 = pin 13 ret // const long delay = 1000; #define delay 1000 // ms .global loop loop: push yl push yh call millis ; call millis(): 4-byte return value in r25...r22 // Use Y as a pointer to fetch the next time to switch the LED ldi yl, lo8(nextSwitchAfterMillis) ldi yh, hi8(nextSwitchAfterMillis) ld r18, y+ ld r19, y+ ld r20, y+ ld r21, y+ ld r17, y // ledStatus comes immediately after lastMillis, so we can use y // Compare nextSwitchAfterMillis with value returned by millis() sub r18, r22 sbc r19, r23 sbc r20, r24 sbc r21, r25 brcc tooEarly ; carry is set if r18...r21(nextSwitchAfterMillis) < r22...r25(millis()) // Toggle LED state: 0 -> 1, 1 -> 0 inc r17 andi r17, 1 // Store ledStatus for next time. y still points at its memory location st y, r17 // set LED state brne turnoff cbi _SFR_IO_ADDR(PORTB), PORTB5; Bit 5 = pin 13 rjmp ledSwitched turnoff: sbi _SFR_IO_ADDR(PORTB), PORTB5; Bit 5 = pin 13 ledSwitched: // Add long delay; to result of call to millis() ldi r17, lo8(delay) add r22, r17 ldi r17, hi8(delay) adc r23, r17 ldi r17, hlo8(delay) adc r24, r17 ldi r17, hhi8(delay) adc r25, r17 // Store this as the next point in time when we need to toggle the LED st -y, r25 st -y, r24 st -y, r23 st -y, r22 tooEarly: pop yh pop yl ret .data nextSwitchAfterMillis: .long 0 ledStatus: .byte 0
I verify the no blink result, but I don’t know enough about assembly to be able to troubleshoot the code.
I found this #AVR_002dModifiers bread crumb. But, I don't understand it enough to know if it helps find a solution.
22.214.171.124 Relocatable Expression Modifiers The assembler supports several modifiers when using relocatable addresses in AVR instruction operands. The general syntax is the following:
This modifier allows you to use bits 0 through 7 of an address expression as 8 bit relocatable expression.
This modifier allows you to use bits 7 through 15 of an address expression as 8 bit relocatable expression. This is useful with, for example, the AVR
ldi' instruction andlo8' modifier.
ldi r26, lo8(sym+10) ldi r27, hi8(sym+10)
This modifier allows you to use bits 16 through 23 of an address expression as 8 bit relocatable expression. Also, can be useful for loading 32 bit constants.
This modifier allows you to use bits 24 through 31 of an expression as 8 bit expression. This is useful with, for example, the AVRldi' instruction and
ldi r26, lo8(285774925) ldi r27, hi8(285774925) ldi r28, hlo8(285774925) ldi r29, hhi8(285774925) ; Druid_0011 provided explanation in next two lines :) ; 285774925 = 0x1108944D ; r29 = 0x11, r28 = 0x08, r27 = 0x94, r26 = 0x4D
This modifier allows you to use bits 0 through 7 of an address expression as 8 bit relocatable expression. This modifier useful for addressing data or code from Flash/Program memory. The using of
pm_lo8' similar tolo8'.
This modifier allows you to use bits 8 through 15 of an address expression as 8 bit relocatable expression. This modifier useful for addressing data or code from Flash/Program memory.
This modifier allows you to use bits 15 through 23 of an address expression as 8 bit relocatable expression. This modifier useful for addressing data or code from Flash/Program memory.
Are you looking for a working example of an assembly language blink program?
No, not really. The 3 phase assembly example that included a .ino, .h, and .S works on my board as specified. I want to know why my board does not work as indicated by the pure .S blinky-ware. I was thinking that I could put some leds or a volt-meter on the other digital ports to see if the signal was incorrectly routed for some strange reason. It certainly appears that it is not making itself known on Pin 13.
I guess I don’t understand what you are asking. The included code above works on a Duemilanove board of mine. I have no .h file and an empty blink.ino file. That code could be significantly cleaned.
// blink.S #include <avr/io.h> .global setup .global loop setup: sbi _SFR_IO_ADDR(DDRB), (DDB5) ;pinMode(13, OUTPUT); ret loop: sbi _SFR_IO_ADDR(PORTB), PORTB5 ;turn LED on rcall _delay cbi _SFR_IO_ADDR(PORTB), PORTB5 ;turn LED off rcall _delay rjmp loop _delay: ldi r24, 0x00 ;one second delay iteration ldi r23, 0xd4 ldi r22, 0x30 _d1: ;delay ~1 second subi r24, 1 sbci r23, 0 sbci r22, 0 brcc _d1 ret
The original code is not a good example, IMO. It’s not “pure asm”, since it calls millis() from the arduino code, which is in C or C++, and will load in significant parts of Arduino core code. It’s overly complex (auto-Increment y-register addressing to load a 32bit variable?) It’s wrong (doesn’t handle wrap of millis())
JimEli’s code is better, but still loads a lot of core code in addition to the blinking...
I can appreciate your points, westfw. thanks.
I would like to thank JimEli for the very clean .S blinky file. It does work for me and is understandable in that I can make the delay time shorter and longer. I am just getting started renewing my asm skills. The fellow* on youtube has a multipart lesson on the topic. He promises to give me "Mad Skills". LOL
*Dr. Robert Paz of the Klipsch School of Electrical and Computer Engineering
Druid_0011: ... The fellow* on youtube has a multipart lesson on the topic. He promises to give me "Mad Skills". LOL ...
use the force for good
Huh. This was puzzling, because the code looks ok. I think I've tracked it down to...
.data nextSwitchAfterMillis: .long 0
The .data section doesn't seem to be getting initialized, and contains random values. Change it to the following and things work...
.global setup setup: sbi _SFR_IO_ADDR(DDRB), DDB5 ; Bit 5 = pin 13 ldi r30, lo8(nextSwitchAfterMillis) ldi r31, hi8(nextSwitchAfterMillis) st z+, r1 ;; zero nextSwitchAfterMillis st z+, r1 st z+, r1 st z+, r1 ret
The next question: why isn't the .data section getting initialized???
You nailed it.
Program is running with your changes. Thanks!
Shifu Westfw, when you have a spare minute or two -
could you please share what you may know about
with an aim to using timer1 in lieu of millis();
in a pure .S program built on the Arduino IDE
a la this youtube vid.
The next question: why isn't the .data section getting initialized???
Apparently, if there is no initialized data in the C/C++ part of your program, nothing signals the linker to include the code that copies the the values from flash to RAM at the start of the program. (a function called __do_copy_data()) It is sufficient to add this statement to the .S file:
.global __do_copy_data ; ensure that .data gets copied from flash at startup
It all seems a bit strange, but it does seem to work. This probably means that the program worked correctly when it was first written - all that would have been required is a bit of .data in the parts of the Arduino core that are "not seen here", but included in the build. Parts that are either no longer included, or that have changed the way that they initialize.
That is very interesting and impressive. Is there someone to inform that they might enable a documentation/fixation of this newly discovered "six legged feature"? Just a thought, no pressure.
You can report the issue with the code in the comment section here: https://rorohiko.blogspot.com/2012/12/fun-with-arduino.html