Pure AVR Assembler programming

Cleaning compilation files is done by executing the pio run -t clean target.

You can also just create projects from the CLI without having to generate project files for an IDE. For example, run pio run -b attiny85 in an empty folder. Then adapt the platformio.ini to your needs again and upload using the CLI with pio run -t upload etc.

in the Terminal it gives me: pio: command not found

Ah, because when you install the PlatformIO IDE (VScode), it has its own seperate PIO core and is not included in the system path. You can install a system-wide PlatformIO using pip install -U platformio and then, if needed, tell the VSCode PIO plugin to use the global core instead of the builtin core. See Redirecting.... Also refer to Redirecting....

That was helpful, thank you again! I read through the documentation, I am able to use pio commands from the bash and I’m now able to create workspace folders and write assembly code in it. What i didn’t make running was to integrate e.g. a file like tn85def.inc … lets say i create this file in the include folder of a workspace. The file was only recognized when I saved it (copy and paste in Finder) into my project folder, but gave me a lot of errors (e.g. tn85def.inc:664: Error: expected comma after “PCI0addr”).
Any idea how i can integrate certain *.inc files? (in your first comment you warned me of using files such as tn85def.inc because they might cause syntax issues. Assume its happening what you meant, isn’t it?)

Exactly. If it’s all just definitions, a well-written regex will transform the .EQU XY = Z; into C-style #define XY Z or whatever syntax is accepted by the avr-as assembler invoked by avr-gcc.

… but isn’t somewhere with the PlatformIO IDE Package coming a folder with all Atmel Chip definitions, which I could just integrate in a certain way?
If not, I should integrate register definitions in the header of my codes for keeping it minimal and simple (can be a good way of learning and memorizing by heart all hex addresses of any register, port and so on of these Atmel chips)

1 Like

Yes. I take everything back. The toolchain-atmelavr\avr\include\avr folder has everything you need. There is avr/iotnx5.h, and avr/iotn85.h etc for the the I/O definitions, though you must use #include <avr/io.h> to include them. You basically have avr-libc/avr-libc/include at master · vancegroup-mirrors/avr-libc · GitHub. This works the same:

#include <avr/io.h>

.global main

main:
	ldi r16,0b00100000
	out DDRB,r16
	out PORTB,r16
Start:
	rjmp Start

Unfortunately I dont find that folder in my PlatformIO installation. How can I install and integrate it in my system so that i can just add the header #include <avr/io.h>?
Thanks for your patience and help!

In your PIO home folder (/home/<user>/.platformio or C:\Users\<user>\.platformio) you must have packages/toolchain-atmelavr folder where the compiler tools are, otherwise you wouldn’t have been able to compile the previous project. Do you find your PIO home folder at the expected place? (maybe cd / && find . -name ".platformio"?)

Yes. Found it. But when I use the #include … header, its compiling without errors, but the LEDs (even after playing a bit around with the LEDS or setting to HIGH other bits at the port, the LED doesnt light up.

And when using the previous #defines does it work or not work? PB5 is reset on the ATTiny85, maybe you should try another pin, like PB3.

Yes, the #define commands work.
Even the include <var/io.h> command doesnt produce an error when I “Upload”, but when I “Build” it says that "no such file or directory. When i put the entire folder root, nothing. “Upload” doesnt produce an error, but of course the LED do not light up (only with the #define commands.

In this simple program you can maybe use the avr-objdump tool disassemble the binary again to see where the difference is in the instruction code.

That tool is awesome. Thanks a lot. will play arround a bit. … not sure what I am looking for actually…

It disassembles you the entire firmware. Look e.g. here

C:\Users\Maxi\Desktop\atmega_bare_assembly\.pioenvs\attiny85>"C:\Users\Maxi\.platformio\packages\toolchain-atmelavr\bin\avr-objdump.exe" -d firmware.elf

firmware.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0e c0           rjmp    .+28            ; 0x1e <__ctors_end>
   2:   15 c0           rjmp    .+42            ; 0x2e <__bad_interrupt>
   4:   14 c0           rjmp    .+40            ; 0x2e <__bad_interrupt>
   6:   13 c0           rjmp    .+38            ; 0x2e <__bad_interrupt>
   8:   12 c0           rjmp    .+36            ; 0x2e <__bad_interrupt>
   a:   11 c0           rjmp    .+34            ; 0x2e <__bad_interrupt>
   c:   10 c0           rjmp    .+32            ; 0x2e <__bad_interrupt>
   e:   0f c0           rjmp    .+30            ; 0x2e <__bad_interrupt>
  10:   0e c0           rjmp    .+28            ; 0x2e <__bad_interrupt>
  12:   0d c0           rjmp    .+26            ; 0x2e <__bad_interrupt>
  14:   0c c0           rjmp    .+24            ; 0x2e <__bad_interrupt>
  16:   0b c0           rjmp    .+22            ; 0x2e <__bad_interrupt>
  18:   0a c0           rjmp    .+20            ; 0x2e <__bad_interrupt>
  1a:   09 c0           rjmp    .+18            ; 0x2e <__bad_interrupt>
  1c:   08 c0           rjmp    .+16            ; 0x2e <__bad_interrupt>

