OUT can only reach 0 to 63 (in IO space) yet UBRR0H is at RAM address 0x90
In the table of contents of the d/s click "Register Summary" and notice how some locations have teo addresses, one in () and one not? The ones in () are RAM addresses and the ones not are IO addresses. IN/OUT can only be used on those registers that have an IO address (which is offset from the RAM address by 0x20 because of the 32 AVR registers)
===
I'm currently teaching myself assembly language by building an assembler for the Arduino Uno. However, I am struggling with converting RJMP to binary.
From the amtel documents, RJMP is represented in binary as 1100 kkkk kkkk kkkk where k is the relative address to jump to.
Assembling the following code:
check_press_loop:
sbis PIND, 2
rjmp check_press_loop
And disassembling I get:
10: fe cf rjmp .-4 ; 0xe
I understand why the relative jump is -4 bytes, what I don't understand is how this is represented by the hex fc ef. Swapping the order due to the "little endian" byte order (cf fe) gives a binary value of 0b1100111111111110. Removing the instruction (1100) part of the binary value, how does 111111111110 represent -4?
Hopefully you know that 111111111110 is -2 when interpreted as a twos-complement signed integer. The reason why -2 is used here is because check_press_loop is two 16-bit words back from the end the RJMP instruction. Since AVR instructions are always word aligned there's no reason for RJMP instruction to be able to encode a jump offset measured in bytes. The processor isn't capable of executing an instruction at an odd byte address in program memory, the PC register isn't capable of holding such an address.
Brilliant. Thanks very much for the explanation. Yes I had calculated the twos complement as -2 but was thrown by the disassembler's value of - 4! –
====
almost every tutorial and example you will find on the internet about programming AVRs in assembler have been written to use Atmel Asm2 instead [of avr-as gcc assembler]
i have to replace ".equ PORTB = 0x05" with this "PORTB = 0x05" - WHY IS THIS?
What Cliff didn't say explicitly is that there are TWO assembler formats available for AVR. There is the Atmel official syntax, supported by the Atmel assemblers like AVRASM2 (and also some open source tools like avra and tavrasm), and then there is the syntax used by the gnu assembler (avr-as, as part of winavr, or the Atmel Studio C compiler install. Usually invoked via "avr-gcc ...")
The gnu assembler uses directives (like .equ) that have common syntax across all of the architectures supported by the Gnu Compiler Collection, but aren't exactly compatible with Atmel's syntax for similar directives (eg ".equ PORTB, 5" vs ".equ PORTB = 5"), and some of the instruction syntax will be different as well. The gnu assembler produces relocatable object files (.o) that can be linked with other languages, but also lack absolute addresses, and are subject to being linked with startup code (as Cliff described.)
The Atmel-syntax assemblers produce binary output that cannot be linked with other code (at least, not easily),
but it IS easy to put pieces of code at desired absolute addresses (handy if you're writing a bootloader!),
and it is the Atmel syntax that is likely to appear in examples, tutorials, classes, and books...
https://arduino.stackexchange.com/questions/67839/i-need-help-programming-my-arduino-in-assembly
It "needs" to be done in assembly as an assignment, right? Otherwise you would "want to learn" assembly.
What I suggest you do is change the C program to use port manipulation, for example:
void setup() {
DDRB |= 0b00100000; // D13 to output mode
}
void loop() {
PORTB |= 0b00100000; // turn on D13
delay(2000);
PORTB &= ~0b00100000; // turn off D13
delay(2000);
}
Now at least you are flashing D13 without using the rather complex digitalWrite and pinMode library functions. The delay is another issue I'll leave you to investigate however my page about timers might help you.
Now you can disassemble the generated code to see what the compiler generated in assembler and use that to help you learn how to do it.
For example, when I compiled the above code and turned on "verbose compiling" I found the location of the generated .elf file.
Running avr-objdump on that file (like this):
avr-objdump -S /tmp/build436d41bc5c0da39afe99cd9c05ac272a.tmp/sketch_aug13a.ino.elf > temp.txt
Gives me the following code for "loop":
00000094 :
94: 2d 9a sbi 0x05, 5 ; 5
96: 60 ed ldi r22, 0xD0 ; 208
98: 77 e0 ldi r23, 0x07 ; 7
9a: 80 e0 ldi r24, 0x00 ; 0
9c: 90 e0 ldi r25, 0x00 ; 0
9e: 0e 94 c5 00 call 0x18a ; 0x18a
a2: 2d 98 cbi 0x05, 5 ; 5
a4: 60 ed ldi r22, 0xD0 ; 208
a6: 77 e0 ldi r23, 0x07 ; 7
a8: 80 e0 ldi r24, 0x00 ; 0
aa: 90 e0 ldi r25, 0x00 ; 0
ac: 0c 94 c5 00 jmp 0x18a ; 0x18a
What we basically see there is one "sbi" instruction to turn the LED on and one "cbi" instruction to turn it off.
As for the rest, well I don't want to do your homework for you. Have fun learning how to program in assembler!
3
It doesn't matter what instructions are going to be used, the processor can get at all parts of SRAM and Flash memory. So ignore registers and instructions for now.
Initialised variables must be placed in SRAM, or they can't be used as variables, i.e. the program can't update their value.
Their initial values must be stored in flash, otherwise the initial values would be lost when power is removed.
The processor copies values from Flash to SRAM to initialise the values of all the initialised variables (they are normally stored next to each other in SRAM, and before the uninitialised variables in SRAM). The processor executes a loop which copies an area of Flash to SRAM. That loop is an early part of the program that initialises the program's state. In C, all that initialisation is run before main() is called.
The variables are stored in SRAM when the program is running, and the space is reserved by the assembler reading the program's .dseg directives containing .BYTE to reserve the space. Their initial values are stored in Flash, in a .CSEG, and the literal values are stored within the .CSEG using .DB or .DW.
If all of the initialised variables are next to each other, then the program needs to know the start address of the SRAM variables to initialise, and the start address of the Flash that contains their initial values. It also needs the length of the initialised variables area.
In almost-C it would be:
unsigned char* flash_values = flash_address_of_initial_values_of_initialised_variables;
unsigned char* SRAM_initialised_vars = SRAM_address_of_initialised_variables;
for (int i=0; i