Can someone help me understand assembly instructions for setting DIRSET?

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

I hope this is the appropriate section for my question. If not, please let me know.

 

I was reading about Read-Modify-Write and thought to look at how the assembly differs between PORTx.DIR |= PINn_bm and PORTx.DIRSET = PINn_bm. Here's what the first looked like:

9:             #include <avr/io.h>
10:
11:            int main(void)
12:            {
005C  EAE0     LDI R30, 0xA0
005D  E0F4     LDI R31, 0x04
005E  8180     LD R24, Z
005F  6280     ORI R24, 0x20
0060  8380     ST Z, R24
0061  CFFF     RJMP 0x61
13:                PORTF.DIR |= PIN5_bm;
14:                while (1) {}
15:            }

That's Read-Modify-Write pretty clearly. ...but then... PORTx.DIRSET = PINn_bm yielded me this:

9:             #include <avr/io.h>
10:
11:            int main(void)
12:            {
005C  E280     LDI R24, 0x20
005D  9380     STS 0x00, R24
005E  04A1     CPC R10, R1
005F  CFFF     RJMP 0x5F
13:                PORTF.DIRSET = PIN5_bm;
14:                while (1) {}
15:            }

LDI, check, STS ...wait? CPC ??

 

Yes, true.. I don't really know assembly language, and the second snippet makes no sense to me most likely just because of that. I have REALLY tried, but no matter how many times I read the Instrution Set Manual or how much I try to google something to shine some light into this... I don't seem to get any smarter.

 

Could some kind person explain to me how those two instructions achieve setting the bit in PORTF.DIRSET (at 0x04A1)?

 

Edit: I should have included some tool information. Compiler is XC8 v2.20 (unlicensed) and target device is ATmega4809 (Curiosity Nano evaluation board).

 

SOLVED: Turns out that the MPLAB disassembly listing cannot correctly disassemble the result. STS, as pointed out here in the helpful comments, is a 16-bit instruction that also needs a 16-bit address, which was disassembled as two 16-bit instructions... resulting in nonsense (exists as an issue MPLABX-3894, thank you meolsen for pointing this out). Correct code segment is/was ("main.S"):

 

	.file	"main.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__CCP__ = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
	.section	.text,code
.global	main
	.type	main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r24,lo8(32)
	sts 1185,r24
.L2:
	rjmp .L2
	.size	main, .-main
	.ident	"GCC: (AVR_8_bit_GNU_Toolchain_3.6.4_220) 5.4.0"

...and now even I can understand what is going on!

This topic has a solution.
Last Edited: Sat. Jun 27, 2020 - 08:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I’d suggest the cpc r10:r1 is just a remnant of the while(1) that didn’t get cleaned up.

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

Try this-

 

https://godbolt.org/z/-WuGm8

 

You have 3 ways to set these bits, with the vport being the 'best' as it becomes atomic with the sbi/cbi instructions and is the least code. The ..SET/...CLR registers also take care of atomic issues but results in a little more code.

Last Edited: Sun. Jun 21, 2020 - 10:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is this for a Xmega chip? I would ad a bit more code in the 2nd example, maybe the compiler is optimising the code.

 

R24 is being loaded with bit 5 (0x20 or 0b0010 0000) and then stored at location 0x00 HOWEVER PORTF.DIRSET is a port structure so I'm guessing that it's in fact offset 0x00.

 

Don't know which chip you are using, the Xmega I use doesn't have a portf.

 

EDIT and the CPC instruction seems "fake news" as it is the address of PORTF.DIRSET 0x04A1??

005E  04A1     CPC R10, R1

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Sun. Jun 21, 2020 - 11:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apologies, I definitely should have included chip and compiler information. Target device is ATmega4809 (Curiosity Nano evaluation board) and the compiler is XC8 v2.20 (unlicensed). -O1 switch is being used. If I change that to -O0, I get:

