Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A (UK) celebration

10 views
Skip to first unread message

pe...@nospam.demon.co.uk

unread,
Jun 3, 2012, 3:46:08 AM6/3/12
to
I wrote the following piece of code around 15 years ago in connection
with the IOCCC Obfuscated C competition and it has just sat on my PC
doing nothing since then. You might be aware that the UK is celebrating
its Queen's 60th year on the throne this weekend, so now seemed an
appropriate time to re-release this piece of fripperie, just for a bit
of fun. It is for pure MSDOS; Windows? forget it!

It compiles straight out of the box for MSC 5.1 but might need some
tweaking for other (16 bit DOS) compilers. You might need to redefine
_I_ and/or _O_ if your lib uses different names for inp() and outp()
to access the hardware. For example if your IN and OUT functions are
called inb() and outb(), compile using

$(cc) -D_I_=inb -D_O_=outb -o jubilee.exe jubilee.c

(best viewed using a fixed pitch font)

-------- Start jubilee.c --------

#ifndef _I_
#define _I_ inp
#endif
#ifndef _O_
#define _O_ outp
#endif

#define I int
#define C char
#define L long
#define V void
#define S static
#define R register
#define U unsigned

L m[]={0x08E90801L,0x08E90801L,0x07F00801L,0x096F1001L,0x08E90301L,0x7F00802L,
0x07120801L,0x07120801L,0x06AD0801L,/*00*/0x07121001L,0x07F00301L,0x08E90802L,
0x07F00801L, 0x08E90801L, 0x096F0801L,
/**/0x08E90802L, 0x08E90201L, 0x07F00201L,/**/
/* */0x07120201L, 0x06AD0202L, 0x05F10801L,/* */
/* */0x05F10801L, 0x05F10801L, 0x05F11001L,/* */
/* */0x06AD0301L, 0x07120802L, 0x06AD0801L,/* */
/* */0x06AD0801L, 0x06AD0801L, 0x06AD1001L,/* */
/*00000*/0x07120301L,0x07F00802L,0x07120801L,0x06AD0201L,0x07120201L,/*00000*/
/*00000*/0x07F00201L,0x08E90201L,0x07121001L,0x06AD0301L,0x05F10802L,/*00000*/
/*00000*/0x054B0301L,0x06AD0301L,0x07120C01L,0x07F00C01L,0x08E91001L,/*00000*/
0L} ;S V _0_(),_1_ ();I main() {R L *p;for(p=m; *p
!= 0L;p++)_1_(p); return(0);} S V _1_(s)R U C *s
;{ _O_(0x43,0xb6); _O_(0x42,s[2] );_O_(0x42,s[ 3]
); _O_(0x61,_I_( 0x61)|3);_0_ (s[1]);_O_(0x61,/**/
_I_(0x61)&~3); _0_(s[0]);}/* 0000000000000 */
#define K (*(U I far*) 0x46cL)/***/
S V _0_ (t) R U I t;{R U I n;++t;do{n=K;while(n==K);}while(--t);}/*0x7F00802L,
0x07120801L,0x07120801L,0x06AD0801L,0x07121001L,0x07F00301L,0x08E90802L,0000*/

-------- End jubilee.c --------

Enjoy!
Pete
--
Believe those who are seeking the truth.
Doubt those who find it. - André Gide

Nomen Nescio

unread,
Jun 3, 2012, 9:29:06 AM6/3/12
to
Fantastic...really a piece of art.

Rod Pemberton

unread,
Jun 3, 2012, 5:23:33 PM6/3/12
to
<pe...@nospam.demon.co.uk> wrote in message
news:133870...@nospam.demon.co.uk...
> I wrote the following piece of code around 15 years ago in connection
> with the IOCCC Obfuscated C competition and it has just sat on my PC
> doing nothing since then. You might be aware that the UK is celebrating
> its Queen's 60th year on the throne this weekend, so now seemed an
> appropriate time to re-release this piece of fripperie, just for a bit
> of fun. It is for pure MSDOS; Windows? forget it!
>

It runs in a Windows 98/SE DOS "console" window just fine.

