AVR assembly programming
This is a repo containing my attempts at programming in AVR assembly
I want the every byte of macihne code executed by my processor to be written by myself, so no libraries, no definitions, no nothing.
DISCLAIMER
Please have a sense of humor and don't take the jokes in this README seriously.
WARNING
ChatGPT and any other genAI, as of 2025-09-05, can NOT write proper AVR assembly. And I doubt they'll learn any time soon. Try it for yourself, maybe you'll get something working if they write it completely from scratch, but as for debugging, you are on your own. Use debugging tools mentioned below.
Examples of generative AI being completely useless include: them trying to
persuade me that I've used the wrong interrupt vector address, mixing up
out
/sts
and in
/lds
instructions, mixing up addresses of IO registers,
writing/reading data from wrong registers (like mixing up TIMSK
/TIFR
,
PINx/PORTx
), not knowing how to actually compile the code and so much more,
not unserstanding how timers work and so on. Save yourself some time, don't use
genAI.
How can I use it?
Firstly, as an example of how to program in assembly for AVR. I am not responsible for the quality though :silly:. My only source is datasheets.
Secondly, there is a really useful Makefile that you can copy to use
with your own projects. Most variables do not need to be changed there, maybe
just the MCUPATH
. Name a file something.s
, and then run make
TARGET=something
to build, or make TARGET=something flash
to upload your
code to the arduino. You can set the TARGET
as an environment variable to
aviod typing it in each invokation of make
.
Thirdly, for debugging. Run make TARGET=something sim
, then run avr-gdb
something
, when inside of it, type target remote :1234
and you're good to go.
make sim
automatically detects wheher the simulation is already running and
forks to background, so to rerun the simulation, just type make sim
again: it
will rebuild the file, and you won't even have to restart gdb (it will preserve
breakpoints), just retype the target remote :1234
command. I find this
workflow satisfying, and it does not exhaust my arduino flash memory by
flashing the program every 30 seconds there. I think I will add a debugging
guide as well...
Dependencies
Software
make
- avr toolchain (
avr-as
,avr-ld
,avr-objcopy
,avr-objdump
) avrdude
(flash tool)- For debugging:
avr-gdb
+simavr
(orqemu-system-avr
, but I had problems with it)
Not that you don't actually need avr-libc
or avr-gcc
to build assembly.
Hardware
- an ARDUINO UNO board, or any other AVR-based microcontroller if you change the variables in Makefile. Note that most of the variables can be changed from the environment.
- wirez
- rizz
Dependency installation
- Arch linux:
# pacman -S avr-binutils avrdude
. For C development, you also need to installavr-gcc
andavr-libc
. For debugging,avr-gdb
andsimavr
[AUR] [CLONE] - Any other non-superior distro: read the fucking manual
Why?
- to have fun.
Useful resources
- ATMega328p datasheet
- AVR instruction manual
- AVR130: Setup and use of AVR Timers
- Arduino UNO R3 info, PINOUT
Read this if you are going into it blind
This section contains some of the pain that I've gone through while doing this programming journey. If you don't want to make same mistakes, please take your time to read it.
- AVR interrupt addresses from datasheet are not the same ones you specify in
assembly!
For example, TIMER1 COMPA (timer 1 compare A match) has address0x0016
in the datasheet, but in assembly you have to align it like this:asm .org 0x002C: jmp timer1_int
Why? Because they probably refer to "instruction address", not to "memory address". Is it said anywhere? No. Can you figure it out by compiling corresponding C code and looking at disassembly? Yes, but you will spend 5 hours trying to understand why your interrupt is not working. - Don't forget to link your file after compiling!
This would have not been a problem on a processor that has an OS, because the file just wouldn't run unless you link it. But here you're usingobjcopy
. That means that ALL jumps and calls will be broken in your assembly. I thought that this was a bug for so long, dont be like me. - I've said this before, but don't ever try to use AI, or even worse, vibe code the microcontroller. It's just not going to work, you will loose time instead of saving it. You can vibecode a crappy website in react only because there is so much training data for it. But almost entirety of AVR programming is done in C++, thanks to fucking nobody. But I mean, it is a good thing that AI can't comprehend assembly. It means you'll have more fun exploring the old fashined way ;)
- I will add entries to this list as I go...