9:             #include <avr/io.h>
10:
11:            int main(void)
12:            {
005C  93CF     PUSH R28
005D  93DF     PUSH R29
005E  B7CD     IN R28, 0x3D
005F  B7DE     IN R29, 0x3E
13:                PORTF.DIRSET = PIN5_bm;
0060  EA80     LDI R24, 0xA0
0061  E094     LDI R25, 0x04
0062  E220     LDI R18, 0x20
0063  01FC     MOVW R30, R24
0064  8321     STD Z+1, R18
14:                while (1) {}
0065  CFFF     RJMP 0x65
15:            }

 

The entire (redundant lines removed) disassembly file reads:

Disassembly Listing for AssemblyTest
Generated From:
C:/Users/jani_/MPLABXProjects/AssemblyTest.X/dist/default/production/AssemblyTest.X.production.elf
Jun 22, 2020 8:29:48 AM

---  C:/Users/jani_/MPLABXProjects/AssemblyTest.X/main.c  -----------------------------------------------
1:             /*
2:              * File:   main.c
3:              * Author: jani_
4:              *
5:              * Created on June 21, 2020, 9:21 PM
6:              */
7:
8:
9:             #include <avr/io.h>
10:
11:            int main(void)
12:            {
005C  E280     LDI R24, 0x20
005D  9380     STS 0x00, R24
005E  04A1     CPC R10, R1
005F  CFFF     RJMP 0x5F
13:                PORTF.DIRSET = PIN5_bm;
14:                while (1) {}
15:            }
---  ././././crt1/gcrt1.S  ------------------------------------------------------------------------------
0000  940C     JMP __eeprom_end
0002  940C     JMP __eeprom_end
0004  940C     JMP __eeprom_end

(snip-snip)

004C  940C     JMP __eeprom_end
004E  940C     JMP __eeprom_end
0060  940C     JMP __eeprom_end
0050  2411     EOR R1, R1
0051  BE1F     OUT 0x3F, R1
0052  EFCF     LDI R28, 0xFF
0053  BFCD     OUT 0x3D, R28
0054  E3DF     LDI R29, 0x3F
0055  BFDE     OUT 0x3E, R29
0056  940E     CALL __eeprom_end
0058  940C     JMP __eeprom_end

(Not sure what that stuff at the end is though)

 

