# 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](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` (or `qemu-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 install `avr-gcc` and `avr-libc`. For debugging, `avr-gdb` and `simavr` [\[AUR\]](https://aur.archlinux.org/packages/simavr) [\[CLONE\]](https://aur.archlinux.org/simavr.git) * Any other non-superior distro: read the fucking manual ## Why? - to have fun. ## Useful resources * [ATMega328p datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf) * [AVR instruction manual](https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-Instruction-Set-Manual-DS40002198A.pdf) * [AVR130: Setup and use of AVR Timers](https://ww1.microchip.com/downloads/en/Appnotes/Atmel-2505-Setup-and-Use-of-AVR-Timers_ApplicationNote_AVR130.pdf) * [Arduino UNO R3 info](https://docs.arduino.cc/hardware/uno-rev3/), [PINOUT](https://docs.arduino.cc/resources/pinouts/A000066-full-pinout.pdf) ## 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. 0. AVR interrupt addresses from datasheet are not the same ones you specify in assembly!
For example, TIMER1 COMPA (timer 1 compare A match) has address `0x0016` 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. 0. 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 using `objcopy`. 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. 0. 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 ;) 0. I will add entries to this list as I go...