Edge Rewrite
Jump to content

Talk:Duff's device

Page contents not supported in other languages.
Add topic
From Wikipedia, the free encyclopedia

The article is clear. Here traces for count=1 to 16

[edit]

The article is clear. Here are traces from count=1 to 16, some may be picked to insert in the article. I didn't attempted to change the article because it is clear to me. But this can be clearer for some readers. I don't know if it is possible to hide and show the code clicking a button. If it is not possible, maybe the trace for code=10 is more illustrative. This discussion is short, if the traces become obstructive for reading, because it is not possible to hide and show them clicking a button, leave the more representative 1,7,8,10,16 cases to delete the rest.

void send(to, from, count:=1)
  register short *to, *from;
  register int count;
{
  assert(count<1> > 0);<True>
  predicate(0 == (count % 8)<1>)<False>
  register n<1> = (count<1> + 7) / 8;
  switch(count<1> % 8)<1>
  {
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=2)
  register short *to, *from;
  register int count;
{
  assert(count<2> > 0);<True>
  predicate(0 == (count % 8)<2>)<False>
  register n<1> = (count<2> + 7) / 8;
  switch(count<2> % 8)<2>
  {
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=3)
  register short *to, *from;
  register int count;
{
  assert(count<3> > 0);<True>
  predicate(0 == (count % 8)<3>)<False>
  register n<1> = (count<3> + 7) / 8;
  switch(count<3> % 8)<3>
  {
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=4)
  register short *to, *from;
  register int count;
{
  assert(count<4> > 0);<True>
  predicate(0 == (count % 8)<4>)<False>
  register n<1> = (count<4> + 7) / 8;
  switch(count<4> % 8)<4>
  {
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=5)
  register short *to, *from;
  register int count;
{
  assert(count<5> > 0);<True>
  predicate(0 == (count % 8)<5>)<False>
  register n<1> = (count<5> + 7) / 8;
  switch(count<5> % 8)<5>
  {
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=6)
  register short *to, *from;
  register int count;
{
  assert(count<6> > 0);<True>
  predicate(0 == (count % 8)<6>)<False>
  register n<1> = (count<6> + 7) / 8;
  switch(count<6> % 8)<6>
  {
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=7)
  register short *to, *from;
  register int count;
{
  assert(count<7> > 0);<True>
  predicate(0 == (count % 8)<7>)<False>
  register n<1> = (count<7> + 7) / 8;
  switch(count<7> % 8)<7>
  {
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=8)
  register short *to, *from;
  register int count;
{
  assert(count<8> > 0);<True>
  predicate(0 == (count % 8)<0>)<True>
  register n<1> = (count<8> + 7) / 8;
  switch(count<8> % 8)<0>
  {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=9)
  register short *to, *from;
  register int count;
{
  assert(count<9> > 0);<True>
  predicate(0 == (count % 8)<1>)<False>
  register n<2> = (count<9> + 7) / 8;
  switch(count<9> % 8)<1>
  {
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=10)
  register short *to, *from;
  register int count;
{
  assert(count<10> > 0);<True>
  predicate(0 == (count % 8)<2>)<False>
  register n<2> = (count<10> + 7) / 8;
  switch(count<10> % 8)<2>
  {
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=11)
  register short *to, *from;
  register int count;
{
  assert(count<11> > 0);<True>
  predicate(0 == (count % 8)<3>)<False>
  register n<2> = (count<11> + 7) / 8;
  switch(count<11> % 8)<3>
  {
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=12)
  register short *to, *from;
  register int count;
{
  assert(count<12> > 0);<True>
  predicate(0 == (count % 8)<4>)<False>
  register n<2> = (count<12> + 7) / 8;
  switch(count<12> % 8)<4>
  {
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=13)
  register short *to, *from;
  register int count;
{
  assert(count<13> > 0);<True>
  predicate(0 == (count % 8)<5>)<False>
  register n<2> = (count<13> + 7) / 8;
  switch(count<13> % 8)<5>
  {
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=14)
  register short *to, *from;
  register int count;
{
  assert(count<14> > 0);<True>
  predicate(0 == (count % 8)<6>)<False>
  register n<2> = (count<14> + 7) / 8;
  switch(count<14> % 8)<6>
  {
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=15)
  register short *to, *from;
  register int count;
{
  assert(count<15> > 0);<True>
  predicate(0 == (count % 8)<7>)<False>
  register n<2> = (count<15> + 7) / 8;
  switch(count<15> % 8)<7>
  {
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

void send(to, from, count:=16)
  register short *to, *from;
  register int count;
{
  assert(count<16> > 0);<True>
  predicate(0 == (count % 8)<0>)<True>
  register n<2> = (count<16> + 7) / 8;
  switch(count<16> % 8)<0>
  {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<1> > 0);
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n<0> > 0);
  }
}

Original Duff's device implementation is confusing

[edit]

The original Duff's device implementation presented in the "Duff's technique" section is miss-leading: the validity of the original *to = *from++; statements does not appear to be sufficiently established and causes confusion with people stating that the to++ is missing.

The current article only states that this is "K&R C" and that to is a "memory-mapped output", which is not sufficient to support validity. Classic K&R C does not have a clear specification and rules about memory access and there was no guarantee that this memory-mapped I/O code snippet is safe.

the original code may be valid under some conventions that are currently not documented (specific compiler version and flags, etc.). I don't think that the curent code would be correct with modern compilers without the usage of volatile or other equivalent for the memory-mapped output.

Some solutions to improve the article:

  • Replace the original formulation by a "modern" equivalent, or at least include it. I believe that the original formulation is not that relevant to understand the concept. Most articles about Duff's device use the `to++` variant, including most Wikipedia pages in other languages.
  • Clarify under what circonstances the original formulation is correct. The article could simply mention that the to variable is assumed to be volatile or whatever guarantees the code to be correct (language specification, no compiler optimisation, etc.).

Laclouis5 (talk) 16:24, 4 January 2026 (UTC)Reply

The functionally equivalent version is surely incorrect

[edit]

If count <= 7 then the do while block is executed once (they are always executed at least once) whereas it should not be executed at all. do while should be while. Epo001 (talk) 12:38, 11 January 2026 (UTC)Reply

@Epo001 You're right but changing it to a while fixes a bug present in the original code where if count is 0, the whole thing still runs eight times. Changing the do-while to a while makes it so if count is 0, the whole thing doesn't run.
A better idea is to change register n = count / 8; to register n = (count + 7) / 8; like the original code does. .jhotc (talk) 08:35, 31 January 2026 (UTC)Reply
Or use the do-while starting with case: 0, but start with a sanity check above the switch: if (!(count>0)) return; (or assert, etc.) Wegesrand (talk) 13:23, 17 April 2026 (UTC).Reply

Extraneous info about C versions: void functions, default variable type

[edit]

Just below the first example, the un-optimized loop, the paragraph about differences between K&R and ANSI C is true but completely irrelevant to this topic. I will delete it – this is your chance to argue for preservation. Wegesrand (talk) 13:16, 17 April 2026 (UTC)Reply

It explains to those who don't know K&R why Duff's original code does not match what one used to more modern C would expect. I think that makes it relevant. --John (User:Jwy/talk) 15:11, 17 April 2026 (UTC)Reply
Thanks, it makes sense that that's the intended purpose. Nonetheless, (1) the paragraph confuses the reader by wandering away from the topic; (2) the introduction above the snippet explicitly says the language is K&R C, with a link, so the reader should not be confused by teh fact that it's not contemporary C; (3) the register keyword still exists in C, and hence doesn't need explanation as a feature of K&R C. Anyway, (4), I guess it just didn't occur to me that present-day C readers are not conversant with the K&R dialect. Okay, I guess I'll tighten this paragraph and make its intention explicit rather than deleting it outright. Wegesrand (talk) 14:04, 23 April 2026 (UTC)Reply
Tightening is good. I still question why we need to explain why 40-year-old code doesn't look like modern code. SarekOfVulcan (talk) 15:38, 23 April 2026 (UTC)Reply