0000001e <__ctors_end>:
  1e:   11 24           eor     r1, r1
  20:   1f be           out     0x3f, r1        ; 63
  22:   cf e5           ldi     r28, 0x5F       ; 95
  24:   d2 e0           ldi     r29, 0x02       ; 2
  26:   de bf           out     0x3e, r29       ; 62
  28:   cd bf           out     0x3d, r28       ; 61
  2a:   02 d0           rcall   .+4             ; 0x30 <main>
  2c:   05 c0           rjmp    .+10            ; 0x38 <_exit>

0000002e <__bad_interrupt>:
  2e:   e8 cf           rjmp    .-48            ; 0x0 <__vectors>

00000030 <main>:
  30:   00 e2           ldi     r16, 0x20       ; 32
  32:   07 bf           out     0x37, r16       ; 55
  34:   08 bf           out     0x38, r16       ; 56

00000036 <Start>:
  36:   ff cf           rjmp    .-2             ; 0x36 <Start>

00000038 <_exit>:
  38:   f8 94           cli

0000003a <__stop_program>:
  3a:   ff cf           rjmp    .-2             ; 0x3a <__stop_program>

You can see the main function after the initializer-stub inserted by avr-gcc. You can also see all the interrupts in the interrupt vector table, most of them being unpopulated.

And here we can also solve the mystery of why stuff doesn’t light up anymore. In the previous program we had

#define PORTB 0x18
#define DDRB 0x17
..
	ldi r16,0b00100000
	out DDRB,r16
	out PORTB,r16

which worked, now here when using <avr/io.h> we suddenly have

00000030 <main>:
  30:   00 e2           ldi     r16, 0x20       ; 32
  32:   07 bf           out     0x37, r16       ; 55
  34:   08 bf           out     0x38, r16       ; 56

which is completley wrong. I.e. a wrong definition for DDRB and PORTB get included… probably because there isn’t the correct macro set to select the device? I’ll have to look into this later.

if 0x37 should point to the DDRB then its wrong on the first sight. But shouldn’t we also count with the 32 general purpose working registers? According to the data sheet of the Attiny85 the address of DDRB is 0x17 (see page 200 “Register Summary”) but they are not including the 32 working registers in that calculation. Because deducting these 0d32 from the 0x37 we end up at 0x17 (point is, I think it should be 31 because “0” is one register as well … too bad we don’t see what address has R16 … was reading today a couple of hours through the structure of the memory and addressing of the Atmega chips, esp. attiny85 and atmega328)

Think i found a solution and it has indeed to do with the hex address and the 32 registers. In the Atmega328 (i know, addresses of the commands are different compared to the Attiny85) datasheet on page 624 ("Register Summary), there is written at the address 0x17 the other address in brackets (0x37) and then i looked at note 4 that points out to use the ST commands that adds these 0x20 addresses (think my terminology is not sharp enough yet). So I replaced in the assembler program the command “OUT” with the command “STS”, then i uploaded, and everything worked fine.
I have to dig deeper in these differences of addressing or writing directly to the SRAM and what difference all these commands make.

… or as stated here: https://www.avrfreaks.net/forum/assembler-compiler-and-linker-questions

just integrating #define __SFR_OFFSET 0 on top of `#include avr/io.h

That works!

PS: It says in that link, that is has something to do with the fact that its expecting still some C code? How can I get the environment to accept that I just want pure AVR assembler??

1 Like

That seems to be the valid explanation and the correct fix.

Programs which use <avr/io.h> and friends are expected to be C code, but noone prevents these headers and their definitions to be used in assembler, so it’s just up to the programmer’s usage of them.

I would prefer my assembly codes being completely free of C commands or structures. So, I would like to use e.g. the .equ command instead of others and so on.
I slowly start to believe that I should leave PlatformIO aside until I start with C programming. I thought I could just use this project environment of PlatformIO in order to have my assembly code file, the additional chip specific files (e.g. tn85def.inc) as well as an implemented pdf reader, the Terminal and of course the assembling and programming functions (initiating and using the USBasp for example) together with Debugging (which is not supported for Atmel chips yet) in one place and not to worry about connecting the right files with each other, but just to focus on learning AVR assembler. Any suggestion where I could find such a platform? Because if I have to think sometimes in C terms in order to get my assembler programs running, this is not really target leading for me now. Especially, if I want to apply some assembly coding examples from the web or tutorials, I have the impression that I have to rewrite the code so that it works with my PlatformIO enviroment.