1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
# 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! <br>
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! <br>
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...
|