1
\$\begingroup\$

I've an ATmega328P.

Register Summary Page 275 ATmega328P datasheet.

enter image description here

The first address is the I/O address, and the second is the data memory address.

I'm going to set all (D ports) Data Direction Registers To 1 and all (D ports) Port Registers to 1.

Using I/O addresses

    int main(void)
    {
    
    asm("ldi 16,0xff"); //load 0xFF to the general purpose register 16
    asm("out 0x0A,16"); //load register 16 data to PORTD
    asm("ldi 16,0xFF"); //load 0xFF to the general purpose register 16
    asm("out 0x0B,16"); //load register 16 data to PORTD

    }

enter image description here

Next I'm going to set all (D ports) Data Direction Registers To 1 and half of (D ports) Port Registers to 1

Using memory addresses:


    volatile unsigned char* DDRD  =  (unsigned char*)(0x2A);
    volatile unsigned char* PORTD =  (unsigned char*)(0x2B);
    int main()
    {
     DDRD = 0xff;  
     PORTD = 0X0f;
     
     
    }

The code is compiled to:

    main:
    lds     r30, 0x102 #r30 = *(0x102) = 0x2A
    lds     r31, 0x103 #r31 = *(0x103) = 0x00
    ser     r24        #r24 = 0xff
    st      Z, r24     #*(0x002A) = r24 = 0xff    DDRD Memory Address Is 0x2A
    lds     r30, 0x100 #r30 = *(0x102) = 0x2B
    lds     r31, 0x101 #r31 = *(0x102) = 0x00
    ldi     r24, 0xF   #r24 = 0x0f
    st      Z, r24     #*(0x002B) = r24 = 0xff    PORTD Memory Address Is 0x2B
    ldi     r24, 0
    ldi     r25, 0
    ret
    ; End of function main

enter image description here

They both work properly. Why did they build the CPU to have instructions to deal with ports I/O addresses (like in, out instructions) while they already built a set of instructions that deal with memory space (like: lds, sts, ld, st?) Why are there still instructions like in and out? Why ddin't they just get rid of them as they are useless after they mapped the I/O ports in memory

\$\endgroup\$
5
  • \$\begingroup\$ What compiler version and settings were used to generated the quoted section? \$\endgroup\$ Commented Dec 3, 2023 at 16:11
  • \$\begingroup\$ Turn on the optimization flag. godbolt.org/z/fxKas636q \$\endgroup\$
    – G36
    Commented Dec 3, 2023 at 16:13
  • 2
    \$\begingroup\$ answer here arduino.stackexchange.com/a/56311/81100 \$\endgroup\$
    – bobflux
    Commented Dec 3, 2023 at 16:25
  • 1
    \$\begingroup\$ Of course you could have only one way to access them, but can anyone really know what the designers were thinking and decided with some reason that they can be accessed in two ways. I don't think we can have a factual answer to that, just speculation. \$\endgroup\$
    – Justme
    Commented Dec 3, 2023 at 19:18
  • \$\begingroup\$ The old 8-bitters always had lots of quirks and strange instruction sets to enable some tiny bit of faster code - another example is the "zero page" which was featured by many MCUs. My general recommendation would be not to worry about it - just seek to port to a modern 32-bitter over time. Most did so well over a decade ago... but well, I too have to maintain various old 8 bit crap. The sooner that old stuff hits the grave, the better. \$\endgroup\$
    – Lundin
    Commented Dec 4, 2023 at 8:50

2 Answers 2

4
\$\begingroup\$

Take a deeper look on the instruction set, and you will find:

  1. The in and out instructions use an immediate address of 6 bits width. They are limited to addresses fixed at the compile time, you cannot change the address at run time.
  2. The ld and st instructions with an immediate address need a 16-bit argument. They are limited to addresses fixed at the compile time, you cannot change the address at run time.
  3. The ld and st instructions with an indirect address use the value in X, Y, or Z. You are able to provide the address at run time.

The ld and st instructions need more program space and/or more cycles to execute.

This gives you freedom to use the best instruction to reach your goal:

  • You have a fixed address: Use in and out, respectively.
  • You need a variable address: Use ld and st, respectively.

Note 1: Because of the memory layout, the IO registers have this offset, starting right after the 32 general purpose registers.

Note 2: Depending on the optimization level of your compiler, it recognizes the "fixed address" case and emits the shorter and/or faster instructions.


Final note:

Questions asking "Why?" are rarely satisfyingly answerable, if you ask anybody but the inventors. Therefore, we can just speculate.

Despite the design as a RISC machine, on the one hand they apparently wanted to optimize I/O accesses, as these are so common on microcontrollers. On the other hand all I/O registers and the general purpose registers are memory mapped for presumably easier compiler creation, which was a design goal.

\$\endgroup\$
3
  • \$\begingroup\$ One minor correction, in and out can only operate on a 5-bit address, hence only 32 registers can be accessed. The out instruction takes an 8-bit constant value to output to the addressed register. \$\endgroup\$ Commented Dec 5, 2023 at 10:04
  • 1
    \$\begingroup\$ @TomCarpenter Thanks for pointing out the limitation. However, according to the instruction set documentation, it's a 6 bit address and a 5 bit register selector. \$\endgroup\$ Commented Dec 5, 2023 at 10:58
  • \$\begingroup\$ This looks pretty clear now that if my port pointer in c code is holding a constant address(points to one register along the code) it's most likely that the compiler will choose out/in over lds/sts - that also uses immediate address but bigger than the one out/in uses - that needs less cycles and space than sts/lds , but if I make this pointer points to more than one register along the code the compiler will choose to use st/ld with an indirect address which also takes more space and cycles but it's probably the only way to compile that code.. I think that's what you mean here right? \$\endgroup\$ Commented Dec 5, 2023 at 16:52
0
\$\begingroup\$

They are accessible as registers so that you can do bit read/set/clear operations on them.

They are accessible as RAM memory locations. because everything is.

Also some of the AVR instructions have different names but assemble to the same machine code. in that case the multiple names are just a convenience for the programmer.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.