ATtiny202 - ASM code

Go To Last Post
30 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't fully understand the addressig mode od AVR ATtinyxxx 0-Series.

The following dissasembled code shows:

r = ~(r ^ 0b10101010);

0x78: LDS R25, 0x00
0x79: CPI R24, 0xF1
0x7A: LDI R24, 0x55
0x7B: EOR R24, R25
0x7C: STS 0x00, R24
0x7D: CPI R24, 0xF1

When I step the ASM rows, the program jumps from 0x78 to 0x7A without stopping at 0x79 row. Maybe it has something to do with the fact that LDS variable address for AVR 0-Series is not 32 bits but only 16 bits.

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Kevil wrote:
When I step the ASM rows,
Tell us more about how you are doing that.

 

BTW if I build this:

#include <avr/io.h>

volatile uint8_t r;

int main(void) {
    r = ~(r ^ 0b10101010);
}

The code I see generated is:

int main(void) {
    r = ~(r ^ 0b10101010);
  56:	90 91 80 3f 	lds	r25, 0x3F80	; 0x803f80 <__DATA_REGION_ORIGIN__>
  5a:	85 e5       	ldi	r24, 0x55	; 85
  5c:	89 27       	eor	r24, r25
  5e:	80 93 80 3f 	sts	0x3F80, r24	; 0x803f80 <__DATA_REGION_ORIGIN__>

so I would suggest that in the code sequence you show the CPIs are part of some other statement and nothing to do with the "r = ~(r ^ 0b10101010);" statement.

 

Also I kind of wonder what "r" actually is in your example. Looking at the 202 memory map:

then for one things the 0x3F80 in my example should come as no surprise as 'r' has been positioned as the first variable in SRAM so sits at the start: 0x3F80.

 

For you to get an LDS R25,0x00 is very odd indeed. Actually when you think about it (as shown in my assembler code) LDS is a FOUR BYTE opcode so it would occupy the whole of 16 bit locations 0x78 and 0x79. Similary for STS, it would occupy 0x7C and 0x7D so the disassembler you have used to produce the above display has decoded the code incorrectly. What disassembler is that?

 

In my case if I start the code in the AS7/MCS7 simulator I see it decode the code as:

--- D:\test\mcs7_test\Debug/.././main.c ----------------------------------------
int main(void) {
    r = ~(r ^ 0b10101010);
0000002B 90.91.80.3f          LDS R25,0x3F80		Load direct from data space
0000002D 85.e5                LDI R24,0x55		Load immediate
0000002E 89.27                EOR R24,R25		Exclusive OR
0000002F 80.93.80.3f          STS 0x3F80,R24		Store direct to data space
}

By the way the upper nibble of a CPI opcode is fixed at 0b0011 (3) so when your disassembler shows CPI I believe it is misreading the 3 in 3F80.

 

Last Edited: Wed. Mar 10, 2021 - 09:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I forgot to mention that

 

r = 0b11111100;

 

I ran the program again (MPLAB X IDE, XC8, Simulator, ATtiny202),  with the same result:

 

!        r = 0b11111100;
0x4C: LDI R18, 0xFC
0x63: STS 0x00, R18
0x64: CPI R24, 0xF1
!    }
!        r = ~(r ^ 0b10101010);
0x68: LDS R25, 0x00
0x69: CPI R24, 0xF1
0x6A: LDI R24, 0x55
0x6B: EOR R24, R25
0x6C: STS 0x00, R24
0x6D: CPI R24, 0xF1

 

Although I used: #include <xc.h>

I get the same result with: #include <avr/io.h>

 

It looks like that CPI R24, 0xF1 is a dummy optcode for AVR 0-Series not using 32 bit addressing mode.

 

 

Last Edited: Wed. Mar 10, 2021 - 11:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Show your complete function. You can use the online compiler if wanted-

https://godbolt.org/z/Y3j7Y8

 

