Shop OBEX P1 Docs P2 Docs Learn Events
Question about Propeller assembly multiply and divide operations — Parallax Forums

Question about Propeller assembly multiply and divide operations

I've been trying to learn a bit about propeller assembly. I hope to be able to eventually write code in assembly, which I can offload to a cog to run when called. To start I coded a toy function in C, which worked as expected. However, I wanted to see what the associated assembly looked like, so I took a look and got some unexpected results (see below).

Here's the function in C:

void AssAlg1(int *in1)
{
unsigned short temp=(*in1);
unsigned short temp2=5;
unsigned short temp3;

temp++;
temp++;
temp=temp+temp2;

temp=temp*3;
temp=temp-13;
temp=temp/temp2;
(*in1)=(temp);
return;
}

And here's the resulting assembly output (generated from propeller-elf-objdump):
000007f8 <_AssAlg1>:
7f8: f2 00 0e 04 rdword 1c <r7>, 0 <__clkfreq>
7fc: 27 70 add r7, #0x7
7fe: 0a 60 mov r6,r0
800: 0a 07 mov r0,r7
802: a1 03 mov r1,#0x3
804: f2 2e 01 60 and 0 <__clkfreq>, 4b8 <__MASK_0000FFFF>
808: 07 mul
809: e7 70 d1 xmov r7, r0 sub r7, #0xd
80c: 0a 07 mov r0,r7
80e: f2 2e 01 60 and 0 <__clkfreq>, 4b8 <__MASK_0000FFFF>
812: a1 05 mov r1,#0x5
814: 08 udiv
815: f2 2e 01 60 and 0 <__clkfreq>, 4b8 <__MASK_0000FFFF>
819: 10 6f wrlong r0, r6
81b: 02 lret

What's bothering me is how it handles the multiply and divide operations. I took a look as the assembly reference and can't find a mul or udiv instruction. Exactly what's going on here? I assume this is some kind of macro, but I haven't been able to locate any reference to either.

Any ideas?

For completeness here's the full assembly code listing from .asm file:
5:assembCode.c **** void AssAlg1(int *in1)
6:assembCode.c **** {
7 .loc 1 6 0
8 .LVL0
7:assembCode.c **** unsigned short temp=(*in1);
9 .loc 1 7 0
10 0000 F2000E04 rdword r7, r0
11 .LVL1
8:assembCode.c **** unsigned short temp2=5;
9:assembCode.c **** unsigned short temp3;
10:assembCode.c ****
11:assembCode.c **** temp++;
12:assembCode.c **** temp++;
13:assembCode.c **** temp=temp+temp2;
12 .loc 1 13 0
13 0004 2770 add r7, #7
14 .LVL2
6:assembCode.c **** {
15 .loc 1 6 0
16 0006 0A60 mov r6, r0
17 .loc 1 13 0
18 0008 0A07 mov r0, r7
19 .LVL3
14:assembCode.c ****
15:assembCode.c **** temp=temp*3;
20 .loc 1 15 0
21 000a A103 mov r1, #3
13:assembCode.c **** temp=temp+temp2;
22 .loc 1 13 0
23 000c F2000060 and r0,__MASK_0000FFFF
24 .LVL4
25 .loc 1 15 0
26 0010 07 lmul
27 .LVL5
28 .LVL6
29 0011 E770D1 xmov r7,r0 sub r7,#13
16:assembCode.c **** temp=temp-13;
30 .loc 1 16 0
31 0014 0A07 mov r0, r7
32 .LVL7
33 0016 F2000060 and r0,__MASK_0000FFFF
34 .LVL8
17:assembCode.c **** temp=temp/temp2;
35 .loc 1 17 0
36 001a A105 mov r1, #5
37 001c 08 ludiv
38 .LVL9
18:assembCode.c **** (*in1)=(temp);
39 .loc 1 18 0
40 001d F2000060 and r0,__MASK_0000FFFF
41 0021 106F wrlong r0, r6
19:assembCode.c **** return;
20:assembCode.c **** }
42 .loc 1 20 0
43 0023 02 lret

Comments

  • tonyp12tonyp12 Posts: 1,951
    edited 2016-08-17 17:56
    There are no hardware mul or div pasm.
    A good compiler will use its internal 16*16 call function for run time math,
    but if the compiler sees that it can simple use left or right shift and maybe finish with a addition/subtraction, it will create custom inline routine just for that.

  • Heater.Heater. Posts: 21,230
    There are no multiply or divide instructions in the Propeller.

    So if you want to do mul and div you need to write the PASM to do that, using shift, add, subtract or whatever.

    After all, I'm sure you learned in primary school that multiply is just repeated additions. You can use the primary school methods of mul and div.

    You can do that by hand in PASM. Or you can use what the compilers generate for you. Spin, C and so on.

    Sorry I can't provide links off hand but if you search around you will find mul and div code in PASM if you want to write it by hand.
  • btw, if you add "-save-temps" to your compile flags, you won't have to run objdump over the binary. It also produces much more legible assembly.
  • I'm aware there are no mul or div commands, and yet you can see assembly code that was generated by the assembler/linker. I think I have found the macros I mentioned though. In dump file I generated I found __macro_mul and __macro_udiv labels, which appear to reference __MULSI and __DUIVSI routines.

    I'm still not sure how exactly the routines are being called in my code. If you look in the attached output.txt dumpfile that I generated you should be able to find the aforementioned mul and udiv macros. But I still question how exactly they are being called by the assembly code without using an explicit 'call' command.

    Also, @DavidZemon, thanks for the tip. That should save me some trouble.
  • Your code has been compiled in CMM mode -- that's why some instructions are 2 bytes long, some are 1 byte (like "mul" and "div") and some are longer. In CMM mode the C compiler actually produces a special kind of variable length bytecode, which gets interpreted by the CMM kernel. The macro_mul and macro_div functions are called by the interpreter. Real Propeller assembly instructions are always 4 bytes long, so CMM code is typically more compact (but slower).

    If you're trying to learn Propeller Assembly (PASM) you'll have much better luck looking at code compiled in LMM mode or even better COG mode. In COG mode the compiler produces pure PASM, which must run in the COGs internal memory. In LMM mode it produces PASM that is "interpreted" by a very simple interpreter which loads instructions from HUB and executes them one at a time. The difference mainy shows up in jumps -- in COG mode real jump instructions are produced, in LMM special sequences of instructions are produced to modify the register the interpreter is using to fetch from HUB.
  • Appendix B of the propellor manual has "official" examples of multiply, divide, and sqrt.
  • @ersmith: Thanks! That was exactly the information I needed.
Sign In or Register to comment.