Hello, there are some question about MOVE Instruction, that how to use (d16,An) and (d8,Ay,Xi*SF)? what is their function ?
> what is their function ?
Their basic function as assembly instruction addressing modes is to move data around.
Do you mean "HOW do they function" (technical details) or "WHAT are they good for"? They're very different questions. I don't know which one you meant.
You have pasted that table in from section "3.3.5.2 MOVE Instruction Execution Times" of the "MCF52259 ColdFire Integrated MCU Reference Manual".
That part only tells you the execution time of the instructions.
That's not the section that details how the instructions work. That isn't in this manual, but is in another one.
Read Chapter 3 from the top of the very first page. Look for references to other documents. Find, download and read them (actually it, there's one that details the instructions).
If you want to know "what programming and data constructs are they used for) then just disassemble something. The "Address Register Indirect with Displacement Mode" (d16,An) is used for absolutely everything.
As for the "Address Register Indirect with Index (8-Bit Displacement) Mode" (d8,Ay,Xi*SF), I have no idea. I think it is an "orphan" that happened to fit into the "one or two extension word restriction" imposed on the Coldfire that the more useful 68000 "Address Register Indirect with Index (Base Displacement) Mode" didn't survive.
For the "historical context" you should read up on the addressing modes of the 68000. Then read about the next evolution of that architecture, which was the 68020 and 68030, They had things like "Memory Indirect Postindexed ([bd,An],Xn.SIZE*SCALE.od)" and "Memory Indirect Preindexed ([bd,An.Xn.SIZE*SCALE],od)". They were cute and powerful and generally completely unused.
Tom
I wrote:
> As for the "Address Register Indirect with Index (8-Bit Displacement) Mode" (d8,Ay,Xi*SF),
>I have no idea. I think it is an "orphan"
It is a pretty busy "orphan". Disassembling a Coldfire bootstrap we have here that has 119k bytes of code in it consisting of about 34434 instructions:
$ m68k-elf-objdump -S boot.elf | grep "@.*:" | grep -v pc | wc
385 1930 21752
$ m68k-elf-objdump -S boot.elf | grep "@.*:" | grep -v pc | less
800006d6: 2191 0c00 movel %a1@,%a0@(00000000,%d0:l:4)
800006f0: 2191 1c00 movel %a1@,%a0@(00000000,%d1:l:4)
8000070a: 2191 0c08 movel %a1@,%a0@(00000008,%d0:l:4)
8000073c: 2191 0c08 movel %a1@,%a0@(00000008,%d0:l:4)
8000083a: 81b6 1ccc orl %d0,%fp@(ffffffcc,%d1:l:4)
80000840: 3031 0a00 movew %a1@(00000000,%d0:l:2),%d0
8000084a: 2d80 1ccc movel %d0,%fp@(ffffffcc,%d1:l:4)
80000872: 1033 8800 moveb %a3@(00000000,%a0:l),%d0
8000087e: 1233 8800 moveb %a3@(00000000,%a0:l),%d1
8000088a: 1780 8800 moveb %d0,%a3@(00000000,%a0:l)
80000a20: 4870 aa00 pea %a0@(00000000,%a2:l:2)
80001316: 41f1 3c00 lea %a1@(00000000,%d3:l:4),%a0
8000131c: 47f0 5800 lea %a0@(00000000,%d5:l),%a3
80001346: 4872 0800 pea %a2@(00000000,%d0:l)
800013bc: 41f1 1c0c lea %a1@(0000000c,%d1:l:4),%a0
800013c0: 41f0 5800 lea %a0@(00000000,%d5:l),%a0
8000168c: 52b6 0cec addql #1,%fp@(ffffffec,%d0:l:4)
800016a2: 4ab6 0cec tstl %fp@(ffffffec,%d0:l:4)
80001730: 4872 0800 pea %a2@(00000000,%d0:l)
80001a02: 3031 0a00 movew %a1@(00000000,%d0:l:2),%d0
80001a68: 41f1 2c00 lea %a1@(00000000,%d2:l:4),%a0
80001a6c: 43f3 2a01 lea %a3@(00000001,%d2:l:2),%a1
80001a70: 47f2 8800 lea %a2@(00000000,%a0:l),%a3
80001a80: 2032 9a00 movel %a2@(00000000,%a1:l:2),%d0
80002670: 2270 0800 moveal %a0@(00000000,%d0:l),%a1
8000268c: cab0 0808 andl %a0@(00000008,%d0:l),%d5
8000269a: b0b0 1c04 cmpl %a0@(00000004,%d1:l:4),%d0
800027d4: c2b0 0808 andl %a0@(00000008,%d0:l),%d1
800027e8: b2b0 0c04 cmpl %a0@(00000004,%d0:l:4),%d1
80002802: 2430 2800 movel %a0@(00000000,%d2:l),%d2
8000293e: c2b0 0808 andl %a0@(00000008,%d0:l),%d1
80002952: b2b0 0c04 cmpl %a0@(00000004,%d0:l:4),%d1
... And about another 350 like that.
The gcc compiler is making very good use of that instruction, most times just using it to add two registers together with a zero offset.
And as for the normal (and way more popular) indexed ones:
$ m68k-elf-objdump -S boot.elf | grep "@([0-9]*)" | wc
5365 27050 238828
m68k-elf-objdump -S boot.elf | grep "@([0-9]*)" | less
800005ee: 4fef 000c lea %sp@(12),%sp
80000690: 2f2e 0008 movel %fp@(8),%sp@-
800006ae: 262e 0008 movel %fp@(8),%d3
800006b2: 206a 0146 moveal %a2@(326),%a0
800006ba: 2028 0004 movel %a0@(4),%d0
800006d0: 43ee 0010 lea %fp@(16),%a1
800006da: 43ee 0014 lea %fp@(20),%a1
800006e0: 2828 0004 movel %a0@(4),%d4
800006e4: 2228 0004 movel %a0@(4),%d1
800006f4: 85a8 0188 orl %d2,%a0@(392)
80000702: 43ee 000c lea %fp@(12),%a1
80000706: 2141 0004 movel %d1,%a0@(4)
8000071c: 41ea 014a lea %a2@(330),%a0
80000722: 2028 0004 movel %a0@(4),%d0
80000726: 2548 0146 movel %a0,%a2@(326)
80000730: 2028 0004 movel %a0@(4),%d0
80000734: 43ee 000c lea %fp@(12),%a1
80000740: 85a8 018c orl %d2,%a0@(396)
80000744: 2141 0004 movel %d1,%a0@(4)
80000772: 20aa 0146 movel %a2@(326),%a0@
80000778: 2548 0146 movel %a0,%a2@(326)
8000077c: 42a8 0004 clrl %a0@(4)
Count of normal "move" and "movea" instructions, and 26 "movec" ones:
$ m68k-elf-objdump -S boot.elf | grep "move" | wc
13940 65080 620299
Tom
Hi Tom,
Thanks for the answer. Actually, I don't know how they work and what they are good for in my question.
As your suggestion , I find a document named "ColdFire Family Programmer’s Reference Manual" at the head of Chapter 3.Now , I see it is Address Register Indirect with Displacement Mode. I try to execute "move (2,a0),d0", but ,a error occur. Anything I was wrong?
David
Now you know how the CPU instructions work.
What you now have is a very different problem.
The assembly syntax shown in the Programmer's Manual is only one way of representing that instruction to an assembler.
The people who write assemblers don't seem to take much notice of the manuals. They seem to "know better" and have their own favourite ways of representing parts of the assembly instruction.
So it may be "move" or "movel" or "move.l" or "move_l" and "2" or "$2" or "#2", and "a0" or "%a0".
I'm using gcc and it seems to prefer things like "movew %a0@(4),%d2".
So you now have to read the manual for the Assembly Manual that you're using to see how they expect assembly code to be written.
But then you're trying to write assembly code and then present it to a C Compiler.
Different C compilers have different ways of accepting Assembly code, often with even more complicated coding. You then have to tell the system which C Variable you want in which Assembly Register. it is useless putting assembly in C code if you can't have them work on each others variables.
I hope you're not just typing "move (2, a0), d0)" into a file and trying to compile it like that as that isn't "Valid C". That has to be wrapped in "asm("move ..."); or something special. And something non-standard (and different for each different compiler).
What compiler are you using? If you're using the Freescale one, then you should read all the documentation for it. You should then post in the CW forum with questions on its use if you can't work it out.
You should also look through sample code for examples on how to use assembly in the system you're using.
This Forum isn't Twitter, so you should be providing FAR more context and sample code than you're doing.
Why are you trying to use assembly code in "fpgaCAN.c" which looks like a CAN driver? There shouldn't be any need to write assembly, and the compiler can usually do a better job converting C code to assembly that you could anyway.
Tom
Hi Tom,
Thanks for the examples and the kindly answer.
You are right.As your suspect, I had used "asm {}" in a C file and I use the CodeWarrior 7.2 .So, it seems the Compiler is not spported to this instruction.
>>Why are you trying to use assembly code in "fpgaCAN.c" which looks like a CAN driver?
Yes, it is in a CAN interrupt . More important thing is that I am learning assembly language of Freescale, so that , I can handle some bad conditions that the code can run more faster.
In conclusion, I know more about assembly and instructions and I can use some basic instructions to finish some code instead with C.
A new question , if I want to rewrite codes "CANRXMSG *pCurrentMsgBuf = &(CANstruct.RxBuffer[CANstruct.usRxBufHead]);" CANstruct is a global variable , how can I do?
Now I write them as :
I think I use a bad method to get the pointer.
Thanks again.
David
You've got an 80MHz CPU. The fastest CAN port is 1MHz and takes between 50 and 110 bits to deliver a message. So the maximum message arrival rate is one every 2,800 CPU instructions.
So why do you need to try and make it a bit faster when the CPU has that much speed?
You can probably save a lot of time somewhere else in your code if you need it.
Writing maintainable code is far more important that writing something "just a bit quicker". If I was your boss I wouldn't let you write assembly code in that function.
If you must have assembly you should write it as a separate standalone assembly code file, not using the "asm" construct.
Your code is a little inefficient. You should be using more registers and you shouldn't be using address registers for data operation as they're more restricted in what they can do. The first thing your code should do is a "moveml" to free up registers for efficient use.
I can't see that the compiler would do that bad a job, UNLESS you've got it configured in "debug mode" which generates unoptimised code so it is easy to single-step. You should let it generate "release" code with a high level of optimisation and compare that with your assembly.
It may be that CW isn't that good, even if you configure it properly.
Can you provide a stand-alone (main() plus function calls) program with the internals of the interrupt handler written in C. I'll compile it with gcc and we'll then see if it can do any better?
I've just done that for the part of the code that moves and merges the extended ID and the code from gcc ends up like this:
#define CAN_MSG_IDX_IDE 0x80000000
if (pReg->CAN_MSG_ID_REG & CAN_MSG_IDX_IDE)
{
pMsg->ulMsgId = ((pReg->CAN_MSG_ID_REG & 0x3ff) << 18) |
((pReg->CAN_MSG_ID_REG & 0x1ffff800) >> 11);
16: 2211 movel %a1@,%d1
18: 2f02 movel %d2,sp@-
1a: 4a81 tst %d1
1c: 6d18 blts 36
36: 2001 movel %d1,%d0
38: 740b moveq #11,%d2
3a: 0280 1fff f800 andil #536868864,%d0
40: 0281 0000 03ff andil #1023,%d1
46: e4a8 lsrl %d2,%d0
48: 143c 0012 moveb #18,%d2
4c: e5a9 lsll %d2,%d1
4e: 740f moveq #15,%d2
50: 8081 orl %d1,%d0
52: 2140 0004 movel %d0,%a0@(4)
So that's 9 instructions, doing the same as your code does in 10. The "moveq #15" in there is just setting up for masking the DLC.
It would be better to move that operation out of the interrupt routine and perform that operation in the mainline code though. The interrupt routine should do the bare minimum.
What you do need is to declare a static inline assembly function that can contains the "byterev.l" instruction (ONLY) to do the byte swap for you. That SHOULD already be a part of a provided library for the ColdFire chips. I'd look to see if one has already been provided in CW before writing one yourself.
On Linux systems, the header file "netinet/in.h" defines macros ntohs(), htons(), ntohl() and htonl() to perform byte swapping from "network order" to "host order". On a big-endian coldfire these do nothing (the bytes are already in "network order"). See if you can find equivalent definitions in CW. in Linux these are defined in terms of a "__bswap_32()". There should be more standard definitions in an "endian.h" header file. You might have a "byteswap.h" header.
Here's one from the Linux distribution. That's for gcc. You need an equivalent for CW:
http://lxr.free-electrons.com/source/arch/m68k/include/asm/swab.h?v=3.4
Here's where that question has come up before - simply searching the community for "byterev":
Re: Link Error : Undefined : mcf5xxx_byterev
Re: Swapping bytes & ASR/ASL
Re: Coldfire SEC and SKHA Benchmarks
It would be more efficient to fix the FPGA CAN implementation to give you the data in the right order so you don't have to fix it in your code though. It looks like someone bolted a little-endian CAN implementation onto a big-endian CPU. That's never a good idea. But you'll notice in the "SKHA" post above that Freescale did exactly that in the MCF5235!
EDIT:
Thinking some more about this, what order are the data bytes in the FPGA? The "standard" order for them in two 32-bit registers is "[ 0 | 1 | 2 | 3 ] [ 4 | 5 | 6 | 7 ]", which matches the byte order in the big-endian Coldfire chip. Is the FPGA providing them in this order and you're swapping to little-endian, or is it weirdly providing them as "[ 3 | 2 | 1 | 0 ] [ 7 | 6 | 5 | 4 ]" and you're swapping them back to native big-endian?
CAN Data shouldn't be considered as a stream of 8 bytes. It is a stream of 64 BITS. Breaking those bits into smaller fields (1, 2, 3, 4, 8, 16 and 32 bits are common) can be done on any bit boundary, so leaving the data in the original 32-bit order (or 64 bits on a 64-bit CPU) can make the final data extraction more efficient. If you swap them into "little endian byte order" you'll later have to swap them back to extract the fields.
Tom