> It compiles straight out of the box for MSC 5.1 but might need some
> tweaking for other (16 bit DOS) compilers. You might need to redefine
> _I_ and/or _O_ if your lib uses different names for inp() and outp()
> to access the hardware. For example if your IN and OUT functions are
> called inb() and outb(), compile using
>
> $(cc) -D_I_=inb -D_O_=outb -o jubilee.exe jubilee.c
>
> (best viewed using a fixed pitch font)
>
> -------- Start jubilee.c --------
>
> [program]
> -------- End jubilee.c --------
>


Well, I don't have MSC 5.1. With a couple of changes, it'll compile with
OpenWatcom v1.3 from the command line. Sorry, I don't have a more
recent version of OW installed to confirm if it compiles properly for it.

For OWv1.3, the 7th line from bottom needs a cast.
Change this:
0L;p++)_1_(p);

To this:
0L;p++)_1_((U C *)p);

For OWv1.3, the 3rd line from bottom needs the "volatile" keyword.
Change this:
(*(U I far*)

To this:
(*(volatile U I far*)

Then:
wcl/l=dos jubilee.c


I hope you don't mind, but I un-obfuscated your program. I removed all the
unecessary C qualifiers too - "static" "register" "L" - and converted part
of it to unsigned types. I reworked it so that it works for OpenWatcom
(both RM and PM) and DJGPP (PM). Hopefully, the code is clean enough that
others can convert it to the compiler of their choice. Then, they can
listen to it, or learn how to program the PC speaker. I sort of intuitively
and randomly assigned names for your procedures. The commented out notes at
the end of the program didn't seem to add anything to the song, so I didn't
add them.

My conversion of your program for just those two compilers is after my .sig.
Other DOS compilers will need a compiler specific version of the "k" pointer
and appropriate defines for "inport" and "outport".

I haven't used the "volatile" keyword nor accessed DOS memory locations via
C pointers in a few years... So, getting the "K" #define to work as a
pointer - needed for DJGPP - was not pleasant. I'm still not sure why the
'L' is needed for the one pointer for OW ... I almost switched to using
MK_FP(). That's probably something to do with the use of "int" instead of
"long" or "short" or perhaps lack of "unsigned". I should've but didn't
convert the "int" ...


Rod Pemberton


/* gcc -O2 *//* PM DPMI */
#ifdef __DJGPP__
#include <sys/nearptr.h>
#include <pc.h>
#define inport inportb
#define outport outportb
#endif

/* wcl/l=dos *//* RM */
/* wcl386/l=dos4g *//* PM DPMI */
#ifdef __WATCOMC__
#include <i86.h>
#include <conio.h>
#define inport inp
#define outport outp
#endif

unsigned long music[]=
{
0x08E90801,0x08E90801,0x07F00801,0x096F1001,
0x08E90301,0x07F00802,0x07120801,0x07120801,
0x06AD0801,0x07121001,0x07F00301,0x08E90802,
0x07F00801,0x08E90801,0x096F0801,0x08E90802,
0x08E90201,0x07F00201,0x07120201,0x06AD0202,
0x05F10801,0x05F10801,0x05F10801,0x05F11001,
0x06AD0301,0x07120802,0x06AD0801,0x06AD0801,
0x06AD0801,0x06AD1001,0x07120301,0x07F00802,
0x07120801,0x06AD0201,0x07120201,0x07F00201,
0x08E90201,0x07121001,0x06AD0301,0x05F10802,
0x054B0301,0x06AD0301,0x07120C01,0x07F00C01,
0x08E91001,
0L
};

void length(unsigned int time)
{
unsigned int n;

#ifdef __DJGPP__
volatile unsigned int *k;
k=(volatile unsigned int *)
(0x46c+__djgpp_conventional_base);
#endif

#ifdef __WATCOMC__
#ifdef __386__
volatile unsigned int *k;
k=(volatile unsigned int *)0x46c;
#else
volatile unsigned int far *k;
k=(volatile unsigned int *)0x46cL;
#endif
#endif

time++;
do{
n=*k;
while(n==*k);
time--;
}while(time);

}

void note(unsigned char *song)
{
outport(0x43,0xb6);
outport(0x42,song[2]);
outport(0x42,song[3]);
outport(0x61,inport(0x61)|3);
length(song[1]);
outport(0x61,inport(0x61)&~3);
length(song[0]);
}

int main(void)
{
unsigned long *play;

#ifdef __DJGPP__
__djgpp_nearptr_enable();
#endif

for(play=music;*play!=0;play++)
note((unsigned char *)play);

return(0);
}



pe...@nospam.demon.co.uk

unread,
Jun 4, 2012, 2:31:14 AM6/4/12
to
In article <jqgkir$7j5$1...@speranza.aioe.org>
do_no...@notemailntt.cmm "Rod Pemberton" writes:

> <pe...@nospam.demon.co.uk> wrote in message
> news:133870...@nospam.demon.co.uk...
> > I wrote the following piece of code around 15 years ago in connection
> > with the IOCCC Obfuscated C competition and it has just sat on my PC
> > doing nothing since then. You might be aware that the UK is celebrating
> > its Queen's 60th year on the throne this weekend, so now seemed an
> > appropriate time to re-release this piece of fripperie, just for a bit
> > of fun. It is for pure MSDOS; Windows? forget it!
> >
>
> It runs in a Windows 98/SE DOS "console" window just fine.

Of course -- I was thinking XP/Win7 and forgot that lurkers here would
still be running Win9x...

> > It compiles straight out of the box for MSC 5.1 but might need some
> > [...]
>
> Well, I don't have MSC 5.1. With a couple of changes, it'll compile with
> OpenWatcom v1.3 from the command line. Sorry, I don't have a more
> recent version of OW installed to confirm if it compiles properly for it.

The older the compiler, the fewer the problems you're likely to run in
to :-) MSC5 handles K&R C so doesn't bitch too much about abouts
casts and pointer abuse...

> For OWv1.3, the 7th line from bottom needs a cast.
> Change this:
> 0L;p++)_1_(p);
>
> To this:
> 0L;p++)_1_((U C *)p);
>
> For OWv1.3, the 3rd line from bottom needs the "volatile" keyword.
> Change this:
> (*(U I far*)
>
> To this:
> (*(volatile U I far*)
>
> Then:
> wcl/l=dos jubilee.c
>
>
> I hope you don't mind, but I un-obfuscated your program.

Not at all -- I would have done the same! It was after all just a bit
of fun.

> I removed all the
> unecessary C qualifiers too - "static" "register" "L" - and converted part
> of it to unsigned types. I reworked it so that it works for OpenWatcom
> (both RM and PM) and DJGPP (PM). Hopefully, the code is clean enough that
> others can convert it to the compiler of their choice. Then, they can
> listen to it, or learn how to program the PC speaker. I sort of intuitively
> and randomly assigned names for your procedures. The commented out notes at
> the end of the program didn't seem to add anything to the song, so I didn't
> add them.

[ASCII] Artistic licence ;-) Some more "code" was needed to complete
the picture/

> My conversion of your program for just those two compilers is after my .sig.
> Other DOS compilers will need a compiler specific version of the "k" pointer
> and appropriate defines for "inport" and "outport".
>
> I haven't used the "volatile" keyword nor accessed DOS memory locations via
> C pointers in a few years... So, getting the "K" #define to work as a
> pointer - needed for DJGPP - was not pleasant. I'm still not sure why the
> 'L' is needed for the one pointer for OW ... I almost switched to using
> MK_FP(). That's probably something to do with the use of "int" instead of
> "long" or "short" or perhaps lack of "unsigned". I should've but didn't
> convert the "int" ...

FWIW the first cut of the program was for MWC-86 (small model only)
and used its peek(offset, seg) lib function for reading the clock counter.

>
> Rod Pemberton
>
[snip un-obfuscated code]

Thanks for taking the time to deconstruct and clean up the program for
other compilers!

Pete

PS. I just retried the original code with BC++ 3.1 and it compiled OK,
albeit with several warnings about lack of prototypes.

pe...@nospam.demon.co.uk

unread,
Jun 5, 2012, 9:39:05 AM6/5/12
to
Just to put this thread to bed (for a few more years) I thought I
would update Rod's deconstructed code to remove the obfuscation of the
"long *" and "char *" (and modern compilers' need to explicitly cast
them). And also provide some support code for anyone else wanting to
write "music" for the PC speaker.

1. Instead of the "long music[]" array, try this instead:

struct asbyte {
unsigned char lo;
unsigned char hi;
};

union DIVISOR {
unsigned short w;
struct asbyte b;
};

struct TUNE {
union DIVISOR divisor;
unsigned char ontime;
unsigned char offtime;
};

initialised as:

struct TUNE music[] = {

{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x07F0 }, 0x08, 0x01 },
{ { 0x096F }, 0x10, 0x01 },
{ { 0x08E9 }, 0x03, 0x01 },
{ { 0x07F0 }, 0x08, 0x02 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x0712 }, 0x10, 0x01 },
{ { 0x07F0 }, 0x03, 0x01 },
{ { 0x08E9 }, 0x08, 0x02 },
{ { 0x07F0 }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x096F }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x02 },
{ { 0x08E9 }, 0x02, 0x01 },
{ { 0x07F0 }, 0x02, 0x01 },
{ { 0x0712 }, 0x02, 0x01 },
{ { 0x06AD }, 0x02, 0x02 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x10, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x0712 }, 0x08, 0x02 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x10, 0x01 },
{ { 0x0712 }, 0x03, 0x01 },
{ { 0x07F0 }, 0x08, 0x02 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x06AD }, 0x02, 0x01 },
{ { 0x0712 }, 0x02, 0x01 },
{ { 0x07F0 }, 0x02, 0x01 },
{ { 0x08E9 }, 0x02, 0x01 },
{ { 0x0712 }, 0x10, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x05F1 }, 0x08, 0x02 },
{ { 0x054B }, 0x03, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x0712 }, 0x0C, 0x01 },
{ { 0x07F0 }, 0x0C, 0x01 },
{ { 0x08E9 }, 0x10, 0x01 },
};

