Stand alone .S assembly sample loads but does not BLINK

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:
https://rwf.co/dokuwiki/doku.php?id=smallcpus#blinkwithoutdelay_in_assembler_only

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.

9.4.2.3 Relocatable Expression Modifiers The assembler supports several modifiers when using relocatable addresses in AVR instruction operands. The general syntax is the following:

modifier(relocatable-expression) lo8 This modifier allows you to use bits 0 through 7 of an address expression as 8 bit relocatable expression. hi8 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. For example

ldi r26, lo8(sym+10) ldi r27, hi8(sym+10)

hh8 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. hlo8 Synonym of hh8'. hhi8 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 lo8',hi8', hlo8',hhi8', modifier. For example

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

pm_lo8 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'. pm_hi8 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. pm_hh8 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???

:grin:

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

#include <avr/interrupt.h>

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.

Happy Trails!

Post Script:
Being a cleaver lad; I did this forum search and some other searches
and discovered that you have a deep knowledge of the ATmega328P.

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