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 <loop>:
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 <delay> 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 <delay>
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<length_of_initialised_variables; ++i) {
SRAM_initialised_vars[i] = PROGRAM_MEMORY(flash_values[i]);
}
Edit: PROGRAM_MEMORY(…) retrieves the value from Flash memory. Share Cite Follow edited Sep 17 '14 at 3:54 answered Sep 17 '14 at 0:56 gbulmer 9,8761717 silver badges2828 bronze badges
very nice answer, useful stuff, but I accept the other one as a more to the subject. Thanks you both, you clarified the situation greatly. – xealits Sep 17 '14 at 13:21 1 @xealits - Okay. Just to be sure though, you do understand there is small problem with the other question? For the AVR assembler, which is what you asked about, the other one is misleading. There is no need for the linker. The AVR assembler uses the .dseg and .cseg directives. Without them it can't generate output. With them it can do the entire job. That is what I am discussing the answer with alex.forencich. – gbulmer Sep 17 '14 at 13:29
As I understand, I called “AVR assembler” 2 different things in my question: AVR instruction set and the program AVR Assembler. By “bare bones”, “AVR assembly” and “AVR instruction set” I mean the code which is written into the chip and run by it, i.e. the machine code, but expressed in assembly instead of bytes (like in avr-asm.tripod.com they say “ASSEMBLY LANGUAGE: The Mnemonic Representation of Machine Code”). So, the main issue was how these things relate: the chip, instructions run by it, higher-level tools for programming (AVR Assembler, avr-gcc, etc.) And you cleared it. –