#define NR_NOTES (sizeof(music)/sizeof(struct TUNE))

The note() and main() functions then become

void note(struct TUNE *song)
{
outport(0x43,0xb6);
outport(0x42,song->divisor.b.lo);
outport(0x42,song->divisor.b.hi);
outport(0x61,inport(0x61)|3);
length(song->ontime);
outport(0x61,inport(0x61)&~3);
length(song->offtime);
}

int main(void)
{
struct TUNE *play;

#ifdef __DJGPP__
__djgpp_nearptr_enable();
#endif

for (play = music; play < music + NR_NOTES; play++)
note(play);

return 0;
}

2. This program is an aid to establishing the correct timer divisor to
produce the frequency of the desired notes:

#include <stdio.h>
#include <math.h>

double timer_freq = 1193180.0; /* countdown timer freq */
double middleC = 261.625; /* frequency (Hz) of middle C */

char *octave[] = {
"C ", "C#", "D ", "D#", "E ", "F ",
"F#", "G ", "G#", "A ", "A#", "B "
};

#define OSIZE (sizeof(octave)/sizeof(char *))

#define NR_OCTAVES 8 /* or however many you want */

int main()
{
int oct;
double startC;
double _12root2 = pow (2.0, 1.0/12.0); /* twelfth root of 2 */

for (oct = 0, startC = middleC / 8.0; /* start 3 octaves lower */
oct < NR_OCTAVES;
oct++, startC *= 2.0)
{
int i;
double freq = startC;

printf ("---------\nOctave %d:\n---------\n", oct);

for (i = 0; i < OSIZE; i++, freq *= _12root2)
{
double div = timer_freq / freq;
printf ("%s freq = %7.2lf, divisor = %8.2lf [0x%04lx]\n",
octave[i], freq, div, (long) (div + 0.5));
}
}
return 0;
}

Pete
0 new messages