shithub: wl3d

Download patch

ref: 5288bbb7556c87e95b0813bf5769eec6b3caf9bc
parent: 35a6b626209bdb216d0c0df0237c4d9a4af3eae9
author: Konstantinn Bonnet <[email protected]>
date: Wed May 18 22:21:18 EDT 2016

add sound and music, misc fixes

- fs
	. restore extension after everything is loaded for ver>SOD
	. fix wrong offsets for demos and epis
	. use end pointers rather than size where appropriate; remove unused
	  array end pointers
	. skip pc sound effects forever
	. strip imf and al lumps to just the data
- snd
	. implement sfx and music playback with an opl2 emulator based on
	  fmopl.c from MAME
	. resample and mix pcm. resampling method is a sort-of simplified
	  version of pcmconv(1)'s implementation of the method described on the
	  "Digital Audio Resampling Home Page" at
	  http://ccrma.stanford.edu/~jos/resample/
	. opl2m: separate general purpose decoder using opl2.c for playback of
	  pseudo-imf format streams
- todo
	. pcm stereo positional attenuation
- problems
	. imf playback cracks on loop, as in reference. this seems to stem from
	  the inclusion of reset shit in the lumps themselves. it sucks.
	. al playback cracks way more than reference. this is a limitation of
	  fmopl.c, and everything based on it inherits this (so, everything).
	  it sucks.
	. unless sound is disabled, #A/audio is written to on every frame even if
	  there is nothing to write, to avoid additional cracking. it fucking
	  sucks.
	. opl2 keeps spinning even if music is disabled and nothing is playing,
	  to properly simulate sfx echo, since some sounds are dependent on it.
	  it sucking fucks.
	. resampling: unlike doom(?)/quake, pcm is sampled at 7kHz, then
	  resampled by the hardware at 48kHz or whatever. we therefore need to
	  do the same (for 44.1kHz). since the goal here is to recreate the
	  functionality of the reference implementation, doing just e.g.
	  (int)*p++ * 256 - 32768 and duplicating these n times sounds wrong.
	  the real problem is i'm sick of working on this and i suck at signal
	  processing. there. this stuff is way overkill (and cargocult, AGAIN),
	  since both sampling rates are constant, the number of samples is
	  always between 1 and 100, etc. it sucks, many times per second.

--- a/dat.h
+++ b/dat.h
@@ -23,6 +23,7 @@
 	Ke
 };
 extern int cson, kbon, mson;
+extern int sfxon, muson, pcmon;
 extern Rune keys[];
 extern void (*step)(void);
 
@@ -57,21 +58,21 @@
 typedef struct Pic Pic;
 typedef struct Fnt Fnt;
 typedef struct Sfx Sfx;
+typedef struct Al Al;
 
 struct Dat{
 	u16int sz;
 	uchar *p;
+	uchar *e;
 };
-extern Dat *wals, *sprs, *spre;
-extern Dat *imfs, *imfe;
-extern Dat *maps, *mape, *map;
-extern Dat *exts, *exte;
-extern Dat *dems, *deme, *epis;
+extern Dat *wals, *sprs, *imfs;
+extern uchar **exts, **dems, **epis;
+extern uchar **maps, *map;
 
 struct Pic{
 	int x;
 	int y;
-	Dat;
+	uchar *p;
 };
 extern Pic *pics, *pice;
 extern uchar *pict;
@@ -80,16 +81,21 @@
 	int h;
 	int ofs[256];
 	char w[256];
-	Dat;
+	uchar *p;
 };
 extern Fnt fnts[], *fnt;
 
+struct Al{
+	int pri;
+	uchar inst[10];
+	int blk;
+	Dat;
+};
 struct Sfx{
-	Dat pc;
-	Dat al;
+	Al;
 	Dat *pcm;
 };
-extern Sfx *sfxs, *sfxe;
+extern Sfx *sfxs;
 
 enum{
 	Shitwall,
--- a/fns.h
+++ b/fns.h
@@ -22,6 +22,12 @@
 void	gstep(void);
 void	dstep(void);
 void	initg(int);
+uchar*	opl2out(uchar *, int);
+void	opl2wr(int, int);
+void	opl2init(int);
+void	sndstep(void);
+void	stopsfx(void);
 void	sfx(int);
 void	stopmus(void);
 void	mus(int);
+void	initsnd(void);
--- a/fs.c
+++ b/fs.c
@@ -446,8 +446,8 @@
 	Planesz = Mapdxy * Mapdxy * Nplane,
 	Mapsz = Planesz * Nplane
 };
-static Dat *snds;
-static uchar *swpb, *sndb, *mapb;
+static Dat *pcms;
+static uchar *swpb, *mapb;
 static int alofs;
 static u16int rlewtag;
 
@@ -642,18 +642,18 @@
 	while(++p < e){
 		Bseek(bf, 2, 1);
 		n = get16(bf);
-		while(s->sz < n)
-			s->sz += p++->sz;
-		while(p->sz == 0 && p < e-1)
+		while(s->e < s->p + n)
+			s->e = p++->e;
+		while(p->p == nil && p < e-1)
 			p++;
 		s++;
-		s->sz = p->sz;
 		s->p = p->p;
+		s->e = p->e;
 	}
 	n = s-wals;
 	wals = erealloc(wals, n * sizeof *wals);
 	sprs = wals + so;
-	spre = wals + po;
+	pcms = wals + po;
 }
 
 static void
@@ -667,8 +667,8 @@
 
 	bf = bopen("vswap.", OREAD);
 	n = get16(bf);
-	wals = emalloc((n-1) * sizeof *wals);
-	e = wals + n-1;
+	s = wals = emalloc((n-1) * sizeof *wals);
+	e = s + n-1;
 	v = get16(bf);
 	w = get16(bf);
 	p = o = emalloc(n * sizeof *o);
@@ -675,16 +675,18 @@
 
 	while(p < o+n)
 		*p++ = get32(bf);
-	for(s=wals; s<wals+n-1; s++)
-		s->sz = get16(bf);
+	while(s < e)
+		s++->e = (uchar*)(uintptr)get16(bf);
 	u = swpb = emalloc(bsize(bf) - Bseek(bf, 0, 1));
 	Bseek(bf, 2, 1);
 	for(p=o, s=wals; s<e; s++, p++){
-		if(s->sz == 0)
+		n = (uintptr)s->e;
+		if(n == 0)
 			continue;
 		Bseek(bf, *p, 0);
 		s->p = u;
-		u += eread(bf, u, s->sz);
+		s->e = u + n;
+		u += eread(bf, u, n);
 	}
 	Bseek(bf, *p, 0);
 	free(o);
@@ -699,7 +701,7 @@
 {
 	int n;
 	u32int v, p0, p1;
-	uchar *u;
+	uchar *u, **d, **e;
 	Biobuf *hed, *dat;
 
 	hed = bopen("maphead.", OREAD);
@@ -706,13 +708,13 @@
 	dat = bopen("gamemaps.", OREAD);
 	n = ver==WL6 ? 60 : ver==WL1 ? 10 : ver==SDM ? 2 : 20;
 	rlewtag = get16(hed);
-	maps = emalloc(n * sizeof *maps);
-	mapb = emalloc(n * Mapsz);
-
-	for(mape=maps, u=mapb; mape<maps+n; mape++){
+	d = maps = emalloc(n * sizeof *maps);
+	e = d + n;
+	u = mapb = emalloc(n * Mapsz);
+	while(d < e){
 		v = get32(hed);
 		if(v == 0xffffffff)
-			sysfatal("sparse map %zud", mape-maps);
+			sysfatal("sparse map %zud", d-maps);
 		Bseek(dat, v, 0);
 		p0 = get32(dat);
 		p1 = get32(dat);
@@ -719,10 +721,9 @@
 		Bseek(dat, 10, 1);
 		if(get16(dat) != Mapdxy || get16(dat) != Mapdxy)
 			sysfatal("invalid map size");
-		mape->p = u;
+		*d++ = u;
 		u += uncarmack(dat, (u16int*)u, p0);
 		u += uncarmack(dat, (u16int*)u, p1);
-		mape->sz = u - mape->p;
 	}
 	Bterm(hed);
 	Bterm(dat);
@@ -754,7 +755,7 @@
 	}
 	p = pcmt[ver<SDM ? 0 : 1];
 	e = p + (ver==WL6 ? 46 : ver==WL1 ? 21 : ver==SDM ? 26 : 40);
-	for(pcm=spre; p<e; p++, pcm++)
+	for(pcm=pcms; p<e; p++, pcm++)
 		if(*p != Send)
 			sfxs[*p].pcm = pcm;
 	sfxs[Sscream3].pcm = sfxs[ver<SDM ? Sscream2 : Sscream4].pcm;	/* why */
@@ -761,49 +762,66 @@
 }
 
 static void
