Well the :
is wrong, that should be a =
. But there are additional caveats: The -nostartfiles
is a linkerflag, in terms of PlatformIO / SCons build system internals that should be a entry in the env["LINKFLAGS"]
entry. Further, when -nostartfiles
is added and PlatformIO applies the standard -Wl,--gc-sections
(garbage collect sections) and -flto
(link-time optimization) options, the resulting firmware would be empty because the compiler, without its default startup files and vector table, does not see how any function would be used at all and removes it (bootloader - Compiling with -flto and -nostartfiles results in no program at all - Arduino Stack Exchange). So to prevent that, we have to take care of add -nostartfiles
as well as removing the offending -Wl,--gc-sections
option (removing this one suffices). PlatformIO’s built-in scripting capabilities can be used for that.
So a project with
platformio.ini
:
[env:nano_every]
platform = atmelmegaavr
board = nano_every
extra_scripts = no_startfiles.py
no_startfiles.py
(this file has to be in the same folder as the platformio.ini
)
Import("env")
# add nostartfiles option
env.Append(LINKFLAGS=["-nostartfiles"])
# remove default garbage-collection of sections, it kills off the custom code
old_linkflags = env["LINKFLAGS"]
new_linkflags = [x for x in old_linkflags if x != "-Wl,--gc-sections" ]
env["LINKFLAGS"] = new_linkflags
and src\main.S
.global main
.global ccl_vect
.global porta_ext_int_vect
.section .text
isr_table:
.org 0x00
jmp main
.org 0x14
jmp ccl_vect
.org 0x18
jmp porta_ext_int_vect
// first free place after 40-entry vector table with 2 flash WORDS per entry
.org 0xa0
main:
RJMP main //hang at the same place
// vector 5 = CCL - Configurable Custom Logic (CCL_CCL_vect)
ccl_vect:
RETI
// vector 6 = PORTA - External interrupt (PORTA_PORT_vect)
porta_ext_int_vect:
RETI
results in
Compiling .pio\build\nano_every\src\main.o
Linking .pio\build\nano_every\firmware.elf
Checking size .pio\build\nano_every\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM: [ ] 0.0% (used 0 bytes from 6144 bytes)
Flash: [ ] 0.3% (used 166 bytes from 48640 bytes)
Building .pio\build\nano_every\firmware.hex
and when we disassemble the final firmware again:
>C:\Users\Max\.platformio\packages\toolchain-atmelavr\bin\avr-objdump.exe -d .pio\build\nano_every\firmware.elf
.pio\build\nano_every\firmware.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__ctors_end>:
0: 0c 94 50 00 jmp 0xa0 ; 0xa0 <main>
...
14: 0c 94 51 00 jmp 0xa2 ; 0xa2 <ccl_vect>
18: 0c 94 52 00 jmp 0xa4 ; 0xa4 <porta_ext_int_vect>
...
000000a0 <main>:
a0: ff cf rjmp .-2 ; 0xa0 <main>
000000a2 <ccl_vect>:
a2: 18 95 reti
000000a4 <porta_ext_int_vect>:
a4: 18 95 reti
We now get exactly the instructions we wanted – no compiler added sugar, full control over the vector table and all functions. (The ...
gaps in between are undefined and will be 0x00 in the .hex
file).
Compare that to the with-startfiles version
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 50 00 jmp 0xa0 ; 0xa0 <__ctors_end>
4: 0c 94 5a 00 jmp 0xb4 ; 0xb4 <__bad_interrupt>
8: 0c 94 5a 00 jmp 0xb4 ; 0xb4 <__bad_interrupt>
c: 0c 94 5a 00 jmp 0xb4 ; 0xb4 <__bad_interrupt>
10: 0c 94 5a 00 jmp 0xb4 ; 0xb4 <__bad_interrupt>
14: 0c 94 5d 00 jmp 0xba ; 0xba <__vector_5>
18: 0c 94 5e 00 jmp 0xbc ; 0xbc <__vector_6>
[..]
000000a0 <__ctors_end>:
[..]
and everything seems to be in its correct place (vector 5 implemented at absolute address 0x14, first real instruction code after vector table at 0xa0).