What puzzles me is the absence of address ...and even if STS instruction uses some address pointer (I don't think the manual said anything about that though)... X, Y nor Z are set up with any value. For PORTA, it looks very similar:

9:             #include <avr/io.h>
10:
11:            int main(void)
12:            {
005C  E280     LDI R24, 0x20
005D  9380     STS 0x00, R24
005E  0401     CPC R0, R1
005F  CFFF     RJMP 0x5F
13:                PORTA.DIRSET = PIN5_bm;
14:                while (1) {}
15:            }

 

Could it be that this disassembly thing is broken, and it doesn't correspond to what is actually generated? (MPLAB X v5.35, "Window > Debugging > Disassembly Listing File")

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

I had completely disregarded virtual registers!! Once I get home, I will have to see into this properly. (little doubtful that STS to 0x00 would hit the correct register though - but since I don't know, I need to learn this properly).

 

Thank you for the examples!

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

tunari wrote:
(redundant lines removed)
Please don't do that!

 

There is something very odd about that listing as it seems to have code from 005C listed *before* 0000 ?!?!

 

It's a curious listing anyway. It's all in upper case - that suggests the AS7 debugger. Why aren't you studying the .s or the .lss instead ?

 

(.s requires -save-temps).

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

I think your avr-objdump is broken. According to Atmel-0856L-AVR-Instruction-Set-Manual; STS is always a 32-bit instruction, whatever kind of AVR.

 

Last Edited: Mon. Jun 22, 2020 - 09:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
I think your avr-objdump is broken.
avr-objdump outputs in lower case, it's not that. I have a strong suspicion he's looking at the "disassembly window" contents in AS7 (possibly the worst C source annotating disassembler across the entire AVR toolchain!)

 

EDIT: if I build:

#include <avr/io.h>

int main(void)
{
    PORTF.DIRSET = PIN5_bm;
    while (1) {}
}

then in simulator I see:

--- ../../../../crt1/gcrt1.S ---------------------------------------------------
00000050 11.24                CLR R1		Clear Register
00000051 1f.be                OUT 0x3F,R1		Out to I/O location
00000052 cf.ef                SER R28		Set Register
00000053 cd.bf                OUT 0x3D,R28		Out to I/O location
00000054 df.e3                LDI R29,0x3F		Load immediate
00000055 de.bf                OUT 0x3E,R29		Out to I/O location
00000056 02.d0                RCALL PC+0x0003		Relative call subroutine
00000057 05.c0                RJMP PC+0x0006		Relative jump
00000058 a7.cf                RJMP PC-0x0058		Relative jump
--- D:\\test\\test\\Release/.././main.c ----------------------------------------
{
    PORTF.DIRSET = PIN5_bm;
00000059 80.e2                LDI R24,0x20		Load immediate
0000005A 80.93.a1.04          STS 0x04A1,R24		Store direct to data space
0000005C ff.cf                RJMP PC-0x0000		Relative jump
--- No source file -------------------------------------------------------------
0000005D f8.94                CLI 		Global Interrupt Disable
0000005E ff.cf                RJMP PC-0x0000		Relative jump

so maybe it's not this as that shows the opcodes in a different format. The .s file shows:

	.file	"main.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__CCP__ = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.Ltext0:
	.cfi_sections	.debug_frame
	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB0:
	.file 1 ".././main.c"
	.loc 1 4 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 5 0
	ldi r24,lo8(32)
	sts 1185,r24
.L2:
	rjmp .L2
	.cfi_endproc
.LFE0:
	.size	main, .-main

and the .lss file shows:

000000b2 <main>:
#include <avr/io.h>

int main(void)
{
    PORTF.DIRSET = PIN5_bm;
  b2:	80 e2       	ldi	r24, 0x20	; 32
  b4:	80 93 a1 04 	sts	0x04A1, r24	; 0x8004a1 <__TEXT_REGION_LENGTH__+0x7f44a1>
  b8:	ff cf       	rjmp	.-2      	; 0xb8 <main+0x6>

000000ba <_exit>:
  ba:	f8 94       	cli

000000bc <__stop_program>:
  bc:	ff cf       	rjmp	.-2      	; 0xbc <__stop_program>

So personally I cannot seem to recreate what OP is seeing?

 

EDIT2: should have read #1 more closely - so he's using XC8. That's a bit of an "unknown" but, assuming it does it, I'd still be looking at .s or .lss !

Last Edited: Mon. Jun 22, 2020 - 09:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
should have read #1 more closely - so he's using XC8.

And, if the -Ox levels are corresponding, at a low optimization level.  So you pick a toolchain and don't like the code, and don't allow it to do its best work?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I have MPLABX with the XC8 option installed. It took me a while to determine how to even see some Asm from the C but I eventually arrived here:

 

 

So like OPs code in #1 this looks quite, quite wrong. If the addresses are 16 bit it seems to suggest a 16 bit opcode for STS at location 0x5D. As noted above you can't squeeze STS into one opcode.

 

As this thing does appear to create ELF (or even just plain HEX) I must look at the actual binary to see what has been created..

 

EDIT: so what avr-objdump from GCC tools "sees" in the ELF (for main) is:

000000b8 <main>:
/*
 *
 */
int main(int argc, char** argv) {

    PORTF.DIRSET = PIN5_bm;
  b8:   80 e2           ldi     r24, 0x20       ; 32
  ba:   80 93 a1 04     sts     0x04A1, r24
  be:   ff cf           rjmp    .-2             ; 0xbe <main+0x6>

b8 (byte) is 5C (word). So there is an STS but it is a FOUR byte opcode from BA (5D). 

 

So the MPLABX disassembler has wrongly decoded:

80 93 a1 04

as

0x5D: STS 0x00, R24
0x5E: CPC R10, R1

so it's a bug in their disassembler that does not seem to know the length of STS.

 

EDIT: ah ha, finally stumbled over "show diassembly file":

005D  9380     STS 0x00, R24
005E  04A1     CPC R10, R1

so it sees all the bytes of 9380 04A1 but then wrongly disassembles them.

 

3/10 Microchip - must try harder !

Last Edited: Mon. Jun 22, 2020 - 12:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

tunari wrote:
(redundant lines removed)
Please don't do that!

 

No... You see the "(snip-snip)" between 0004 and 004C. They are redundant - identical lines. They have been removed. Nothing else is removed.

 

clawson wrote:

here is something very odd about that listing as it seems to have code from 005C listed *before* 0000 ?!?!

 

I do believe they are two different files; main.c and for some reason that listing includes gcrt1.S in there as well...

 

clawson wrote:

It's a curious listing anyway. It's all in upper case - that suggests the AS7 debugger. Why aren't you studying the .s or the .lss instead ?

(.s requires -save-temps).

 

This is what is instructed for MPLAB X ("Window > Debugging > Output > Disassembly Listing File"). I suppose I could go to command line and issue xc8-cc command with -S, but I can't figure out why would the two instructions that baffle me change. I will try that next though...

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

clawson wrote:

I have a strong suspicion he's looking at the "disassembly window" contents in AS7 (possibly the worst C source annotating disassembler across the entire AVR toolchain!)

 

Yes, bingo! That is what the documentation instructed...  ....aaaand how wonderful - it sucks. I need to learn some decent tools, quite obviously.

 

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

clawson wrote:

 

EDIT: ah ha, finally stumbled over "show diassembly file":

005D  9380     STS 0x00, R24
005E  04A1     CPC R10, R1

so it sees all the bytes of 9380 04A1 but then wrongly disassembles them.

 

3/10 Microchip - must try harder !

 

And by now I also managed to issue command line with -S and indeed found something that made sense. You're right! Incorrect disassembly! ...how could someone like me have ever even suspected a fault like THIS?? surprise

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

Tremendous and heartfelt THANK YOU for everyone here!

 

I don't think I would have ever come to suspect a problem like this from the tool... (I would have preferred Atmel Studio 7, but it doesn't look like running it in Linux is a viable option - however, I would not know if a similar issue would greet me there as well...).

 

What I definitely missed, and people here helping me did not, was that STS opcode was 16-bit AND it also needed a 16-bit address(!). What I was also too blind to see, was that the opcode for the baffling CPC instruction was my address!

 

Indeed - huge thank you for all of you!!

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

I'm interested to see what je_ruud or meolsen from Atmel/Microchip have to say. presumably they've been involved with the development of the XC8 tools for AVR?

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

If you look in the MPLABX readme, this is bug MPLABX-3894. There are multiple (aka different) disassemblers at play, that window uses the MPLAB internal disassembler which is not based on binutils (dont ask why... wink)

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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

meolsen wrote:

If you look in the MPLABX readme, this is bug MPLABX-3894. There are multiple (aka different) disassemblers at play, that window uses the MPLAB internal disassembler which is not based on binutils (dont ask why... wink)

 

Good find. Even with the issue number, it took me several minutes to find the file where that was listed ...and there's quite a few more, I see. (Sequence number of this issue suggests to me that the problem does not have a very high priority).

 

Dare I ask what kind of a solution should I be looking for to view how simple C code is translated to assembly. I am trying to instruct some kids and having an automatically updating window in the IDE seemed nice. Any thoughts? I could stumble in the dark and try different things, but I imagine people with a lot more experience might have good suggestions.

 

 

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

I would look at the disassembly output of the compiler... Not 100% sure how to get that from xc8 though... Should be called something 'listing' file...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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

tunari wrote:
Dare I ask what kind of a solution should I be looking for to view how simple C code is translated to assembly.
[facetious]I personally am sticking with tried and tested AS7 for a year or two longer until the dust settles in MPLABX/XC8/etc. For Atmel with AS7 it only took AS5, AS5.1, AS5.2, AS6, AS6.1, AS6.2, AS6.3 (think it was) until they ironed most things out in the relatively stable AS7. So I'd maybe give it 3..5 years and you should be hot to trot.[/facetious]

meolsen wrote:
I would look at the disassembly output of the compiler... Not 100% sure how to get that from xc8 though... Should be called something 'listing' file...
I rather think you mean:

 Directory of C:\Users\littleoleme\MPLABXProjects\test_disasm.X\disassembly

22/06/2020  13:37    <DIR>          .
22/06/2020  13:37    <DIR>          ..
22/06/2020  13:37             2,520 listing.disasm

But sadly:

C:\Users\littleoleme\MPLABXProjects\test_disasm.X\disassembly>head -n 30 listing.disasm
C:/Users/littleoleme/MPLABXProjects/test_disasm.X/dist/default/debug/test_disasm.X.debug.elf
22-Jun-2020 13:27:35

---  C:/Users/littleoleme/MPLABXProjects/test_disasm.X/newmain.c  ------------------------------------------
1:             /*
2:              * File:   newmain.c
3:              * Author: no_really_me
5:              * Created on 22 June 2020, 13:22
6:              */
7:
8:             #include <avr/io.h>
9:
10:            /*
11:             *
12:             */
13:            int main(int argc, char** argv) {
14:
15:                PORTF.DIRSET = PIN5_bm;
005C  E280     LDI R24, 0x20
005D  9380     STS 0x00, R24
005E  04A1     CPC R10, R1
005F  CFFF     RJMP 0x5F
16:                while(1);
17:            }
18:
---  ././././crt1/gcrt1.S  ------------------------------------------------------------------------------
0000  940C     JMP __eeprom_end

which  is the very file in #1

 

I personally am still a fan of .s file and  https://github.com/wrightflyer/avr-source to explore C compiler generate Asm but it needs you to be using avr-gcc not XC8.

Last Edited: Mon. Jun 22, 2020 - 03:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, I think the .disasm file is MPLABs works... The XC output (any compiler...) should end up in the dist/ folder...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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

All the files I can see:

C:\Users\rathercuriousid\MPLABXProjects>tree /f test_disasm.X
Folder PATH listing for volume OSDISK
Volume serial number is 7AF3-B2D0
C:\USERS\rathercuriousid\MPLABXPROJECTS\TEST_DISASM.X
│   Makefile
│   newmain.c
│
├───build
│   └───default
│       ├───debug
│       │       newmain.o
│       │       newmain.o.d
│       │
│       └───production
│               newmain.o
│               newmain.o.d
│
├───debug
│   └───default
├───disassembly
│       listing.disasm
│
├───dist
│   └───default
│       ├───debug
│       │       memoryfile.xml
│       │       test_disasm.X.debug.elf
│       │       test_disasm.X.debug.map
│       │
│       └───production
│               memoryfile.xml
│               test_disasm.X.production.elf
│               test_disasm.X.production.hex
│               test_disasm.X.production.map
│
└───nbproject
    │   configurations.xml
    │   Makefile-default.mk
    │   Makefile-genesis.properties
    │   Makefile-impl.mk
    │   Makefile-local-default.mk
    │   Makefile-variables.mk
    │   Package-default.bash
    │   project.xml
    │
    └───private
            configurations.xml
            private.xml

Only thing anything like a listing is the one I showed previously.

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

Not sure what I'm thinking of... I cant find it in the xc8 settings at least :D

 

I pinged the bug, so maybe something will happen :)

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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


Ah, found it

 

But, avr-gcc specific indecision

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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

>and the compiler is XC8 v2.20 (unlicensed). -O1 switch is being used. If I change that to -O0

 

 

Although its easy to remove the optimization restrictions on xc8-avr, and getting to -Os means less asm to read and mentally decode/understand, a better solution in my opinion is to just install (unzip) something like the following-

 

http://downloads.arduino.cc/tool...

 

I have the Linux version of the above, and you can simply have MPLABX add this as another compiler choice (unzip file, point mplabx to the bin dir via the tools,options,embedded,build tools menu). You will need to modify a linker script, but its one line and easy to do (just ask).  Compile times will be faster, and I think you end up with a better compiler.

 

In addition to enabling the lst file, you can also add a post-build step something like-

${MP_CC_DIR}/avr-objdump -dC ${ImageDir}/${PROJECTNAME}.${IMAGE_TYPE}.elf > list.txt

where you can customize what you get out of objdump.