-audiot(void)
+al(Biobuf *dat, Biobuf *aux, int n)
 {
-	int n, c;
-	u32int v, w;
-	uchar *u;
-	Sfx *s;
-	Biobuf *hed, *dat;
+	int m;
+	u32int v;
+	uchar *p;
+	Sfx *s, *e;
 
-	hed = bopen("audiohed.", OREAD);
-	dat = bopen("audiot.", OREAD);
-	n = ver < SDM ? Send : Ssend;
-	sfxs = emalloc(n * sizeof *sfxs);
-	sfxe = sfxs + n;
-	u = sndb = emalloc(bsize(dat));
-	v = get32(hed);
-	for(c=0, s=sfxs; s<sfxe; s++){
-		w = get32(hed);
+	s = sfxs = emalloc(n * sizeof *sfxs);
+	e = s + n;
+	while(s < e){
+		v = get32(aux);
 		Bseek(dat, v, 0);
-		if(c++ < n){
-			s->pc.sz = w-v;
-			s->pc.p = u;
-		}else{
-			s->al.sz = w-v;
-			s->al.p = u;
-		}
-		u += eread(dat, u, w-v);
-		v = w;
-		if(c == n)
-			s = sfxs-1;
+		m = get32(dat);
+		s->pri = get16(dat);
+		eread(dat, s->inst, sizeof s->inst);
+		Bseek(dat, 6, 1);
+		s->blk = (get8(dat) & 7) << 2 | 1<<5;
+		p = emalloc(m);
+		eread(dat, p, m);
+		s->p = p;
+		s->e = p + m;
+		s++;
 	}
+}
 
-	Bseek(hed, (n-1)*4, 1);
+static void
+imf(Biobuf *dat, Biobuf *aux)
+{
+	int n;
+	uchar *p;
+	u32int v;
+	Dat *d, *e;
+
 	n = ver < SDM ? 27 : 24;
-	imfs = emalloc(n * sizeof *imfs);
-	v = get32(hed);
-	for(imfe=imfs; imfe<imfs+n; imfe++){
-		w = get32(hed);
+	d = imfs = emalloc(n * sizeof *imfs);
+	e = d + n;
+	while(d < e){
+		v = get32(aux);
 		Bseek(dat, v, 0);
-		imfe->sz = w-v;
-		imfe->p = u;
-		u += eread(dat, u, w-v);
-		v = w;
+		n = get16(dat);
+		d->p = p = emalloc(n);
+		d->e = p + n;
+		eread(dat, p, n);
+		d++;
 	}
+}
+
+static void
+audiot(void)
+{
+	int n;
+	Biobuf *hed, *dat;
+
+	hed = bopen("audiohed.", OREAD);
+	dat = bopen("audiot.", OREAD);
+	n = ver < SDM ? Send : Ssend;
+	Bseek(hed, n*4, 0);
+	al(dat, hed, n);
+	Bseek(hed, n*4, 1);
+	imf(dat, hed);
 	Bterm(hed);
 	Bterm(dat);
 
@@ -847,7 +865,6 @@
 		deplane(p, u, s->x*s->y/4);
 		free(u);
 		s->p = p;
-		s->sz = n;
 	}
 	pict = picts[ver];
 }
@@ -859,9 +876,11 @@
 	u32int v, n;
 	uchar *u, *p;
 	char *w;
-	Fnt *f;
+	Fnt *f, *e;
 
-	for(f=fnts; f<fnts+2; f++){
+	f = fnts;
+	e = f + 2;
+	while(f < e){
 		v = get24(aux);
 		Bseek(dat, v, 0);
 		n = get32(dat);
@@ -874,9 +893,9 @@
 			*w = *p++;
 		n -= p-u;
 		f->p = emalloc(n);
-		f->sz = n;
 		memcpy(f->p, p, n);
 		free(u);
+		f++;
 	}
 }
 
@@ -884,23 +903,22 @@
 getexts(Biobuf *dat, Biobuf *aux, u16int hf[])
 {
 	int n, m;
-	uchar *u;
+	uchar *u, **d, **e;
 	u32int v;
 
 	n = (bsize(aux) - Bseek(aux, 0, 1)) / 3 - 1;
-	exts = emalloc(n * sizeof *exts);
-	for(exte=exts; exte<exts+n; exte++){
+	d = exts = emalloc(n * sizeof *exts);
+	e = d + n;
+	while(d < e){
 		v = get24(aux);
 		Bseek(dat, v, 0);
 		m = get32(dat);
 		u = emalloc(m);
 		unhuff(dat, hf, u, m);
-		exte->p = u;
-		exte->sz = m;
+		*d++ = u;
 	}
-	dems = exts + (ver==WL6 || ver==SDM ? 3 : ver==SOD ? 13 : 0);
-	deme = dems + (ver==WL6 || ver==SOD ? 4 : ver==SDM ? 1 : 0);
-	epis = exts + (ver==WL6 ? 7 : 17);
+	dems = exts + (ver < SOD ? 3 : 13);
+	epis = dems + (ver == SDM ? 1 : 4);
 }
 
 static void
@@ -942,16 +960,19 @@
 void
 dat(char *dir)
 {
+	char *e;
+
 	rfork(RFNAMEG);
 	if(bind(".", dir, MBEFORE|MCREATE) < 0 || chdir(dir) < 0)
 		fprint(2, "dat: %r\n");
 	vswap();
 	gamemaps();
+	e = ext;
 	if(ver == SOD)
 		ext = "sod";
 	audiot();
 	gfx();
-
+	ext = e;
 	if(ver >= SDM)
 		fixpal();
 	pal = pals[C0];
--- a/game.c
+++ b/game.c
@@ -117,16 +117,10 @@
 	gx -= viewx;
 	gy -= viewy;
 
-//
-// calculate newx
-//
 	xt = FixedByFrac(gx,viewcos);
 	yt = FixedByFrac(gy,viewsin);
 	x = (xt - yt) >> TILESHIFT;
 
-//
-// calculate newy
-//
 	xt = FixedByFrac(gx,viewsin);
 	yt = FixedByFrac(gy,viewcos);
 	y = (yt + xt) >> TILESHIFT;
@@ -141,14 +135,6 @@
 		x = ATABLEMAX - 1;
 	leftchannel  =  lefttable[x][y + ATABLEMAX];
 	rightchannel = righttable[x][y + ATABLEMAX];
-
-#if 0
-	CenterWindow(8,1);
-	US_PrintSigned(leftchannel);
-	US_Print(",");
-	US_PrintSigned(rightchannel);
-	VW_UpdateScreen();
-#endif
 }
 
 /*
@@ -1033,12 +1019,11 @@
 ==================
 */
 
-void PlayDemo (s16int demonumber)
+void PlayDemo (uchar *p)
 {
 	s16int length;
 
-	/* deme==dems → no demos, etc. */
-	demoptr = dems+demonumber;
+	demoptr = p;
 
 	NewGame (1,0);
 	gamestate.mapon = *demoptr++;
--- a/in.c
+++ b/in.c
@@ -1,83 +1,13 @@
-#define	KeyInt		9	// The keyboard ISR number
-
-
-/*
-=============================================================================
-
-					GLOBAL VARIABLES
-
-=============================================================================
-*/
-
-//
-// configuration variables
-//
-int			MousePresent;
-
-
-// 	Global variables
 		int		Keyboard[NumCodes];
-		int		Paused;
-		char		LastASCII;
-		u8int	LastScan;
 
 		KeyboardDef	KbdDefs = {0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51};
 		ControlType	Controls[MaxPlayers];
 
-		u32int MouseDownCount;
-
 		Demo		DemoMode = demo_Off;
 		u8int _seg	*DemoBuffer;
 		u16int		DemoOffset,DemoSize;
 
-/*
-=============================================================================
 
-					LOCAL VARIABLES
-
-=============================================================================
-*/
-static	u8int        far ASCIINames[] =		// Unshifted ASCII for scan codes
-					{
-//	 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
-	0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,	// 0
-	'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',	// 1
-	'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',	// 2
-	'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,	// 3
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',	// 4
-	'2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 5
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 6
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0		// 7
-					},
-					far ShiftNames[] =		// Shifted ASCII for scan codes
-					{
-//	 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
-	0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,	// 0
-	'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',	// 1
-	'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',	// 2
-	'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,	// 3
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',	// 4
-	'2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 5
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 6
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0   	// 7
-					},
-					far SpecialNames[] =	// ASCII for 0xe0 prefixed codes
-					{
-//	 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 0
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,13 ,0  ,0  ,0  ,	// 1
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 2
-	0  ,0  ,0  ,0  ,0  ,'/',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 3
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 4
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 5
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,	// 6
-	0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0   	// 7
-					};
-
-
-static	int		CapsLock;
-static	u8int	CurCode,LastCode;
-
 static	Direction	DirTable[] =		// Quick lookup for total direction
 					{
 						dir_NorthWest,	dir_North,	dir_NorthEast,
@@ -85,204 +15,7 @@
 						dir_SouthWest,	dir_South,	dir_SouthEast
 					};
 
-static	void			(*INL_KeyHook)(void);
-static	void interrupt	(*OldKeyVect)(void);
-
-//	Internal routines
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	INL_KeyService() - Handles a keyboard interrupt (key up/down)
-//
-///////////////////////////////////////////////////////////////////////////
-static void interrupt
-INL_KeyService(void)
-{
-static	int	special;
-		u8int	k,c,
-				temp;
-		s16int		i;
-
-	k = inportb(0x60);	// Get the scan code
-
-	// Tell the XT keyboard controller to clear the key
-	outportb(0x61,(temp = inportb(0x61)) | 0x80);
-	outportb(0x61,temp);
-
-	if (k == 0xe0)		// Special key prefix
-		special = true;
-	else if (k == 0xe1)	// Handle Pause key
-		Paused = true;
-	else
-	{
-		if (k & 0x80)	// Break code
-		{
-			k &= 0x7f;
-
-// DEBUG - handle special keys: ctl-alt-delete, print scrn
-
-			Keyboard[k] = false;
-		}
-		else			// Make code
-		{
-			LastCode = CurCode;
-			CurCode = LastScan = k;
-			Keyboard[k] = true;
-
-			if (special)
-				c = SpecialNames[k];
-			else
-			{
-				if (k == sc_CapsLock)
-				{
-					CapsLock ^= true;
-					// DEBUG - make caps lock light work
-				}
-
-				if (Keyboard[sc_LShift] || Keyboard[sc_RShift])	// If shifted
-				{
-					c = ShiftNames[k];
-					if ((c >= 'A') && (c <= 'Z') && CapsLock)
-						c += 'a' - 'A';
-				}
-				else
-				{
-					c = ASCIINames[k];
-					if ((c >= 'a') && (c <= 'z') && CapsLock)
-						c -= 'a' - 'A';
-				}
-			}
-			if (c)
-				LastASCII = c;
-		}
-
-		special = false;
-	}
-
-	if (INL_KeyHook && !special)
-		INL_KeyHook();
-	outportb(0x20,0x20);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	INL_GetMouseDelta() - Gets the amount that the mouse has moved from the
-//		mouse driver
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-INL_GetMouseDelta(s16int *x,s16int *y)
-{
-	Mouse(MDelta);
-	*x = _CX;
-	*y = _DX;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	INL_GetMouseButtons() - Gets the status of the mouse buttons from the
-//		mouse driver
-//
-///////////////////////////////////////////////////////////////////////////
-static u16int
-INL_GetMouseButtons(void)
-{
-	u16int	buttons;
-
-	Mouse(MButtons);
-	buttons = _BX;
-	return(buttons);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	INL_StartKbd() - Sets up my keyboard stuff for use
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-INL_StartKbd(void)
-{
-	INL_KeyHook = NULL;			// no key hook routine
-
-	IN_ClearKeysDown();
-
-	OldKeyVect = getvect(KeyInt);
-	setvect(KeyInt,INL_KeyService);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_Startup() - Starts up the Input Mgr
-//
-///////////////////////////////////////////////////////////////////////////
 void
-IN_Startup(void)
-{
-	INL_StartKbd();
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_Default() - Sets up default conditions for the Input Mgr
-//
-///////////////////////////////////////////////////////////////////////////
-void
-IN_Default(int gotit,ControlType in)
-{
-	if
-	(
-		(!gotit)
-	|| 	((in == ctrl_Mouse) && !MousePresent)
-	)
-		in = ctrl_Keyboard1;
-	IN_SetControlType(0,in);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_Shutdown() - Shuts down the Input Mgr
-//
-///////////////////////////////////////////////////////////////////////////
-void
-IN_Shutdown(void)
-{
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()
-//			everytime a real make/break code gets hit
-//
-///////////////////////////////////////////////////////////////////////////
-void
-IN_SetKeyHook(void (*hook)())
-{
-	INL_KeyHook = hook;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_ClearKeysDown() - Clears the keyboard array
-//
-///////////////////////////////////////////////////////////////////////////
-void
-IN_ClearKeysDown(void)
-{
-	s16int	i;
-
-	LastScan = sc_None;
-	LastASCII = key_None;
-	memset (Keyboard,0,sizeof(Keyboard));
-}
-
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_ReadControl() - Reads the device associated with the specified
-//		player and fills in the control info struct
-//
-///////////////////////////////////////////////////////////////////////////
-void
 IN_ReadControl(s16int player,ControlInfo *info)
 {
 			int		realdelta;
@@ -399,157 +132,4 @@
 			DemoBuffer[DemoOffset + 1] = dbyte;
 		}
 	}
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_SetControlType() - Sets the control type to be used by the specified
-//		player
-//
-///////////////////////////////////////////////////////////////////////////
-void
-IN_SetControlType(s16int player,ControlType type)
-{
-	// DEBUG - check that requested type is present?
-	Controls[player] = type;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_WaitForKey() - Waits for a scan code, then clears LastScan and
-//		returns the scan code
-//
-///////////////////////////////////////////////////////////////////////////
-u8int
-IN_WaitForKey(void)
-{
-	u8int	result;
-
-	while (!(result = LastScan))
-		;
-	LastScan = 0;
-	return(result);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and
-//		returns the ASCII value
-//
-///////////////////////////////////////////////////////////////////////////
-char
-IN_WaitForASCII(void)
-{
-	char		result;
-
-	while (!(result = LastASCII))
-		;
-	LastASCII = '\0';
-	return(result);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_Ack() - waits for a button or key press.  If a button is down, upon
-// calling, it must be released for it to be recognized
-//
-///////////////////////////////////////////////////////////////////////////
-
-int	btnstate[8];
-
-void IN_StartAck(void)
-{
-	u16int	i,buttons;
-
-//
-// get initial state of everything
-//
-	IN_ClearKeysDown();
-	memset (btnstate,0,sizeof(btnstate));
-
-	if (MousePresent)
-		buttons |= IN_MouseButtons ();
-
-	for (i=0;i<8;i++,buttons>>=1)
-		if (buttons&1)
-			btnstate[i] = true;
-}
-
-
-int IN_CheckAck (void)
-{
-	u16int	i,buttons;
-
-//
-// see if something has been pressed
-//
-	if (LastScan)
-		return true;
-
-	if (MousePresent)
-		buttons |= IN_MouseButtons ();
-
-	for (i=0;i<8;i++,buttons>>=1)
-		if ( buttons&1 )
-		{
-			if (!btnstate[i])
-				return true;
-		}
-		else
-			btnstate[i]=false;
-
-	return false;
-}
-
-
-void IN_Ack (void)
-{
-	IN_StartAck ();
-
-	while (!IN_CheckAck ())
-	;
-}
-
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	IN_UserInput() - Waits for the specified delay time (in ticks) or the
-//		user pressing a key or a mouse button. If the clear flag is set, it
-//		then either clears the key or waits for the user to let the mouse
-//		button up.
-//
-///////////////////////////////////////////////////////////////////////////
-int IN_UserInput(u32int delay)
-{
-	u32int lasttime;
-
-	lasttime = TimeCount;
-	IN_StartAck ();
-	do
-	{
-		if (IN_CheckAck())
-			return true;
-	} while (TimeCount - lasttime < delay);
-	return(false);
-}
-
-//===========================================================================
-
-/*
-===================
-=
-= IN_MouseButtons
-=
-===================
-*/
-
-u8int	IN_MouseButtons (void)
-{
-	if (MousePresent)
-	{
-		Mouse(MButtons);
-		return _BX;
-	}
-	else
-		return 0;
 }
--- a/in.h
+++ b/in.h
@@ -1,97 +1,6 @@
-//
-//	ID Engine
-//	ID_IN.h - Header file for Input Manager
-//	v1.0d1
-//	By Jason Blochowiak
-//
-
 #define	MaxPlayers	4
-#define	MaxKbds		2
 #define	NumCodes	128
 
-#define	sc_None			0
-#define	sc_Bad			0xff
-#define	sc_Return		0x1c
-#define	sc_Enter		sc_Return
-#define	sc_Escape		0x01
-#define	sc_Space		0x39
-#define	sc_BackSpace	0x0e
-#define	sc_Tab			0x0f
-#define	sc_Alt			0x38
-#define	sc_Control		0x1d
-#define	sc_CapsLock		0x3a
-#define	sc_LShift		0x2a
-#define	sc_RShift		0x36
-#define	sc_UpArrow		0x48
-#define	sc_DownArrow	0x50
-#define	sc_LeftArrow	0x4b
-#define	sc_RightArrow	0x4d
-#define	sc_Insert		0x52
-#define	sc_Delete		0x53
-#define	sc_Home			0x47
-#define	sc_End			0x4f
-#define	sc_PgUp			0x49
-#define	sc_PgDn			0x51
-#define	sc_F1			0x3b
-#define	sc_F2			0x3c
-#define	sc_F3			0x3d
-#define	sc_F4			0x3e
-#define	sc_F5			0x3f
-#define	sc_F6			0x40
-#define	sc_F7			0x41
-#define	sc_F8			0x42
-#define	sc_F9			0x43
-#define	sc_F10			0x44
-#define	sc_F11			0x57
-#define	sc_F12			0x59
-
-#define	sc_1			0x02
-#define	sc_2			0x03
-#define	sc_3			0x04
-#define	sc_4			0x05
-#define	sc_5			0x06
-#define	sc_6			0x07
-#define	sc_7			0x08
-#define	sc_8			0x09
-#define	sc_9			0x0a
-#define	sc_0			0x0b
-
-#define	sc_A			0x1e
-#define	sc_B			0x30
-#define	sc_C			0x2e
-#define	sc_D			0x20
-#define	sc_E			0x12
-#define	sc_F			0x21
-#define	sc_G			0x22
-#define	sc_H			0x23
-#define	sc_I			0x17
-#define	sc_J			0x24
-#define	sc_K			0x25
-#define	sc_L			0x26
-#define	sc_M			0x32
-#define	sc_N			0x31
-#define	sc_O			0x18
-#define	sc_P			0x19
-#define	sc_Q			0x10
-#define	sc_R			0x13
-#define	sc_S			0x1f
-#define	sc_T			0x14
-#define	sc_U			0x16
-#define	sc_V			0x2f
-#define	sc_W			0x11
-#define	sc_X			0x2d
-#define	sc_Y			0x15
-#define	sc_Z			0x2c
-
-#define	key_None		0
-#define	key_Return		0x0d
-#define	key_Enter		key_Return
-#define	key_Escape		0x1b
-#define	key_Space		0x20
-#define	key_BackSpace	0x08
-#define	key_Tab			0x09
-#define	key_Delete		0x7f
-
 // 	Stuff for the mouse
 #define	MReset		0
 #define	MButtons	3
@@ -149,24 +58,3 @@
 #define	IN_KeyDown(code)	(Keyboard[(code)])
 #define	IN_ClearKey(code)	{Keyboard[code] = false;\
 							if (code == LastScan) LastScan = sc_None;}
-
-// DEBUG - put names in prototypes
-extern	void		IN_Startup(void),IN_Shutdown(void),
-					IN_Default(int gotit,ControlType in),
-					IN_SetKeyHook(void (*)()),
-					IN_ClearKeysDown(void),
-					IN_ReadCursor(CursorInfo *),
-					IN_ReadControl(s16int,ControlInfo *),
-					IN_SetControlType(s16int,ControlType),
-					IN_StopDemo(void),IN_FreeDemoBuffer(void),
-					IN_Ack(void),IN_AckBack(void);
-extern	int		IN_UserInput(u32int delay);
-extern	char		IN_WaitForASCII(void);
-extern	u8int	IN_WaitForKey(void);
-extern	u8int		*IN_GetScanName(u8int);
-
-
-u8int	IN_MouseButtons (void);
-
-void IN_StartAck(void);
-int IN_CheckAck (void);
--- a/main.c
+++ b/main.c
@@ -490,27 +490,7 @@
 	return true;
 }
 
-//===========================================================================
-
 /*
-==========================
-=
-= ShutdownId
-=
-= Shuts down all ID_?? managers
-=
-==========================
-*/
-
-void ShutdownId (void)
-{
-	SD_Shutdown ();
-}
-
-
-//===========================================================================
-
-/*
 ==================
 =
 = BuildTables
@@ -671,7 +651,6 @@
 	s16int                     i,x,y;
 	u16int        *blockstart;
 
-	SD_Startup ();
 	mapon = -1;
 
 //
@@ -803,8 +782,6 @@
 	 screen = Eerror;
 	}
 
-	ShutdownId ();
-
 	if (error && *error)
 	{
 	  movedata ((u16int)screen,7,0xb800,0,7*160);
@@ -887,6 +864,7 @@
 
 	while (1)
 	{
+		uchar *p = dems;
 		while (!NoWait)
 		{
 //
@@ -928,12 +906,9 @@
 //
 // demo
 //
-
-			#ifndef SPEARDEMO
-			PlayDemo (LastDemo++%4);
-			#else
-			PlayDemo (0);
-			#endif
+			PlayDemo(p++);
+			if(p >= epis)
+				p = dems;
 
 			if (playstate == ex_abort)
 				break;
--- a/map.c
+++ b/map.c
@@ -3,4 +3,4 @@
 #include "dat.h"
 #include "fns.h"
 
-Dat *maps, *mape, *map;
+uchar **maps, *map;
--- a/mkfile
+++ b/mkfile
@@ -1,13 +1,16 @@
 </$objtype/mkfile
 
-BIN=$home/bin/$objtype
-TARG=wl3d
+TARG=\
+	opl2\
+	wl3d\
 
-OFILES=\
+OFILES=
+WOFILES=\
 	fs.$O\
 	gm.$O\
 	map.$O\
 	mn.$O\
+	opl2.$O\
 	rend.$O\
 	snd.$O\
 	wl3d.$O\
@@ -14,4 +17,11 @@
 
 HFILES= dat.h fns.h
 
-</sys/src/cmd/mkone
+</sys/src/cmd/mkmany
+BIN=$home/bin/$objtype
+
+$O.wl3d: $WOFILES
+	$LD -o $target $prereq
+
+$O.opl2: opl2.$O opl2m.$O
+	$LD -o $target $prereq
--- a/mn.c
+++ b/mn.c
@@ -325,7 +325,8 @@
 		m = i->m;
 		if(m == nil)
 			break;
-		sfx(Sshoot);
+		if(m != ml+Lquit && m != ml+Lesc)
+			sfx(Sshoot);
 		reset(m);
 	}
 	if(r != 'i')
@@ -368,7 +369,7 @@
 {
 	pic(0, 0, pict[Pid1]);
 	pic(0, 80, pict[Pid2]);
-	palpic(exts[Eid].p);
+	palpic(exts[Eid]);
 	fadeop(mp->c, mp->qs->dt);
 	mus(Mnazjazz);
 }
@@ -458,7 +459,7 @@
 {
 	pic(0, 0, pict[Ptitle1]);
 	pic(0, 80, pict[Ptitle2]);
-	palpic(exts[Etitpal].p);
+	palpic(exts[Etitpal]);
 	mus(Mtower);
 }
 
--- /dev/null
+++ b/opl2.c
@@ -1,0 +1,740 @@
+#include <u.h>
+#include <libc.h>
+
+typedef char s8int;
+typedef short s16int;
+typedef int s32int;
+typedef struct Envelope Envelope;
+typedef struct Phase Phase;
+typedef struct Op Op;
+typedef struct Chan Chan;
+
+enum{
+	Rwse = 0x01,
+		Mwse = 1<<5,	/* wave selection enable */
+	Rcsm = 0x08,
+		Mnse = 1<<6,	/* note selection enable */
+	Rctl = 0x20,
+		Mame = 1<<7,	/* enable amplitude modulation */
+		Mvbe = 1<<6,	/* enable vibrato */
+		Msse = 1<<5,	/* enable sustain */
+		Mkse = 1<<4,	/* enable keyboard scaling */
+		Mmfq = 15<<0,	/* modulator freq multiple */
+	Rsca = 0x40,
+		Mlvl = 63<<0,	/* total level */
+		Mscl = 3<<6,	/* scaling level */
+	Ratk = 0x60,
+		Mdec = 15<<0,	/* decay rate */
+		Matk = 15<<4,	/* attack rate */
+	Rsus = 0x80,
+		Mrel = 15<<0,	/* release rate */
+		Msus = 15<<4,	/* sustain level */
+	Rnum = 0xa0,		/* f number lsb */
+	Roct = 0xb0,
+		Mmsb = 3<<0,	/* f number msb */
+		Moct = 7<<2,
+		Mkon = 1<<5,
+	Ropm = 0xbd,
+		Mamp = 1<<7,	/* amplitude mod depth */
+		Mvib = 1<<6,	/* vibrato depth */
+		Mrms = 63<<0,
+		Mrhy = 1<<5,	/* rhythm enable */
+		Mbas = 1<<4,
+		Msna = 1<<3,
+		Mtom = 1<<2,
+		Mcym = 1<<1,
+		Mhat = 1<<0,
+	Rfed = 0xc0,
+		Mmod = 1<<0,	/* enable operator modulation */
+		Mfed = 7<<1,	/* feedback strength */
+	Rwav = 0xe0,
+		Mwav = 3<<0,
+
+	Sfreq = 16,
+	Sfreqd = Sfreq - 10,
+	Seg = 16,
+	Slfo = 24,
+	Stm = 16,
+
+	Mfreq = (1 << Sfreq) - 1,
+
+	Lampb = 12,
+	Lsignb = 2,
+	Ly = Lampb * Lsignb,
+	Lx = 256,
+	Lxy = Lx * Ly,
+
+	Sinb = 10,
+	Sinlen = 1<<Sinb,
+	Sinm = Sinlen - 1,
+	Nsines = 4,
+
+	Erstep = 8,
+	Equiet = Lxy >> 4,
+	Eb = 10,
+	Elen = 1 << Eb,
+	Nerates = 16 + 16 * 4 + 16,	/* infinite, regular, dummy */
+	Nlfoa = 210,
+	Attmax = (1 << Eb-1) - 1,
+	Attmin = 0
+};
+#define Clk	3579545.0
+#define Estep	(128.0/Elen)
+
+enum{
+	Eoff,
+	Erel,
+	Esus,
+	Edec,
+	Eatk,
+};
+struct Envelope{
+	int state;
+	int key;
+	int son;
+	int sus;
+	int atksh;
+	int atk;
+	int decsh;
+	int dec;
+	int relsh;
+	int rel;
+	int lvl0;
+	int lvl;
+	s32int vol;
+	u32int tl;
+};
+struct Phase{
+	u32int fq;
+	u32int dfq;
+	int fbv;
+	int opmodoff;
+	int out[2];
+	int *p;
+};
+struct Op{
+	Envelope;
+	Phase;
+	int Aon;
+	int φon;
+	int atkn;
+	int decn;
+	int reln;
+	int mul;
+	int scl;
+	int ks;
+	int kssh;
+	u16int wav;
+};
+struct Chan{
+	Op *op;
+	u32int scl0;
+	u32int dfq0;
+	int fn;
+	int oct;
+	int kcode;
+};
+static int lta[Lxy];
+static uint sint[Sinlen * Nsines];
+static int wse, nse, rhythm;
+static u32int noise, noiseφ, noiseT;
+static u32int egtic, egt, egdt, egfdt;
+static u32int lfoA, lfoAt, lfoAdt, lfoφt, lfoφdt;
+static s32int lfoφ;
+static int lfoAd, lfoφd;
+static Chan chs[9];
+static Op ops[2*nelem(chs)];
+static int φmod, tout;
+
+static u32int fn[1024];	/* 20bit φ increment counter */
+/* 27 output levels (triangle waveform), 1 level takes one of 192, 256 or 448
+ * samples. each value is repeated on 64 consecutive samples. total length is
+ * then 64*210 samples. when am=1 data is used directly, else it is shl 2 before
+ * use. */
+static u8int lfoAs[Nlfoa] = {
+	0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+	5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10,
+	11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15,
+	15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
+	20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24,
+	24, 24, 25, 25, 25, 25, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 23,
+	23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19,
+	19, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 14,
+	14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10,
+	10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4,
+	4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1
+};
+/* 8 output 1024 samples long levels (triangle waveform) with 16 values (depth 0
+ * then depth 1) each */
+static s8int lfoφs[8*8*2] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0,
+	1, 0, 0, 0, -1, 0, 0, 0, 2, 1, 0, -1, -2, -1, 0, 1,
+	1, 0, 0, 0, -1, 0, 0, 0, 3, 1, 0, -1, -3, -1, 0, 1,
+	2, 1, 0, -1, -2, -1, 0, 1, 4, 2, 0, -2, -4, -2, 0, 2,
+	2, 1, 0, -1, -2, -1, 0, 1, 5, 2, 0, -2, -5, -2, 0, 2,
+	3, 1, 0, -1, -3, -1, 0, 1, 6, 3, 0, -3, -6, -3, 0, 3,
+	3, 1, 0, -1, -3, -1, 0, 1, 7, 3, 0, -3, -7, -3, 0, 3
+};
+static uchar mul[16] = {
+	1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
+};
+#define O	(0.1875 / 2.0)	/* convert 3 dB/oct → 6 dB/oct */
+static u32int lsca[8 * 16] = {	/* 0.1875: bit0 weight of op->vol in dB */
+	0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O,
+	0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O,
+	0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O,
+	0.000/O, 0.750/O, 1.125/O, 1.500/O, 1.875/O, 2.250/O, 2.625/O, 3.000/O,
+	0.000/O, 0.000/O, 0.000/O, 0.000/O, 0.000/O, 1.125/O, 1.875/O, 2.625/O,
+	3.000/O, 3.750/O, 4.125/O, 4.500/O, 4.875/O, 5.250/O, 5.625/O, 6.000/O,
+	0.000/O, 0.000/O, 0.000/O, 1.875/O, 3.000/O, 4.125/O, 4.875/O, 5.625/O,
+	6.000/O, 6.750/O, 7.125/O, 7.500/O, 7.875/O, 8.250/O, 8.625/O, 9.000/O,
+	0.000/O, 0.000/O, 3.000/O, 4.875/O, 6.000/O, 7.125/O, 7.875/O, 8.625/O,
+	9.000/O, 9.750/O, 10.125/O, 10.500/O, 10.875/O, 11.250/O, 11.625/O,
+	12.000/O,
+	0.000/O, 3.000/O, 6.000/O, 7.875/O, 9.000/O, 10.125/O, 10.875/O,
+	11.625/O, 12.000/O, 12.750/O, 13.125/O, 13.500/O, 13.875/O, 14.250/O,
+	14.625/O, 15.000/O,
+	0.000/O, 6.000/O, 9.000/O, 10.875/O, 12.000/O, 13.125/O, 13.875/O,
+	14.625/O, 15.000/O, 15.750/O, 16.125/O, 16.500/O, 16.875/O, 17.250/O,
+	17.625/O, 18.000/O,
+	0.000/O, 9.000/O, 12.000/O, 13.875/O, 15.000/O, 16.125/O, 16.875/O,
+	17.625/O, 18.000/O, 18.750/O, 19.125/O, 19.500/O, 19.875/O, 20.250/O,
+	20.625/O, 21.000/O
+};
+#undef O
+#define O(n)	(n * (2.0/Estep))	/* n*3 dB */
+static u32int sust[16] = {
+	O(0), O(1), O(2), O(3), O(4), O(5), O(6), O(7), O(8), O(9), O(10),
+	O(11), O(12), O(13), O(14), O(31)
+};
+#undef O
+static uchar estep[15*Erstep] = {
+	0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
+	0, 1, 1, 1, 1, 1, 1, 1,	/* rate 0-12 */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+	1, 2, 2, 2, 1, 2, 2, 2,	/* rate 13 */
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4,
+	2, 4, 4, 4, 2, 4, 4, 4, /* rate 14 */
+	4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, /* rate 15 + atk */
+	0, 0, 0, 0, 0, 0, 0, 0, /* rate ∞ (atk/dec) */
+};
+#define O(n)	(n * Erstep)
+static uchar erate[Nerates] = {	/* O(13) is directly in code */
+	O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14),
+	O(14), O(14), O(14), O(14), O(14), O(14), O(0), O(1), O(2), O(3), O(0),
+	O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0),
+	O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0),
+	O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0),
+	O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(0), O(1), O(2), O(3), O(4),
+	O(5), O(6), O(7), O(8), O(9), O(10), O(11), O(12), O(12), O(12), O(12),
+	O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12),
+	O(12), O(12), O(12), O(12), O(12), O(12)
+};
+#undef O
+static uchar eratesh[Nerates] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 11, 11,
+	11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6,
+	5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0
+};
+
+void	opl2wr(int, int);
+
+/* 23-bit shift register noise generator. T=2²³-2 samples, sampling rate equal
+ * to that of the chip. */
+static void
+noiseg(void)
+{
+	int i;
+
+	noiseφ += noiseT;
+	i = noiseφ >> Sfreq;
+	noiseφ &= Mfreq;
+	while(i-- > 0){
+		if(noise & 1)
+			noise ^= 0x800302;
+		noise >>= 1;
+	}
+}
+
+static void
+advlfo(void)
+{
+	int a;
+
+	lfoAt += lfoAdt;
+	if(lfoAt >= (u32int)Nlfoa << Slfo)
+		lfoAt -= (u32int)Nlfoa << Slfo;
+	a = lfoAs[lfoAt >> Slfo];
+	lfoA = lfoAd ? a : a>>2;
+	lfoφt += lfoφdt;
+	lfoφ = lfoφt >> Slfo & 7 | lfoφd;
+}
+
+static void
+adv(void)
+{
+	int i, f, oc, v;
+	Chan *c;
+	Op *o;
+
+	egt += egfdt;
+	while(egt >= egdt){
+		egt -= egdt;
+		egtic++;
+		o = ops;
+		while(o < ops + nelem(ops)){
+			switch(o->state){
+			case Eatk:
+				if(egtic & (1 << o->atksh)-1)
+					break;
+				v = o->atk + (egtic >> o->atksh & 7);
+				/* sign extend it */
+				o->vol += (s32int)(~o->vol * estep[v]) >> 3;
+				if(o->vol <= Attmin){
+					o->vol = Attmin;
+					o->state = Edec;
+				}
+				break;
+			case Edec:
+				if(egtic & (1 << o->decsh)-1)
+					break;
+				v = o->dec + (egtic >> o->decsh & 7);
+				o->vol += estep[v];
+				if(o->vol >= o->sus)
+					o->state = Esus;
+				break;
+			case Esus:
+				if(o->son || egtic & (1 << o->relsh)-1)
+					break;
+				v = o->rel + (egtic >> o->relsh & 7);
+				o->vol += estep[v];
+				if(o->vol >= Attmax)
+					o->vol = Attmax;
+				break;
+			case Erel:
+				if(egtic & (1 << o->relsh)-1)
+					break;
+				v = o->rel + (egtic >> o->relsh & 7);
+				o->vol += estep[v];
+				if(o->vol >= Attmax){
+					o->vol = Attmax;
+					o->state = Eoff;
+				}
+				break;
+			}
+			o++;
+		}
+	}
+	o = ops;
+	while(o < ops + nelem(ops)){
+		c = chs + (o-ops)/2;
+		if(o->φon){
+			f = c->fn;
+			i = lfoφs[lfoφ + (f >> 7 << 4)];
+			if(i != 0){
+				f += i;
+				oc = 7 - c->oct + (f >> 10);
+				o->fq += (fn[f & 0x3ff] >> oc) * o->mul;
+			}else
+				o->fq += o->dfq;
+		}else
+			o->fq += o->dfq;
+		o++;
+	}
+	noiseg();
+}
+
+static int
+op(u32int φ, u32int v, int dφ, u16int w)
+{
+	u32int p;
+
+	p = (v<<4) + sint[w + ((int)((φ & ~Mfreq) + dφ) >> Sfreq & Sinm)];
+	return p < Lxy ? lta[p] : 0;
+}
+
+static void
+chan(Op *o)
+{
+	u32int v;
+	int out;
+
+	φmod = 0;
+	out = o->out[0] + o->out[1];
+	o->out[0] = o->out[1];
+	*o->p += o->out[0];
+	o->out[1] = 0;
+	v = o->tl;
+	if(v < Equiet){
+		if(o->fbv == 0)
+			out = 0;
+		o->out[1] = op(o->fq, v, out << o->fbv, o->wav);
+	}
+	o++, v = o->tl;
+	if(v < Equiet)
+		tout += op(o->fq, v, φmod << 16, o->wav);
+}
+
+static void
+rchan(void)
+{
+	int out, nr;
+	u32int u, v;
+	Op *o;
+
+	nr = noise & 1;
+	φmod = 0;
+	o = chs[6].op;
+	out = o->out[0] + o->out[1];
+	o->out[0] = o->out[1];
+	if(o->opmodoff == 0)
+		φmod = o->out[0];
+	o->out[1] = 0;
+
+	v = o->tl;
+	if(v < Equiet){
+		if(o->fbv == 0)
+			out = 0;
+		o->out[1] = op(o->fq, v, out << o->fbv, o->wav);
+	}
+	o++, v = o->tl;
+	if(v < Equiet)
+		tout += op(o->fq, v, φmod << 16, o->wav) * 2;
+	o++, v = o->tl;
+	if(v < Equiet){
+		u = ops[17].fq >> Sfreq;
+		if((u ^ u<<2) & 1<<5)
+			u = 0x200 | 0xd0 >> 2;
+		else{
+			u = o->fq >> Sfreq;
+			u = ((u ^ u<<5 | u<<4) & 1<<7) ? 0x200 | 0xd0>>2 : 0xd0;
+		}
+		if(nr)
+			u = u & 0x200 ? 0x200 | 0xd0 : 0xd0>>2;
+		tout += op(u << Sfreq, v, 0, o->wav) * 2;
+	}
+	o++, v = o->tl;
+	if(v < Equiet){
+		u = (ops[14].fq >> Sfreq & 1<<8) ? 1<<9 : 1<<8;
+		u = (u ^ nr << 8) << Sfreq;
+		tout += op(u, v, 0, o->wav) * 2;
+	}
+	o++, v = o->tl;
+	if(v < Equiet)
+		tout += op(o->fq, v, 0, o->wav) * 2;
+	o++, v = o->tl;
+	if(v < Equiet){
+		u = o->fq >> Sfreq;
+		if((u ^ u<<2) & 1<<5)
+			u = 0x300;
+		else{
+			u = ops[14].fq >> Sfreq;
+			u = ((u ^ u<<5 | u<<4) & 1<<7) ? 0x300 : 0x100;
+		}
+		tout += op(u << Sfreq, v, 0, o->wav) * 2;
+	}
+}
+
+static void
+setk(Op *o, int k, int on)
+{
+	if(on){
+		if(o->key == 0){
+			o->fq = 0;
+			o->state = Eatk;
+		}
+		o->key |= k;
+	}else{
+		if(o->key == 0)
+			return;
+	
+		o->key &= ~k;
+		if(o->key == 0 && o->state > Erel)
+			o->state = Erel;
+	}
+}
+
+static void
+upop(Chan *c, Op *o)
+{
+	int n;
+
+	o->dfq = c->dfq0 * o->mul;
+	n = c->kcode >> o->kssh;
+
+	if(o->ks != n){
+		o->ks = n;
+		if(o->atkn + n < 16+62){
+			o->atksh = eratesh[o->atkn + n];
+			o->atk = erate[o->atkn + n];
+		}else{
+			o->atksh = 0;
+			o->atk = 13 * Erstep;
+		}
+		o->decsh = eratesh[o->decn + n];
+		o->dec = erate[o->decn + n];
+		o->relsh = eratesh[o->reln + n];
+		o->rel = erate[o->reln + n];
+	}
+}
+
+static void
+ctl(Chan *c, Op *o, int v)
+{
+	o->mul = mul[v & Mmfq];
+	o->kssh = v & Mkse ? 0 : 2;
+	o->son = v & Msse;
+	o->φon = v & Mvbe;
+	o->Aon = v & Mame ? ~0 : 0;
+	upop(c, o);
+}
+
+static void
+sca(Chan *c, Op *o, int v)
+{
+	uchar u;
+
+	u = v >> 6;
+	o->scl = u ? u % 3 : 31;	/* shift to 0, 3, 1.5, 6 dB/oct */
+	o->lvl0 = (v & Mlvl) << Eb-1-7;
+	o->lvl = o->lvl0 + (c->scl0 >> o->scl);
+}
+
+static void
+atkdec(Op *o, int v)
+{
+	int a, n;
+
+	n = o->ks;
+	o->atkn = a = v >> 4 ? 16 + (v >> 4 << 2) : 0;
+	a += n;
+	if(a < 16+62){
+		o->atksh = eratesh[a];
+		o->atk = erate[a];
+	}else{
+		o->atksh = 0;
+		o->atk = 13 * Erstep;
+	}
+	o->decn = a = v & Mdec ? 16 + ((v & Mdec) << 2) : 0;
+	a += n;
+	o->decsh = eratesh[a];
+	o->dec = erate[a];
+}
+
+static void
+susrel(Op *o, int v)
+{
+	o->sus = sust[v >> 4];
+	o->reln = v & Mrel ? 16 + ((v & Mrel) << 2) : 0;
+	o->relsh = eratesh[o->reln + o->ks];
+	o->rel = erate[o->reln + o->ks];
+}
+
+static void
+opm(int v)
+{
+	lfoAd = v & Mamp;
+	lfoφd = v & Mvib ? 8 : 0;
+	rhythm = v & Mrms;
+	if(v & ~Mrhy)
+		v = 0;
+	setk(chs[6].op, 2, v & Mbas);
+	setk(chs[6].op+1, 2, v & Mbas);
+	setk(chs[7].op, 2, v & Mhat);
+	setk(chs[7].op+1, 2, v & Msna);
+	setk(chs[8].op, 2, v & Mtom);
+	setk(chs[8].op+1, 2, v & Mcym);
+}
+
+static void
+foct(int r, int v)
+{
+	int n, b, o, f;
+	u32int u;
+	Chan *c;
+	Op *op0, *op1;
+
+	n = r & 0xf;
+	if(n > 8)
+		return;
+	c = chs+n;
+	op0 = c->op;
+	op1 = op0 + 1;
+	o = c->oct;
+	if(r & 0x10){	/* Roct */
+		f = c->fn & 0xff | (v & Mmsb) << 8;
+		o = (v & Moct) >> 2;
+		setk(op0, 1, v & Mkon);
+		setk(op1, 1, v & Mkon);
+	}else
+		f = c->fn & 0x300 | v;
+	b = f | o << 10;
+
+	if(c->fn != f || c->oct != o){
+		c->fn = f;
+		c->oct = o;
+		c->scl0 = u = lsca[b >> 6];
+		c->dfq0 = fn[f] >> 7 - o;
+		/* ignore manual full of lies */
+		c->kcode = o << 1 | (nse ? f >> 8 : f >> 9) & 1;
+		op0->lvl = op0->lvl0 + (u >> op0->scl);
+		op1->lvl = op1->lvl0 + (u >> op1->scl);
+		upop(c, op0);
+		upop(c, op1);
+	}
+}
+
+static void
+feedb(int r, int v)
+{
+	int n;
+	Op *o;
+
+	if(r > 8)
+		return;
+	n = v & Mmod;
+	v = (v & Mfed) >> 1;
+	o = chs[r].op;
+	o->opmodoff = n;
+	o->fbv = v ? v + 7 : 0;
+	o->p = n ? &tout : &φmod;
+}
+
+static void
+tab(void)
+{
+	int i, x, n, *l, *p;
+	uint *s;
+	double o, m;
+
+	for(x=0; x<Lx; x++){
+		m = (1<<16) / pow(2, (x+1) * (Estep/4.0) / 8.0);
+		m = floor(m);
+		n = (int)m >> 4;     /* always fits in 12 (16) bits */
+		n = (n>>1) + (n&1) << 1;	/* rnr */
+		l = lta + x*2;
+		l[0] = n;
+		l[1] = -n;
+		for(i=1, p=l; i<12; i++){
+			p += 2*Lx;
+			p[0] = n >> i;
+			p[1] = -(n >> i);
+		}
+	}
+
+	for(i=0, s=sint; i<Sinlen; i++){
+		m = sin((i*2+1) * PI / Sinlen);
+		o = 8 * log((m > 0.0 ? 1.0 : -1.0)/m) / log(2.0);	/* dB */
+		o /= Estep / 4.0;
+		n = o * 2.0;
+		n = (n>>1) + (n&1);	/* rnr */
+		*s++ = n*2 + (m >= 0.0 ? 0 : 1);
+	}
+	for(i=0, s=sint; i<Sinlen; i++, s++){
+		/* half-sine, abs-sine, pulse-sine */
+		s[Sinlen] = i & 1<<Sinb-1 ? Lxy : *s;
+		s[Sinlen*2] = sint[i & Sinm>>1];
+		s[Sinlen*3] = i & 1<<Sinb-2 ? Lxy : sint[i & Sinm>>2];
+	}
+}
+
+uchar *
+opl2out(uchar *s, int n)
+{
+	int v, r;
+	uchar *e;
+	Op *o;
+
+	r = rhythm & Mrhy;
+	e = s + n;
+	while(s < e){
+		tout = 0;
+		advlfo();
+		for(o=ops; o<ops+nelem(ops); o++)
+			o->tl = o->lvl + (u32int)o->vol + (lfoA & o->Aon);
+		for(o=ops; o<ops+nelem(ops); o+=2){
+			if(o == ops+6*2 && r){
+				rchan();
+				break;
+			}
+			chan(o);
+		}
+		v = tout;
+		if(v > 32767)
+			v = 32767;
+		else if(v < -32768)
+			v = -32768;
+		s[0] = s[2] = v;
+		s[1] = s[3] = v>>8;
+		s += 4;
+		adv();
+	}
+	return s;
+}
+
+void
+opl2wr(int r, int v)
+{
+	int n;
+	Op *o;
+	Chan *c;
+
+	v &= 0xff;
+	n = r & 0x1f;
+	c = chs + ((n>>3)+(n>>3<<1) + (n+(n+1>>2&1)&3));
+	o = c->op + (n + 1 >> 2 & 1);
+	n = r & 6 ^ 6 && n < 22;
+
+	switch(r){
+	case Rwse: wse = v & Mwse; return;
+	case Rcsm: nse = v & Mnse; return;
+	case Ropm: opm(v); return;
+	}
+	switch(r & 0xe0){
+	case Rctl: if(n) ctl(c, o, v); break;
+	case Rsca: if(n) sca(c, o, v); break;
+	case Ratk: if(n) atkdec(o, v); break;
+	case Rsus: if(n) susrel(o, v); break;
+	case Rnum: foct(r, v); break;
+	case Rfed: feedb(r & 0xf, v); break;
+	case Rwav: if(wse && n) o->wav = (v & Mwav) * Sinlen; break;
+	}
+}
+
+void
+opl2init(int rate)
+{
+	int i;
+	u32int *fp;
+	double f0, n;
+	Chan *c;
+	Op *o;
+
+	tab();
+	f0 = (Clk / 72.0) / rate;
+	fp = fn;
+	n = 0;
+	while(fp < fn+nelem(fn))
+		*fp++ = n++ * 64 * f0 * (1 << Sfreqd);
+	lfoAdt = (1.0 / 64.0) * (1 << Slfo) * f0;
+	lfoφdt = (1.0 / 1024.0) * (1 << Slfo) * f0;
+	noiseT = 1.0 * (1 << Sfreq) * f0;
+	egfdt = (1 << Seg) * f0;
+	egdt = 1 << Seg;
+	noise = 1;
+
+	c = chs;
+	o = ops;
+	while(c < chs+nelem(chs)){
+		c++->op = o;
+		o->state = Eoff;
+		o++->vol = Attmax;
+		o->state = Eoff;
+		o++->vol = Attmax;
+	}
+	for(i=Rctl; i<Rwav+22; i++)
+		opl2wr(i, 0);
+}
--- /dev/null
+++ b/opl2m.c
@@ -1,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+uchar*	opl2out(uchar *, int);
+void	opl2wr(int, int);
+void	opl2init(int);
+
+enum{
+	Rate = 44100,
+	Hz = 700,
+	Samp = Rate / Hz
+};
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-n nsamp] [file]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int n, r, v, dt, nsamp, fd;
+	uchar *sb, u[4];
+	Biobuf *bi, *bo;
+
+	fd = 0;
+	nsamp = Samp;
+	ARGBEGIN{
+	case 'n':
+		nsamp = Rate / atoi(EARGF(usage()));
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(*argv != nil){
+		fd = open(*argv, OREAD);
+		if(fd < 0)
+			sysfatal("open: %r");
+	}
+	bi = Bfdopen(fd, OREAD);
+	bo = Bfdopen(1, OWRITE);
+	if(bi == nil || bo == nil)
+		sysfatal("Bfdopen: %r");
+	nsamp *= 4;
+	sb = malloc(nsamp);
+	if(sb == nil)
+		sysfatal("malloc: %r");
+	opl2init(Rate);
+
+	while(n = Bread(bi, u, sizeof u), n > 0){
+		r = u[0];
+		v = u[1];
+		dt = u[3]<<8 | u[2];
+		opl2wr(r, v);
+		while(dt-- > 0){
+			opl2out(sb, nsamp);
+			Bwrite(bo, sb, nsamp);
+		}
+	}
+	if(n < 0)
+		sysfatal("Bread: %r");
+}
--- a/rend.c
+++ b/rend.c
@@ -5,9 +5,8 @@
 
 Fnt fnts[2], *fnt;
 Pic *pics, *pice;
-Dat *exts, *exte;
-Dat *dems, *deme, *epis;
-Dat *wals, *sprs, *spre;
+uchar **exts, **dems, **epis;
+Dat *wals, *sprs;
 
 int scale, npx;
 uchar *px;
--- a/sd.asm
+++ /dev/null
@@ -1,526 +1,0 @@
-;
-;	ID_SD_A.ASM
-;	Id Sound Manager assembly stuff
-
-	.286C
-	IDEAL
-	MODEL	MEDIUM,C
-	JUMPS
-
-	INCLUDE 'ID_SD.EQU'
-
-DEBUG	=	0
-
-	EXTRN	SDL_DigitizedDone:FAR
-	EXTRN	alOut:FAR
-
-;============================================================================
-
-DATASEG
-
-	EXTRN	sqActive:WORD
-	EXTRN	ssSample:DWORD
-	EXTRN	ssLengthLeft:WORD
-	EXTRN	ssControl:WORD
-	EXTRN	ssStatus:WORD
-	EXTRN	ssData:WORD
-	EXTRN	ssOn:BYTE
-	EXTRN	ssOff:BYTE
-
-	EXTRN	pcSound:DWORD
-	EXTRN	pcLengthLeft:WORD
-	EXTRN	pcLastSample:BYTE
-	EXTRN	pcSoundLookup:WORD
-
-	EXTRN	alSound:DWORD
-	EXTRN	alBlock:WORD
-	EXTRN	alLengthLeft:WORD
-	EXTRN	alTimeCount:DWORD
-
-	EXTRN	sqHack:DWORD
-	EXTRN	sqHackPtr:DWORD
-	EXTRN	sqHackLen:WORD
-	EXTRN	sqHackSeqLen:WORD
-	EXTRN	sqHackTime:DWORD
-
-	EXTRN	HackCount:WORD
-	EXTRN	TimeCount:WORD
-	EXTRN	LocalTime:WORD
-
-	EXTRN	TimerCount:WORD
-	EXTRN	TimerDivisor:WORD
-	EXTRN	t0OldService:DWORD
-
-	EXTRN	SoundMode:WORD
-	EXTRN	DigiMode:WORD
-
-	EXTRN	SoundNumber:WORD
-	EXTRN	SoundPriority:WORD
-
-count_time	dw	?
-count_fx	dw	?
-
-pcdtab	db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b,00b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-		db	10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b,10b
-
-
-;============================================================================
-
-CODESEG
-
-MyDS	dw	?
-
-pcindicate	dw	?
-extreme		dw	?
-
-	PROC	SDL_SetDS
-	PUBLIC	SDL_SetDS
-
-	mov	ax,ds
-	mov	[cs:MyDS],ds
-	ret
-
-	ENDP
-
-;
-;	COMMONSTART
-;	Macro used for common prefix code
-;
-	MACRO	COMMONSTART
-	IF DEBUG
-	push dx
-	push ax
-	mov	dx,STATUS_REGISTER_1
-	in	al,dx
-	mov	dx,ATR_INDEX
-	mov	al,ATR_OVERSCAN
-	out	dx,al
-	mov	al,4	; red
-	out	dx,al
-	ENDIF
-
-	push ds
-	push ax
-
-	mov	ds,[cs:MyDS]
-	inc	[HackCount]
-	ENDM
-
-;
-;	DOFX
-;	Macro used to do the sound effects code
-;
-	MACRO	DOFX
-	les	di,[pcSound]				; PC sound effects
-	mov	ax,es
-	or	ax,di
-	jz	@@nopc						; nil pointer - no PC sound effect going
-
-	mov	bl,[es:di]					; Get the byte
-	inc	[WORD PTR pcSound]			; Increment pointer
-	cmp	[pcLastSample],bl			; Is this sample the same as last?
-	jz	@@pcsame					; Yep - don't do anything
-	mov	[pcLastSample],bl			; No, save it for next time
-
-	or	bl,bl
-	jz	@@pcoff						; If 0, turn sounds off
-	xor	bh,bh
-	shl	bx,1
-	mov	bx,[pcSoundLookup+bx]		; Use byte as index into frequency table
-
-	mov	al,0b6h						; Write to channel 2 (speaker) timer
-	out	pcTAccess,al
-	mov	al,bl
-	out	pcTimer,al					; Low byte
-	mov	al,bh
-	out	pcTimer,al					; High byte
-
-	in	al,pcSpeaker				; Turn the speaker & gate on
-	or	al,3
-	out	pcSpeaker,al
-
-	jmp @@pcsame
-
-@@pcoff:
-	in	al,pcSpeaker  				; Turn the speaker & gate off
-	and	al,0fch						; ~3
-	out	pcSpeaker,al
-
-@@pcsame:
-	dec	[pcLengthLeft]				; Decrement length
-	jnz	@@nopc						; If not 0, we're not done with the sound
-
-	mov	ax,0
-	mov	[WORD PTR pcSound],ax		; Zero the pointer
-	mov	[WORD PTR pcSound + 2],ax
-	mov	[SoundNumber],ax    		; Indicate no sound
-	mov	[SoundPriority],ax			;  with no priority
-
-	in	al,pcSpeaker  				; Turn the speaker off
-	and	al,0fdh						; ~2
-	out	pcSpeaker,al
-@@nopc:
-
-	les	di,[alSound]				; AdLib sound effects
-	mov	ax,es
-	or	ax,di
-	jz	@@noal						; nil pointer - no AdLib effect going
-
-	xor	ah,ah
-	mov	al,[es:di]
-	or	al,al
-	jz	@@aldone
-
-	CALL alOut C,alFreqL,ax
-	mov	ax,[alBlock]
-
-@@aldone:
-	CALL alOut C,alFreqH,ax
-	inc	[WORD PTR alSound]
-	dec	[alLengthLeft]
-	jnz	@@noal
-
-	mov	ax,0
-	mov	[WORD PTR alSound],ax		; Zero the pointer
-	mov	[WORD PTR alSound + 2],ax
-	mov	[SoundNumber],ax    		; Indicate no sound
-	mov	[SoundPriority],ax			;  with no priority
-	CALL alOut C,alFreqH,ax			; Turn off the sound
-@@noal:
-
-	ENDM
-
-;
-;
-;
-	MACRO	TIME
-	cmp	[count_time],2
-	jb	@@notime
-	add	[LocalTime],1
-	adc	[LocalTime+2],0
-	add	[TimeCount],1
-	adc	[TimeCount+2],0
-	mov	[count_time],0
-@@notime:
-	ENDM
-
-;
-;	COMMONEND
-;	Macro used for common suffix code
-;
-	MACRO	COMMONEND
-@@fullexit:
-	pop	es
-	popa
-
-@@nosave:
-	mov	ax,[TimerDivisor]
-	add	[TimerCount],ax
-	jnc	@@myack
-
-	pushf
-	call [t0OldService]
-	jmp	@@out
-
-@@myack:
-	mov	al,20h
-	out	20h,al
-
-@@out:
-	pop	ax
-	pop	ds
-
-	IF DEBUG
-	mov	dx,STATUS_REGISTER_1
-	in	al,dx
-	mov	dx,ATR_INDEX
-	mov	al,ATR_OVERSCAN
-	out	dx,al
-	mov	al,3	; blue
-	out	dx,al
-	mov	al,20h	; normal
-	out	dx,al
-	pop	ax
-	pop	dx
-	ENDIF
-
-	iret
-	ENDM
-
-;
-;	SDL_IndicatePC
-;
-	PROC	SDL_IndicatePC on:WORD
-	PUBLIC	SDL_IndicatePC
-
-	mov	ax,[on]
-	mov	[cs:pcindicate],ax
-	ret
-
-	ENDP
-
-;
-;	SDL_t0ExtremeAsmService
-;	Timer 0 ISR 7000Hz interrupts
-;
-	PROC	SDL_t0ExtremeAsmService
-	PUBLIC	SDL_t0ExtremeAsmService
-
-	push ax
-	mov	al,[BYTE PTR cs:pcindicate]
-	or	al,al
-	jz	@@done
-
-	push ds
-	push es
-	pusha
-
-	mov	ds,[cs:MyDS]
-
-	les	di,[pcSound]
-	mov	ax,es
-	or	ax,di
-	jz	@@donereg					; nil pointer
-
-	mov	bl,[es:di]					; Get the byte
-	inc	[WORD PTR pcSound]			; Increment pointer
-
-	and	bl,11100000b				; Nuke some of the precision (DEBUG - do this in the table)
-
-	xor	bh,bh
-	mov	ah,[pcdtab+bx]				; Translate the byte
-
-	in	al,pcSpeaker
-	and	al,11111100b
-	or	al,ah
-	out	pcSpeaker,al
-
-	dec	[pcLengthLeft]
-	jnz	@@donereg
-
-	mov	[WORD PTR pcSound],0		; We're done with this sample
-	mov	[WORD PTR pcSound+2],0
-
-	in	al,pcSpeaker
-	and	al,11111100b
-	out	pcSpeaker,al
-
-	call SDL_DigitizedDone
-
-@@donereg:
-	popa
-	pop	es
-	pop	ds
-
-@@done:
-	inc	[cs:extreme]
-	cmp	[cs:extreme],10
-	jae	@@tofast
-
-	mov	al,20h
-	out	20h,al
-	pop	ax
-	iret
-
-@@tofast:
-	mov	[cs:extreme],0
-	pop	ax
-
-;	jmp	SDL_t0FastAsmService			; Drops through to SDL_t0FastAsmService
-
-	ENDP
-
-;
-;	SDL_t0FastAsmService
-;	Timer 0 ISR for 700Hz interrupts
-;
-	PROC	SDL_t0FastAsmService
-	PUBLIC	SDL_t0FastAsmService
-
-	COMMONSTART
-
-	inc	[count_fx]						; Time to do PC/AdLib effects & time?
-	cmp	[count_fx],5
-	jae	@@dofull
-
-	mov	ax,[sqActive]					; Is the sequencer active?
-	or	ax,ax
-	jnz	@@dofull
-
-	mov	ax,[WORD PTR ssSample]			; Is there a sample for the Sound Src?
-	or	ax,[WORD PTR ssSample+2]
-	jz	@@nosave
-
-@@dofull:
-	pusha
-	push es
-
-	cmp	[count_fx],5
-	jb	@@nofx
-	mov	[count_fx],0
-	DOFX
-
-	inc	[count_time]
-	TIME
-@@nofx:
-
-	mov	ax,[sqActive]
-	or	ax,ax
-	jz	@@nosq
-
-	mov	ax,[sqHackLen]
-	or	ax,ax
-	jz	@@sqdone
-
-	les	di,[sqHackPtr]
-@@sqloop:
-	mov	ax,[WORD PTR sqHackTime+2]
-	cmp	ax,[WORD PTR alTimeCount+2]
-	ja	@@sqdone
-	mov	ax,[WORD PTR sqHackTime]
-	cmp	ax,[WORD PTR alTimeCount]
-	ja	@@sqdone
-
-	mov	ax,[es:di+2]					; Get time to next event
-	add	ax,[WORD PTR alTimeCount]
-	mov	[WORD PTR sqHackTime],ax
-	mov	ax,[WORD PTR alTimeCount+2]
-	adc	ax,0
-	mov	[WORD PTR sqHackTime+2],ax
-
-	mov	ax,[es:di]						; Get register/value pair
-	xor	bh,bh
-	mov	bl,ah
-	xor	ah,ah
-	CALL alOut C,ax,bx
-
-	add	di,4
-	mov	[WORD PTR sqHackPtr],di
-
-	sub	[sqHackLen],4
-	jnz	@@sqloop
-
-@@sqdone:
-	add	[WORD PTR alTimeCount],1
-	adc	[WORD PTR alTimeCount+2],0
-	mov	ax,[sqHackLen]
-	or	ax,ax
-	jnz	@@nosq
-
-	mov	ax,[WORD PTR sqHack]		; Copy pointer
-	mov	[WORD PTR sqHackPtr],ax
-	mov	ax,[WORD PTR sqHack+2]
-	mov	[WORD PTR sqHackPtr+2],ax
-
-	mov	ax,[sqHackSeqLen]			; Copy length
-	mov	[sqHackLen],ax
-
-	mov	ax,0
-	mov	[WORD PTR alTimeCount],ax	; Reset time counts
-	mov	[WORD PTR alTimeCount+2],ax
-	mov	[WORD PTR sqHackTime],ax
-	mov	[WORD PTR sqHackTime+2],ax
-@@nosq:
-
-	les	di,[ssSample]			; Get pointer to Sound Source sample
-	mov	ax,es
-	or	ax,di
-	jz	@@ssdone				; If nil, skip this
-
-@@ssloop:
-	mov	dx,[ssStatus]			; Check to see if FIFO has any empty slots
-	in	al,dx
-	test al,40h
-	jnz	@@ssdone				; Nope - don't push any more data out
-
-	mov	dx,[ssData]
-	mov	al,[es:di]				; al = *ssSample
-	out	dx,al					; Pump the value out
-
-	mov	dx,[ssControl]			; Pulse printer select
-	mov	al,[ssOff]
-	out	dx,al
-	push ax
-	pop	ax
-	mov	al,[ssOn]
-	out	dx,al
-
-	push ax						; Delay a short while
-	pop	ax
-
-	inc	di
-	mov	[WORD PTR ssSample],di	; ssSample++
-
-	dec	[ssLengthLeft]
-	jnz @@ssloop
-
-	mov	[WORD PTR ssSample],0	; We're done with this sample
-	mov	[WORD PTR ssSample+2],0
-
-	call SDL_DigitizedDone
-@@ssdone:
-
-	COMMONEND
-
-	ENDP
-
-;
-;	SDL_t0SlowAsmService
-;	Timer 0 ISR for 140Hz interrupts
-;
-	PROC	SDL_t0SlowAsmService
-	PUBLIC	SDL_t0SlowAsmService
-
-	IF DEBUG
-	push dx
-	push ax
-	mov	dx,STATUS_REGISTER_1
-	in	al,dx
-	mov	dx,ATR_INDEX
-	mov	al,ATR_OVERSCAN
-	out	dx,al
-	mov	al,4	; red
-	out	dx,al
-	ENDIF
-
-	push ds
-	push ax
-
-	mov	ds,[cs:MyDS]
-
-	inc	[count_time]
-	TIME
-
-	mov	ax,[WORD PTR pcSound]		; Is there a PC sound effect going?
-	or	ax,[WORD PTR pcSound+2]
-	jnz	@@dofull
-
-	mov	ax,[WORD PTR alSound]		; Is there an AdLib sound effect going?
-	or	ax,[WORD PTR alSound+2]
-	jz	@@nosave
-
-@@dofull:
-	pusha
-	push es
-
-	DOFX
-
-	COMMONEND
-
-	ENDP
-
-	END
--- a/sd.c
+++ /dev/null
@@ -1,2162 +1,0 @@
-//
-//	ID Engine
-//	ID_SD.c - Sound Manager for Wolfenstein 3D
-//	v1.2
-//	By Jason Blochowiak
-//
-
-//
-//	This module handles dealing with generating sound on the appropriate
-//		hardware
-//
-//	Depends on: User Mgr (for parm checking)
-//
-//	Globals:
-//		For User Mgr:
-//			SoundSourcePresent - Sound Source thingie present?
-//			SoundBlasterPresent - SoundBlaster card present?
-//			AdLibPresent - AdLib card present?
-//			SoundMode - What device is used for sound effects
-//				(Use SM_SetSoundMode() to set)
-//			MusicMode - What device is used for music
-//				(Use SM_SetMusicMode() to set)
-//			DigiMode - What device is used for digitized sound effects
-//				(Use SM_SetDigiDevice() to set)
-//
-//		For Cache Mgr:
-//			NeedsDigitized - load digitized sounds?
-//			NeedsMusic - load music?
-//
-
-#include "ID_HEADS.H"
-
-#define	SDL_SoundFinished()	{SoundNumber = SoundPriority = 0;}
-
-// Macros for SoundBlaster stuff
-#define	sbOut(n,b)	outportb((n) + sbLocation,b)
-#define	sbIn(n)		inportb((n) + sbLocation)
-#define	sbWriteDelay()	while (sbIn(sbWriteStat) & 0x80);
-#define	sbReadDelay()	while (sbIn(sbDataAvail) & 0x80);
-
-// Macros for AdLib stuff
-#define	selreg(n)	outportb(alFMAddr,n)
-#define	writereg(n)	outportb(alFMData,n)
-#define	readstat()	inportb(alFMStatus)
-
-//	Imports from ID_SD_A.ASM
-extern	void			SDL_SetDS(void),
-						SDL_IndicatePC(int on);
-extern	void interrupt	SDL_t0ExtremeAsmService(void),
-						SDL_t0FastAsmService(void),
-						SDL_t0SlowAsmService(void);
-
-//	Global variables
-	int		SoundSourcePresent,
-				AdLibPresent,
-				SoundBlasterPresent,SBProPresent,
-				NeedsDigitized,NeedsMusic,
-				SoundPositioned;
-	SDMode		SoundMode;
-	SMMode		MusicMode;
-	SDSMode		DigiMode;
-	u32int	TimeCount;
-	u16int		HackCount;
-	u16int		*SoundTable;	// Really * _seg *SoundTable, but that don't work
-	s16int			DigiMap[LASTSOUND];
-
-//	Internal variables
-static	int			SD_Started;
-		int			nextsoundpos;
-		u32int		TimerDivisor,TimerCount;
-static	void			(*SoundUserHook)(void);
-		soundnames		SoundNumber,DigiNumber;
-		u16int			SoundPriority,DigiPriority;
-		s16int				LeftPosition,RightPosition;
-		void interrupt	(*t0OldService)(void);
-		s32int			LocalTime;
-		u16int			TimerRate;
-
-		u16int			NumDigi,DigiLeft,DigiPage;
-		u16int			_seg *DigiList;
-		u16int			DigiLastStart,DigiLastEnd;
-		int			DigiPlaying;
-static	int			DigiMissed,DigiLastSegment;
-static	uchar *DigiNextAddr;
-static	u16int			DigiNextLen;
-
-//	SoundBlaster variables
-static	volatile int		sbSamplePlaying;
-static	u8int					sbOldIntMask = -1;
-static	volatile u8int			huge *sbNextSegPtr;
-static	u8int					sbDMA = 1,
-								sbDMAa1 = 0x83,sbDMAa2 = 2,sbDMAa3 = 3,
-								sba1Vals[] = {0x87,0x83,0,0x82},
-								sba2Vals[] = {0,2,0,6},
-								sba3Vals[] = {1,3,0,7};
-static	s16int						sbLocation = -1,sbInterrupt = 7,sbIntVec = 0xf,
-								sbIntVectors[] = {-1,-1,0xa,0xb,-1,0xd,-1,0xf,-1,-1,-1};
-static	volatile u32int		sbNextSegLen;
-static	volatile SampledSound	huge *sbSamples;
-static	void interrupt			(*sbOldIntHand)(void);
-static	u8int					sbpOldFMMix,sbpOldVOCMix;
-
-//	SoundSource variables
-		int				ssActive;
-		u16int				ssControl,ssStatus,ssData;
-		u8int				ssOn,ssOff;
-		volatile u8int		far *ssSample;
-		volatile u32int	ssLengthLeft;
-
-//	PC Sound variables
-		volatile u8int	pcLastSample,far *pcSound;
-		u32int		pcLengthLeft;
-		u16int			pcSoundLookup[255];
-
-//	AdLib variables
-		u8int			far *alSound;
-		u16int			alBlock;
-		u32int		alLengthLeft;
-		u32int		alTimeCount;
-		Instrument		alZeroInst;
-
-// This table maps channel numbers to carrier and modulator op cells
-static	u8int			carriers[9] =  { 3, 4, 5,11,12,13,19,20,21},
-						modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18},
-// This table maps percussive voice numbers to op cells
-						pcarriers[5] = {19,0xff,0xff,0xff,0xff},
-						pmodifiers[5] = {16,17,18,20,21};
-
-//	Sequencer variables
-		int			sqActive;
-static	u16int			alFXReg;
-static	ActiveTrack		*tracks[sqMaxTracks],
-						mytracks[sqMaxTracks];
-static	u16int			sqMode,sqFadeStep;
-		u16int			far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen;
-		s32int			sqHackTime;
-
-//	Internal routines
-		void			SDL_DigitizedDone(void);
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SetTimer0() - Sets system timer 0 to the specified speed
-//
-///////////////////////////////////////////////////////////////////////////
-#pragma	argsused
-static void
-SDL_SetTimer0(u16int speed)
-{
-asm	pushf
-asm	cli
-
-	outportb(0x43,0x36);				// Change timer 0
-	outportb(0x40,speed);
-	outportb(0x40,speed >> 8);
-	// Kludge to handle special case for digitized PC sounds
-	if (TimerDivisor == (1192030 / (TickBase * 100)))
-		TimerDivisor = (1192030 / (TickBase * 10));
-	else
-		TimerDivisor = speed;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of
-//		interrupts generated by system timer 0 per second
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SetIntsPerSec(u16int ints)
-{
-	TimerRate = ints;
-	SDL_SetTimer0(1192030 / ints);
-}
-
-static void
-SDL_SetTimerSpeed(void)
-{
-	u16int	rate;
-	void interrupt	(*isr)(void);
-
-	if ((DigiMode == sds_PC) && DigiPlaying)
-	{
-		rate = TickBase * 100;
-		isr = SDL_t0ExtremeAsmService;
-	}
-	else if
-	(
-		(MusicMode == smm_AdLib)
-	||	((DigiMode == sds_SoundSource) && DigiPlaying)
-	)
-	{
-		rate = TickBase * 10;
-		isr = SDL_t0FastAsmService;
-	}
-	else
-	{
-		rate = TickBase * 2;
-		isr = SDL_t0SlowAsmService;
-	}
-
-	if (rate != TimerRate)
-	{
-		setvect(8,isr);
-		SDL_SetIntsPerSec(rate);
-		TimerRate = rate;
-	}
-}
-
-//
-//	SoundBlaster code
-//
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SBStopSample() - Stops any active sampled sound and causes DMA
-//		requests from the SoundBlaster to cease
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SBStopSample(void)
-{
-	u8int	is;
-
-asm	pushf
-asm	cli
-
-	if (sbSamplePlaying)
-	{
-		sbSamplePlaying = false;
-
-		sbWriteDelay();
-		sbOut(sbWriteCmd,0xd0);	// Turn off DSP DMA
-
-		is = inportb(0x21);	// Restore interrupt mask bit
-		if (sbOldIntMask & (1 << sbInterrupt))
-			is |= (1 << sbInterrupt);
-		else
-			is &= ~(1 << sbInterrupt);
-		outportb(0x21,is);
-	}
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SBPlaySeg() - Plays a chunk of sampled sound on the SoundBlaster
-//	Insures that the chunk doesn't cross a bank boundary, programs the DMA
-//	 controller, and tells the SB to start doing DMA requests for DAC
-//
-///////////////////////////////////////////////////////////////////////////
-static u32int
-SDL_SBPlaySeg(volatile u8int huge *data,u32int length)
-{
-	u16int		datapage;
-	u32int		dataofs,uselen;
-
-	uselen = length;
-	datapage = FP_SEG(data) >> 12;
-	dataofs = ((FP_SEG(data) & 0xfff) << 4) + FP_OFF(data);
-	if (dataofs >= 0x10000)
-	{
-		datapage++;
-		dataofs -= 0x10000;
-	}
-
-	if (dataofs + uselen > 0x10000)
-		uselen = 0x10000 - dataofs;
-
-	uselen--;
-
-	// Program the DMA controller
-asm	pushf
-asm	cli
-	outportb(0x0a,sbDMA | 4);					// Mask off DMA on channel sbDMA
-	outportb(0x0c,0);							// Clear byte ptr flip-flop to lower byte
-	outportb(0x0b,0x49);						// Set transfer mode for D/A conv
-	outportb(sbDMAa2,(u8int)dataofs);			// Give LSB of address
-	outportb(sbDMAa2,(u8int)(dataofs >> 8));		// Give MSB of address
-	outportb(sbDMAa1,(u8int)datapage);			// Give page of address
-	outportb(sbDMAa3,(u8int)uselen);				// Give LSB of length
-	outportb(sbDMAa3,(u8int)(uselen >> 8));		// Give MSB of length
-	outportb(0x0a,sbDMA);						// Re-enable DMA on channel sbDMA
-
-	// Start playing the thing
-	sbWriteDelay();
-	sbOut(sbWriteCmd,0x14);
-	sbWriteDelay();
-	sbOut(sbWriteData,(u8int)uselen);
-	sbWriteDelay();
-	sbOut(sbWriteData,(u8int)(uselen >> 8));
-asm	popf
-
-	return(uselen + 1);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SBService() - Services the SoundBlaster DMA interrupt
-//
-///////////////////////////////////////////////////////////////////////////
-static void interrupt
-SDL_SBService(void)
-{
-	u32int	used;
-
-	sbIn(sbDataAvail);	// Ack interrupt to SB
-
-	if (sbNextSegPtr)
-	{
-		used = SDL_SBPlaySeg(sbNextSegPtr,sbNextSegLen);
-		if (sbNextSegLen <= used)
-			sbNextSegPtr = nil;
-		else
-		{
-			sbNextSegPtr += used;
-			sbNextSegLen -= used;
-		}
-	}
-	else
-	{
-		SDL_SBStopSample();
-		SDL_DigitizedDone();
-	}
-
-	outportb(0x20,0x20);	// Ack interrupt
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SBPlaySample() - Plays a sampled sound on the SoundBlaster. Sets up
-//		DMA to play the sound
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SBPlaySample(u8int huge *data,u32int len)
-{
-	u32int	used;
-
-	SDL_SBStopSample();
-
-asm	pushf
-asm	cli
-
-	used = SDL_SBPlaySeg(data,len);
-	if (len <= used)
-		sbNextSegPtr = nil;
-	else
-	{
-		sbNextSegPtr = data + used;
-		sbNextSegLen = len - used;
-	}
-
-	// Save old interrupt status and unmask ours
-	sbOldIntMask = inportb(0x21);
-	outportb(0x21,sbOldIntMask & ~(1 << sbInterrupt));
-
-	sbWriteDelay();
-	sbOut(sbWriteCmd,0xd4);						// Make sure DSP DMA is enabled
-
-	sbSamplePlaying = true;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PositionSBP() - Sets the attenuation levels for the left and right
-//		channels by using the mixer chip on the SB Pro. This hits a hole in
-//		the address map for normal SBs.
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PositionSBP(s16int leftpos,s16int rightpos)
-{
-	u8int	v;
-
-	if (!SBProPresent)
-		return;
-
-	leftpos = 15 - leftpos;
-	rightpos = 15 - rightpos;
-	v = ((leftpos & 0x0f) << 4) | (rightpos & 0x0f);
-
-asm	pushf
-asm	cli
-
-	sbOut(sbpMixerAddr,sbpmVoiceVol);
-	sbOut(sbpMixerData,v);
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_CheckSB() - Checks to see if a SoundBlaster resides at a
-//		particular I/O location
-//
-///////////////////////////////////////////////////////////////////////////
-static int
-SDL_CheckSB(s16int port)
-{
-	s16int	i;
-
-	sbLocation = port << 4;		// Initialize stuff for later use
-
-	sbOut(sbReset,true);		// Reset the SoundBlaster DSP
-asm	mov	dx,0x388				// Wait >4usec
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-asm	in	al, dx
-
-	sbOut(sbReset,false);		// Turn off sb DSP reset
-asm	mov	dx,0x388				// Wait >100usec
-asm	mov	cx,100
-usecloop:
-asm	in	al,dx
-asm	loop usecloop
-
-	for (i = 0;i < 100;i++)
-	{
-		if (sbIn(sbDataAvail) & 0x80)		// If data is available...
-		{
-			if (sbIn(sbReadData) == 0xaa)	// If it matches correct value
-				return(true);
-			else
-			{
-				sbLocation = -1;			// Otherwise not a SoundBlaster
-				return(false);
-			}
-		}
-	}
-	sbLocation = -1;						// Retry count exceeded - fail
-	return(false);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	Checks to see if a SoundBlaster is in the system. If the port passed is
-//		-1, then it scans through all possible I/O locations. If the port
-//		passed is 0, then it uses the default (2). If the port is >0, then
-//		it just passes it directly to SDL_CheckSB()
-//
-///////////////////////////////////////////////////////////////////////////
-static int
-SDL_DetectSoundBlaster(s16int port)
-{
-	s16int	i;
-
-	if (port == 0)					// If user specifies default, use 2
-		port = 2;
-	if (port == -1)
-	{
-		if (SDL_CheckSB(2))			// Check default before scanning
-			return(true);
-
-		if (SDL_CheckSB(4))			// Check other SB Pro location before scan
-			return(true);
-
-		for (i = 1;i <= 6;i++)		// Scan through possible SB locations
-		{
-			if ((i == 2) || (i == 4))
-				continue;
-
-			if (SDL_CheckSB(i))		// If found at this address,
-				return(true);		//	return success
-		}
-		return(false);				// All addresses failed, return failure
-	}
-	else
-		return(SDL_CheckSB(port));	// User specified address or default
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SBSetDMA() - Sets the DMA channel to be used by the SoundBlaster
-//		code. Sets up sbDMA, and sbDMAa1-sbDMAa3 (used by SDL_SBPlaySeg()).
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SDL_SBSetDMA(u8int channel)
-{
-	if (channel > 3)
-		Quit("SDL_SBSetDMA() - invalid SoundBlaster DMA channel");
-
-	sbDMA = channel;
-	sbDMAa1 = sba1Vals[channel];
-	sbDMAa2 = sba2Vals[channel];
-	sbDMAa3 = sba3Vals[channel];
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_StartSB() - Turns on the SoundBlaster
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_StartSB(void)
-{
-	u8int	timevalue,test;
-
-	sbIntVec = sbIntVectors[sbInterrupt];
-	if (sbIntVec < 0)
-		Quit("SDL_StartSB: Illegal or unsupported interrupt number for SoundBlaster");
-
-	sbOldIntHand = getvect(sbIntVec);	// Get old interrupt handler
-	setvect(sbIntVec,SDL_SBService);	// Set mine
-
-	sbWriteDelay();
-	sbOut(sbWriteCmd,0xd1);				// Turn on DSP speaker
-
-	// Set the SoundBlaster DAC time constant for 7KHz
-	timevalue = 256 - (1000000 / 7000);
-	sbWriteDelay();
-	sbOut(sbWriteCmd,0x40);
-	sbWriteDelay();
-	sbOut(sbWriteData,timevalue);
-
-	SBProPresent = false;
-
-	// Check to see if this is a SB Pro
-	sbOut(sbpMixerAddr,sbpmFMVol);
-	sbpOldFMMix = sbIn(sbpMixerData);
-	sbOut(sbpMixerData,0xbb);
-	test = sbIn(sbpMixerData);
-	if (test == 0xbb)
-	{
-		// Boost FM output levels to be equivilent with digitized output
-		sbOut(sbpMixerData,0xff);
-		test = sbIn(sbpMixerData);
-		if (test == 0xff)
-		{
-			SBProPresent = true;
-
-			// Save old Voice output levels (SB Pro)
-			sbOut(sbpMixerAddr,sbpmVoiceVol);
-			sbpOldVOCMix = sbIn(sbpMixerData);
-
-			// Turn SB Pro stereo DAC off
-			sbOut(sbpMixerAddr,sbpmControl);
-			sbOut(sbpMixerData,0);				// 0=off,2=on
-		}
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ShutSB() - Turns off the SoundBlaster
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ShutSB(void)
-{
-	SDL_SBStopSample();
-
-	if (SBProPresent)
-	{
-		// Restore FM output levels (SB Pro)
-		sbOut(sbpMixerAddr,sbpmFMVol);
-		sbOut(sbpMixerData,sbpOldFMMix);
-
-		// Restore Voice output levels (SB Pro)
-		sbOut(sbpMixerAddr,sbpmVoiceVol);
-		sbOut(sbpMixerData,sbpOldVOCMix);
-	}
-
-	setvect(sbIntVec,sbOldIntHand);		// Set vector back
-}
-
-//	Sound Source Code
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SSStopSample() - Stops a sample playing on the Sound Source
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SSStopSample(void)
-{
-asm	pushf
-asm	cli
-
-	(s32int)ssSample = 0;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SSService() - Handles playing the next sample on the Sound Source
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SSService(void)
-{
-	int	gotit;
-	u8int	v;
-
-	while (ssSample)
-	{
-	asm	mov		dx,[ssStatus]	// Check to see if FIFO is currently empty
-	asm	in		al,dx
-	asm	test	al,0x40
-	asm	jnz		done			// Nope - don't push any more data out
-
-		v = *ssSample++;
-		if (!(--ssLengthLeft))
-		{
-			(s32int)ssSample = 0;
-			SDL_DigitizedDone();
-		}
-
-	asm	mov		dx,[ssData]		// Pump the value out
-	asm	mov		al,[v]
-	asm	out		dx,al
-
-	asm	mov		dx,[ssControl]	// Pulse printer select
-	asm	mov		al,[ssOff]
-	asm	out		dx,al
-	asm	push	ax
-	asm	pop		ax
-	asm	mov		al,[ssOn]
-	asm	out		dx,al
-
-	asm	push	ax				// Delay a short while
-	asm	pop		ax
-	asm	push	ax
-	asm	pop		ax
-	}
-done:;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SSPlaySample() - Plays the specified sample on the Sound Source
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SSPlaySample(u8int huge *data,u32int len)
-{
-asm	pushf
-asm	cli
-
-	ssLengthLeft = len;
-	ssSample = (volatile u8int far *)data;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_StartSS() - Sets up for and turns on the Sound Source
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_StartSS(void)
-{
-	ssControl = 0x3be;	// If using LPT1
-	ssStatus = ssControl - 1;
-	ssData = ssStatus - 1;
-
-	ssOn = 0x04;
-	ssOff = 0x0c;
-	outportb(ssControl,ssOn);		// Enable SS
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ShutSS() - Turns off the Sound Source
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ShutSS(void)
-{
-	outportb(ssControl,ssOff);
-}
-
-//
-//	PC Sound code
-//
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PCPlaySample() - Plays the specified sample on the PC speaker
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PCPlaySample(u8int huge *data,u32int len)
-{
-asm	pushf
-asm	cli
-
-	SDL_IndicatePC(true);
-
-	pcLengthLeft = len;
-	pcSound = (volatile u8int far *)data;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PCStopSample() - Stops a sample playing on the PC speaker
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PCStopSample(void)
-{
-asm	pushf
-asm	cli
-
-	(s32int)pcSound = 0;
-
-	SDL_IndicatePC(false);
-
-asm	in	al,0x61		  	// Turn the speaker off
-asm	and	al,0xfd			// ~2
-asm	out	0x61,al
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PCPlaySound() - Plays the specified sound on the PC speaker
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PCPlaySound(PCSound far *sound)
-{
-asm	pushf
-asm	cli
-
-	pcLastSample = -1;
-	pcLengthLeft = sound->common.length;
-	pcSound = sound->data;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PCStopSound() - Stops the current sound playing on the PC Speaker
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PCStopSound(void)
-{
-asm	pushf
-asm	cli
-
-	(s32int)pcSound = 0;
-
-asm	in	al,0x61		  	// Turn the speaker off
-asm	and	al,0xfd			// ~2
-asm	out	0x61,al
-
-asm	popf
-}
-
-#if 0
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_PCService() - Handles playing the next sample in a PC sound
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_PCService(void)
-{
-	u8int	s;
-	u16int	t;
-
-	if (pcSound)
-	{
-		s = *pcSound++;
-		if (s != pcLastSample)
-		{
-		asm	pushf
-		asm	cli
-
-			pcLastSample = s;
-			if (s)					// We have a frequency!
-			{
-				t = pcSoundLookup[s];
-			asm	mov	bx,[t]
-
-			asm	mov	al,0xb6			// Write to channel 2 (speaker) timer
-			asm	out	43h,al
-			asm	mov	al,bl
-			asm	out	42h,al			// Low byte
-			asm	mov	al,bh
-			asm	out	42h,al			// High byte
-
-			asm	in	al,0x61			// Turn the speaker & gate on
-			asm	or	al,3
-			asm	out	0x61,al
-			}
-			else					// Time for some silence
-			{
-			asm	in	al,0x61		  	// Turn the speaker & gate off
-			asm	and	al,0xfc			// ~3
-			asm	out	0x61,al
-			}
-
-		asm	popf
-		}
-
-		if (!(--pcLengthLeft))
-		{
-			SDL_PCStopSound();
-			SDL_SoundFinished();
-		}
-	}
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ShutPC() - Turns off the pc speaker
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ShutPC(void)
-{
-asm	pushf
-asm	cli
-
-	pcSound = 0;
-
-asm	in	al,0x61		  	// Turn the speaker & gate off
-asm	and	al,0xfc			// ~3
-asm	out	0x61,al
-
-asm	popf
-}
-
-//
-//	Stuff for digitized sounds
-//
-uchar *
-SDL_LoadDigiSegment(u16int page)
-{
-	uchar *addr;
-
-#if 0	// for debugging
-asm	mov	dx,STATUS_REGISTER_1
-asm	in	al,dx
-asm	mov	dx,ATR_INDEX
-asm	mov	al,ATR_OVERSCAN
-asm	out	dx,al
-asm	mov	al,10	// bright green
-asm	out	dx,al
-#endif
-
-	addr = PM_GetSoundPage(page);
-	PM_SetPageLock(PMSoundStart + page,pml_Locked);
-
-#if 0	// for debugging
-asm	mov	dx,STATUS_REGISTER_1
-asm	in	al,dx
-asm	mov	dx,ATR_INDEX
-asm	mov	al,ATR_OVERSCAN
-asm	out	dx,al
-asm	mov	al,3	// blue
-asm	out	dx,al
-asm	mov	al,0x20	// normal
-asm	out	dx,al
-#endif
-
-	return(addr);
-}
-
-void
-SDL_PlayDigiSegment(uchar *addr,u16int len)
-{
-	switch (DigiMode)
-	{
-	case sds_PC:
-    	SDL_PCPlaySample(addr,len);
-		break;
-	case sds_SoundSource:
-		SDL_SSPlaySample(addr,len);
-		break;
-	case sds_SoundBlaster:
-		SDL_SBPlaySample(addr,len);
-		break;
-	}
-}
-
-void
-SD_StopDigitized(void)
-{
-	s16int	i;
-
-asm	pushf
-asm	cli
-
-	DigiLeft = 0;
-	DigiNextAddr = nil;
-	DigiNextLen = 0;
-	DigiMissed = false;
-	DigiPlaying = false;
-	DigiNumber = DigiPriority = 0;
-	SoundPositioned = false;
-	if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
-		SDL_SoundFinished();
-
-	switch (DigiMode)
-	{
-	case sds_PC:
-		SDL_PCStopSample();
-		break;
-	case sds_SoundSource:
-		SDL_SSStopSample();
-		break;
-	case sds_SoundBlaster:
-		SDL_SBStopSample();
-		break;
-	}
-
-asm	popf
-
-	for (i = DigiLastStart;i < DigiLastEnd;i++)
-		PM_SetPageLock(i + PMSoundStart,pml_Unlocked);
-	DigiLastStart = 1;
-	DigiLastEnd = 0;
-}
-
-void
-SD_Poll(void)
-{
-	if (DigiLeft && !DigiNextAddr)
-	{
-		DigiNextLen = (DigiLeft >= PMPageSize)? PMPageSize : (DigiLeft % PMPageSize);
-		DigiLeft -= DigiNextLen;
-		if (!DigiLeft)
-			DigiLastSegment = true;
-		DigiNextAddr = SDL_LoadDigiSegment(DigiPage++);
-	}
-	if (DigiMissed && DigiNextAddr)
-	{
-		SDL_PlayDigiSegment(DigiNextAddr,DigiNextLen);
-		DigiNextAddr = nil;
-		DigiMissed = false;
-		if (DigiLastSegment)
-		{
-			DigiPlaying = false;
-			DigiLastSegment = false;
-		}
-	}
-	SDL_SetTimerSpeed();
-}
-
-void
-SD_SetPosition(s16int leftpos,s16int rightpos)
-{
-	if
-	(
-		(leftpos < 0)
-	||	(leftpos > 15)
-	||	(rightpos < 0)
-	||	(rightpos > 15)
-	||	((leftpos == 15) && (rightpos == 15))
-	)
-		Quit("SD_SetPosition: Illegal position");
-
-	switch (DigiMode)
-	{
-	case sds_SoundBlaster:
-		SDL_PositionSBP(leftpos,rightpos);
-		break;
-	}
-}
-
-void
-SD_PlayDigitized(u16int which,s16int leftpos,s16int rightpos)
-{
-	u16int	len;
-	uchar *addr;
-
-	if (!DigiMode)
-		return;
-
-	SD_StopDigitized();
-	if (which >= NumDigi)
-		Quit("SD_PlayDigitized: bad sound number");
-
-	SD_SetPosition(leftpos,rightpos);
-
-	DigiPage = DigiList[(which * 2) + 0];
-	DigiLeft = DigiList[(which * 2) + 1];
-
-	DigiLastStart = DigiPage;
-	DigiLastEnd = DigiPage + ((DigiLeft + (PMPageSize - 1)) / PMPageSize);
-
-	len = (DigiLeft >= PMPageSize)? PMPageSize : (DigiLeft % PMPageSize);
-	addr = SDL_LoadDigiSegment(DigiPage++);
-
-	DigiPlaying = true;
-	DigiLastSegment = false;
-
-	SDL_PlayDigiSegment(addr,len);
-	DigiLeft -= len;
-	if (!DigiLeft)
-		DigiLastSegment = true;
-
-	SD_Poll();
-}
-
-void
-SDL_DigitizedDone(void)
-{
-	if (DigiNextAddr)
-	{
-		SDL_PlayDigiSegment(DigiNextAddr,DigiNextLen);
-		DigiNextAddr = nil;
-		DigiMissed = false;
-	}
-	else
-	{
-		if (DigiLastSegment)
-		{
-			DigiPlaying = false;
-			DigiLastSegment = false;
-			if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
-			{
-				SDL_SoundFinished();
-			}
-			else
-				DigiNumber = DigiPriority = 0;
-			SoundPositioned = false;
-		}
-		else
-			DigiMissed = true;
-	}
-}
-
-void
-SD_SetDigiDevice(SDSMode mode)
-{
-	int	devicenotpresent;
-
-	if (mode == DigiMode)
-		return;
-
-	SD_StopDigitized();
-
-	devicenotpresent = false;
-	switch (mode)
-	{
-	case sds_SoundBlaster:
-		if (!SoundBlasterPresent)
-		{
-			if (SoundSourcePresent)
-				mode = sds_SoundSource;
-			else
-				devicenotpresent = true;
-		}
-		break;
-	case sds_SoundSource:
-		if (!SoundSourcePresent)
-			devicenotpresent = true;
-		break;
-	}
-
-	if (!devicenotpresent)
-	{
-		if (DigiMode == sds_SoundSource)
-			SDL_ShutSS();
-
-		DigiMode = mode;
-
-		if (mode == sds_SoundSource)
-			SDL_StartSS();
-
-		SDL_SetTimerSpeed();
-	}
-}
-
-void
-SDL_SetupDigi(void)
-{
-	uchar *list;
-	u16int	far *p,
-			pg;
-	s16int		i;
-
-	PM_UnlockMainMem();
-	MM_GetPtr(&list,PMPageSize);
-	PM_CheckMainMem();
-	p = (u16int far *)MK_FP(PM_GetPage(ChunksInFile - 1),0);
-	_fmemcpy((void far *)list,(void far *)p,PMPageSize);
-	pg = PMSoundStart;
-	for (i = 0;i < PMPageSize / (sizeof(u16int) * 2);i++,p += 2)
-	{
-		if (pg >= ChunksInFile - 1)
-			break;
-		pg += (p[1] + (PMPageSize - 1)) / PMPageSize;
-	}
-	PM_UnlockMainMem();
-	MM_GetPtr((uchar **)&DigiList,i * sizeof(u16int) * 2);
-	_fmemcpy((void far *)DigiList,(void far *)list,i * sizeof(u16int) * 2);
-	MM_FreePtr(&list);
-	NumDigi = i;
-
-	for (i = 0;i < LASTSOUND;i++)
-		DigiMap[i] = -1;
-}
-
-// 	AdLib Code
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	alOut(n,b) - Puts b in AdLib card register n
-//
-///////////////////////////////////////////////////////////////////////////
-void
-alOut(u8int n,u8int b)
-{
-asm	pushf
-asm	cli
-
-asm	mov	dx,0x388
-asm	mov	al,[n]
-asm	out	dx,al
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	inc	dx
-asm	mov	al,[b]
-asm	out	dx,al
-
-asm	popf
-
-asm	dec	dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-asm	in	al,dx
-}
-
-#if 0
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_SetInstrument() - Puts an instrument into a generator
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_SetInstrument(s16int track,s16int which,Instrument far *inst,int percussive)
-{
-	u8int		c,m;
-
-	if (percussive)
-	{
-		c = pcarriers[which];
-		m = pmodifiers[which];
-	}
-	else
-	{
-		c = carriers[which];
-		m = modifiers[which];
-	}
-
-	tracks[track - 1]->inst = *inst;
-	tracks[track - 1]->percussive = percussive;
-
-	alOut(m + alChar,inst->mChar);
-	alOut(m + alScale,inst->mScale);
-	alOut(m + alAttack,inst->mAttack);
-	alOut(m + alSus,inst->mSus);
-	alOut(m + alWave,inst->mWave);
-
-	// Most percussive instruments only use one cell
-	if (c != 0xff)
-	{
-		alOut(c + alChar,inst->cChar);
-		alOut(c + alScale,inst->cScale);
-		alOut(c + alAttack,inst->cAttack);
-		alOut(c + alSus,inst->cSus);
-		alOut(c + alWave,inst->cWave);
-	}
-
-	alOut(which + alFeedCon,inst->nConn);	// DEBUG - I think this is right
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ALStopSound() - Turns off any sound effects playing through the
-//		AdLib card
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ALStopSound(void)
-{
-asm	pushf
-asm	cli
-
-	(s32int)alSound = 0;
-	alOut(alFreqH + 0,0);
-
-asm	popf
-}
-
-static void
-SDL_AlSetFXInst(Instrument far *inst)
-{
-	u8int		c,m;
-
-	m = modifiers[0];
-	c = carriers[0];
-	alOut(m + alChar,inst->mChar);
-	alOut(m + alScale,inst->mScale);
-	alOut(m + alAttack,inst->mAttack);
-	alOut(m + alSus,inst->mSus);
-	alOut(m + alWave,inst->mWave);
-	alOut(c + alChar,inst->cChar);
-	alOut(c + alScale,inst->cScale);
-	alOut(c + alAttack,inst->cAttack);
-	alOut(c + alSus,inst->cSus);
-	alOut(c + alWave,inst->cWave);
-
-	// Note: Switch commenting on these lines for old MUSE compatibility
-//	alOut(alFeedCon,inst->nConn);
-	alOut(alFeedCon,0);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ALPlaySound() - Plays the specified sound on the AdLib card
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ALPlaySound(AdLibSound far *sound)
-{
-	Instrument	far *inst;
-	u8int		huge *data;
-
-	SDL_ALStopSound();
-
-asm	pushf
-asm	cli
-
-	alLengthLeft = sound->common.length;
-	data = sound->data;
-	data++;
-	data--;
-	alSound = (u8int far *)data;
-	alBlock = ((sound->block & 7) << 2) | 0x20;
-	inst = &sound->inst;
-
-	if (!(inst->mSus | inst->cSus))
-	{
-	asm	popf
-		Quit("SDL_ALPlaySound() - Bad instrument");
-	}
-
-	SDL_AlSetFXInst(&alZeroInst);	// DEBUG
-	SDL_AlSetFXInst(inst);
-
-asm	popf
-}
-
-#if 0
-///////////////////////////////////////////////////////////////////////////
-//
-// 	SDL_ALSoundService() - Plays the next sample out through the AdLib card
-//
-///////////////////////////////////////////////////////////////////////////
-//static void
-void
-SDL_ALSoundService(void)
-{
-	u8int	s;
-
-	if (alSound)
-	{
-		s = *alSound++;
-		if (!s)
-			alOut(alFreqH + 0,0);
-		else
-		{
-			alOut(alFreqL + 0,s);
-			alOut(alFreqH + 0,alBlock);
-		}
-
-		if (!(--alLengthLeft))
-		{
-			(s32int)alSound = 0;
-			alOut(alFreqH + 0,0);
-			SDL_SoundFinished();
-		}
-	}
-}
-#endif
-
-#if 0
-void
-SDL_ALService(void)
-{
-	u8int	a,v;
-	u16int	w;
-
-	if (!sqActive)
-		return;
-
-	while (sqHackLen && (sqHackTime <= alTimeCount))
-	{
-		w = *sqHackPtr++;
-		sqHackTime = alTimeCount + *sqHackPtr++;
-	asm	mov	dx,[w]
-	asm	mov	[a],dl
-	asm	mov	[v],dh
-		alOut(a,v);
-		sqHackLen -= 4;
-	}
-	alTimeCount++;
-	if (!sqHackLen)
-	{
-		sqHackPtr = (u16int far *)sqHack;
-		sqHackLen = sqHackSeqLen;
-		alTimeCount = sqHackTime = 0;
-	}
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ShutAL() - Shuts down the AdLib card for sound effects
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_ShutAL(void)
-{
-asm	pushf
-asm	cli
-
-	alOut(alEffects,0);
-	alOut(alFreqH + 0,0);
-	SDL_AlSetFXInst(&alZeroInst);
-	alSound = 0;
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_CleanAL() - Totally shuts down the AdLib card
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_CleanAL(void)
-{
-	s16int	i;
-
-asm	pushf
-asm	cli
-
-	alOut(alEffects,0);
-	for (i = 1;i < 0xf5;i++)
-		alOut(i,0);
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_StartAL() - Starts up the AdLib card for sound effects
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_StartAL(void)
-{
-	alFXReg = 0;
-	alOut(alEffects,alFXReg);
-	SDL_AlSetFXInst(&alZeroInst);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster
-//		emulating an AdLib) present
-//
-///////////////////////////////////////////////////////////////////////////
-static int
-SDL_DetectAdLib(void)
-{
-	u8int	status1,status2;
-	s16int		i;
-
-	alOut(4,0x60);	// Reset T1 & T2
-	alOut(4,0x80);	// Reset IRQ
-	status1 = readstat();
-	alOut(2,0xff);	// Set timer 1
-	alOut(4,0x21);	// Start timer 1
-#if 0
-	SDL_Delay(TimerDelay100);
-#else
-asm	mov	dx,0x388
-asm	mov	cx,100
-usecloop:
-asm	in	al,dx
-asm	loop usecloop
-#endif
-
-	status2 = readstat();
-	alOut(4,0x60);
-	alOut(4,0x80);
-
-	if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0))
-	{
-		for (i = 1;i <= 0xf5;i++)	// Zero all the registers
-			alOut(i,0);
-
-		alOut(1,0x20);	// Set WSE=1
-		alOut(8,0);		// Set CSM=0 & SEL=0
-
-		return(true);
-	}
-	else
-		return(false);
-}
-
-#if 0
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_t0Service() - My timer 0 ISR which handles the different timings and
-//		dispatches to whatever other routines are appropriate
-//
-///////////////////////////////////////////////////////////////////////////
-static void interrupt
-SDL_t0Service(void)
-{
-static	u16int	count = 1;
-
-#if 1	// for debugging
-asm	mov	dx,STATUS_REGISTER_1
-asm	in	al,dx
-asm	mov	dx,ATR_INDEX
-asm	mov	al,ATR_OVERSCAN
-asm	out	dx,al
-asm	mov	al,4	// red
-asm	out	dx,al
-#endif
-
-	HackCount++;
-
-	if ((MusicMode == smm_AdLib) || (DigiMode == sds_SoundSource))
-	{
-		SDL_ALService();
-		SDL_SSService();
-//		if (!(++count & 7))
-		if (!(++count % 10))
-		{
-			LocalTime++;
-			TimeCount++;
-			if (SoundUserHook)
-				SoundUserHook();
-		}
-//		if (!(count & 3))
-		if (!(count % 5))
-		{
-			switch (SoundMode)
-			{
-			case sdm_PC:
-				SDL_PCService();
-				break;
-			case sdm_AdLib:
-				SDL_ALSoundService();
-				break;
-			}
-		}
-	}
-	else
-	{
-		if (!(++count & 1))
-		{
-			LocalTime++;
-			TimeCount++;
-			if (SoundUserHook)
-				SoundUserHook();
-		}
-		switch (SoundMode)
-		{
-		case sdm_PC:
-			SDL_PCService();
-			break;
-		case sdm_AdLib:
-			SDL_ALSoundService();
-			break;
-		}
-	}
-
-asm	mov	ax,[WORD PTR TimerCount]
-asm	add	ax,[WORD PTR TimerDivisor]
-asm	mov	[WORD PTR TimerCount],ax
-asm	jnc	myack
-	t0OldService();			// If we overflow a u16int, time to call old long handler
-asm	jmp	olddone
-myack:;
-	outportb(0x20,0x20);	// Ack the interrupt
-olddone:;
-
-#if 1	// for debugging
-asm	mov	dx,STATUS_REGISTER_1
-asm	in	al,dx
-asm	mov	dx,ATR_INDEX
-asm	mov	al,ATR_OVERSCAN
-asm	out	dx,al
-asm	mov	al,3	// blue
-asm	out	dx,al
-asm	mov	al,0x20	// normal
-asm	out	dx,al
-#endif
-}
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-//
-//	SDL_ShutDevice() - turns off whatever device was being used for sound fx
-//
-////////////////////////////////////////////////////////////////////////////
-static void
-SDL_ShutDevice(void)
-{
-	switch (SoundMode)
-	{
-	case sdm_PC:
-		SDL_ShutPC();
-		break;
-	case sdm_AdLib:
-		SDL_ShutAL();
-		break;
-	}
-	SoundMode = sdm_Off;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_CleanDevice() - totally shuts down all sound devices
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_CleanDevice(void)
-{
-	if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib))
-		SDL_CleanAL();
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SDL_StartDevice() - turns on whatever device is to be used for sound fx
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-SDL_StartDevice(void)
-{
-	switch (SoundMode)
-	{
-	case sdm_AdLib:
-		SDL_StartAL();
-		break;
-	}
-	SoundNumber = SoundPriority = 0;
-}
-
-//	Public routines
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_SetSoundMode() - Sets which sound hardware to use for sound effects
-//
-///////////////////////////////////////////////////////////////////////////
-int
-SD_SetSoundMode(SDMode mode)
-{
-	int	result = false;
-	u16int	tableoffset;
-
-	SD_StopSound();
-
-	if ((mode == sdm_AdLib) && !AdLibPresent)
-		mode = sdm_PC;
-
-	switch (mode)
-	{
-	case sdm_Off:
-		NeedsDigitized = false;
-		result = true;
-		break;
-	case sdm_PC:
-		tableoffset = STARTPCSOUNDS;
-		NeedsDigitized = false;
-		result = true;
-		break;
-	case sdm_AdLib:
-		if (AdLibPresent)
-		{
-			tableoffset = STARTADLIBSOUNDS;
-			NeedsDigitized = false;
-			result = true;
-		}
-		break;
-	}
-
-	if (result && (mode != SoundMode))
-	{
-		SDL_ShutDevice();
-		SoundMode = mode;
-		SoundTable = (u16int *)(&audiosegs[tableoffset]);
-		SDL_StartDevice();
-	}
-
-	SDL_SetTimerSpeed();
-
-	return(result);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_SetMusicMode() - sets the device to use for background music
-//
-///////////////////////////////////////////////////////////////////////////
-int
-SD_SetMusicMode(SMMode mode)
-{
-	int	result = false;
-
-	SD_FadeOutMusic();
-	while (SD_MusicPlaying())
-		;
-
-	switch (mode)
-	{
-	case smm_Off:
-		NeedsMusic = false;
-		result = true;
-		break;
-	case smm_AdLib:
-		if (AdLibPresent)
-		{
-			NeedsMusic = true;
-			result = true;
-		}
-		break;
-	}
-
-	if (result)
-		MusicMode = mode;
-
-	SDL_SetTimerSpeed();
-
-	return(result);
-}
-
-void
-SD_Startup(void)
-{
-	s16int	i;
-
-	if (SD_Started)
-		return;
-
-	SDL_SetDS();
-
-	SoundUserHook = 0;
-
-	t0OldService = getvect(8);	// Get old timer 0 ISR
-
-	LocalTime = TimeCount = alTimeCount = 0;
-
-	SD_SetSoundMode(sdm_Off);
-	SD_SetMusicMode(smm_Off);
-
-	/* → detect and select sound card */
-
-	SoundSourcePresent = true;
-
-	if (1)
-	{
-		AdLibPresent = SDL_DetectAdLib();
-		if (AdLibPresent)
-		{
-			s16int port = -1;
-			char *env = getenv("BLASTER");
-			if (env)
-			{
-				s32int temp;
-				while (*env)
-				{
-					while (isspace(*env))
-						env++;
-
-					switch (toupper(*env))
-					{
-					case 'A':
-						temp = strtol(env + 1,&env,16);
-						if
-						(
-							(temp >= 0x210)
-						&&	(temp <= 0x260)
-						&&	(!(temp & 0x00f))
-						)
-							port = (temp - 0x200) >> 4;
-						else
-							Quit("SD_Startup: Unsupported address value in BLASTER");
-						break;
-					case 'I':
-						temp = strtol(env + 1,&env,10);
-						if
-						(
-							(temp >= 0)
-						&&	(temp <= 10)
-						&&	(sbIntVectors[temp] != -1)
-						)
-						{
-							sbInterrupt = temp;
-							sbIntVec = sbIntVectors[sbInterrupt];
-						}
-						else
-							Quit("SD_Startup: Unsupported interrupt value in BLASTER");
-						break;
-					case 'D':
-						temp = strtol(env + 1,&env,10);
-						if ((temp == 0) || (temp == 1) || (temp == 3))
-							SDL_SBSetDMA(temp);
-						else
-							Quit("SD_Startup: Unsupported DMA value in BLASTER");
-						break;
-					default:
-						while (isspace(*env))
-							env++;
-						while (*env && !isspace(*env))
-							env++;
-						break;
-					}
-				}
-			}
-			SoundBlasterPresent = SDL_DetectSoundBlaster(port);
-		}
-	}
-
-	for (i = 0;i < 255;i++)
-		pcSoundLookup[i] = i * 60;
-
-	if (SoundBlasterPresent)
-		SDL_StartSB();
-
-	SDL_SetupDigi();
-
-	SD_Started = true;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_Default() - Sets up the default behaviour for the Sound Mgr whether
-//		the config file was present or not.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_Default(int gotit,SDMode sd,SMMode sm)
-{
-	int	gotsd,gotsm;
-
-	gotsd = gotsm = gotit;
-
-	if (gotsd)	// Make sure requested sound hardware is available
-	{
-		switch (sd)
-		{
-		case sdm_AdLib:
-			gotsd = AdLibPresent;
-			break;
-		}
-	}
-	if (!gotsd)
-	{
-		if (AdLibPresent)
-			sd = sdm_AdLib;
-		else
-			sd = sdm_PC;
-	}
-	if (sd != SoundMode)
-		SD_SetSoundMode(sd);
-
-
-	if (gotsm)	// Make sure requested music hardware is available
-	{
-		switch (sm)
-		{
-		case sdm_AdLib:
-			gotsm = AdLibPresent;
-			break;
-		}
-	}
-	if (!gotsm)
-	{
-		if (AdLibPresent)
-			sm = smm_AdLib;
-	}
-	if (sm != MusicMode)
-		SD_SetMusicMode(sm);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_Shutdown() - shuts down the Sound Mgr
-//		Removes sound ISR and turns off whatever sound hardware was active
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_Shutdown(void)
-{
-	if (!SD_Started)
-		return;
-
-	SD_MusicOff();
-	SD_StopSound();
-	SDL_ShutDevice();
-	SDL_CleanDevice();
-
-	if (SoundBlasterPresent)
-		SDL_ShutSB();
-
-	if (SoundSourcePresent)
-		SDL_ShutSS();
-
-	asm	pushf
-	asm	cli
-
-	SDL_SetTimer0(0);
-
-	setvect(8,t0OldService);
-
-	asm	popf
-
-	SD_Started = false;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th
-//		of a second from its timer 0 ISR
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_SetUserHook(void (* hook)(void))
-{
-	SoundUserHook = hook;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_PositionSound() - Sets up a stereo imaging location for the next
-//		sound to be played. Each channel ranges from 0 to 15.
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_PositionSound(s16int leftvol,s16int rightvol)
-{
-	LeftPosition = leftvol;
-	RightPosition = rightvol;
-	nextsoundpos = true;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_PlaySound() - plays the specified sound on the appropriate hardware
-//
-///////////////////////////////////////////////////////////////////////////
-int
-SD_PlaySound(soundnames sound)
-{
-	int		ispos;
-	SoundCommon	far *s;
-	s16int	lp,rp;
-
-	lp = LeftPosition;
-	rp = RightPosition;
-	LeftPosition = 0;
-	RightPosition = 0;
-
-	ispos = nextsoundpos;
-	nextsoundpos = false;
-
-	if (sound == -1)
-		return(false);
-
-	s = MK_FP(SoundTable[sound],0);
-	if ((SoundMode != sdm_Off) && !s)
-		Quit("SD_PlaySound() - Uncached sound");
-
-	if ((DigiMode != sds_Off) && (DigiMap[sound] != -1))
-	{
-		if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
-		{
-			if (s->priority < SoundPriority)
-				return(false);
-
-			SDL_PCStopSound();
-
-			SD_PlayDigitized(DigiMap[sound],lp,rp);
-			SoundPositioned = ispos;
-			SoundNumber = sound;
-			SoundPriority = s->priority;
-		}
-		else
-		{
-		asm	pushf
-		asm	cli
-			if (DigiPriority && !DigiNumber)
-			{
-			asm	popf
-				Quit("SD_PlaySound: Priority without a sound");
-			}
-		asm	popf
-
-			if (s->priority < DigiPriority)
-				return(false);
-
-			SD_PlayDigitized(DigiMap[sound],lp,rp);
-			SoundPositioned = ispos;
-			DigiNumber = sound;
-			DigiPriority = s->priority;
-		}
-
-		return(true);
-	}
-
-	if (SoundMode == sdm_Off)
-		return(false);
-	if (!s->length)
-		Quit("SD_PlaySound() - Zero length sound");
-	if (s->priority < SoundPriority)
-		return(false);
-
-	switch (SoundMode)
-	{
-	case sdm_PC:
-		SDL_PCPlaySound((void far *)s);
-		break;
-	case sdm_AdLib:
-		SDL_ALPlaySound((void far *)s);
-		break;
-	}
-
-	SoundNumber = sound;
-	SoundPriority = s->priority;
-
-	return(false);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_SoundPlaying() - returns the sound number that's playing, or 0 if
-//		no sound is playing
-//
-///////////////////////////////////////////////////////////////////////////
-u16int
-SD_SoundPlaying(void)
-{
-	int	result = false;
-
-	switch (SoundMode)
-	{
-	case sdm_PC:
-		result = pcSound? true : false;
-		break;
-	case sdm_AdLib:
-		result = alSound? true : false;
-		break;
-	}
-
-	if (result)
-		return(SoundNumber);
-	else
-		return(false);
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_StopSound() - if a sound is playing, stops it
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_StopSound(void)
-{
-	if (DigiPlaying)
-		SD_StopDigitized();
-
-	switch (SoundMode)
-	{
-	case sdm_PC:
-		SDL_PCStopSound();
-		break;
-	case sdm_AdLib:
-		SDL_ALStopSound();
-		break;
-	}
-
-	SoundPositioned = false;
-
-	SDL_SoundFinished();
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_WaitSoundDone() - waits until the current sound is done playing
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_WaitSoundDone(void)
-{
-	while (SD_SoundPlaying())
-		;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_MusicOn() - turns on the sequencer
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_MusicOn(void)
-{
-	sqActive = true;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_MusicOff() - turns off the sequencer and any playing notes
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_MusicOff(void)
-{
-	u16int	i;
-
-
-	switch (MusicMode)
-	{
-	case smm_AdLib:
-		alFXReg = 0;
-		alOut(alEffects,0);
-		for (i = 0;i < sqMaxTracks;i++)
-			alOut(alFreqH + i + 1,0);
-		break;
-	}
-	sqActive = false;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_StartMusic() - starts playing the music pointed to
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_StartMusic(MusicGroup far *music)
-{
-	SD_MusicOff();
-asm	pushf
-asm	cli
-
-	if (MusicMode == smm_AdLib)
-	{
-		sqHackPtr = sqHack = music->values;
-		sqHackSeqLen = sqHackLen = music->length;
-		sqHackTime = 0;
-		alTimeCount = 0;
-		SD_MusicOn();
-	}
-
-asm	popf
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()
-//		to see if the fadeout is complete
-//
-///////////////////////////////////////////////////////////////////////////
-void
-SD_FadeOutMusic(void)
-{
-	switch (MusicMode)
-	{
-	case smm_AdLib:
-		// DEBUG - quick hack to turn the music off
-		SD_MusicOff();
-		break;
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	SD_MusicPlaying() - returns true if music is currently playing, false if
-//		not
-//
-///////////////////////////////////////////////////////////////////////////
-int
-SD_MusicPlaying(void)
-{
-	int	result;
-
-	switch (MusicMode)
-	{
-	case smm_AdLib:
-		result = false;
-		// DEBUG - not written
-		break;
-	default:
-		result = false;
-	}
-
-	return(result);
-}
--- a/sd.h
+++ /dev/null
@@ -1,213 +1,0 @@
-void	alOut(u8int n,u8int b);
-
-#define	TickBase	70		// 70Hz per tick - used as a base for timer 0
-
-typedef	enum	{
-					sdm_Off,
-					sdm_PC,sdm_AdLib,
-				}	SDMode;
-typedef	enum	{
-					smm_Off,smm_AdLib
-				}	SMMode;
-typedef	enum	{
-					sds_Off,sds_PC,sds_SoundSource,sds_SoundBlaster
-				}	SDSMode;
-typedef	struct
-		{
-			u32int	length;
-			u16int		priority;
-		} SoundCommon;
-
-//	PC Sound stuff
-#define	pcTimer		0x42
-#define	pcTAccess	0x43
-#define	pcSpeaker	0x61
-
-#define	pcSpkBits	3
-
-typedef	struct
-		{
-			SoundCommon	common;
-			u8int		data[1];
-		} PCSound;
-
-// 	Registers for the Sound Blaster card - needs to be offset by n0 (0x10,0x20,0x30,0x40,0x50,0x60)
-#define	sbReset		0x206	// W
-#define	sbFMStatus	0x208	// R
-#define	sbFMAddr	0x208	// W
-#define	sbFMData	0x209	// W
-#define	sbReadData	0x20a	// R
-#define	sbWriteCmd	0x20c	// W
-#define	sbWriteData	0x20c	// W
-#define	sbWriteStat	0x20c	// R
-#define	sbDataAvail	0x20e	// R
-
-//	Registers for the Sound Blaster Pro card - needs to be offset by n0 (0x20 or 0x40)
-#define	sbpLFMStatus	0x200	// R
-#define	sbpLFMAddr		0x200	// W
-#define	sbpLFMData		0x201	// W
-#define	sbpRFMStatus	0x202	// R
-#define	sbpRFMAddr		0x202	// W
-#define	sbpRFMData		0x203	// W
-#define	sbpMixerAddr	0x204	// W
-#define	sbpMixerData	0x205	// RW
-#define	sbpCDData		0x210	// R
-#define	sbpCDCommand	0x210	// W
-#define	sbpCDStatus		0x211	// R
-#define	sbpCDReset		0x212	// W
-
-//	SBPro Mixer addresses
-#define	sbpmReset		0x00
-#define	sbpmVoiceVol	0x04
-#define	sbpmMicMix		0x0a
-#define	sbpmFilterADC	0x0c
-#define	sbpmControl		0x0e
-#define	sbpmMasterVol	0x22
-#define	sbpmFMVol		0x26
-#define	sbpmCDVol		0x28
-#define	sbpmLineVol		0x2e
-
-typedef	struct
-		{
-			SoundCommon	common;
-			u16int		hertz;
-			u8int		bits,
-						reference,
-						data[1];
-		} SampledSound;
-
-// 	Registers for the AdLib card
-#define	alFMStatus	0x388	// R
-#define	alFMAddr	0x388	// W
-#define	alFMData	0x389	// W
-
-//	Register addresses
-// Operator stuff
-#define	alChar		0x20
-#define	alScale		0x40
-#define	alAttack	0x60
-#define	alSus		0x80
-#define	alWave		0xe0
-// Channel stuff
-#define	alFreqL		0xa0
-#define	alFreqH		0xb0
-#define	alFeedCon	0xc0
-// Global stuff
-#define	alEffects	0xbd
-
-typedef	struct
-		{
-			u8int	mChar,cChar,
-					mScale,cScale,
-					mAttack,cAttack,
-					mSus,cSus,
-					mWave,cWave,
-					nConn,
-
-					// These are only for Muse - these bytes are really unused
-					voice,
-					mode,
-					unused[3];
-		} Instrument;
-
-typedef	struct
-		{
-			SoundCommon	common;
-			Instrument	inst;
-			u8int		block,
-						data[1];
-		} AdLibSound;
-
-//
-//	Sequencing stuff
-//
-#define	sqMaxTracks	10
-#define	sqMaxMoods	1	// DEBUG
-
-#define	sev_Null		0	// Does nothing
-#define	sev_NoteOff		1	// Turns a note off
-#define	sev_NoteOn		2	// Turns a note on
-#define	sev_NotePitch	3	// Sets the pitch of a currently playing note
-#define	sev_NewInst		4	// Installs a new instrument
-#define	sev_NewPerc		5	// Installs a new percussive instrument
-#define	sev_PercOn		6	// Turns a percussive note on
-#define	sev_PercOff		7	// Turns a percussive note off
-#define	sev_SeqEnd		-1	// Terminates a sequence
-
-// 	Flags for MusicGroup.flags
-#define	sf_Melodic		0
-#define	sf_Percussive	1
-
-#if 1
-typedef	struct
-		{
-			u16int	length,
-					values[1];
-		} MusicGroup;
-#else
-typedef	struct
-		{
-			u16int	flags,
-					count,
-					offsets[1];
-		} MusicGroup;
-#endif
-
-typedef	struct
-		{
-			/* This part needs to be set up by the user */
-			u16int        mood,far *moods[sqMaxMoods];
-
-			/* The rest is set up by the code */
-			Instrument	inst;
-			int		percussive;
-			u16int		far *seq;
-			u32int	nextevent;
-		} ActiveTrack;
-
-#define	sqmode_Normal		0
-#define	sqmode_FadeIn		1
-#define	sqmode_FadeOut		2
-
-#define	sqMaxFade		64	// DEBUG
-
-
-// Global variables
-extern	int		AdLibPresent,
-					SoundSourcePresent,
-					SoundBlasterPresent,
-					NeedsMusic,					// For Caching Mgr
-					SoundPositioned;
-extern	SDMode		SoundMode;
-extern	SDSMode		DigiMode;
-extern	SMMode		MusicMode;
-extern	int		DigiPlaying;
-extern	int			DigiMap[];
-extern	u32int	TimeCount;					// Global time in ticks
-
-// Function prototypes
-extern	void	SD_Startup(void),
-				SD_Shutdown(void),
-				SD_Default(int gotit,SDMode sd,SMMode sm),
-
-				SD_PositionSound(int leftvol,int rightvol);
-extern	int	SD_PlaySound(soundnames sound);
-extern	void	SD_SetPosition(int leftvol,int rightvol),
-				SD_StopSound(void),
-				SD_WaitSoundDone(void),
-
-				SD_StartMusic(MusicGroup far *music),
-				SD_MusicOn(void),
-				SD_MusicOff(void),
-				SD_FadeOutMusic(void),
-
-				SD_SetUserHook(void (*hook)(void));
-extern	int	SD_MusicPlaying(void),
-				SD_SetSoundMode(SDMode mode),
-				SD_SetMusicMode(SMMode mode);
-extern	u16int	SD_SoundPlaying(void);
-
-extern	void	SD_SetDigiDevice(SDSMode),
-				SD_PlayDigitized(u16int which,int leftpos,int rightpos),
-				SD_StopDigitized(void),
-				SD_Poll(void);
--- a/snd.c
+++ b/snd.c
@@ -4,29 +4,430 @@
 #include "dat.h"
 #include "fns.h"
 
-Dat *imfs, *imfe;
-Sfx *sfxs, *sfxe;
+Dat *imfs;
+Sfx *sfxs;
+int sfxon, muson, pcmon;
 
-static int mi;
+enum{
+	Rate = 44100,
+	PcmHz = 7000,
+	ImfHz = 700,
+	SfxHz = 140,
+	Nsamp = Rate/ImfHz * 4,
+	Mdiv = ImfHz / Tb,
+	Pdiv = PcmHz / Tb,
+	Nbuf = Nsamp * Mdiv,
 
+	Rwse = 0x01,
+	Rnum = 0xa0,
+	Roct = 0xb0,
+	Ropm = 0xbd,
+	Rfed = 0xc0
+};
+/* channel 0, op 0-1 registers */
+static uchar inst[] = {0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xe0, 0xe3};
+static uchar *sfxp, *sfxe, *pcm, *pcme, *imf;
+static Sfx *sfxd, *pcmd;
+static Dat *imfd;
+static vlong stc, sdt, mdt;
+static int sfd = -1;
+static uchar sbuf[Nbuf];
+
+static int h[] = {	/* stolen property coefficients */
+	32767, 32766, 32764, 32760, 32755, 32749, 32741, 32731, 32721, 32708,
+	32695, 32679, 32663, 32645, 32625, 32604, 32582, 32558, 32533, 32506,
+	32478, 32448, 32417, 32385, 32351, 32316, 32279, 32241, 32202, 32161,
+	32119, 32075, 32030, 31984, 31936, 31887, 31836, 31784, 31731, 31676,
+	31620, 31563, 31504, 31444, 31383, 31320, 31256, 31191, 31124, 31056,
+	30987, 30916, 30845, 30771, 30697, 30621, 30544, 30466, 30387, 30306,
+	30224, 30141, 30057, 29971, 29884, 29796, 29707, 29617, 29525, 29433,
+	29339, 29244, 29148, 29050, 28952, 28852, 28752, 28650, 28547, 28443,
+	28338, 28232, 28125, 28017, 27908, 27797, 27686, 27574, 27461, 27346,
+	27231, 27115, 26998, 26879, 26760, 26640, 26519, 26398, 26275, 26151,
+	26027, 25901, 25775, 25648, 25520, 25391, 25262, 25131, 25000, 24868,
+	24735, 24602, 24467, 24332, 24197, 24060, 23923, 23785, 23647, 23507,
+	23368, 23227, 23086, 22944, 22802, 22659, 22515, 22371, 22226, 22081,
+	21935, 21789, 21642, 21494, 21346, 21198, 21049, 20900, 20750, 20600,
+	20449, 20298, 20146, 19995, 19842, 19690, 19537, 19383, 19230, 19076,
+	18922, 18767, 18612, 18457, 18302, 18146, 17990, 17834, 17678, 17521,
+	17365, 17208, 17051, 16894, 16737, 16579, 16422, 16264, 16106, 15949,
+	15791, 15633, 15475, 15317, 15159, 15001, 14843, 14685, 14527, 14369,
+	14212, 14054, 13896, 13739, 13581, 13424, 13266, 13109, 12952, 12795,
+	12639, 12482, 12326, 12170, 12014, 11858, 11703, 11548, 11393, 11238,
+	11084, 10929, 10776, 10622, 10469, 10316, 10164, 10011, 9860, 9708,
+	9557, 9407, 9256, 9106, 8957, 8808, 8659, 8511, 8364, 8216, 8070,
+	7924, 7778, 7633, 7488, 7344, 7200, 7057, 6914, 6773, 6631, 6490,
+	6350, 6210, 6071, 5933, 5795, 5658, 5521, 5385, 5250, 5115, 4981,
+	4848, 4716, 4584, 4452, 4322, 4192, 4063, 3935, 3807, 3680, 3554,
+	3429, 3304, 3180, 3057, 2935, 2813, 2692, 2572, 2453, 2335, 2217,
+	2101, 1985, 1870, 1755, 1642, 1529, 1418, 1307, 1197, 1088, 979, 872,
+	765, 660, 555, 451, 348, 246, 145, 44, -54, -153, -250, -347, -443,
+	-537, -631, -724, -816, -908, -998, -1087, -1175, -1263, -1349, -1435,
+	-1519, -1603, -1685, -1767, -1848, -1928, -2006, -2084, -2161, -2237,
+	-2312, -2386, -2459, -2531, -2603, -2673, -2742, -2810, -2878, -2944,
+	-3009, -3074, -3137, -3200, -3261, -3322, -3381, -3440, -3498, -3554,
+	-3610, -3665, -3719, -3772, -3824, -3875, -3925, -3974, -4022, -4069,
+	-4116, -4161, -4205, -4249, -4291, -4333, -4374, -4413, -4452, -4490,
+	-4527, -4563, -4599, -4633, -4666, -4699, -4730, -4761, -4791, -4820,
+	-4848, -4875, -4901, -4926, -4951, -4974, -4997, -5019, -5040, -5060,
+	-5080, -5098, -5116, -5133, -5149, -5164, -5178, -5192, -5205, -5217,
+	-5228, -5238, -5248, -5257, -5265, -5272, -5278, -5284, -5289, -5293,
+	-5297, -5299, -5301, -5303, -5303, -5303, -5302, -5300, -5298, -5295,
+	-5291, -5287, -5282, -5276, -5270, -5263, -5255, -5246, -5237, -5228,
+	-5217, -5206, -5195, -5183, -5170, -5157, -5143, -5128, -5113, -5097,
+	-5081, -5064, -5047, -5029, -5010, -4991, -4972, -4952, -4931, -4910,
+	-4889, -4867, -4844, -4821, -4797, -4774, -4749, -4724, -4699, -4673,
+	-4647, -4620, -4593, -4566, -4538, -4510, -4481, -4452, -4422, -4393,
+	-4363, -4332, -4301, -4270, -4238, -4206, -4174, -4142, -4109, -4076,
+	-4042, -4009, -3975, -3940, -3906, -3871, -3836, -3801, -3765, -3729,
+	-3693, -3657, -3620, -3584, -3547, -3510, -3472, -3435, -3397, -3360,
+	-3322, -3283, -3245, -3207, -3168, -3129, -3091, -3052, -3013, -2973,
+	-2934, -2895, -2855, -2816, -2776, -2736, -2697, -2657, -2617, -2577,
+	-2537, -2497, -2457, -2417, -2377, -2337, -2297, -2256, -2216, -2176,
+	-2136, -2096, -2056, -2016, -1976, -1936, -1896, -1856, -1817, -1777,
+	-1737, -1698, -1658, -1619, -1579, -1540, -1501, -1462, -1423, -1384,
+	-1345, -1306, -1268, -1230, -1191, -1153, -1115, -1077, -1040, -1002,
+	-965, -927, -890, -854, -817, -780, -744, -708, -672, -636, -600,
+	-565, -530, -494, -460, -425, -391, -356, -322, -289, -255, -222,
+	-189, -156, -123, -91, -59, -27, 4, 35, 66, 97, 127, 158, 188, 218,
+	247, 277, 306, 334, 363, 391, 419, 447, 474, 501, 528, 554, 581, 606,
+	632, 657, 683, 707, 732, 756, 780, 803, 827, 850, 872, 895, 917, 939,
+	960, 981, 1002, 1023, 1043, 1063, 1082, 1102, 1121, 1139, 1158, 1176,
+	1194, 1211, 1228, 1245, 1262, 1278, 1294, 1309, 1325, 1340, 1354,
+	1369, 1383, 1397, 1410, 1423, 1436, 1448, 1461, 1473, 1484, 1496,
+	1507, 1517, 1528, 1538, 1548, 1557, 1566, 1575, 1584, 1592, 1600,
+	1608, 1616, 1623, 1630, 1636, 1643, 1649, 1654, 1660, 1665, 1670,
+	1675, 1679, 1683, 1687, 1690, 1694, 1697, 1700, 1702, 1704, 1706,
+	1708, 1709, 1711, 1712, 1712, 1713, 1713, 1713, 1713, 1712, 1711,
+	1710, 1709, 1708, 1706, 1704, 1702, 1700, 1697, 1694, 1691, 1688,
+	1685, 1681, 1677, 1673, 1669, 1664, 1660, 1655, 1650, 1644, 1639,
+	1633, 1627, 1621, 1615, 1609, 1602, 1596, 1589, 1582, 1575, 1567,
+	1560, 1552, 1544, 1536, 1528, 1520, 1511, 1503, 1494, 1485, 1476,
+	1467, 1458, 1448, 1439, 1429, 1419, 1409, 1399, 1389, 1379, 1368,
+	1358, 1347, 1337, 1326, 1315, 1304, 1293, 1282, 1271, 1260, 1248,
+	1237, 1225, 1213, 1202, 1190, 1178, 1166, 1154, 1142, 1130, 1118,
+	1106, 1094, 1081, 1069, 1057, 1044, 1032, 1019, 1007, 994, 981, 969,
+	956, 943, 931, 918, 905, 892, 879, 867, 854, 841, 828, 815, 802, 790,
+	777, 764, 751, 738, 725, 713, 700, 687, 674, 662, 649, 636, 623, 611,
+	598, 585, 573, 560, 548, 535, 523, 510, 498, 486, 473, 461, 449, 437,
+	425, 413, 401, 389, 377, 365, 353, 341, 330, 318, 307, 295, 284, 272,
+	261, 250, 239, 228, 217, 206, 195, 184, 173, 163, 152, 141, 131, 121,
+	110, 100, 90, 80, 70, 60, 51, 41, 31, 22, 12, 3, -5, -14, -23, -32,
+	-41, -50, -59, -67, -76, -84, -93, -101, -109, -117, -125, -133, -140,
+	-148, -156, -163, -170, -178, -185, -192, -199, -206, -212, -219,
+	-226, -232, -239, -245, -251, -257, -263, -269, -275, -280, -286,
+	-291, -297, -302, -307, -312, -317, -322, -327, -332, -336, -341,
+	-345, -349, -354, -358, -362, -366, -369, -373, -377, -380, -384,
+	-387, -390, -394, -397, -400, -402, -405, -408, -411, -413, -416,
+	-418, -420, -422, -424, -426, -428, -430, -432, -433, -435, -436,
+	-438, -439, -440, -442, -443, -444, -445, -445, -446, -447, -447,
+	-448, -448, -449, -449, -449, -449, -449, -449, -449, -449, -449,
+	-449, -449, -448, -448, -447, -447, -446, -445, -444, -443, -443,
+	-442, -441, -440, -438, -437, -436, -435, -433, -432, -430, -429,
+	-427, -426, -424, -422, -420, -419, -417, -415, -413, -411, -409,
+	-407, -405, -403, -400, -398, -396, -393, -391, -389, -386, -384,
+	-381, -379, -376, -374, -371, -368, -366, -363, -360, -357, -355,
+	-352, -349, -346, -343, -340, -337, -334, -331, -328, -325, -322,
+	-319, -316, -313, -310, -307, -304, -301, -298, -294, -291, -288,
+	-285, -282, -278, -275, -272, -269, -265, -262, -259, -256, -252,
+	-249, -246, -243, -239, -236, -233, -230, -226, -223, -220, -217,
+	-213, -210, -207, -204, -200, -197, -194, -191, -187, -184, -181,
+	-178, -175, -172, -168, -165, -162, -159, -156, -153, -150, -147,
+	-143, -140, -137, -134, -131, -128, -125, -122, -120, -117, -114,
+	-111, -108, -105, -102, -99, -97, -94, -91, -88, -86, -83, -80, -78,
+	-75, -72, -70, -67, -65, -62, -59, -57, -55, -52, -50, -47, -45, -43,
+	-40, -38, -36, -33, -31, -29, -27, -25, -22, -20, -18, -16, -14, -12,
+	-10, -8, -6, -4, -2, 0, 0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 17, 19, 21,
+	22, 24, 25, 27, 28, 29, 31, 32, 33, 35, 36, 37, 38, 40, 41, 42, 43,
+	44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+	59, 60, 61, 62, 62, 63, 63, 64, 64, 65, 66, 66, 66, 67, 67, 68, 68,
+	69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72,
+	72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+	72, 71, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68,
+	67, 67, 67, 66, 66, 66, 65, 65, 64, 64, 64, 63, 63, 62, 62, 62, 61,
+	61, 60, 60, 59, 59, 58, 58, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53,
+	53, 52, 52, 51, 51, 50, 50, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44,
+	44, 43, 43, 42, 42, 41, 41, 40, 39, 39, 38, 38, 37, 37, 36, 36, 35,
+	35, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27,
+	26, 26, 25, 25, 24, 24, 23, 23, 23, 22, 22, 21, 21, 20, 20, 20, 19,
+	19, 18, 18, 17, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 12,
+	12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6,
+	6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1,
+	1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2,
+	-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+	-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+	-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+	-2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+enum{
+	Nl = 8,		/* 2^Nl samples per zero crossing in fir */
+	Nη = 8,		/* phase bits for filter interpolation */
+	Np = Nl+Nη,	/* phase bits (fract of fixed point) */
+	TΔ = ((uvlong)PcmHz<<Np) / Rate,
+	One = 1<<Np,
+	LΔ = 1<<Np,
+	Le = nelem(h)<<Nη,
+	Lscale = 13128,
+	Nextra = 1 + (Le / LΔ),
+	Npbuf = Nextra*2 + (Nsamp - 1)
+};
+static int hΔ[nelem(h)], xb[Npbuf], xbi;
+static ulong xt;
+
+static void
+stopal(void)
+{
+	if(sfxd == nil)
+		return;
+	opl2wr(Roct, 0);
+	opl2wr(Rfed, 0);
+	sfxp = sfxe = nil;
+	sfxd = nil;
+}
+
+static void
+imfcmd(void)
+{
+	int r, v, dt;
+	uchar *u;
+
+	dt = 0;
+	while(dt == 0){
+		u = imf;
+		r = u[0];
+		v = u[1];
+		dt = u[3]<<8 | u[2];
+		opl2wr(r, v);
+		imf += 4;
+		if(imf >= imfd->e)
+			imf = imfd->p;
+	}
+	mdt = stc + dt;
+}
+
+static void
+alcmd(void)
+{
+	int v;
+
+	if(sfxp >= sfxe){
+		stopal();
+		return;
+	}
+	v = *sfxp++;
+	if(v != 0){
+		opl2wr(Rnum, v);
+		opl2wr(Roct, sfxd->blk);
+	}else
+		opl2wr(Roct, 0);
+	sdt = stc + ImfHz / SfxHz;
+}
+
+/* this spins constantly even when there's no music being played continuously,
+ * in essence only because some sound effects need an echo to play correctly.
+ * it sucks. */
+static void
+opl2step(void)
+{
+	uchar *p, *e;
+
+	p = sbuf;
+	e = p + sizeof sbuf;
+	while(p < e){
+		if(stc == sdt && sfxd != nil)
+			alcmd();
+		if(stc == mdt && imfd != nil)
+			imfcmd();
+		p = opl2out(p, Nsamp);
+		stc++;
+	}
+}
+
+/* stolen property cargocult upsampling */
+static void
+filter(int *xb, uchar *s)
+{
+	ulong l, p, i;
+	int *x, o, a;
+	vlong v;
+
+	if(s >= sbuf+sizeof sbuf)
+		return;
+	v = 0;
+	x = &xb[xt>>Np];	/* left side */
+	l = p = xt & (1<<Np)-1;
+	while(l < Le){
+		i = l >> Nη;
+		a = ((int)(l & (1<<Nη)-1) * hΔ[i] >> Nη) + h[i];
+		v += (vlong)*(--x) * a;
+		l += LΔ;
+	}
+	x = &xb[xt>>Np];	/* right side */
+	l = One-p & (1<<Np)-1;
+	if(l == 0)	/* h[0] was already summed above if p == 0 */
+		l += LΔ;
+	while(l < Le){
+		i = l >> Nη;
+		a = ((int)(l & (1<<Nη)-1) * hΔ[i] >> Nη) + h[i];
+		v += (vlong)*x++ * a;
+		l += LΔ;
+	}
+	v >>= 2;	/* scale */
+	v *= Lscale;
+	v >>= 27;
+	o = (v >> 16) + (short)(s[0] | s[1]<<8);	/* mix */
+	if(o > 32767)
+		o = 32767;
+	else if(o < -32768)
+		o = -32768;
+	s[0] = s[2] = o;
+	s[1] = s[3] = o>>8;
+}
+
+static void
+resample(uchar *p, int n)
+{
+	int i, s;
+	uchar *b;
+	ulong e;
+
+	b = sbuf;
+	do{
+		for(i=xbi; n>0 && i<Npbuf; n--, i++)
+			xb[i] = ((uint)*p++ << 24) - 0x7fffffff;
+		xbi = i;
+		if(i < 2*Nextra)	/* minimum for filter */
+			break;
+		e = i - Nextra << Np;
+		while(xt < e){	/* process buffer and mix */
+			filter(xb, b);
+			b += 4;
+			xt += TΔ;
+		}
+		e = xt >> Np;
+		if(e >= Npbuf - Nextra){	/* wrap at eob */
+			xt = xt - (e << Np) + (Nextra << Np);
+			s = (xt >> Np) - Nextra;
+			e -= Nextra;
+			i -= e;
+			if(i > 0){
+				memmove(xb+s, xb+e, i * sizeof i);
+				s += i;
+			}
+			xbi = s;
+		}
+	}while(n > 0);
+}
+
+static void
+pcmstep(void)
+{
+	uchar *e, *p;
+
+	p = pcm;
+	e = pcme;
+	if(p == e)
+		return;
+	if(p + Pdiv < e)
+		e = p + Pdiv;
+	resample(p, e-p);
+	pcm = e;
+}
+
 void
+sndstep(void)
+{
+	if(!muson && !sfxon)
+		return;
+	opl2step();
+	pcmstep();
+	write(sfd, sbuf, sizeof sbuf);
+}
+
+void
+stopsfx(void)
+{
+	stopal();
+	pcm = pcme = nil;
+	pcmd = nil;
+}
+
+void
 sfx(int n)
 {
 	Sfx *s;
+	uchar *r, *i, *e;
 
 	s = sfxs+n;
-	USED(s);
+	if(sfd < 0 || !sfxon)
+		return;
+	if(pcmon && s->pcm != nil){
+		if(pcmd != nil && s->pri < pcmd->pri)
+			return;
+		pcmd = s;
+		pcm = s->pcm->p;
+		pcme = s->pcm->e;
+		xbi = Nextra;
+		xt = Nextra << Np;
+	}else{
+		if(sfxd != nil && s->pri < sfxd->pri)
+			return;
+		stopal();
+		sfxd = s;
+		sfxp = s->p;
+		sfxe = s->e;
+		sdt = stc;
+		i = s->inst;
+		r = inst;
+		e = r + sizeof inst;
+		while(r < e)
+			opl2wr(*r++, *i++);
+	}
 }
 
 void
 stopmus(void)
 {
+	int i;
+
+	stopsfx();
+	opl2wr(Ropm, 0);
+	for(i=Roct+1; i<Roct+9; i++)
+		opl2wr(i, 0);
+	imf = nil;
+	imfd = nil;
 }
 
 void
 mus(int n)
 {
-	if(n == mi)
+	Dat *d;
+
+	d = imfs+n;
+	if(sfd < 0 || !muson || d == imfd)
 		return;
 	stopmus();
+	mdt = stc;
+	imfd = d;
+	imf = d->p;
+}
+
+void
+initsnd(void)
+{
+	int n, fd;
+
+	fd = open("/dev/audio", OWRITE);
+	if(fd < 0){
+		fprint(2, "initsnd: %r\n");
+		return;
+	}
+	opl2init(Rate);
+	opl2wr(Rwse, 0x20);
+	for(n=0; n<nelem(hΔ)-1; n++)
+		hΔ[n] = h[n+1] - h[n];
+	sfd = fd;
+	muson = sfxon = pcmon = 1;
 }
--- a/wl3d.c
+++ b/wl3d.c
@@ -253,6 +253,7 @@
 	dat(datdir);
 
 	notify(croak);
+	initsnd();
 	if(initdraw(nil, nil, "wl3d") < 0)
 		sysfatal("initdraw: %r");
 	resetfb();
@@ -274,6 +275,7 @@
 			resetfb();
 		}
 		step();
+		sndstep();
 		t0 += Td;
 		t = nsec();
 		dt = (t0 - t) / Te6;