Are you doing some register attribute things? I'm not sure how you get STS/LDS with an address of 0x00 on an avr0/1 unless the disassembly is screwed up. I would also look at the lss listing instead of the ide produced disassem file.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is just a test program to test how to expand one nibble (4 bits) to byte (8 bit) and generate the Manchester code. I.e. from 0xE expanded to 0xFC and after NXOR is 0xA9 (Manchester).

The breakpoint is set to row 49 (asm "nop").

 

#define F_CPU 3333333

//#include <avr/io.h>
#include <xc.h>         // F_CPU
#include <util/delay.h> // _delay_ms()

volatile uint8_t x, r;

int main(void) {
    asm("nop");
    x = 14;
    r = 0;

    //    asm volatile("swap %0" : "=r" (r) : "0" (x));
    //    asm("nop");

    for (uint8_t i = 0; i < 16; i++) {

        x = i;

        asm volatile(
                "bst %0, 3" "\n\t"
                "bld %0, 6" "\n\t"
                "bld %0, 7" "\n\t"
                "bst %0, 2" "\n\t"
                "bld %0, 4" "\n\t"
                "bld %0, 5" "\n\t"
                "bst %0, 1" "\n\t"
                "bld %0, 2" "\n\t"
                "bld %0, 3" "\n\t"
                "bst %0, 0" "\n\t"
                "bld %0, 1" "\n\t"
                "mov __tmp_reg__, %0" "\n\t"
                "CPI %0, 0xF1" "\n\t"
                "LDI %0, 0x55" "\n\t"
                "EOR %0, __tmp_reg__" "\n\t"
                : "=&r" (r)
                : "0" (x)
                );

        asm("nop");
        r = 0b11111100;
    }
        r = ~(r ^ 0b10101010);
        asm("nop");

    for (int i = 4; i > 0; i--) {
        r = (r << 2);
        if ((x & 0b00001000) == 0b00001000) {
            r = (r | 0b00000011);
        }

        x = (x << 1);

    }

    r = ~(r ^ 0b10101010);
    asm("nop");

    PORTA.DIR = PIN1_bm;

    while (1) {

        PORTA.OUTTGL = PIN1_bm; // Hi
        asm("nop");
        _delay_ms(500);
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

there must be something wrong with your settings (or MPLAB X IDE, XC8 ), what Cliff show is how it should be correct, and the AVR 0 has normal STS (it's only the 16 register AVR's that have a special STS ) , show the generated ASM file.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Kevil wrote:
Although I used: #include <xc.h>
I suspect this is where your problem lies. Looks like a bug in the disassembler used in XC8 tools.

 

As I said above:

0x69: CPI R24, 0xF1

If you look at the opcode manual:

So the binary code for CPI R24, 0xF1 in which the first KKKK group is F and the second is 1 and dddd is 24 which is 1000. So it would be 3F81. Which is NOT an opcode it is the address part of an STS or LDS so the disassembler you are using has a SERIOUS BUG. The last time Atmel got involved in developing a toolchain for AVR it took them about 4-5 years before they could build something fairly stable. So I would probably be giving XC8 a wide berth for maybe the next 3 to 4 years at least until it "settles down".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Iirc, the mplabx disassembled in the debugger does NOT properly disassemble 2-word instructions like LDS and STS.

instead is show them as two single-word instructions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So still avoid  XC8 tools for AVR :(

 

Use studio 7

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:

Iirc, the mplabx disassembled in the debugger does NOT properly disassemble 2-word instructions like LDS and STS.

instead is show them as two single-word instructions.

How could someone write a disassembler and not ice such a catastrophic error? Incredible!

 

(of course it does make writing the disassembler easier - you don't need to keep a table of instruction lengths - it may output pure garbage but at least the programmer got to go out and play golf that afternoon rather than bothering to program!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Kevil, which xc8 version are you using?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> the mplabx disassembled in the debugger does NOT properly disassemble 2-word instructions like LDS and STS

 

That is true. I gave up long ago letting the ide show me any asm. Enable the lss listing and view the objdump version when needed. Doesn't help when wanting to single step actual asm code, but there are other ways to see what is going on.

 

I would also skip trying to create asm in c when not necessary. The compiler can produce good code.

https://godbolt.org/z/dP9G56

For a tiny202, I would assume small code size is most important (saving flash space) so a loop version would most likely be the smallest and I doubt a few extra clocks makes any difference.

 

 

> which xc8 version are you using?

 

Its not a compiler problem, its the ide decoding- Debugging-Disassembly. Makes no difference what compiler- xc8, avr-gcc 7.3.0 etc,.

Last Edited: Wed. Mar 10, 2021 - 12:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using XC8 v2.10 (x86), newer version v2.31 (64 bit) doesn't work with ATtinyxxx 0-Series.
 

Actually I don't mind if the XC8 doesn't properly disassemble the code, which is working well.
 

I was just curious to know, what the ASM instructions are doing and to understand them (I am familiar with i7 x64 ASM well).

 

Finnaly I will use 16 bytes lookup table to get the result. If I would have almost full 128 bytes of RAM I can store the data to program flash or use the discussed function to calculate the result.
 

Disassembled code:
 

!int main(void) {
!    asm("nop");
0x45: NOP
!    x = 14;
0x46: LDI R24, 0x0E
0x47: STS 0x00, R24
0x48: CPI R24, 0xF0
!    r = 0;
0x49: STS 0x00, R1
0x4A: CPI R24, 0xF1
!    //    asm volatile("swap %0" : "=r" (r) : "0" (x));
!    //    asm("nop");
!    for (uint8_t i = 0; i < 16; i++) {
0x4B: LDI R25, 0x00
0x65: SUBI R25, 0xFF
0x66: CPI R25, 0x10
0x67: BRNE 0x4D
!        x = i;
0x4D: STS 0x00, R25
0x4E: CPI R24, 0xF0
!        asm volatile(
0x4F: LDS R24, 0x00
0x50: CPI R24, 0xF0
0x51: BST R24, 3
0x52: BLD R24, 6
0x53: BLD R24, 7
0x54: BST R24, 2
0x55: BLD R24, 4
0x56: BLD R24, 5
0x57: BST R24, 1
0x58: BLD R24, 2
0x59: BLD R24, 3
0x5A: BST R24, 0
0x5B: BLD R24, 1
0x5C: MOV R0, R24
0x5D: CPI R24, 0xF1
0x5E: LDI R24, 0x55
0x5F: EOR R24, R0
0x60: STS 0x00, R24
0x61: CPI R24, 0xF1
!                "bst %0, 3" "\n\t"
!                "bld %0, 6" "\n\t"
!                "bld %0, 7" "\n\t"
!                "bst %0, 2" "\n\t"
!                "bld %0, 4" "\n\t"
!                "bld %0, 5" "\n\t"
!                "bst %0, 1" "\n\t"
!                "bld %0, 2" "\n\t"
!                "bld %0, 3" "\n\t"
!                "bst %0, 0" "\n\t"
!                "bld %0, 1" "\n\t"
!                "mov __tmp_reg__, %0" "\n\t"
!                "CPI %0, 0xF1" "\n\t"
!                "LDI %0, 0x55" "\n\t"
!                "EOR %0, __tmp_reg__" "\n\t"
!                : "=&r" (r)
!                : "0" (x)
!                );
!        asm("nop");
0x62: NOP
!        r = 0b11111100;
0x4C: LDI R18, 0xFC
0x63: STS 0x00, R18
0x64: CPI R24, 0xF1
!    }
!        r = ~(r ^ 0b10101010);
0x68: LDS R25, 0x00
0x69: CPI R24, 0xF1
0x6A: LDI R24, 0x55
0x6B: EOR R24, R25
0x6C: STS 0x00, R24
0x6D: CPI R24, 0xF1
!        asm("nop");
0x6E: NOP
0x6F: LDI R24, 0x04
0x70: LDI R25, 0x00
!    for (int i = 4; i > 0; i--) {
0x86: BRNE 0x71
!        r = (r << 2);
0x71: LDS R18, 0x00
0x72: CPI R24, 0xF1
0x73: ADD R18, R18
0x74: ADD R18, R18
0x75: STS 0x00, R18
0x76: CPI R24, 0xF1
!        if ((x & 0b00001000) == 0b00001000) {
0x77: LDS R18, 0x00
0x78: CPI R24, 0xF0
0x79: SBRS R18, 3
0x7A: RJMP 0x80
!            r = (r | 0b00000011);
0x7B: LDS R18, 0x00
0x7C: CPI R24, 0xF1
0x7D: ORI R18, 0x03
0x7E: STS 0x00, R18
0x7F: CPI R24, 0xF1
!        }
!        x = (x << 1);
0x80: LDS R18, 0x00
0x81: CPI R24, 0xF0
0x82: ADD R18, R18
0x83: STS 0x00, R18
0x84: CPI R24, 0xF0
0x85: SBIW R24, 0x01
!    }
!    r = ~(r ^ 0b10101010);
0x87: LDS R25, 0x00
0x88: CPI R24, 0xF1
0x89: LDI R24, 0x55
0x8A: EOR R24, R25
0x8B: STS 0x00, R24
0x8C: CPI R24, 0xF1
!    asm("nop");
0x8D: NOP
!    PORTA.DIR = PIN1_bm;
0x8E: LDI R24, 0x02
0x8F: STS 0x00, R24
0x90: CPC R0, R0
!    while (1) {
!        PORTA.OUTTGL = PIN1_bm; // Hi
0x91: LDI R30, 0x00
0x92: LDI R31, 0x04
0x93: STD Z+7, R24
!        asm("nop");
0x94: NOP
0x95: LDI R18, 0x15
0x96: LDI R19, 0x16
0x97: LDI R25, 0x05
0x98: SUBI R18, 0x01
0x99: SBCI R19, 0x00
0x9A: SBCI R25, 0x00
0x9B: BRNE 0x98
0x9C: RJMP 0x93

 

Last Edited: Wed. Mar 10, 2021 - 01:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But how will you debug (single step in ASM view), if it don't show/know the code correct !

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Project Properties- Avr GCC (Global Options). option categories- output files, enable lss file. Now you get something useful, and will be located in the same folder as the hex file.

 

There is probably a way to enable this as default, but I simply enable it for any new project. I usually have the lss listing open.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

But how will you debug (single step in ASM view), if it don't show/know the code correct !

F7 key wink, single step in.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but not any help if the code are showed wrong!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Kevil wrote:
I don't mind if the XC8 doesn't properly disassemble the code,

But if the disassembly is rubbish, you won't learn anything useful from it - it will just mislead you!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually I am trying to replace broken Oregon Scientific THN132N Wireless Temperature Sensor with ATtiny202 which I use for onchip temperature measurement, sleep mode, RTC clock (to send data to Oregon Scientific base station each 40 seconds), CCL to generate Manchester code by IRQ SYNC USART and TCB to generate 9.36 ms pause between sending data twice.

I am almost done with the code smiley.

 

int main(void) {
    ccl_init();
    atmel_start_init();
    asm("sei");

    /* Replace with your application code */
    while (1) {
        // Preamble
        for (int i = 0; i < 4; i++){
            USART_0_write(0xAA);
        }
        // SYNC
        USART_0_write(0x99);
        for (int i = 0; i < 4; i++){    // Test sending 0xEC40 - THN132N Id
            x = XNOR[field[i]]; // ~(A^B)
            asm ("nop");
            USART_0_write(x);
        }
//        // Postamble 2x '0'
//        USART_0_write(0x55);
//        USART_0_write(0x55);

        _delay_ms(100);
    }
}

Generated 0xE, LSB first 0111
 

Last Edited: Wed. Mar 10, 2021 - 02:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kevil wrote:
Maybe it has something to do with the fact that LDS variable address for AVR 0-Series is not 32 bits

Kevil wrote:
I was just curious to know, what the ASM instructions are doing and to understand them (I am familiar with i7 x64 ASM well).

AVR's are 8 bit devices, so none of them have any native 32 or 64 bit instructions!

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Jim

Hi, I think you understand that I am not a very beginner in MCU's and I know the difference between 8/16/32/64 bits wink.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, there-in lies the rub.

 

Data space (that is, RAM) is 8 bits wide. That means that variables will be some integer multiple of 8 bits wide (e.g. 8, 16, 32, etc). Anything more than 8 takes multiple operations.

 

Instructions (that is, FLASH) are 16 bits wide. Many of those instructions are a single word (e.g. 16 bits) wide. They have to carry the instruction AND, sometimes, a constant value or register address. If the instruction needs more space to reference something like a memory address, then it will be two words wide. 

 

So, one needs to be really careful when you call an AVR to be an "8 bit machine".

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Wed. Mar 10, 2021 - 08:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry a normal AVR is a 8 bitter no doubt about that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So, one needs to be really careful when you call an AVR to be an "8 bit machine".

For the most part that seems a good call, since almost every data operation is done using 8 bits (a few 16 bit operations, which are prob two 8 bit ops "hardwired" together).   

Maybe like a 3.5 digit multimeter, it is an 8.0625 bit AVR  

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

even a Z80 have more 16 bit instructions than a AVR and I have never heard anybody called that other than a 8 bitter.

(it's the size of the Acc (and registers) and logic operations that count.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


We'll give the Z80 some bonus points & rate it 8.075 bits, just to put it ahead of the AVR.  But yes, they are 8 bitters in the databooks.

I remember programming the Z80 & CDP1802 with some crazy cartridge programming system & an EPROM burner at the ready...those were "fun" times.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Mar 10, 2021 - 10:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I liked to program MOS 6502 in Commodore 64 wink.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if the disassembly is rubbish, you won't learn anything useful from it - it will just mislead you!

It's not THAT bad:

  1. this is only the disassembly that shows up in the debugger as you're looking at flash or stepping through code.  The compiler/assembler/etc gives you correct listings in other windows.  (not that havig several different windows onto the same code is a great thing, either.
  2. There are only a couple of 32bit instructions on AVR:  LDS, STS, JMP, and CALL, I think.  JMP and CALL are somewhat rare.  The debugger gets the opcode OK, it just fails to properly decode the operand, and shows that bogus extra instruction afterward, which is never executed.
  3. That makes it still usable.  Just an annoying and rather amateurish error.  :-(

 

 

if you're trying to look at and understand the code, it's better to look at the output from "objdump -S" (which I think mplabx generates, given correct configuration.)  The ".lss" files produced by the compiler are hopelessly obscured with the extra symbol and debugging info that gcc puts in there.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
which is never executed.
Oh I guess that is key. OP did say:

Kevil wrote:
the program jumps from 0x78 to 0x7A without stopping at 0x79 row
I didn't really understand why that might be at the time - do now.

 

But for someone learning Asm and who might not recognize that LDS is a 32 bit opcode and see the error in this disassembly it sure is confusing (as this thread proves!)

 

What I don't understand is that this seems such a trivial thing to fix I wonder why no one has when it seems to be fairly well known ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi the org. AVR assembler had an error for years, rjmp on a 8515 can reach the hole flash but there was a distance I think 2048 (1/2 way), where there came an error, (I had a project where I often had to add or remove nops to remove the error.)