shithub: itdec

Download patch

ref: 69b97132c75dc22b44c78db1061ea6828a1cb211
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Dec 9 07:33:01 EST 2020

first

--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,25 @@
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+++ b/it_disk.c
@@ -1,0 +1,938 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "it_struc.h"
+
+const char *HeaderMsg = "File Header";
+const char *InstrumentMsg = "Instrument \375D";
+const char *SampleMsg = "Sample \375D";
+const char *SHLoadMsg = "Sample Header \375D";
+const char *PatternMsg = "Pattern \375D";
+
+// TODO: prevent buffer read overruns
+// note, it's entirely likely that I'm slightly off for exceptional behaviour --GM
+void D_Decompress16BitData(uint16_t *dest, uint8_t *src, uint16_t len)
+{
+	// Register usage:
+	// BX = LastValue
+	// CH = Bitdepth
+	// CL = 16-Bitdepth, 0 for Bitdepth > 16
+	// DL = Bitsread
+	// DH = scratch
+
+	int counter = len>>1;
+	uint8_t bitdepth = 17;
+	uint8_t ibitdepth = 0;
+	uint16_t lastvalue = 0;
+	uint8_t bitsread = 0;
+	uint8_t scratch = 0;
+	USED(scratch);
+
+	while(counter != 0)
+	{
+		// Push    CX
+
+		uint32_t eax = *(uint32_t *)src;
+		eax >>= bitsread;
+
+		bitsread += bitdepth;
+		src += bitsread>>3;
+		bitsread &= 7;
+
+		// Pop     CX
+
+		if(bitdepth <= 6)
+		{
+			eax <<= ibitdepth;
+
+			if((eax & 0xFFFF) != 0x8000)
+			{
+				lastvalue += (uint16_t)(((int16_t)eax)>>ibitdepth);
+				*(dest++) = lastvalue;
+				counter--;
+				continue;
+			}
+
+			uint8_t newbits = (eax>>16) & 15;
+			newbits++;
+
+			// Advance bits
+			bitsread += 4;
+			if(newbits >= bitdepth)
+				newbits += 1;
+
+			ibitdepth = 16;
+			bitdepth = newbits;
+			if(ibitdepth < bitdepth)
+			{
+				ibitdepth -= bitdepth;
+				ibitdepth++;
+			} else {
+				ibitdepth -= bitdepth;
+			}
+
+		} else if(bitdepth <= 16) {
+
+			// Push    DX
+
+			uint16_t tmp_dx = 0xFFFF;
+			tmp_dx >>= ibitdepth;
+			uint16_t tmp_ax = (uint16_t)eax;
+			tmp_ax &= (uint16_t)tmp_dx;
+			tmp_dx >>= 1;
+			tmp_dx += 8;
+			tmp_dx -= 16;
+			if(tmp_ax > (uint16_t)(tmp_dx+16) || tmp_ax <= (uint16_t)tmp_dx)
+			{
+				eax <<= ibitdepth;
+				lastvalue += (uint16_t)(((int16_t)eax)>>ibitdepth);
+				*(dest++) = lastvalue;
+				counter--;
+				continue;
+			}
+
+			// Pop     DX
+
+			uint8_t newbits = (uint8_t)(tmp_ax - tmp_dx);
+			if(newbits >= bitdepth)
+				newbits += 1;
+
+			ibitdepth = 16;
+			bitdepth = newbits;
+			if(ibitdepth < bitdepth)
+			{
+				ibitdepth -= bitdepth;
+				ibitdepth++;
+			} else {
+				ibitdepth -= bitdepth;
+			}
+		} else {
+
+			if((eax & 0x10000) == 0)
+			{
+				lastvalue += (uint16_t)eax;
+				*(dest++) = lastvalue;
+				counter--;
+				continue;
+			}
+
+			eax++; // Inc AX actually
+			bitdepth = (uint8_t)(eax&0xFF);
+			ibitdepth = 16 - bitdepth;
+		}
+	}
+
+}
+
+void D_Decompress8BitData(uint8_t *dest, uint8_t *src, uint16_t len)
+{
+	// DS:SI = source
+	// ES:DI = destination
+	// CX = count.
+
+	// Register usage:
+	// BH = Bitdepth
+	// BL = lastvalue
+	// CL = 8-bitdepth, undefined for bitdepth > 8
+	// CH = Bitsread
+
+	// DX = scratch
+
+	int counter = len; // BP = counter;
+	uint8_t bitdepth = 9;
+	uint8_t ibitdepth = 0;
+	uint8_t lastvalue = 0;
+	uint8_t bitsread = 0;
+	uint16_t scratch = 0;
+
+	USED(scratch);
+
+	while(counter != 0)
+	{
+		// Get bits loaded into AX properly.
+		uint16_t ax = *(uint16_t *)src;
+		ax >>= bitsread;
+
+		// Advance SI as necessary.
+		bitsread += bitdepth;
+		src += bitsread>>3;
+		bitsread &= 7;
+
+		uint8_t tmp_al;
+		if(bitdepth <= 6)
+		{
+			ax <<= ibitdepth;
+
+			if((ax & 0xFF) != 0x80)
+			{
+				lastvalue += (uint8_t)(((int8_t)ax)>>ibitdepth);
+				*(dest++) = lastvalue;
+				counter--;
+				continue;
+			}
+
+			tmp_al = (ax>>8);
+			bitsread += 3;
+
+			tmp_al &= 7;
+			scratch = bitsread;
+			bitsread &= 7;
+			scratch >>= 3;
+
+			src += scratch;
+
+		} else {
+			tmp_al = (ax & 0xFF);
+
+			if(bitdepth > 8)
+			{
+				// 9 bit representation
+				ax &= 0x1FF;
+
+				if((ax & 0x100) == 0)
+				{
+					lastvalue += (uint8_t)tmp_al;
+					*(dest++) = lastvalue;
+					counter--;
+					continue;
+				}
+
+			} else if(bitdepth == 8) {
+				if(tmp_al < 0x7C || tmp_al > 0x83)
+				{
+					lastvalue += (uint8_t)tmp_al;
+					*(dest++) = lastvalue;
+					counter--;
+					continue;
+				}
+
+				tmp_al -= 0x7C;
+
+			} else {
+
+				tmp_al <<= 1;
+				if(tmp_al < 0x78 || tmp_al > 0x86)
+				{
+					lastvalue += (uint8_t)(((int8_t)tmp_al)>>1);
+					*(dest++) = lastvalue;
+					counter--;
+					continue;
+				}
+
+				tmp_al >>= 1;
+				tmp_al -= 0x3C;
+
+			}
+		}
+
+		ibitdepth = 8;
+		tmp_al++;
+
+		//Cmp     AL, BH
+		//SBB     AL, 0FFh
+		//Mov     BH, AL
+		//Sub     CL, AL
+		//AdC     CL, 0
+		if(tmp_al >= bitdepth)
+			tmp_al++;
+		bitdepth = tmp_al;
+
+		if(ibitdepth < tmp_al)
+		{
+			ibitdepth -= tmp_al;
+			ibitdepth++;
+		} else {
+			ibitdepth -= tmp_al;
+		}
+
+		continue;
+
+	}
+}
+
+int D_LoadSampleData(it_engine *ite, int fp, uint16_t ax)
+{
+	// DS:SI points to sample header
+	// AX = sample number (0 based)
+
+	it_sample *smp = &ite->smp[ax];
+
+	uint32_t edx = smp->Length;
+	if(edx == 0) return 0;
+
+	uint8_t cl = smp->Flg;
+	uint16_t bp = ((uint16_t)smp->Cvt) | (((uint16_t)smp->DfP)<<8);
+
+	smp->Flg &= ~0x0C;
+	smp->Cvt = 1;
+	bp &= 0xFF;
+
+	bp <<= 1;
+	uint8_t ch = cl;
+	ch &= 0x8; cl &= 0x02;
+	ch <<= 4;
+	cl >>= 1;
+	bp |= ((uint16_t)ch)<<8;
+	bp |= ((uint16_t)cl);
+
+	// BP flags:
+	// 1: 16 bit
+	// 2: Convert unsigned->signed
+	// 4: Swap bytes
+	// 8: Delta values.
+	// 16: Byte delta values
+	// 32: 12-bit sample.
+	// 64: Stereo prompt.
+	// 8000h: Compressed.
+
+
+	if((bp & 1) != 0)
+		edx *= 2;
+
+	if((bp & 64) != 0)
+	{
+		/*
+		Cmp     CS:DisableStereoMenu, 0
+		JNE     D_NoStereoMenu
+		{
+
+			PushAD
+			Push    DS ES
+
+			Mov     DI, Offset O1_StereoSampleList
+			Mov     CX, 0FFFFh
+			Call    M_Object1List
+
+			Mov     CS:TempData, DX
+
+			Pop     ES DS
+			PopAD
+
+			Or      BP, CS:TempData                 // 64 = left
+								// 64+128 = right
+		}
+		D_NoStereoMenu:
+		*/
+		edx *= 2;
+	}
+
+	uint8_t *data = Music_AllocateSample(ite, ax, edx);
+	ax++;
+	USED(ax);
+
+	int32_t edi = edx;
+
+	if(data == NULL)
+	{
+		seek(fp, smp->Length, 1); // Advance file ptr.
+		// (pretty sure there's a bug somewhere --GM)
+
+		//PEFunction_OutOfMemoryMessage(ite);
+
+		return 1;
+	}
+
+	// AX = sample no.
+	// CH = page no.
+	// EDI = bytes remaining
+	// SI = delta accumulator
+	// BX = file pointer // using variable "fp" instead --GM
+
+	ch = 0; // Start with page 0
+	uint16_t si = 0;
+
+	uint8_t *srcdata = data;
+	for(; edi > 0; edi -= 32768)
+	{
+		uint32_t ecx = 0;
+		int is8bit = 0;
+		USED(ecx, is8bit);
+		//uint8_t *srcdata = Music_GetSampleLocation(ite, ax, &ecx, &is8bit);
+
+		uint32_t buflen = 32768;
+
+		if(edi < buflen)
+			buflen = edi;
+
+		if((bp & 32) != 0) // 12 bit format?
+		{
+			buflen &= 0xFFFF;
+			buflen *= 3;
+			buflen += 3;
+			buflen >>= 2; // = bytes to read.
+		}
+
+		uint8_t *newsrcdata = srcdata + buflen;
+		uint16_t packed_len;
+		uint8_t packed_data[0x10000];
+		size_t packed_len_long;
+
+		// DS:DX point to buffer. For compressed samples, use patterndata area.
+		if((bp & 0x8000) != 0)
+		{
+			/*
+			Push    DS
+			Push    CX
+			Push    DX
+
+			Mov     DX, Pattern
+			Mov     DS, DX
+				Assume DS:Pattern
+
+			Mov     DS, Word Ptr [PatternDataArea]
+				Assume DS:Nothing
+
+			Xor     DX, DX
+			Mov     CX, 2
+			Mov     AH, 3Fh
+			Int     21h
+			Mov     CX, [DS:0]              // Bytes to read.
+			Xor     DX, DX                  // Compressed chunk.
+			*/
+			readn(fp, &packed_len, 2);
+			packed_len_long = packed_len;
+			packed_len_long = read(fp, packed_data, packed_len_long);
+			USED(packed_len_long);
+
+		} else {
+			buflen = read(fp, srcdata, buflen);
+		}
+
+		// Now to decompress samples, if required.
+		if((bp & 0x8000) != 0)
+		{
+			// TODO
+			if((bp & 1) == 0)
+				D_Decompress8BitData(srcdata, packed_data, buflen); // 8 bit decode.
+			else
+				D_Decompress16BitData((uint16_t *)srcdata, packed_data, buflen); // 16 bit decode
+
+			si = 0;
+		}
+
+		// flag skipped if sample compressed
+		if((bp & 0x8000) == 0 && (bp & 32) != 0) // 12-bit sample?
+		{
+			// TODO!
+			abort();
+			/*
+			// CX = number of bytes read.
+			//    = 3*2 number of sample read
+			buflen = (buflen + 2) / 3;
+
+			// SI = AX * 3
+			LEA     SI, [EAX*2+EAX]         // SI = AX * 3
+			LEA     DI, [EAX*4+EDX]
+			Add     SI, DX
+
+			Test    CX, CX
+			JZ      ConvertTXSample2
+
+			Push    CX
+
+		ConvertTXSample1:
+			Sub     SI, 3
+			Sub     DI, 4
+
+			Mov     AX, [SI+1]
+			ShL     AL, 4
+			ShL     EAX, 16
+			Mov     AH, [SI]
+			Mov     AL, [SI+1]
+			And     AL, 0F0h
+			Mov     [DI], EAX
+
+			Loop    ConvertTXSample1
+
+			Pop     CX
+			Pop     SI
+			ShL     CX, 1
+			Jmp     D_LoadSampleData10
+
+		ConvertTXSample2:
+			Pop     SI
+			*/
+		} else {
+			// CX = number of bytes read
+
+		//SecondDelta:
+			if((bp & 1) != 0) // 16 bit?
+				buflen >>= 1;
+
+		//D_LoadSampleData10:
+			if((bp & 5) == 5) // 16 bit and BSwap?
+			{
+				uint16_t ctr = buflen;
+				uint8_t *dfol = srcdata;
+
+				while(ctr-- != 0)
+				{
+					uint8_t datl = dfol[0];
+					uint8_t dath = dfol[1];
+					dfol[0] = dath;
+					dfol[1] = datl;
+
+					dfol += 2;
+				}
+			}
+
+			if((bp & 24) != 0) // Delta values?
+			{
+				if((bp & 1) != 0 && (bp & 16) == 0)
+				{
+					// 16 bit delta
+					uint16_t ctr = buflen;
+					uint16_t *dfol = (uint16_t *)srcdata;
+
+					while(ctr-- != 0)
+					{
+						si += *dfol;
+						*(dfol++) = si;
+					}
+
+				} else {
+					// 8 bit delta
+					uint16_t ctr = buflen;
+					uint8_t *dfol = srcdata;
+
+					if((bp & 1) != 0)
+						ctr *= 2;
+
+					while(ctr-- != 0)
+					{
+						si += ((uint16_t)(*dfol))<<8;
+						*(dfol++) = (si>>8);
+					}
+				}
+			}
+
+			if((bp & 2) == 0) // unsigned->signed?
+			{
+				if((bp & 1) == 0)
+				{
+					// 8 bit
+					uint16_t ctr = buflen;
+					uint8_t *dfol = srcdata;
+
+					while(ctr-- != 0)
+						*(dfol++) ^= 0x80;
+				} else {
+					// 16 bit..
+					uint16_t ctr = buflen;
+					uint16_t *dfol = (uint16_t *)srcdata;
+
+					while(ctr-- != 0)
+						*(dfol++) ^= 0x8000;
+				}
+			}
+		}
+
+	//D_LoadSampleData6:
+		if((bp & 64) != 0) // Stereo?
+		{
+			// TODO!
+			abort();
+			/*
+			Push    SI ES
+
+			Push    DS
+			Pop     ES
+
+			Mov     SI, DX
+			Mov     DI, DX
+
+			ShR     CX, 1
+			JZ      D_LoadSampleDataEndStereo
+
+			Test    BP, 1           // 8/16 bit?
+			JNZ     D_LoadSampleDataStereo16BitStart
+
+			Test    BP, 128
+			JZ      D_LoadSampleDataStereo8Bit
+
+			Inc     SI
+
+		D_LoadSampleDataStereo8Bit:
+			MovsB
+			Inc     SI
+			Loop    D_LoadSampleDataStereo8Bit
+			Jmp     D_LoadSampleDataEndStereo
+
+		D_LoadSampleDataStereo16BitStart:
+			Test    BP, 128
+			JZ      D_LoadSampledataStereo16Bit
+
+			LodsW
+
+		D_LoadSampleDataStereo16Bit:
+			MovsW
+			LodsW
+			Loop    D_LoadSampleDataStereo16Bit
+
+		D_LoadSampleDataEndStereo:
+			Pop     ES SI
+
+			Pop     EDI
+			Pop     CX
+			Pop     AX
+
+			Inc     CH
+			Jmp     D_LoadSampleDataNextChain
+			*/
+		}
+
+		srcdata = newsrcdata;
+		ch += 2;
+
+	//D_LoadSampleDataNextChain:
+	}
+
+	return 0;
+}
+
+int D_PreLoadModule(it_engine *ite, int fp, uint8_t al)
+{
+	// Returns ES = song segment
+	// BX = file handle
+	// DS = Diskdata area
+	// AX = SaveFormat
+
+#if DEFAULTFORMAT
+	if(al == 0)
+		al = DEFAULTFORMAT;
+#endif
+
+	ite->SaveFormat = al;
+
+	//I_ClearTables(ite);
+	Music_ReleaseAllPatterns(ite);
+	Music_ReleaseAllSamples(ite);
+	//Music_ClearAllSampleNames(ite);
+	Music_ClearAllInstruments(ite);
+	//Msg_ResetMessage(ite);
+	//ReleaseTimerData(ite);
+
+	// this is ridiculous, let's write something simpler for now --GM
+	return fp;
+
+	/*
+	Mov     DS, CS:DiskDataArea
+
+	Mov     BX, CS:CurrentFile
+	Add     BX, BX
+	Mov     BX, [BX]
+	Add     BX, 8
+
+	Push    CS
+	Pop     ES
+	Mov     DI, Offset FileName             // OK...
+	Mov     SI, BX                          // Data area no longer
+	Mov     CX, 13                          //  reqd
+	Rep     MovsB
+
+	Mov     AX, 3D00h
+	Mov     DX, BX
+	Int     21h
+	JC      D_LoadFileOpenErrorMsg
+
+	Mov     BX, AX                          // BX = file handle.
+
+	Call    Music_GetSongSegment
+	Mov     ES, AX
+	*/
+}
+
+void D_PostLoadModule(it_engine *ite, int fp)
+{
+	close(fp); // Close file.
+
+	//CheckTimerData(ite);
+	//GetCurrentTime(ite);
+	ite->TopTimerData = 0;
+	//ite->EditTimer = GetTimerCounter();
+
+	//PE_ResetOrderPattern(ite);
+}
+
+int D_LoadIT(it_engine *ite, int fp)
+{
+	D_PreLoadModule(ite, fp, 0);
+	printf("loading %d\n", fp);
+
+	S_DrawString(ite, 4, 16, HeaderMsg, 5);
+
+	// why does this load 2KB i mean what --GM
+	// TODO: serialise this --GM
+	readn(fp, &ite->hdr, 0xC0);
+
+	printf("ord=%-3i ins=%-3i smp=%-3i pat=%-3i\n",
+		ite->hdr.OrdNum,
+		ite->hdr.InsNum,
+		ite->hdr.SmpNum,
+		ite->hdr.PatNum);
+	/*
+	Mov     AH, 3Fh
+	Mov     CX, 2048
+	Xor     DX, DX
+	Int     21h
+	*/
+
+	if(ite->hdr.Cwt_v >= 0x0208)
+	{
+		ite->hdr.Time_Stamp ^= 0x4B525449; // 'ITRK' - TODO CONFIRM BYTE ORDER
+		ite->hdr.Time_Stamp = (ite->hdr.Time_Stamp >> 7) | (ite->hdr.Time_Stamp << (32-7));
+		ite->hdr.Time_Stamp = -ite->hdr.Time_Stamp;
+		ite->hdr.Time_Stamp = (ite->hdr.Time_Stamp << 4) | (ite->hdr.Time_Stamp >> (32-4));
+		ite->hdr.Time_Stamp ^= 0x4C48544A; // 'JTHL' - TODO CONFIRM BYTE ORDER
+		printf("Timestamp: %08X\n", ite->hdr.Time_Stamp);
+	}
+
+	if((ite->hdr.Special & 2) != 0) // Time data?
+	{
+		// Seek to 0C0+Orders+
+		// (ins+samp+pat)*4
+
+		// TODO!
+		/*
+		Mov     DX, [DS:22h]
+		Add     DX, [DS:24h]
+		Add     DX, [DS:26h]
+		ShL     DX, 2
+		Add     DX, [DS:20h]
+		Add     DX, 0C0h
+		Xor     CX, CX
+		Mov     AX, 4200h
+		Int     21h
+
+		Push    DS
+
+		Push    CS
+		Pop     DS
+
+		Mov     AH, 3Fh
+		Mov     CX, 2
+		Mov     DX, Offset NumTimerData
+		Int     21h
+
+		Push    BX              // Allocate data for timedata
+		Mov     BX, NumTimerData
+		Cmp     BX, 0FFFFh
+		JNE     D_NoTimerDataOverFlow
+
+		Dec     BX
+		Mov     NumTimerData, BX
+
+	D_NoTimerDataOverFlow:
+		Mov     CX, BX
+		ShR     BX, 1
+		Inc     BX
+		Mov     AH, 48h
+		Int     21h
+		Pop     BX
+		JC      D_LoadTimeDataEnd
+
+		Mov     TimerData, AX
+		Mov     DS, AX
+		ShL     CX, 3
+		Xor     DX, DX
+		Mov     AH, 3Fh
+		Int     21h
+
+	D_LoadTimeDataEnd:
+		Pop     DS
+		*/
+	}
+
+	if((ite->hdr.Special & 8) != 0)
+	{
+		// TODO: MIDI --GM
+
+		/*
+		PushA
+		Push    DS
+
+		Call    Music_GetMIDIDataArea
+		Xor     DX, DX
+		Mov     CX, 4896
+		Mov     AH, 3Fh
+		Int     21h
+
+		Pop     DS
+		PopA
+		*/
+	}
+
+	if((ite->hdr.Special & 1) != 0)
+	{
+		// Load the message
+		// Move to offset first.
+
+		// TODO
+		/*
+		Mov     AX, 4200h
+		Mov     CX, [DS:3Ah]
+		Mov     DX, [DS:38h]
+		Int     21h             // Seek to position
+
+		Push    DS
+
+		Mov     CX, [DS:36h]
+		Call    Msg_GetMessageOffset
+		Mov     AH, 3Fh
+		Int     21h
+
+		Pop     DS
+		*/
+	}
+
+	// Actually, load row hilights first...
+	if((ite->hdr.Special & 4) != 0)
+	{
+		// TODO --GM
+		//ite->RowHilight1 = ite->hdr.PHiligt;
+	}
+
+	/*
+	Xor     SI, SI
+	Xor     DI, DI
+	Mov     CX, 192
+	Rep     MovsB                           // Header
+	*/
+
+	// TODO: verify
+	// Orders
+	seek(fp, 0x00C0, 0);
+	readn(fp, ite->ord, ite->hdr.OrdNum); // XXX: limit OrdNum to >= 1
+	ite->ord[ite->hdr.OrdNum] = 0xFF; // TODO: verify
+
+	// SI points to first pointer
+	// this is different from the actual code --GM
+	uint32_t iptrs[100];
+	uint32_t sptrs[100];
+	uint32_t pptrs[200];
+	assert(ite->hdr.InsNum <= 99);
+	assert(ite->hdr.SmpNum <= 99);
+	assert(ite->hdr.PatNum <= 199);
+
+	readn(fp, iptrs, 4*(int)ite->hdr.InsNum);
+	readn(fp, sptrs, 4*(int)ite->hdr.SmpNum);
+	readn(fp, pptrs, 4*(int)ite->hdr.PatNum);
+
+	// Instrument time.
+	uint16_t bp;
+	it_instrument *ins;
+	it_sample *smp;
+	it_pattern *pat;
+
+	for(bp = 0, ins = &ite->ins[0]; bp < ite->hdr.InsNum; bp++, ins++)
+	{
+		// TODO: num args
+		S_DrawString(ite, 4, 17, InstrumentMsg, 5);
+
+		// Move to offset..
+		seek(fp, iptrs[bp], 0);
+
+		readn(fp, ins, 554);
+
+		if(ite->hdr.Cmwt < 0x200)
+		{
+			// TODO!
+			abort();
+			/*
+			Mov     SI, DX
+			Call    ConvertOldInstrument
+			*/
+		}
+	}
+
+	// Sample header time.
+	for(bp = 0, smp = &ite->smp[0]; bp < ite->hdr.SmpNum; bp++, smp++)
+	{
+		// TODO: num args
+		S_DrawString(ite, 4, 18, SHLoadMsg, 5);
+
+		// Move to offset..
+		seek(fp, sptrs[bp], 0);
+
+		readn(fp, smp, 80);
+		//printf("smp len %i = %i\n", bp, smp->Length);
+	}
+
+	// DS now points to song data.
+	for(bp = 0, smp = &ite->smp[0]; bp < 99; bp++, smp++)
+	{
+		if((smp->Flg & 1) != 0)
+		{
+			S_DrawString(ite, 4, 19, SampleMsg, 5);
+
+			// Move file pointer.
+			seek(fp, smp->SamplePointer, 0);
+			ite->SamplePointer[bp] = NULL;
+			smp->SamplePointer = 0;
+
+			D_LoadSampleData(ite, fp, bp);
+			printf("smp %2i flg=%02X len=%i\n", bp, smp->Flg, smp->Length);
+
+		} else {
+			smp->SamplePointer = 0;
+			ite->SamplePointer[bp] = NULL;
+		}
+	}
+
+	// Pattern time.
+	for(bp = 0; bp < ite->hdr.PatNum; bp++)
+	{
+		S_DrawString(ite, 4, 20, PatternMsg, 5);
+
+		// Move to offset..
+		if(pptrs != 0)
+		{
+			seek(fp, pptrs[bp], 0);
+
+			readn(fp, ite->patspace, 8);
+
+			uint16_t length = ((uint16_t)ite->patspace[0])
+				| (((uint16_t)ite->patspace[1])<<8);
+
+			printf("pat %-3i %-3i %-5i\n", bp, ite->patspace[2], length);
+			ite->pat[bp] = pat = Music_AllocatePattern(ite, length + 8);
+			if(pat != NULL)
+			{
+				memcpy((uint8_t *)pat, (uint8_t *)ite->patspace, 8);
+				readn(fp, pat->data, length);
+			} else {
+				//PEFunction_OutOfMemoryMessage(ite);
+				abort();
+			}
+		}
+	}
+
+	D_PostLoadModule(ite, fp);
+	return 0;
+}
+
--- /dev/null
+++ b/it_m_eff.c
@@ -1,0 +1,2230 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "it_struc.h"
+
+uint8_t SlideTable[9] = { 1, 4, 8, 16, 32, 64, 96, 128, 255 };
+
+void InitVolumeEffectH(it_engine *ite, it_host *chn, uint8_t ah);
+void InitCommandD7(it_engine *ite, it_host *chn, it_slave *slave);
+void InitCommandG11(it_engine *ite, it_host *chn);
+void InitCommandX2(it_engine *ite, it_host *chn, uint8_t al);
+void CommandD2(it_engine *ite, it_host *chn, it_slave *slave, uint8_t al);
+void CommandEChain(it_engine *ite, it_host *chn, int16_t bx);
+void CommandFChain(it_engine *ite, it_host *chn, int16_t bx);
+void InitVibrato(it_engine *ite, it_host *chn);
+void CommandH5(it_engine *ite, it_host *chn, it_slave *slave, int8_t al);
+void InitTremelo(it_engine *ite, it_host *chn);
+void CommandR2(it_engine *ite, it_host *chn, it_slave *slave, int8_t al);
+
+/*
+// Not transcribing further unless there is a good reason to.
+int32_t GetC5Speed(uint8_t *ds, uint8_t *es, uint16_t bx)
+{
+	if(*(uint8_t *)(ds + di + 0x0F) == 101)
+	{
+		// OK.. we have a MIDI sample
+		// Check whether the midi sample points to a valid sample
+		// And if so, use that sample's C5 speed.
+
+		uint16_t si = *(uint8_t *)(ds + di + 0x03); // Not a note?
+		uint32_t ebp = *(uint8_t *)(ds + di + 0x04);
+
+		if(ebp != 0) // No sample?
+		{
+			si += si;
+			ebp = *(uint16_t *)(es + 64710 + ebp + ebp);
+			uint8_t al = *(uint8_t *)(es + ebp + si + 0x40) // AL = note
+			uint8_t ah = *(uint8_t *)(es + ebp + si + 0x41) // AH = sample.
+
+			if(ah != 0) // No sample?
+			{
+				si = ah*2;
+				si = *(uint16_t *)(es + 64910 + si) // Sample offset
+
+				if(*(uint8_t *)(es + si + 0x12) == 1)
+				{
+					bx = si;
+				}
+			}
+		}
+	}
+
+	return *(uint8_t *)(es + bx + 0x3C); // EAX = C5Spd
+}
+*/
+
+void InitVolumeEffect(it_engine *ite, it_host *chn) // Done A, B, H
+{
+	if((chn->Msk & 0x44) == 0)
+		return;
+
+	uint8_t al = chn->Vol;
+	uint8_t ah = al;
+
+	al &= 0x7F;
+	if(al < 65)
+		return;
+	al -= 65;
+
+	if((ah & 0x80) != 0)
+	{
+		al += 60;
+	}
+
+	uint8_t dl = 10;
+	uint8_t tmpquo = al / dl;
+	uint8_t tmprem = al % dl;
+	al = tmpquo; // Effect number
+	ah = tmprem; // Effect parameter
+	chn->VCm = al; // Store effect number
+
+	// Memory for effects A->D, (EFG)/H dont' share.
+
+	// Effects Ax and Bx (fine volume slide up and down) require immediate
+	// handling. No flags required. (effect 0 and 1)
+
+	// Effects Cx, Dx, Ex, Fx (volume/pitch slides) require flag to be
+	// set   (effects 2->5)
+
+	// Effects Gx and Hx need init (handling) code + flags.
+	// (effects 6 and 7).
+
+	if(ah != 0)
+	{
+		if(al >= 4)
+		{
+			if(al < 6)
+			{
+				// Ex Fx
+				chn->EFG = ah<<2;
+			} else if(al <= 6) {
+				// Gx
+				if((ite->hdr.Flags & 0x0020) != 0) // Link command G?
+				{
+					chn->GOE = SlideTable[ah-1];
+				} else {
+					chn->EFG = SlideTable[ah-1];
+				}
+			}
+		} else {
+			// Ax Bx Cx Dx
+			chn->VVal = ah;
+		}
+	}
+
+	if((chn->Flags & 0x04) != 0)
+	{
+		// Channel not on!
+		// TODO: work out what this comment means and which it applies to
+		// (I think it applies to this block)
+
+		it_slave *slave = &ite->slave[chn->SCOffst];
+
+		if(al <= 1)
+		{
+			if(al != 1)
+			{
+				al = chn->VVal;
+				al += slave->VS;
+				if(al > 64)
+					al = 64;
+			} else {
+				al = chn->VVal;
+				al -= slave->VS;
+				if((al & 0x80) != 0)
+					al = 0;
+			}
+
+			CommandD2(ite, chn, slave, al);
+			return;
+		} else {
+			chn->Flags |= 0x0100;
+
+			if(al > 6)
+			{
+				InitVolumeEffectH(ite, chn, ah);
+				return;
+			} else if(al == 6) {
+				InitCommandG11(ite, chn);
+				return;
+			}
+			return;
+		}
+	} else {
+		if(al == 7)
+			InitVolumeEffectH(ite, chn, ah);
+	}
+}
+
+void InitVolumeEffectH(it_engine *ite, it_host *chn, uint8_t ah)
+{
+	ah <<= 2;
+	if(ah != 0)
+		chn->VDp = ah;
+
+	if((chn->Flags & 4) != 0)
+		InitVibrato(ite, chn);
+}
+
+void VolumeCommandC(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	uint8_t al = chn->VVal + slave->VS;
+
+	if(al > 64)
+	{
+		chn->Flags &= ~0x0100; // Turn off effect calling
+		al = 64;
+	}
+
+	CommandD2(ite, chn, slave, al);
+}
+
+void VolumeCommandD(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	uint8_t al = slave->VS - chn->VVal;
+
+	//if((al & 0x80) != 0) // JNC, not JNS
+	if(slave->VS < chn->VVal)
+	{
+		chn->Flags &= ~0x0100; // Turn off effect calling
+		al = 0;
+	}
+
+	CommandD2(ite, chn, slave, al);
+}
+
+void VolumeCommandE(it_engine *ite, it_host *chn) // Pitch slide down
+{
+	CommandEChain(ite, chn, ((int16_t)(uint16_t)chn->EFG)<<2);
+}
+
+void VolumeCommandF(it_engine *ite, it_host *chn)
+{
+	CommandFChain(ite, chn, ((int16_t)(uint16_t)chn->EFG)<<2);
+}
+
+void VolumeCommandG(it_engine *ite, it_host *chn)
+{
+	if((chn->Flags & 0x16) == 0)
+		return;
+
+	uint16_t bx = chn->EFG;
+	if((ite->hdr.Flags & 0x0020) != 0) // Link command G?
+		bx = chn->GOE;
+
+	if(bx == 0)
+		return;
+
+	bx <<= 2;
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	if(chn->_42 != 1)
+	{
+		// Slide down
+		PitchSlideDown(ite, chn, slave, bx);
+
+		// Check that frequency is above porta
+		//  to frequency.
+		int32_t eaxs = slave->Frequency;
+		if(eaxs <= chn->Porta_Frequency)
+		{
+			chn->Flags &= ~0x0110; // Turn off calling
+			slave->Frequency = eaxs = chn->Porta_Frequency;
+		}
+
+		slave->Frequency_Set = eaxs;
+
+	} else {
+		// Slide up!
+		PitchSlideUp(ite, chn, slave, bx);
+
+		// Check that
+		//  1) Channel is on
+		//  2) Frequency (set) is below porta to
+		//       frequency
+		int32_t eaxs = slave->Frequency;
+
+		if((slave->Flags & 0x0200) != 0 || eaxs > chn->Porta_Frequency)
+		{
+			slave->Flags &= ~0x0200;
+			chn->Flags |= 0x0004; // Turn on.
+			chn->Flags &= ~0x0110; // Turn off calling
+			slave->Frequency = eaxs = chn->Porta_Frequency;
+		}
+
+		slave->Frequency_Set = eaxs;
+
+	}
+}
+
+void InitNoCommand(it_engine *ite, it_host *chn) // DS:DI points to CIT area.
+{
+	it_slave *slave;
+	uint8_t cl = chn->Msk;   // CL = mask
+	uint8_t ch = chn->Flags; // CH = channel info.
+
+	if((cl & 0x33) == 0)
+		goto NoOldEffect; // InitCommand1
+
+	uint8_t al = chn->Nt2;
+
+	// Note here!
+	// Check for noteoff.
+	if(al >= 120)
+	{
+		if((ch & 0x04) == 0) // Taken if the channel's off.
+		{
+			// Jump point.
+			goto NoOldEffect; // InitNoCommand1
+		}
+
+		slave = &ite->slave[chn->SCOffst];
+
+		//if(0)
+		if(al > 0xFE) // Noteoff
+		{
+			slave->Flags |= 0x0004; // Note off
+			// if(slave->VS == 0) // Volume = 0???
+			// 	goto InitNoCommand13;
+		} else {
+			//if(al == 0xFE || al == 0xFF)
+			if(al == 0xFE)
+			{
+				ch &= ~4;
+
+				if(chn->Smp != 100 && (ite->d.DriverFlags & 2) == 0)
+				{
+					slave->Flags = 0x0200;
+					goto NoOldEffect;
+				}
+
+				// MIDINoteCut:
+				// ch &= ~4;
+				slave->Flags |= 0x0200;
+			} else {
+				slave->Flags |= 8; // Note fade
+				goto NoOldEffect;
+			}
+		}
+	} else {
+		if((ch & 4) != 0 && (cl & 0x11) == 0 && ite->slave[chn->SCOffst].Nte != chn->Nte)
+			goto NoOldEffect; // InitNoCommand1
+
+		if((cl & 0x44) != 0 && chn->Vol >= 193 && chn->Vol <= 202 && (chn->Flags & 4) != 0) {
+			InitVolumeEffect(ite, chn);
+			return;
+		}
+
+		slave = AllocateChannel(ite, chn, &ch);
+		if(slave == NULL)
+			goto NoOldEffect;
+
+		// Channel allocated.
+		// put volume
+		slave->Vol = slave->VS = chn->VSe;
+		it_sample *smp = &ite->smp[slave->SmpOffs];
+
+		if((ite->hdr.Flags & 4) == 0 && (smp->DfP & 0x80) != 0)
+			slave->PS = slave->Pan = chn->CP = smp->DfP & 0x7F;
+
+		uint32_t eax = smp->C5Speed;
+
+		slave->OldSampleOffset = 0;
+		slave->SmpErr = 0;
+		slave->Sample_Offset = 0;
+
+		// Calculate frequency.
+		uint64_t da = (uint64_t)eax * (uint64_t)PitchTable[chn->Nt2];
+		eax = (uint32_t)(((uint64_t)da)>>(uint64_t)16);
+		slave->Frequency = slave->Frequency_Set = eax;
+
+		ch |= 4;
+		ch &= ~16;
+	}
+
+	GetLoopInformation(ite, slave);
+
+//InitNoCommand1:
+	if((cl & (0x22 + 0x44)) == 0)
+		goto InitNoCommand3;
+
+	// Instrument mode and old effects?
+	if((ite->hdr.Flags & 0x14) != 0x14)
+		goto NoOldEffect;
+
+	if(cl & 0x22)
+		goto NoOldEffect;
+
+	if(chn->Ins == 0xFF)
+		goto NoOldEffect;
+
+	slave->FadeOut = 0x0400;
+	InitPlayInstrument(ite, chn, slave, chn->Ins);
+
+NoOldEffect:
+	// TODO: fix this dreadful flow code once everything works --GM
+	if((cl & 0x44) == 0)
+		goto InitNoCommand7;
+
+	if(chn->Vol <= 64)
+	{
+		chn->VSe = chn->Vol;
+		goto InitNoCommand8; // Volume set...
+	}
+
+	if((chn->Vol & 0x7F) >= 65)
+		goto InitNoCommand7;
+
+	// Panning set!
+//InitNoCommandPanning:
+	chn->Flags = (chn->Flags & 0xFF00) | (0xFF & (uint16_t)ch);
+
+	InitCommandX2(ite, chn, chn->Vol - 128); // Destroys (SI), AX
+
+InitNoCommand7:
+	if((cl & 0x22) == 0) // Instrument present? Change volume
+		goto InitNoCommand3;
+
+	// Get instrument offset.
+	if(chn->Smp == 0xFF)
+		goto InitNoCommand3;
+
+	chn->VSe = ite->smp[chn->Smp-1].Vol; // Default volume
+
+InitNoCommand8:
+	if((ch & 4) == 0)
+		goto InitNoCommand3;
+
+	slave = &ite->slave[chn->SCOffst];
+
+	slave->Vol = slave->VS = chn->VSe;
+	slave->Flags |= 0x10; // recalc volume
+
+InitNoCommand3:
+	// Randomise volume if required.
+	if((chn->Flags & 0x80) == 0)
+	{
+		chn->Flags = (chn->Flags & 0xFF00) | (0xFF & (uint16_t)ch);
+		InitVolumeEffect(ite, chn);
+		return;
+	}
+
+	chn->Flags = (chn->Flags & 0xFF00) | (0xFF & (uint16_t)ch);
+	ApplyRandomValues(ite, chn);
+	InitVolumeEffect(ite, chn);
+}
+
+void InitCommandA(it_engine *ite, it_host *chn)
+{
+	uint16_t ax = chn->CVal;
+
+	if(ax != 0)
+	{
+		ite->CurrentTick -= ite->CurrentSpeed;
+		ite->ProcessTick -= ite->CurrentSpeed;
+		ite->CurrentTick += ax;
+		ite->ProcessTick += ax;
+		ite->CurrentSpeed = ax;
+	}
+
+	InitNoCommand(ite, chn);
+}
+
+void InitCommandB(it_engine *ite, it_host *chn)
+{
+	uint16_t ax = chn->CVal;
+
+	if(ax < ite->CurrentOrder)
+		ite->StopSong = 1;
+
+	ax--;
+	ite->ProcessOrder = ax;
+	ite->ProcessRow = 0xFFFE;
+
+	InitNoCommand(ite, chn);
+}
+
+void InitCommandC(it_engine *ite, it_host *chn)
+{
+	if(ite->PatternLooping == 0)
+	{
+		ite->BreakRow = chn->CVal;
+		ite->ProcessRow = 0xFFFE;
+	}
+
+	InitNoCommand(ite, chn);
+}
+
+void InitCommandD(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->DKL = chn->CVal;
+
+	if((chn->Flags & 4) == 0)
+		return;
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	InitCommandD7(ite, chn, slave);
+}
+
+void InitCommandD7(it_engine *ite, it_host *chn, it_slave *slave)
+{
+	// Jmp point for Lxx
+
+	slave->Flags |= 16; // Recalc vol
+
+	// TODO: verify
+	if((chn->DKL & 0x0F) == 0) {
+		// Slide up.
+		chn->VCh = chn->DKL>>4;
+		chn->Flags |= 1;
+
+		if(chn->VCh == 0x0F) {
+			CommandD(ite, chn);
+			return;
+		}
+	} else if((chn->DKL & 0xF0) == 0) {
+		// Slide down
+		chn->VCh = -chn->DKL;
+		chn->Flags |= 1;
+
+		if(chn->VCh == (uint8_t)-0x0F) {
+			CommandD(ite, chn);
+			return;
+		}
+	} else if((chn->DKL & 0x0F) == 0x0F) {
+//	InitCommandD5:
+		// Slide up (fine)
+		chn->VCh = 0;
+
+		uint8_t al = (chn->DKL >> 4) + slave->VS;
+		if(al > 64)
+			al = 64;
+
+		slave->Vol = slave->VS = chn->VSe = al;
+
+	} else if((chn->DKL & 0xF0) == 0xF0) {
+		// Slide down (fine)
+		chn->VCh = 0;
+
+		uint8_t al = slave->VS - (chn->DKL & 0x0F);
+
+		if((al & 0x80) != 0)
+			al = 0;
+
+		slave->Vol = slave->VS = chn->VSe = al;
+	}
+}
+
+void InitCommandE(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->EFG = chn->CVal;
+
+	if((chn->Flags & 4) == 0)
+		return;
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	// OK.. now processing is dependent
+	// upon slide mode.
+
+	if(chn->EFG == 0) // still no slide??
+		return;
+
+	if((chn->EFG & 0xF0) >= 0xE0)
+	{
+		if((chn->EFG & 0x0F) == 0)
+			return;
+
+		uint8_t al = chn->EFG & 0x0F;
+		if((chn->EFG & 0xF0) != 0xE0)
+			al <<= 2;
+
+		PitchSlideDown(ite, chn, slave, al);
+
+		slave->Frequency_Set = slave->Frequency;
+	} else {
+		chn->_40 = ((int16_t)chn->EFG)<<2;
+
+		// call update only if necess.
+		chn->Flags |= 1;
+	}
+}
+
+void InitCommandF(it_engine *ite, it_host *chn)
+{
+	// TODO: verify
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->EFG = chn->CVal;
+
+	if((chn->Flags & 4) == 0)
+		return;
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	// OK.. now processing is dependent
+	// upon slide mode.
+
+	if(chn->EFG == 0) // still no slide??
+		return;
+
+	if((chn->EFG & 0xF0) >= 0xE0)
+	{
+		if((chn->EFG & 0x0F) == 0)
+			return;
+
+		uint8_t al = chn->EFG & 0x0F;
+		if((chn->EFG & 0xF0) != 0xE0)
+			al <<= 2;
+
+		PitchSlideUp(ite, chn, slave, al);
+
+		slave->Frequency_Set = slave->Frequency;
+	} else {
+		// note: not negative. this point proves crucial for some things --GM
+		chn->_40 = (((int16_t)chn->EFG)<<2);
+
+		// call update only if necess.
+		chn->Flags |= 1;
+	}
+}
+
+// InitCommandG15 = InitNoCommand
+void InitCommandG(it_engine *ite, it_host *chn)
+{
+	// Check whether channel on/owned
+	if(chn->CVal != 0)
+	{
+		// Compatibility Gxx?
+		if((ite->hdr.Flags & 0x20) != 0)
+			chn->GOE = chn->CVal;
+		else
+			chn->EFG = chn->CVal;
+	}
+
+	if((chn->Flags & 4) == 0)
+	{
+		InitNoCommand(ite, chn);
+	} else {
+		InitCommandG11(ite, chn);
+	}
+}
+
+void InitCommandG11(it_engine *ite, it_host *chn)
+{
+	// added some flow comments because, well, the flow is weird --GM
+
+	// Jumped to from Lxx
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	int skipnote = 0;
+
+	if((chn->Msk & 0x22) != 0 && (chn->Smp != 0)) // false -> G13
+	{
+		int has_g18 = 0;
+		// Checking for change of Sample or instrument.
+		if((ite->hdr.Flags & 0x20) == 0) // false -> GXXCompat1
+		{
+			// Don't overwrite note if MIDI!
+			if(chn->Smp != 101) // false -> G13
+			{
+				uint8_t oldsmp = slave->Smp;
+				uint8_t oldins = slave->Ins;
+				slave->Nte = chn->Nte;
+				slave->Ins = chn->Ins;
+
+				/*
+				flow here is kinda odd:
+
+				INS SMP
+				 =   =  -> G13
+				 !   =  -> G18
+				 =   !  -> G16
+				 !   !  -> G16
+				*/
+
+				if(oldsmp != chn->Smp-1) // Sample the same?
+				{
+					// G16
+					it_sample *smp = &ite->smp[chn->Smp-1];
+					slave->Flags = (slave->Flags & 0xFF) | 0x0100;
+
+					// Now to update sample info.
+					slave->SmpOffs = chn->Smp-1;
+					slave->Smp = chn->Smp-1;
+
+					slave->ViDepth = 0; // Reset vibrato..
+					slave->LpD = 0; // Reset loop direction.
+					slave->OldSampleOffset = 0;
+					slave->SmpErr = 0;
+					slave->Sample_Offset = 0;
+
+					slave->SVl = smp->GvL*2;
+
+					if((smp->Flg & 1) == 0)
+					{
+						slave->Flags = 0x0200;
+						chn->Flags &= ~4;
+						return;
+					}
+
+					// 16 bit...
+					slave->Bit = (smp->Flg & 2);
+
+					GetLoopInformation(ite, slave);
+
+					// follow onto G18
+					has_g18 = 1;
+				} else if(oldins != chn->Ins) { // Ins the same?
+					// follow onto G18
+					has_g18 = 1;
+				}
+			}
+
+		} else {
+			// GXXCompat1
+			chn->Smp = slave->Smp+1;
+
+			it_sample *smp = &ite->smp[slave->Smp];
+			slave->SVl = smp->GvL*2;
+
+			// follow onto G18
+			has_g18 = 1;
+		}
+
+		// G18
+		if(has_g18)
+		{
+			if((ite->hdr.Flags & 4) != 0) // Instrument/sample mode?
+				// false -> G14
+			{
+				// Now for instruments
+				slave->FadeOut = 0x0400;
+				it_instrument *ins = &ite->ins[chn->Ins-1];
+
+				uint16_t oldflags = slave->Flags;
+
+				InitPlayInstrument(ite, chn, slave, chn->Ins);
+
+				if((oldflags & 1) != 0)
+					slave->Flags &= ~0x100;
+
+				slave->SVl = ((uint16_t)ins->GbV * (uint16_t)slave->SVl)>>7;
+				// follow onto G14
+			}
+
+			skipnote = 1; // -> G14 common jump
+		}
+	}
+
+	// G13 (refers to the actual mask condition)
+	if((skipnote != 0) || (chn->Msk & 0x11) != 0) // false -> G1
+	{
+		// G14
+		// OK. Time to calc freq.
+		if(chn->Nt2 > 119) // false -> G5
+		{
+			if((chn->Flags & 4) != 0) // false -> G1
+			{
+				if(chn->Nt2 > 0xFE)
+				{
+					// Note off
+					slave->Flags |= 4;
+					GetLoopInformation(ite, slave);
+
+				} else if(chn->Nt2 != 0xFE) {
+					slave->Flags |= 8;
+
+				} else {
+					// Note cut!
+					chn->Flags &= ~4;
+					slave->Flags = 0x200; // Cut.
+				}
+			}
+
+		} else {
+			// Don't overwrite note if MIDI!
+			if(chn->Smp != 101)
+				slave->Nte = chn->Nt2;
+
+			it_sample *smp = &ite->smp[slave->SmpOffs];
+
+			uint64_t c5 = ((uint64_t)smp->C5Speed) * (uint64_t)PitchTable[chn->Nt2];
+
+			c5 >>= (uint64_t)16;
+
+			chn->Porta_Frequency = c5;
+			chn->Flags |= 16;
+		}
+	}
+
+	int revol = -1;
+	if((chn->Msk & 0x44) != 0)
+	{
+		if(chn->Vol <= 64)
+		{
+			revol = chn->Vol;
+		} else if((chn->Vol & 0x7F) < 65) {
+			// Panning set...
+			InitCommandX2(ite, chn, chn->Vol - 128);
+		}
+	}
+
+	if(revol == -1 && (chn->Msk & 0x22) != 0)
+		revol = ite->smp[slave->SmpOffs].Vol;
+
+	if(revol != -1)
+	{
+		slave->Flags |= 16; // recalc volume.
+		chn->VSe = slave->Vol = slave->VS = revol;
+	}
+
+	if((chn->Flags & 16) != 0)       // Slide on???
+	{
+		// Work out magnitude + dirn
+		int16_t ax = chn->EFG;
+
+		if((ite->hdr.Flags & 0x20) != 0)   // Command G memory
+			ax = chn->GOE;
+
+		ax <<= 2;
+		if(ax != 0)
+		{
+			chn->_40 = ax;
+
+			if(chn->Porta_Frequency > slave->Frequency_Set)
+			{
+				// slide up
+				chn->_42 = 1 | (chn->_42 & 0xFF00);
+
+				if((chn->Flags & 0x100) == 0)
+					chn->Flags |= 1; // Update effect if necess.
+
+			} else if(chn->Porta_Frequency < slave->Frequency_Set) {
+				// slide down.
+				chn->_42 = 0 | (chn->_42 & 0xFF00);
+
+				if((chn->Flags & 0x100) == 0)
+					chn->Flags |= 1; // Update effect if necess.
+
+			}
+
+			// equal?!?!?
+			// then don't update effect.
+		}
+	}
+
+	// Don't call volume effects if it has a Gxx!
+	if((chn->Flags & 0x100) == 0) // comment this out and you'll get a stack overflow on a Gx VE --GM
+		InitVolumeEffect(ite, chn);
+}
+
+void InitCommandH(it_engine *ite, it_host *chn)
+{
+	if((chn->Msk & 0x11) != 0 && chn->Nte <= 119)
+	{
+		chn->VPo = 0;
+		chn->LVi = 0;
+	}
+
+	uint8_t ah = (chn->CVal & 0x0F); // AH = depth
+	uint8_t al = (chn->CVal & 0xF0); // AL = speed.
+
+	if(ah != 0 || al != 0)
+	{
+		al >>= 2;
+		if(al != 0)
+			chn->VSp = al;
+
+		ah <<= 2;
+		if(ah != 0)
+		{
+			if((ite->hdr.Flags & 0x10) != 0)
+				ah *= 2;
+
+			chn->VDp = ah;
+		}
+	}
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		// Update mode.
+		chn->Flags |= 1;
+		InitVibrato(ite, chn);
+	}
+}
+
+void InitCommandI(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->I00 = chn->CVal;
+
+	if((chn->Flags & 4) != 0)
+	{
+		// OK.. now to handle tremor
+		chn->Flags |= 1;
+
+		uint8_t al = chn->I00 & 0x0F; // AL = Offtime
+		uint8_t ah = chn->I00 >> 4;   // AH = ontime
+
+		if((ite->hdr.Flags & 16) != 0)
+		{
+			al++;
+			ah++;
+		}
+
+		uint16_t ax = (uint16_t)al | (((uint16_t)ah)<<8);
+		chn->_40 = ax; // fucking hell Jeff ;_; --GM
+
+		CommandI(ite, chn);
+	}
+}
+
+void InitCommandJ(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	chn->_40 = 0;
+	if(chn->CVal != 0)
+		chn->J00 = chn->CVal;
+
+	if((chn->Flags & 4) != 0)
+	{
+		chn->Flags |= 1; // Update when channel on
+
+		// TODO: verify - i think this actually stores the freq to mul by,
+		// which is 32-bit --GM
+		chn->_44 = (60+(chn->J00&0x0F))*4;
+		chn->_42 = (60+(chn->J00>>4))*4;
+	}
+}
+
+void InitCommandK(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->DKL = chn->CVal;
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		InitVibrato(ite, chn);
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		InitCommandD7(ite, chn, slave);
+
+		chn->Flags |= 2; // Always update.
+	}
+}
+
+void InitCommandL(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->DKL = chn->CVal;
+
+	if((chn->Flags & 4) != 0)
+	{
+		InitCommandG11(ite, chn);
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		InitCommandD7(ite, chn, slave);
+
+		chn->Flags |= 2; // Always update.
+	}
+}
+
+void InitCommandM2(it_engine *ite, it_host *chn, uint8_t al)
+{
+	if((chn->Flags & 4) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+
+		slave->CVl = al;
+		slave->Flags |= 16; // recalc volume
+	}
+
+	chn->CV = al;
+}
+
+void InitCommandM(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal > 0x40) return;
+
+	InitCommandM2(ite, chn, chn->CVal);
+}
+
+void InitCommandN(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->N00 = chn->CVal;
+
+	InitNoCommand(ite, chn);
+
+	if((chn->N00 & 0x0F) == 0)
+	{
+		chn->_40 = (chn->_40 & 0xFF00) | ((((int16_t)chn->N00)>>4) & 0xFF);
+		chn->Flags |= 2; // Always update effect
+
+	} else if((chn->N00 & 0xF0) == 0) {
+		chn->_40 = (chn->_40 & 0xFF00) | ((-(int16_t)chn->N00) & 0xFF);
+		chn->Flags |= 2;
+
+	} else if((chn->N00 & 0x0F) == 0x0F) {
+		uint8_t al = (chn->N00>>4) + chn->CV;
+
+		if(al > 64) al = 64;
+
+		InitCommandM2(ite, chn, al);
+		return;
+
+	} else if((chn->N00 & 0xF0) == 0xF0) {
+		//
+		uint8_t al = (chn->N00>>4) - chn->CV;
+
+		if((chn->N00>>4) < chn->CV) al = 0;
+
+		InitCommandM2(ite, chn, al);
+		return;
+
+	}
+}
+
+void InitCommandO(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->O00 = chn->CVal;
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Msk & 0x33) == 0) return;
+	if(chn->Nt2 >= 120) return;
+	if((chn->Flags & 4) == 0) return;
+
+	uint32_t eax;
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	eax = ((uint32_t)chn->O00) + (((uint32_t)chn->OxH)<<8);
+	eax <<= 8;
+
+	if(eax >= slave->Loop_End)
+	{
+		if((ite->hdr.Flags & 16) == 0) return;
+		eax = slave->Loop_End-1;
+	}
+
+	slave->OldSampleOffset = slave->Sample_Offset = eax;
+	slave->SmpErr = 0;
+}
+
+void InitCommandP(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->P00 = chn->CVal;
+
+	InitNoCommand(ite, chn);
+
+	uint8_t dl = chn->CP;
+	if((chn->Flags & 4) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		dl = slave->PS; // Pan set
+	}
+
+	if(dl == 100) // Surround??
+		return;
+
+	uint8_t al = chn->P00;
+
+	if((al & 0x0F) == 0)
+	{
+		chn->_40 = (chn->_40 & 0xFF00) + ((-(int16_t)(al >> 4)) & 0x00FF);
+		chn->Flags |= 2; // Always update effect
+
+	} else if((al & 0xF0) == 0) {
+		chn->_40 = (chn->_40 & 0xFF00) + (((int16_t)al) & 0x00FF);
+		chn->Flags |= 2;
+
+	} else if((al & 0x0F) == 0x0F) {
+		uint8_t xal = dl - (al>>4);
+
+		if(dl < (al>>4)) xal = 0;
+
+		InitCommandX2(ite, chn, xal);
+
+	} else if((al & 0xF0) == 0xF0) {
+		al = al + dl;
+
+		if(al > 64) al = 64;
+
+		InitCommandX2(ite, chn, al);
+
+	}
+
+}
+
+void InitCommandQ(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->Q00 = chn->CVal;
+
+	if((chn->Flags & 4) != 0)
+	{
+		chn->Flags |= 1;
+
+		if((chn->Msk & 0x11) != 0)
+		{
+			chn->RTC = chn->CVal & 0x0F; // retrig countdown
+
+		} else {
+			CommandQ(ite, chn);
+
+		}
+	}
+
+}
+
+void InitCommandR(it_engine *ite, it_host *chn)
+{
+	uint8_t ah = chn->CVal & 0x0F; // AH = depth
+	uint8_t al = chn->CVal & 0xF0; // AL = speed.
+
+	if(ah != 0 || al != 0)
+	{
+		al >>= 2;
+		if(al != 0) chn->TSp = al;
+
+		ah <<= 1;
+		if(ah != 0) chn->TDp = ah;
+	}
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		chn->Flags |= 1; // Update mode.
+		InitTremelo(ite, chn);
+	}
+}
+
+void InitCommandS(it_engine *ite, it_host *chn)
+{
+	uint8_t al = chn->CVal;
+	it_slave *slave;
+	uint16_t cx;
+
+	if(al != 0)
+		chn->S00 = al;
+
+	al = chn->S00;
+
+	uint8_t ah = al;
+	ah &= 0xF0;
+	al &= 0x0F;
+
+	chn->_40 = (ah<<8) | al; // Misc effects data.
+
+	switch(ah>>4)
+	{
+		case 0x0: // 0
+		case 0x1: // 1
+		case 0x2: // 2
+			break;
+
+		case 0x3: // 3 - set vibrato waveform
+			if(al <= 3) chn->VWF = al;
+			break;
+
+		case 0x4: // 4 - set tremelo waveform
+			if(al <= 3) chn->TWF = al;
+			break;
+
+		case 0x5: // 5 - set panbrello waveform
+			if(al <= 3)
+			{
+				chn->PWF = al;
+				chn->PPo = 0;
+			}
+			break;
+
+		case 0x6: // 6 - extra delay of x frames
+			ite->CurrentTick += al;
+			ite->ProcessTick += al;
+			break;
+
+		case 0x7: // 7 - instrument functions
+		switch(al)
+		{
+			case 0x0: // Past note cut
+				InitNoCommand(ite, chn);
+				al = chn->HCN | 0x80;
+
+				for(slave = &ite->slave[0], cx = ite->NumChannels; cx != 0; cx--, slave++)
+				{
+					if(al != slave->HCN) continue;
+
+					if((ite->d.DriverFlags & 2) != 0)
+						slave->Flags |= 0x200;
+					else
+						slave->Flags  = 0x200;
+				}
+				return;
+
+			case 0x1: // Past note off
+			case 0x2: // Past note fade
+				InitNoCommand(ite, chn);
+				ah = (al == 0x1 ? 4 : 8);
+				for(slave = &ite->slave[0], cx = ite->NumChannels; cx != 0; cx--, slave++)
+				{
+					if(al == slave->HCN)
+					{
+						slave->Flags |= ah;
+						GetLoopInformation(ite, slave);
+					}
+				}
+				return;
+
+			case 0x3: // Set NNA to cut
+			case 0x4: // Set NNA to continue
+			case 0x5: // Set NNA to off
+			case 0x6: // Set NNA to fade
+				InitNoCommand(ite, chn);
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->NNA = al-3;
+				}
+
+				return;
+
+				// the comments are backwards here so I've fixed them --GM
+			case 0x7: // Set volume envelope off
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags &= ~0x1000;
+				}
+
+				return;
+
+			case 0x8: // Set volume envelope on
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags |= 0x1000;
+				}
+
+				return;
+
+			case 0x9: // Set panning envelope off
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags &= ~0x2000;
+				}
+				break;
+
+			case 0xA: // Set panning envelope on
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags |= 0x2000;
+				}
+				break;
+
+			case 0xB: // Set pitch envelope off
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags |= ~0x4000;
+				}
+				break;
+
+			case 0xC: // Set pitch envelope on
+				InitNoCommand(ite, chn);
+
+				if((chn->Flags & 4) != 0)
+				{
+					it_slave *slave = &ite->slave[chn->SCOffst];
+					slave->Flags |= 0x4000;
+				}
+				break;
+
+			case 0xD:
+			case 0xE:
+			case 0xF:
+				break;
+		} break;
+
+		case 0x8: // 8 - set pan
+			ah = al;
+			ah <<= 4;
+			al |= ah;
+			ah = 0;
+
+			if(((al+2)&0xFF) < al) ah++;
+			al += 2;
+			al >>= 2;
+			al |= (ah << 6);
+			// TODO: verify
+
+			InitNoCommand(ite, chn);
+			InitCommandX2(ite, chn, al);
+			return;
+
+		case 0x9: // 9 - set surround
+			if(al == 1)
+			{
+				al = 100;
+				InitNoCommand(ite, chn);
+				InitCommandX2(ite, chn, al);
+				return;
+			}
+
+			break;
+
+		case 0xA: // A - Set high order offset
+			chn->OxH = al;
+			break;
+
+		case 0xB: // B - loop control
+			InitNoCommand(ite, chn);
+
+			if(al == 0)
+			{
+				chn->PLR = ite->CurrentRow;
+
+			} else if(chn->PLC == 0) {
+				chn->PLC = al;
+				ite->ProcessRow = ((uint16_t)chn->PLR)-1;
+				ite->PatternLooping = 1;
+
+			} else if((--chn->PLC) != 0) {
+				ite->ProcessRow = ((uint16_t)chn->PLR)-1;
+				ite->PatternLooping = 1;
+
+			} else {
+				chn->PLR = 1+ite->CurrentRow;
+
+			}
+			return;
+
+		case 0xC: // C - note cut
+			chn->Flags |= 1;
+			break;
+
+		case 0xD: // D - note delay
+			chn->Flags |= 2;
+			return;
+
+		case 0xE: // E - pattern delay
+			if(ite->RowDelayOn == 0)
+			{
+				ite->RowDelay = al+1;
+				ite->RowDelayOn = 1;
+			}
+
+			break;
+
+		case 0xF: // F - MIDI Macro select
+			chn->SFx = al;
+			break;
+	}
+
+	InitNoCommand(ite, chn);
+}
+
+void InitCommandT(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal != 0)
+		chn->T00 = chn->CVal;
+
+	if(chn->T00 >= 0x20)
+	{
+		ite->Tempo = chn->T00;
+		Music_InitTempo(ite);
+		InitNoCommand(ite, chn);
+
+	} else {
+		InitNoCommand(ite, chn);
+		chn->Flags |= 2; // Update mode
+
+	}
+}
+
+void InitCommandU(it_engine *ite, it_host *chn)
+{
+	if((chn->Msk & 0x11) != 0)
+	{
+		chn->VPo = 0;
+		chn->LVi = 0;
+	}
+
+	uint8_t ah = chn->CVal & 0x0F; // AH = depth
+	uint8_t al = chn->CVal & 0xF0; // AL = speed.
+
+	if(al != 0)
+	{
+		al >>= 2;
+		chn->VSp = al;
+	}
+
+	if(ah != 0)
+	{
+		if((ite->hdr.Flags & 16) != 0)
+			ah *= 2;
+
+		chn->VDp = ah;
+	}
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		chn->Flags |= 1; // Update mode.
+		InitVibrato(ite, chn);
+	}
+
+}
+
+void InitCommandV(it_engine *ite, it_host *chn)
+{
+	if(chn->CVal <= 0x80)
+	{
+		ite->GlobalVolume = chn->CVal;
+		RecalculateAllVolumes(ite);
+	}
+
+	InitNoCommand(ite, chn);
+}
+
+void InitCommandW(it_engine *ite, it_host *chn)
+{
+	// Global volume slides!
+
+	InitNoCommand(ite, chn);
+
+	if(chn->CVal != 0)
+		chn->W00 = chn->CVal;
+
+	if(chn->W00 == 0)
+	{
+		return;
+
+	} else if((chn->W00 & 0xF0) == 0) {
+		chn->_40 = (chn->_40 & 0xFF00) | (0xFF & (int16_t)(-chn->W00));
+		chn->Flags |= 2;
+
+	} else if((chn->W00 & 0x0F) == 0) {
+		chn->_40 = (chn->_40 & 0xFF00) | (0xFF & (int16_t)(chn->W00>>4));
+		chn->Flags |= 2;
+
+	} else if((chn->W00 & 0xF0) == 0xF0) {
+		if(ite->GlobalVolume >= (chn->W00 & 0x0F))
+			ite->GlobalVolume -= (chn->W00 & 0x0F);
+		else
+			ite->GlobalVolume = 0;
+
+		RecalculateAllVolumes(ite);
+
+	} else if((chn->W00 & 0x0F) == 0x0F) {
+		ite->GlobalVolume += (chn->W00>>4);
+		if((ite->GlobalVolume & 0x80) != 0)
+			ite->GlobalVolume = 128;
+
+		RecalculateAllVolumes(ite);
+
+	}
+}
+
+void InitCommandX(it_engine *ite, it_host *chn)
+{
+	InitNoCommand(ite, chn);
+
+	uint16_t ax = chn->CVal;
+	ax += 2;
+	ax >>= 2;
+	InitCommandX2(ite, chn, (uint8_t)ax);
+}
+
+void InitCommandX2(it_engine *ite, it_host *chn, uint8_t al)
+{
+	if((chn->Flags & 4) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		slave->PS = slave->Pan = al;
+		slave->Flags |= 64+2; // Recalculate pan
+	}
+
+	chn->CP = al;
+}
+
+void InitCommandY(it_engine *ite, it_host *chn)
+{
+	uint8_t ah = chn->CVal & 0x0F; // AH = depth
+	uint8_t al = chn->CVal & 0xF0;
+
+	if(ah != 0 || al != 0)
+	{
+		al >>= 4;
+		if(al != 0)
+			chn->PSp = al;
+
+		ah <<= 1;
+		if(ah != 0)
+			chn->PDp = ah;
+	}
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		chn->Flags |= 1; // Update mode.
+		CommandY(ite, chn);
+	}
+}
+
+void InitCommandZ(it_engine *ite, it_host *chn)
+{
+	// Macros start at 120h, 320h
+	InitNoCommand(ite, chn);
+
+	uint16_t bx = chn->CVal;
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	if((bx & 0x80) == 0)
+	{
+		bx = chn->SFx & 0x0F; // 0->7Fh - BX = SFx number
+		bx <<= 5;
+		bx += 0x120;
+		MIDITranslate(ite, chn, slave, bx);
+
+	} else {
+		// Macros!
+		bx &= 0x7F;
+		bx <<= 5; // BX = (xx-80x)*20h
+		bx += 0x320;
+		MIDITranslate(ite, chn, slave, bx);
+
+	}
+}
+
+void NoCommand(it_engine *ite, it_host *chn)
+{
+	// DS:DI points to CIT Area
+	USED(ite, chn);
+}
+
+void CommandD(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	uint8_t al = chn->VCh + slave->VS; // Volset.
+
+	if((al & 0x80) == 0)
+	{
+		if(al > 64)
+		{
+			chn->Flags &= ~1;
+			al = 64;
+		}
+
+	} else {
+		chn->Flags &= ~1;
+		al = 0;
+	}
+
+	CommandD2(ite, chn, slave, al);
+}
+
+void CommandD2(it_engine *ite, it_host *chn, it_slave *slave, uint8_t al)
+{
+	USED(ite);
+	slave->Vol = al;
+	slave->VS = al;
+	chn->VSe = al;
+	slave->Flags |= 16; // Recalc vol
+}
+
+void CommandE(it_engine *ite, it_host *chn)
+{
+	CommandEChain(ite, chn, chn->_40);
+}
+
+void CommandEChain(it_engine *ite, it_host *chn, int16_t bx)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	PitchSlideDown(ite, chn, slave, bx);
+	slave->Frequency_Set = slave->Frequency;
+}
+
+void CommandF(it_engine *ite, it_host *chn)
+{
+	CommandFChain(ite, chn, chn->_40);
+}
+
+void CommandFChain(it_engine *ite, it_host *chn, int16_t bx)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	PitchSlideUp(ite, chn, slave, bx);
+	slave->Frequency_Set = slave->Frequency;
+}
+
+void CommandG(it_engine *ite, it_host *chn)
+{
+	if((chn->Flags & 16) == 0)
+		return;
+
+	int16_t bx = chn->_40;
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	if((chn->_42 & 0xFF) != 1)
+	{
+		// Slide down
+		PitchSlideDown(ite, chn, slave, bx);
+
+		// Check that frequency is above porta
+		//  to frequency.
+		uint32_t eax = slave->Frequency;
+		if(eax <= chn->Porta_Frequency)
+		{
+			eax = chn->Porta_Frequency;
+			chn->Flags &= ~(3 | 16); // Turn off calling
+			slave->Frequency = eax;
+		}
+
+		slave->Frequency_Set = eax;
+
+	} else {
+		// Slide up!
+		PitchSlideUp(ite, chn, slave, bx);
+
+		// Check that
+		//  1) Channel is on
+		//  2) Frequency (set) is below porta to
+		//       frequency
+		uint32_t eax = slave->Frequency;
+
+		if((slave->Flags & 0x200) == 0 && eax < chn->Porta_Frequency)
+		{
+			slave->Frequency_Set = eax;
+			return;
+		}
+
+		slave->Flags &= ~0x200;
+		chn->Flags |= 4; // Turn on.
+		eax = chn->Porta_Frequency;
+		chn->Flags &= ~(3 | 16); // Turn off calling
+		slave->Frequency = eax;
+		slave->Frequency_Set = eax;
+	}
+
+}
+
+void InitVibrato(it_engine *ite, it_host *chn)
+{
+	if((ite->hdr.Flags & 0x10) == 0)
+	{
+		CommandH(ite, chn);
+
+	} else {
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		slave->Flags |= 32; // Freq change...
+		CommandH5(ite, chn, slave, chn->LVi);
+	}
+}
+
+void CommandH(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	slave->Flags |= 32; // Freq change...
+
+	// Add speed. / Save value
+	chn->VPo += chn->VSp;
+
+	// Mov     BH, [DI+38h]            // AL = waveform
+	// gg wp --GM
+
+	int8_t al = 0;
+	if(chn->VWF != 3)
+	{
+		// probably not wise to try to emulate out-of-range vibrato types.
+		// well, at least for now. we can research these later. --GM
+		switch(chn->VWF)
+		{
+			case 0:
+				al = FineSineData[chn->VPo];
+				break;
+			case 1:
+				al = FineRampDownData[chn->VPo];
+				break;
+			case 2:
+				al = FineSquareWave[chn->VPo];
+				break;
+			default:
+				sysfatal("out of range vibrato types not emulated!");
+		}
+
+	} else {
+		al = (Random(ite)&127)-64; // Random.
+
+	}
+
+	chn->LVi = al; // Save last vibrato.
+
+	CommandH5(ite, chn, slave, al);
+}
+
+void CommandH5(it_engine *ite, it_host *chn, it_slave *slave, int8_t al)
+{
+	int16_t ax = ((int16_t)al) * (int16_t)(int8_t)(chn->VDp);
+	ax <<= 2;
+	ax += 0x80;
+	ax >>= 8; // actual code then simply uses AH --GM
+
+	if((ite->hdr.Flags & 16) != 0)
+	{
+		// don't ask me why sackit does weird things here,
+		// i could have sworn Jeff had done a one's complement
+		// but that seems to not be the case (in IT 2.14 at least) --GM
+
+		ax = -ax;
+	}
+
+	//MovZX   BX, AH
+
+	// AH = EEx/FEx command value
+	if(ax < 0)
+		PitchSlideDown(ite, chn, slave, -ax);
+	else
+		PitchSlideUp(ite, chn, slave, ax);
+}
+
+void CommandI(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	slave->Flags |= 16; // recalc volume;
+
+	chn->TCD--;
+
+	if((chn->TCD & 0x80) != 0 || chn->TCD == 0)
+	{
+		chn->Too ^= 1;
+		chn->TCD = (uint8_t)(chn->Too != 0 ? (chn->_40>>8) : (chn->_40&0xFF));
+	}
+
+	if(chn->Too != 1)
+		slave->Vol = 0;
+
+}
+
+void CommandJ(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	int16_t bx = chn->_40;
+
+	slave->Flags |= 32;
+
+	bx += 2;
+	if(bx >= 6)
+	{
+		chn->_40 = 0;
+		return;
+	}
+
+	chn->_40 = bx;
+	uint64_t rad = (uint64_t)slave->Frequency;
+	rad *= (uint64_t)PitchTable[(bx == 2 ? chn->_42 : chn->_44)/4];
+
+	//printf("honk %lX\n", rad);
+	// FIXME work out how this damn thing works
+	rad >>= (uint64_t)16;
+	/*
+	if((rad & (uint64_t)0xFFFF000000000000LLU) != 0)
+		rad &= ~(uint64_t)0xFFFFFFFFLLU;
+	else
+		rad >>= (uint64_t)rad;
+	*/
+
+	slave->Frequency = (uint32_t)rad;
+}
+
+void CommandK(it_engine *ite, it_host *chn)
+{
+	CommandH(ite, chn);
+	CommandD(ite, chn);
+}
+
+void CommandL(it_engine *ite, it_host *chn)
+{
+	if((chn->Flags & 16) != 0)
+	{
+		CommandG(ite, chn);
+		chn->Flags |= 1;
+	}
+
+	CommandD(ite, chn);
+}
+
+void CommandN(it_engine *ite, it_host *chn)
+{
+	uint8_t al = chn->CV + chn->_40;
+
+	if((al & 0x80) == 0)
+		al = 0;
+	else if(al > 64)
+		al = 64;
+
+	InitCommandM2(ite, chn, al);
+}
+
+void CommandP(it_engine *ite, it_host *chn)
+{
+	int8_t al = chn->CP;
+
+	if((chn->Flags & 4) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		al = slave->PS;
+	}
+
+	al += (int8_t)(chn->_40 & 0xFF);
+
+	if(al < 0)
+		al = 0;
+	else if(al > 64)
+		al = 64;
+
+	InitCommandX2(ite, chn, al);
+}
+
+void CommandQ(it_engine *ite, it_host *chn)
+{
+	chn->RTC--;
+	if(chn->RTC != 0 && chn->RTC != 0xFF)
+		return;
+
+	// OK... reset counter.
+	chn->RTC = chn->Q00 & 0x0F; // retrig count done.
+	//And     BX, 0F00Fh
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	if((ite->d.DriverFlags & 2) != 0) // Hiqual?
+	{
+		if((ite->hdr.Flags & 4) == 0) // Instrument mode?
+		{
+			// Sample mode
+			memcpy(slave+64, slave, sizeof(it_slave));
+
+			(slave+64)->Flags |= 0x200; // Cut
+			(slave+64)->HCN |= 0x80;    // Disowned
+		} else {
+			// Instrument mode
+
+			it_slave *dest = &ite->slave[0];
+			uint16_t cx = ite->NumChannels;
+
+			for(; cx != 0; cx--, dest++)
+			{
+				if((dest->Flags & 1) == 0)
+				{
+					memcpy(dest, slave, sizeof(it_slave));
+					dest->Flags |= 0x200; // Cut
+					dest->HCN |= 0x80;    // Disowned
+					slave = dest;
+
+					// TODO: verify C behaviour
+					chn->SCOffst = (dest - &ite->slave[0]);
+					break;
+				}
+			}
+		}
+	}
+
+	slave->OldSampleOffset = 0;
+	slave->SmpErr = 0;
+	slave->Sample_Offset = 0;
+
+	slave->Flags |= 0x540;
+
+	uint8_t al = slave->VS;
+	int check = 0;
+	switch(chn->Q00>>4)
+	{
+		case 0x0:
+			return;
+
+		case 0x1:
+			al--;
+			check = -1;
+			break;
+
+		case 0x2:
+			al -= 2;
+			check = -1;
+			break;
+
+		case 0x3:
+			al -= 4;
+			check = -1;
+			break;
+
+		case 0x4:
+			al -= 8;
+			check = -1;
+			break;
+
+		case 0x5:
+			al -= 16;
+			check = -1;
+			break;
+
+		case 0x6:
+			al <<= 1;
+			al /= 3;
+			break;
+
+		case 0x7:
+			al >>= 1;
+			break;
+
+		case 0x8:
+			return;
+
+		case 0x9:
+			al++;
+			check = 1;
+			break;
+
+		case 0xA:
+			al += 2;
+			check = 1;
+			break;
+
+		case 0xB:
+			al += 4;
+			check = 1;
+			break;
+
+		case 0xC:
+			al += 8;
+			check = 1;
+			break;
+
+		case 0xD:
+			al += 16;
+			check = 1;
+			break;
+
+		case 0xE:
+			al = (al + al + al) >> 1;
+			check = 1;
+			break;
+
+		case 0xF:
+			al <<= 1;
+			check = 1;
+			break;
+	}
+
+	if(check < 0 && (al & 0x80) != 0)
+		al = 0;
+	else if(check > 0 && al > 64)
+		al = 64;
+
+	slave->VS = slave->Vol = al;
+	chn->VSe = al;
+	slave->Flags |= 16; // recalc volume flag
+
+	if(chn->Smp == 101) // MIDI sample
+		MIDITranslate(ite, chn, slave, MIDICOMMAND_STOPNOTE);
+
+}
+
+void InitTremelo(it_engine *ite, it_host *chn)
+{
+	if((ite->hdr.Flags & 0x10) == 0)
+	{
+		CommandR(ite, chn);
+
+	} else {
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		slave->Flags |= 64; // Volume change...
+		CommandR2(ite, chn, slave, chn->LTr);
+
+	}
+}
+
+void CommandR(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	slave->Flags |= 64; // Volume change
+
+	// TODO: verify
+
+	// Add speed. / Save value
+	chn->TPo += chn->TSp;
+
+	int8_t al = 0;
+	if(chn->TWF != 3)
+	{
+		// probably not wise to try to emulate out-of-range vibrato types.
+		// well, at least for now. we can research these later. --GM
+		switch(chn->TWF)
+		{
+			case 0:
+				al = FineSineData[chn->TPo];
+				break;
+			case 1:
+				al = FineRampDownData[chn->TPo];
+				break;
+			case 2:
+				al = FineSquareWave[chn->TPo];
+				break;
+			default:
+				sysfatal("out of range vibrato types not emulated!");
+		}
+
+	} else {
+		al = (Random(ite)&127)-64; // Random.
+
+	}
+
+	CommandR2(ite, chn, slave, al);
+}
+
+void CommandR2(it_engine *ite, it_host *chn, it_slave *slave, int8_t al)
+{
+	USED(ite);
+	int16_t ax;
+	ax = al;
+	ax *= (int16_t)(int8_t)(chn->TDp);
+	ax <<= 2;
+	ax += 0x80;
+	ax >>= 8;
+
+	al = slave->Vol + ax;
+
+	if((al & 0x80) != 0)
+		al = 0;
+	if(al > 64)
+		al = 64;
+
+	slave->Vol = al;
+}
+
+void CommandS(it_engine *ite, it_host *chn)
+{
+	// Have to handle SDx, SCx
+	// AH = command, AL = value.
+	uint8_t ah = (chn->_40>>8);
+	uint8_t al = (chn->_40&0xFF);
+	USED(al);
+
+	if(ah == 0xD0)
+	{
+		// this is just an 8-bit dec in a 16-bit value --GM
+		if(chn->_40 == 0) chn->_40 |= 0xFF;
+		else chn->_40--;
+
+		if((chn->_40 & 0x80) == 0 && (chn->_40 & 0xFF) != 0)
+			return;
+
+		chn->Flags &= ~3;
+		InitNoCommand(ite, chn);
+
+		chn->Flags |= 64;
+
+		// Check whether chn is on
+		if((ite->hdr.Chnl_Vol[chn->HCN] & 0x80) == 0) return;
+		if((chn->Flags & 32) != 0) return;
+		if((chn->Flags & 4) == 0) return; // Channel was off.
+
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		slave->Flags |= 0x800;
+
+	} else if(ah == 0xC0) {
+		if((chn->Flags & 4) == 0) return;
+
+		// this is just an 8-bit dec in a 16-bit value --GM
+		if(chn->_40 == 0) chn->_40 |= 0xFF;
+		else chn->_40--;
+
+		if((chn->_40 & 0x80) == 0 && (chn->_40 & 0xFF) != 0)
+			return;
+
+		it_slave *slave = &ite->slave[chn->SCOffst]; // Note cut.
+
+		chn->Flags &= ~4;
+
+		if(slave->Smp != 100 && (ite->d.DriverFlags & 2) == 0)
+			slave->Flags  = 0x200;
+		else
+			slave->Flags |= 0x200;
+	}
+}
+
+void CommandT(it_engine *ite, it_host *chn)
+{
+	// TODO: verify - flow somewhat different from original source --GM
+
+	if((chn->T00 & 0xF0) != 0)
+	{
+		// Slide up
+		ite->Tempo += chn->T00;
+		ite->Tempo -= 0x10;
+
+	} else {
+		// Slide down
+		ite->Tempo -= 0x10;
+		if(ite->Tempo < 0x20)
+			ite->Tempo = 0x20;
+	}
+
+	ite->d.DriverSetTempo(ite, ite->Tempo);
+}
+
+void CommandW(it_engine *ite, it_host *chn)
+{
+	// Global volume slide!
+	int16_t ax = (int16_t)(int8_t)(chn->_40 & 0xFF);
+	ax += ite->GlobalVolume;
+
+	if((ax & 0x8000) != 0)
+		ax = 0;
+
+	if((ax & 0xFF) > 128)
+		ax = 128;
+
+	ite->GlobalVolume = ax;
+
+	RecalculateAllVolumes(ite);
+}
+
+void CommandY(it_engine *ite, it_host *chn)
+{
+	// TODO!
+	USED(ite, chn);
+}
+
+#if 0
+void CommandY(it_engine *ite, it_host *chn)
+{
+	Test    Byte Ptr [DI], 4
+	JZ      CommandY5
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+
+	Mov     BH, [DI+28h]            // AL = waveform
+	Cmp     BH, 3
+	JAE     CommandY1
+
+	Mov     BL, [DI+29h]            // Pos
+	Add     BL, [DI+2Bh]            // Add speed.
+	Mov     [DI+29h], BL            // Save value
+
+	Mov     AL, [FineSineData+BX]       // AL = -64 -> 64
+
+	Jmp     CommandY2
+
+CommandY1:                                      // Random panning make
+					// speed the delay time.
+	Dec     Byte Ptr [DI+29h]
+	JZ      CommandY6
+	JS      CommandY6
+
+	Mov     AL, [DI+2Ch]
+	Jmp     CommandY2
+
+CommandY6:
+	Mov     BL, [DI+2Bh]
+	Mov     [DI+29h], BL            // reset countdown.
+
+	Call    Random
+	And     AL, 127
+	Sub     AL, 64
+
+	Mov     [DI+2Ch], AL
+
+CommandY2:
+	IMul    Byte Ptr [DI+2Ah]
+	SAL     AX, 2
+	Add     AX, 80h
+	MovZX   BX, AH
+					// AH = panning change
+	Mov     AL, [SI+2Bh]            // AL = panning
+	Cmp     AL, 100                 // Surround?
+	JE      CommandY5
+
+	Add     AL, AH
+	JNS     CommandY3
+
+	Xor     AL, AL
+
+CommandY3:
+	Cmp     AL, 64
+	JBE     CommandY4
+
+	Mov     AL, 64
+
+CommandY4:
+	Or      Byte Ptr [SI], 2        // Panning change
+	Mov     [SI+2Ah], AL
+
+CommandY5:
+	Ret
+
+}
+#endif
+
--- /dev/null
+++ b/it_music.c
@@ -1,0 +1,4781 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "it_struc.h"
+
+// temporary shit to make this work --GM
+
+it_engine *ITEngineNew(void)
+{
+	int i;
+
+	it_engine *ite = calloc(sizeof(it_engine), 1);
+
+	// TODO: initialise properly
+	for(i = 0; i < 200; i++)
+		ite->pat[i] = NULL;
+	for(i = 0; i < 100; i++)
+		ite->SamplePointer[i] = NULL;
+
+	ite->NumChannels = 256;
+	// shifting this here so it doesn't try to use a real unpacked pattern --GM
+	ite->CurrentEditPattern = 199;
+	ite->PatternNumber = 199;
+	ite->CmdLineNumChannels = -1;
+
+	return ite;
+}
+
+it_drvdata *drv_oss_init(it_engine *ite);
+it_drvdata *drv_sdl_init(it_engine *ite);
+
+it_drvdata *DriverSys_GetByName(it_engine *ite, const char *fname)
+{
+	printf("drv: %s\n", fname);
+
+	if(!strcmp(fname, "oss"))
+		return drv_oss_init(ite);
+	if(!strcmp(fname, "sdl"))
+		return drv_sdl_init(ite);
+
+	return NULL;
+}
+
+it_pattern *PE_GetCurrentPattern(it_engine *ite, uint16_t *patnum, uint16_t *patrow)
+{
+	*patnum = ite->PatternNumber;
+	*patrow = ite->MaxRow + 1;
+
+	return NULL; // actually meant to return UNPACKED pattern data area --GM
+}
+
+uint16_t PE_GetLastInstrument(it_engine *ite)
+{
+	return ite->LastInstrument-1;
+}
+
+//
+// Functions for playing control
+//  Music_PlaySong........ parameters, AX = order
+//  Music_Stop............ parameters, None
+//  Music_PlayPattern..... parameters, AX = pattern, BX = number of rows, CX = row
+//  Music_ToggleChannel... parameters, AX = channel
+//  Music_SoloChannel..... parameters, AX = channel
+//
+
+const int8_t FineSineData[] = {
+	  0,  2,  3,  5,  6,  8,  9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+	 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+	 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+	 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+	 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+	 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+	 24, 23, 22, 20, 19, 17, 16, 14, 12, 11,  9,  8,  6,  5,  3,  2,
+	  0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+	-24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+	-45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+	-59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+	-59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+	-45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+	-24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2,
+};
+
+const int8_t FineRampDownData[] = {
+	 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+	 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+	 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+	 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, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+	 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10,  9,  9,  8,
+	  8,  7,  7,  6,  6,  5,  5,  4,  4,  3,  3,  2,  2,  1,  1,  0,
+	  0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+	 -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+	-16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+	-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+	-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+	-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+	-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+	-56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64,
+};
+
+const int8_t FineSquareWave[] = {
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	  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,
+	  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,
+	  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,
+	  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,
+};
+
+const uint8_t EmptyPattern[] = {
+	64, 0, 64, 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, 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, 0, 0, 0, 0, 0,
+};
+
+// Zero globals.
+
+const float PitchDepthConstant               = 98304.0f;
+
+
+// TODO: Turn these two into proper structs
+uint8_t InstrumentHeader[] = {
+        'I', 'M', 'P', 'I',
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	60, 128, 32+128,
+	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, 0, 0,
+        0, 0xFF, 0xFF, 0xFF,
+        0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
+        5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
+        10, 0, 11, 0, 12, 0, 13, 0, 14, 0,
+        15, 0, 16, 0, 17, 0, 18, 0, 19, 0,
+        20, 0, 21, 0, 22, 0, 23, 0, 24, 0,
+        25, 0, 26, 0, 27, 0, 28, 0, 29, 0,
+        30, 0, 31, 0, 32, 0, 33, 0, 34, 0,
+        35, 0, 36, 0, 37, 0, 38, 0, 39, 0,
+        40, 0, 41, 0, 42, 0, 43, 0, 44, 0,
+        45, 0, 46, 0, 47, 0, 48, 0, 49, 0,
+        50, 0, 51, 0, 52, 0, 53, 0, 54, 0,
+        55, 0, 56, 0, 57, 0, 58, 0, 59, 0,
+        60, 0, 61, 0, 62, 0, 63, 0, 64, 0,
+        65, 0, 66, 0, 67, 0, 68, 0, 69, 0,
+        70, 0, 71, 0, 72, 0, 73, 0, 74, 0,
+        75, 0, 76, 0, 77, 0, 78, 0, 79, 0,
+        80, 0, 81, 0, 82, 0, 83, 0, 84, 0,
+        85, 0, 86, 0, 87, 0, 88, 0, 89, 0,
+        90, 0, 91, 0, 92, 0, 93, 0, 94, 0,
+        95, 0, 96, 0, 97, 0, 98, 0, 99, 0,
+        100, 0, 101, 0, 102, 0, 103, 0, 104, 0,
+        105, 0, 106, 0, 107, 0, 108, 0, 109, 0,
+        110, 0, 111, 0, 112, 0, 113, 0, 114, 0,
+        115, 0, 116, 0, 117, 0, 118, 0, 119, 0,
+        0, 2, 0, 0, 0, 0, 64, 0, 0, 64, 100, 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, 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, 0, 0, 0, 0, 0, 0, 0,
+        0, 2, 0, 0, 0, 0,  0, 0, 0,  0, 100, 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, 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, 0, 0, 0, 0, 0, 0, 0,
+        0, 2, 0, 0, 0, 0,  0, 0, 0,  0, 100, 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, 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, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0,
+};
+
+uint8_t SampleHeader[80] = {
+	'I', 'M', 'P', 'S',
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	64, 0, 64,
+	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, 0, 0, 0, 0, 0, 0, 0, 0,
+	(8363&0xFF), (8363>>8), 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+};
+
+uint8_t MIDIPitchSendString[] = {0x65, 0x00, 0x64, 0x00, 0x06};
+
+const char *PrepareSamplesMsg        = "Preparing Samples";
+const char *ReverseMsg               = "Left/right outputs reversed";
+const char *NoSoundCardMsg           = "   No sound card detected";
+const char *MIDIConfigFileName       = "ITMIDI.CFG";
+
+const char *OrderUpdateEnabledMsg    = "Order list unlocked";
+const char *OrderUpdateDisabledMsg   = "Order list locked";
+
+const char *UnsoloMsg                = "Solo disabled";
+const char *SoloSampleMsg            = "Solo sample \375D";
+const char *SoloInstrumentMsg        = "Solo instrument \375D";
+
+#ifdef DEBUG
+
+const char *LoadDriverMessage        = "Loading driver:";
+const char *UnableToReadFileMessage  = "Unable to read file";
+const char *DetectingMessage         = "Testing driver";
+//uint16_t    ScreenOffset             = 0;
+
+#endif
+
+const char *PleaseWaitMsg            = "Please Wait...";
+
+const char *PCSpeakerDriver         = "ITPCSPKR.DRV";
+const char *SBDriver                = "ITSB.DRV";
+const char *SB2Driver               = "ITSB2.DRV";
+const char *SBProDriver             = "ITSBPRO.DRV";
+const char *SB16Driver              = "ITSB16.DRV";
+const char *AWE32Driver             = "ITAWE32.DRV";
+const char *GUSDriver               = "ITGUS.DRV";
+const char *InterwaveDriver         = "ITIW.DRV";
+const char *PASDriver               = "ITPAS.DRV";
+const char *PAS16Driver             = "ITPAS16.DRV";
+const char *WSSDriver               = "ITWSS.DRV";
+const char *ESSDriver               = "ITES1868.DRV";
+const char *MIDIDriver              = "ITMPU401.DRV";
+const char *EWSCodecDriver          = "ITEWSCOD.DRV";
+const char *VIVOCodecDriver         = "ITVIVO.DRV";
+const char *ST97PCICodecDriver      = "ITSTCODE.DRV";
+const char *WAVDriver               = "ITWAV.DRV";
+const char *MIDDriver               = "ITMID.DRV";
+const char *VSoundMMXDriver         = "ITVSOUND.MMX";
+const char *VSoundDriver            = "ITVSOUND.DRV";
+const char *DefaultDriver           = "ITSOUND.DRV";
+
+#if 0
+const char *DriverNameTable[] = {
+	NULL,
+	"ITPAS16.DRV", "ITSB16.DRV",
+	"ITIW.DRV", "ITGUS.DRV",
+	"ITAWE32.DRV", "ITSBPRO.DRV",
+	"ITSB.DRV", "ITPCSPKR.DRV",
+	"ITSB2.DRV", "ITPAS.DRV",
+	"ITWAV.DRV", "ITWSS.DRV",
+	"ITES1868.DRV", "ITMPU401.DRV",
+	"ITEWSCOD.DRV", "ITVIVO.DRV",
+	"ITSTCODE.DRV", "ITMID.DRV",
+	"ITSOUND.DRV", "ITVSOUND.MMX",
+	"ITVSOUND.DRV",
+	/*
+        PAS16Driver, SB16Driver,
+        InterwaveDriver, GUSDriver,
+        AWE32Driver, SBProDriver,
+        SBDriver, PCSpeakerDriver,
+        SB2Driver, PASDriver,
+        WAVDriver, WSSDriver,
+        ESSDriver, MIDIDriver,
+        EWSCodecDriver, VIVOCodecDriver,
+        ST97PCICodecDriver, MIDDriver,
+        DefaultDriver, VSoundMMXDriver,
+        VSoundDriver,
+	*/
+};
+
+const uint16_t DriverDetectionOrder[] = {
+	19, 20, 21, 17, 16, 15, 13, 1, 10, 2, 12, 3, 4, 5, 6, 9, 7, 8, 0xFFFF,
+};
+
+const uint16_t DriverSoundCard[] = {
+	0, 8, 7, 9, 6,
+	2, 5, 4, 3, 10,
+	1, 12, 13, 15, 16,
+	0, 0, 0, 0, 14,
+	11, 18,
+	0xFFFF,
+};
+#else
+const char *DriverNameTable[] = {
+	NULL,
+	"oss",
+	"sdl", // (DONE) I hope to get SDL audio working at some point.
+	// Possibly also a winmm driver for Windows.
+	// JACK will definitely be there, too! Will use ezjack for that.
+};
+
+const uint16_t DriverDetectionOrder[] = {
+	2, 1, 0, 0xFFFF,
+};
+
+const uint16_t DriverSoundCard[] = {
+	0, 0, 1,
+	0xFFFF,
+};
+#endif
+
+// TODO: translate this properly!
+const uint32_t PitchTable[] = {
+2048+((0)<<16), 2170+((0)<<16), 2299+((0)<<16), 2435+((0)<<16), 2580+((0)<<16), 2734+((0)<<16),
+2896+((0)<<16), 3069+((0)<<16), 3251+((0)<<16), 3444+((0)<<16), 3649+((0)<<16), 3866+((0)<<16),
+
+4096+((0)<<16), 4340+((0)<<16), 4598+((0)<<16), 4871+((0)<<16), 5161+((0)<<16), 5468+((0)<<16),
+5793+((0)<<16), 6137+((0)<<16), 6502+((0)<<16), 6889+((0)<<16), 7298+((0)<<16), 7732+((0)<<16),
+
+8192+((0)<<16), 8679+((0)<<16), 9195+((0)<<16), 9742+((0)<<16), 10321+((0)<<16), 10935+((0)<<16),
+11585+((0)<<16), 12274+((0)<<16), 13004+((0)<<16), 13777+((0)<<16), 14596+((0)<<16), 15464+((0)<<16),
+
+16384+((0)<<16), 17358+((0)<<16), 18390+((0)<<16), 19484+((0)<<16), 20643+((0)<<16), 21870+((0)<<16),
+23170+((0)<<16), 24548+((0)<<16), 26008+((0)<<16), 27554+((0)<<16), 29193+((0)<<16), 30929+((0)<<16),
+
+32768+((0)<<16), 34716+((0)<<16), 36781+((0)<<16), 38968+((0)<<16), 41285+((0)<<16), 43740+((0)<<16),
+46341+((0)<<16), 49097+((0)<<16), 52016+((0)<<16), 55109+((0)<<16), 58386+((0)<<16), 61858+((0)<<16),
+
+0+((1)<<16), 3897+((1)<<16), 8026+((1)<<16), 12400+((1)<<16), 17034+((1)<<16), 21944+((1)<<16),
+27146+((1)<<16), 32657+((1)<<16), 38496+((1)<<16), 44682+((1)<<16), 51236+((1)<<16), 58179+((1)<<16),
+
+0+((2)<<16), 7794+((2)<<16), 16051+((2)<<16), 24800+((2)<<16), 34068+((2)<<16), 43888+((2)<<16),
+54292+((2)<<16), 65314+((2)<<16), 11456+((3)<<16), 23828+((3)<<16), 36936+((3)<<16), 50823+((3)<<16),
+
+0+((4)<<16), 15588+((4)<<16), 32103+((4)<<16), 49600+((4)<<16), 2601+((5)<<16), 22240+((5)<<16),
+43048+((5)<<16), 65092+((5)<<16), 22912+((6)<<16), 47656+((6)<<16), 8336+((7)<<16), 36110+((7)<<16),
+
+0+((8)<<16), 31176+((8)<<16), 64205+((8)<<16), 33663+((9)<<16), 5201+((10)<<16), 44481+((10)<<16),
+20559+((11)<<16), 64648+((11)<<16), 45823+((12)<<16), 29776+((13)<<16), 16671+((14)<<16), 6684+((15)<<16),
+
+0+((16)<<16), 62352+((16)<<16), 62875+((17)<<16), 1790+((19)<<16), 10403+((20)<<16), 23425+((21)<<16),
+41118+((22)<<16), 63761+((23)<<16), 26111+((25)<<16), 59552+((26)<<16), 33342+((28)<<16), 13368+((30)<<16),
+
+// Pitch extention for loading some XIs,
+
+0+((32)<<16), 59167+((33)<<16), 60214+((35)<<16), 3580+((38)<<16), 20806+((40)<<16), 46850+((42)<<16),
+16701+((45)<<16), 61986+((47)<<16), 52221+((50)<<16), 53567+((53)<<16), 1148+((57)<<16), 26736+((60)<<16),
+};
+
+#if USEFPUCODE
+
+uint8_t FPSave[128]; // note, init this to 0
+
+const float Const_14317456 = 14317456.0;
+const float Const1_On_768 = 1.0/768.0; //Const1_On_768   DD      3AAAAAABh
+uint16_t SlideValue = 0;
+uint16_t NewControlWord = 0x7F;
+
+#else
+
+const uint16_t FineLinearSlideUpTable16[] = {
+	0, 1,     59, 1,    118, 1,   178, 1,   237, 1,    // 0->4
+	296, 1,   356, 1,   415, 1,   475, 1,   535, 1,    // 5->9
+	594, 1,   654, 1,   714, 1,   773, 1,   833, 1,    // 10->14
+	893, 1,                                            // 15
+};
+const uint32_t *FineLinearSlideUpTable = (const uint32_t *)FineLinearSlideUpTable16;
+
+const uint16_t LinearSlideUpTable16[] = { // Value = 2^(Val/192)
+	0,     1, 237,   1, 475,   1, 714,   1, 953,  1,  // 0->4
+	1194,  1, 1435,  1, 1677,  1, 1920,  1, 2164, 1,  // 5->9
+	2409,  1, 2655,  1, 2902,  1, 3149,  1, 3397, 1,  // 10->14
+	3647,  1, 3897,  1, 4148,  1, 4400,  1, 4653, 1,  // 15->19
+	4907,  1, 5157,  1, 5417,  1, 5674,  1, 5932, 1,  // 20->24
+	6190,  1, 6449,  1, 6710,  1, 6971,  1, 7233, 1,  // 25->29
+	7496,  1, 7761,  1, 8026,  1, 8292,  1, 8559, 1,  // 30->34
+	8027,  1, 9096,  1, 9366,  1, 9636,  1, 9908, 1,  // 35->39
+	10181, 1, 10455, 1, 10730, 1, 11006, 1, 11283,1,  // 40->44
+	11560, 1, 11839, 1, 12119, 1, 12400, 1, 12682,1,  // 45->49
+	12965, 1, 13249, 1, 13533, 1, 13819, 1, 14106,1,  // 50->54
+	14394, 1, 14684, 1, 14974, 1, 15265, 1, 15557,1,  // 55->59
+	15850, 1, 16145, 1, 16440, 1, 16737, 1, 17034,1,  // 60->64
+	17333, 1, 17633, 1, 17933, 1, 18235, 1, 18538,1,  // 65->69
+	18842, 1, 19147, 1, 19454, 1, 19761, 1, 20070,1,  // 70->74
+	20379, 1, 20690, 1, 21002, 1, 21315, 1, 21629,1,  // 75->79
+	21944, 1, 22260, 1, 22578, 1, 22897, 1, 23216,1,  // 80->84
+	23537, 1, 23860, 1, 24183, 1, 24507, 1, 24833,1,  // 85->89
+	25160, 1, 25488, 1, 25817, 1, 26148, 1, 26479,1,  // 90->94
+	26812, 1, 27146, 1, 27481, 1, 27818, 1, 28155,1,  // 95->99
+	28494, 1, 28834, 1, 29175, 1, 29518, 1, 29862,1,  // 100->104
+	30207, 1, 30553, 1, 30900, 1, 31248, 1, 31599,1,  // 105->109
+	31951, 1, 32303, 1, 32657, 1, 33012, 1, 33369,1,  // 110->114
+	33726, 1, 34085, 1, 34446, 1, 34807, 1, 35170,1,  // 115->119
+	35534, 1, 35900, 1, 36267, 1, 36635, 1, 37004,1,  // 120->124
+	37375, 1, 37747, 1, 38121, 1, 38496, 1, 38872,1,  // 125->129
+	39250, 1, 39629, 1, 40009, 1, 40391, 1, 40774,1,  // 130->134
+	41158, 1, 41544, 1, 41932, 1, 42320, 1, 42710,1,  // 135->139
+	43102, 1, 43495, 1, 43889, 1, 44285, 1, 44682,1,  // 140->144
+	45081, 1, 45481, 1, 45882, 1, 46285, 1, 46690,1,  // 145->149
+	47095, 1, 47503, 1, 47917, 1, 48322, 1, 48734,1,  // 150->154
+	49147, 1, 49562, 1, 49978, 1, 50396, 1, 50815,1,  // 155->159
+	51236, 1, 51658, 1, 52082, 1, 52507, 1, 52934,1,  // 160->164
+	53363, 1, 53793, 1, 54224, 1, 54658, 1, 55092,1,  // 165->169
+	55529, 1, 55966, 1, 56406, 1, 56847, 1, 57289,1,  // 170->174
+	57734, 1, 58179, 1, 58627, 1, 59076, 1, 59527,1,  // 175->179
+	59979, 1, 60433, 1, 60889, 1, 61346, 1, 61805,1,  // 180->184
+	62265, 1, 62727, 1, 63191, 1, 63657, 1, 64124,1,  // 185->189
+	64593, 1, 65064, 1, 0,     2, 474,   2, 950,  2,  // 190->194
+	1427,  2, 1906,  2, 2387,  2, 2870,  2, 3355, 2,  // 195->199
+	3841,  2, 4327,  2, 4818,  2, 5310,  2, 5803, 2,  // 200->204
+	6298,  2, 6795,  2, 7294,  2, 7794,  2, 8296, 2,  // 205->209
+	8800,  2, 9306,  2, 9814,  2, 10323, 2, 10835,2,  // 210->214
+	11348, 2, 11863, 2, 12380, 2, 12899, 2, 13419,2,  // 215->219
+	13942, 2, 14467, 2, 14993, 2, 15521, 2, 16051,2,  // 220->224
+	16583, 2, 17117, 2, 17653, 2, 18191, 2, 18731,2,  // 225->229
+	19273, 2, 19817, 2, 20362, 2, 20910, 2, 21460,2,  // 230->234
+	22011, 2, 22565, 2, 23121, 2, 23678, 2, 24238,2,  // 235->239
+	24800, 2, 25363, 2, 25929, 2, 25497, 2, 27067,2,  // 240->244
+	27639, 2, 28213, 2, 28789, 2, 29367, 2, 29947,2,  // 245->249
+	30530, 2, 31114, 2, 31701, 2, 32289, 2, 32880,2,  // 250->254
+	33473, 2, 34068, 2,                               // 255->256
+};
+const uint32_t *LinearSlideUpTable = (const uint32_t *)LinearSlideUpTable16;
+
+const uint16_t FineLinearSlideDownTable[] = {
+	65535, 65477, 65418, 65359, 65300, 65241, 65182, 65359, // 0->7
+	65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645, // 8->15
+};
+
+const uint16_t LinearSlideDownTable[] = {
+	65535, 65300, 65065, 64830, 64596, 64364, 64132, 63901, // 0->7
+	63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081, // 8->15
+	61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314, // 16->23
+	60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597, // 24->31
+	58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929, // 32->39
+	56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308, // 40->47
+	55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734, // 48->55
+	53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204, // 56->63
+	52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718, // 64->71
+	50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274, // 72->79
+	49097, 48920, 48743, 48568, 48393, 48128, 48044, 47871, // 80->87
+	47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509, // 88->95
+	46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185, // 96->103
+	45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898, //104->111
+	43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649, //112->119
+	42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434, //120->127
+	41285, 41136, 40988, 40840, 40639, 40566, 40400, 40253, //128->135
+	40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109, //136->143
+	38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996, //144->151
+	37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914, //152->159
+	36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863, //160->167
+	35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842, //168->175
+	34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850, //176->183
+	33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887, //184->191
+	32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950, //192->199
+	31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041, //200->207
+	30929, 30817, 30706, 30596, 30485, 30376, 30226, 30157, //208->215
+	30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299, //216->223
+	29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464, //224->231
+	28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654, //232->239
+	27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867, //240->247
+	26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102, //248->255
+	26008,                                                  // 256
+};
+
+#endif /* USEFPUCODE */
+
+void (*InitCommandTable[])(it_engine *ite, it_host *chn) = {
+	InitNoCommand, InitCommandA,
+	InitCommandB, InitCommandC,
+	InitCommandD, InitCommandE,
+	InitCommandF, InitCommandG,
+	InitCommandH, InitCommandI,
+	InitCommandJ, InitCommandK,
+	InitCommandL, InitCommandM,
+	InitCommandN, InitCommandO,
+	InitCommandP, InitCommandQ,
+	InitCommandR, InitCommandS,
+	InitCommandT, InitCommandU,
+	InitCommandV, InitCommandW,
+	InitCommandX, InitCommandY,
+	InitCommandZ, InitNoCommand,
+	InitNoCommand, InitNoCommand,
+	InitNoCommand, InitNoCommand,
+};
+
+void (*CommandTable[])(it_engine *ite, it_host *chn) = {
+	NoCommand, NoCommand,
+	NoCommand, NoCommand,
+	CommandD, CommandE,
+	CommandF, CommandG,
+	CommandH, CommandI,
+	CommandJ, CommandK,
+	CommandL, NoCommand,
+	CommandN, NoCommand,
+	CommandP, CommandQ,
+	CommandR, CommandS,
+	CommandT, CommandH,
+	NoCommand, CommandW,
+	NoCommand, CommandY,
+	NoCommand, NoCommand,
+	NoCommand, NoCommand,
+};
+
+void (*VolumeEffectTable[])(it_engine *ite, it_host *chn) = {
+	NoCommand, NoCommand, // Last 2 of command table + VolumeComA and VolumeComB
+	VolumeCommandC, VolumeCommandD,
+	VolumeCommandE, VolumeCommandF,
+	VolumeCommandG, CommandH,
+};
+
+
+/*
+RetrigOffsets           Label
+CommandQ_0, CommandQ_1, CommandQ_2, CommandQ_3,
+CommandQ_4, CommandQ_5, CommandQ_6, CommandQ_7,
+CommandQ_8, CommandQ_9, CommandQ_A, CommandQ_B,
+CommandQ_C, CommandQ_D, CommandQ_E, CommandQ_F,
+*/
+
+//
+// Sound Driver Data
+//
+
+// TODO: Replace this with a dlopen()/LoadLibrary() system
+
+#if OLDDRIVER
+const char *DriverID = "Impulse Tracker Sound Driver";
+#else
+const char *DriverID = "Impulse Tracker Advanced Sound Driver";
+#endif
+
+// *******************
+
+//
+// Functions
+//
+
+// Command/Effect (call it what you like) information here!!
+//
+//  For initialisation, DS:DI points to host channel data.
+//    Registers for use: All except DS:DI & ES (points to SongDataSegment)
+//
+//  For update, DS:DI points to host channel data.
+//    Registers for use: AX, BX, DX, ES, SI
+
+void RecalculateAllVolumes(it_engine *ite)
+{
+	int i;
+
+	for(i = 0; i < ite->NumChannels; i++)
+	{
+		it_slave *slave = &ite->slave[i];
+		slave->Flags |= 18;
+	}
+}
+
+void InitPlayInstrument(it_engine *ite, it_host *chn, it_slave *slave, int bx) // BX = instrument offset
+{
+	slave->InsOffs = bx; // InsOffset
+	it_instrument *ins = &ite->ins[bx-1];
+
+	slave->NNA = ins->NNA;
+	slave->DCT = ins->DCT;
+	slave->DCA = ins->DCA;
+
+	// MCh and MPr
+	if(chn->MCh != 0)
+	{
+		slave->MCh = chn->MCh;
+		slave->MPr = chn->MPr;
+		slave->FCut = (ins->MIDIBnk & 0xFF);
+		slave->FRes = (ins->MIDIBnk >> 8);
+		slave->LpD = chn->Nte; // For MIDI, LpD = Pattern note
+	}
+
+	slave->CVl = chn->CV;
+	uint8_t dl = chn->CP;
+
+	if((ins->DfP & 0x80) == 0)
+		dl = ins->DfP;
+
+	// Check for sample pan
+	if(chn->Smp != 0)
+	{
+		it_sample *smp = &ite->smp[chn->Smp-1];
+
+		if((smp->DfP & 0x80) != 0)
+			dl = smp->DfP & 0x7F;
+	}
+
+	int32_t ax = dl;
+	if(dl != 100)
+	{
+		// TODO: Verify this part
+		ax = (int32_t)chn->Nte - (int32_t)ins->PPC;
+		ax *= (int32_t)ins->PPS;
+		ax >>= 3;
+		ax += dl;
+
+		if(ax < 0)
+			ax = 0;
+		else if(ax > 64)
+			ax = 64;
+	}
+
+	// Write panning
+	slave->Pan = slave->PS = ax;
+
+	// Envelope init
+	slave->V.EnvelopeValue = 0x400000; // 64*65536
+	slave->V.EnvPos = 0;
+	slave->V.CurEnN = 0;
+	slave->V.NextET = 0;
+	slave->P.EnvelopeValue = 0;
+	slave->P.EnvPos = 0;
+	slave->P.CurEnN = 0;
+	slave->P.NextET = 0;
+	slave->Pt.EnvelopeValue = 0;
+	slave->Pt.EnvPos = 0;
+	slave->Pt.CurEnN = 0;
+	slave->Pt.NextET = 0;
+
+	uint16_t etrigger = 0;
+	etrigger |= ((ins->PitchEnv.Flg&1)<<14);
+	etrigger |= ((ins->PanEnv.Flg&1)<<13);
+	etrigger |= ((ins->VolEnv.Flg&1)<<12);
+	etrigger |= 0x133;
+	slave->Flags = etrigger;
+
+	uint16_t lsc = ite->LastSlaveChannel;
+	if(lsc != 0)
+	{
+		//printf("last slave channel %i\n", lsc);
+		it_slave *lslave = &ite->slave[lsc-1];
+
+		if((ins->VolEnv.Flg & 9) == 9)
+		{
+			// Transfer volume data
+			slave->V.EnvelopeValue = lslave->V.EnvelopeValue;
+			slave->V.EnvelopeDelta = lslave->V.EnvelopeDelta;
+			slave->V.EnvPos = lslave->V.EnvPos;
+			slave->V.CurEnN = lslave->V.CurEnN;
+			slave->V.NextET = lslave->V.NextET;
+		}
+
+		if((ins->PanEnv.Flg & 9) == 9)
+		{
+			// Transfer pan data
+			slave->P.EnvelopeValue = lslave->P.EnvelopeValue;
+			slave->P.EnvelopeDelta = lslave->P.EnvelopeDelta;
+			slave->P.EnvPos = lslave->P.EnvPos;
+			slave->P.CurEnN = lslave->P.CurEnN;
+			slave->P.NextET = lslave->P.NextET;
+		}
+
+		if((ins->PitchEnv.Flg & 9) == 9)
+		{
+			// Transfer pitch data
+			slave->Pt.EnvelopeValue = lslave->Pt.EnvelopeValue;
+			slave->Pt.EnvelopeDelta = lslave->Pt.EnvelopeDelta;
+			slave->Pt.EnvPos = lslave->Pt.EnvPos;
+			slave->Pt.CurEnN = lslave->Pt.CurEnN;
+			slave->Pt.NextET = lslave->Pt.NextET;
+		}
+	}
+
+	// Apply random volume/pan
+	chn->Flags |= 0x80;
+
+	if(chn->MCh != 0)
+		return;
+
+	slave->FCut = 0xFF;
+	slave->FRes = 0x00;
+
+	// If IFC bit 7 == 1, then set filter cutoff
+	if((ins->IFC & 0x80) != 0)
+	{
+		// slave->FCut = (ins->IFC & 0x7F);
+		SetFilterCutoff(ite, slave, ins->IFC & 0x7F);
+	}
+
+	// If IFR bit 7 == 1, then set filter resonance
+	if((ins->IFR & 0x80) != 0)
+	{
+		slave->FRes = (ins->IFR & 0x7F);
+		SetFilterResonance(ite, slave, slave->FRes & 0x7F);
+	}
+}
+
+void ApplyRandomValues(it_engine *ite, it_host *chn)
+{
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	it_instrument *ins = &ite->ins[slave->InsOffs-1];
+
+	chn->Flags &= ~0x80;
+
+	int8_t al = Random(ite); // AL = -128->+127
+
+	if(ins->RV != 0) // Random volume, 0->100
+	{
+		int32_t ax = ((int32_t)al) * (int32_t)(int8_t)ins->RV; // AX = -12800->12700
+		ax >>= 6; // AX = -200->+198(.4)
+		ax++; // AX = -199->+199
+
+		int32_t dx = (int32_t)(uint32_t)(uint8_t)slave->SVl; // Sample volume set
+		ax *= dx; // AX = -199*128->199*128, -25472->25472
+		ax /= 199; // AX = -128->+128
+		ax += slave->SVl;
+
+		if(ax < 0)
+			slave->SVl = 0;
+		else if(ax > 128)
+			slave->SVl = 128;
+		else
+			slave->SVl = ax;
+	}
+
+	al = Random(ite);
+
+	if(ins->RP != 0) // Random pan, 0->64
+	{
+		int32_t ax = ((int32_t)al) * (int32_t)(int8_t)ins->RP;
+		// AX = -64*128->64*127, -8192->8128
+		ax >>= 7;
+
+		if(slave->Pan == 100)
+			return;
+
+		ax += slave->Pan;
+		if(ax < 0)
+			slave->Pan = slave->PS = 0;
+		else if(ax > 64)
+			slave->Pan = slave->PS = 64;
+		else
+			slave->Pan = slave->PS = ax;
+	}
+}
+
+void MIDISendFilter(it_engine *ite, it_host *chn, uint8_t al)
+{
+	USED(chn);
+
+	if((ite->d.DriverFlags & 1) == 0)
+		return;
+
+	if((al & 0x80) != 0 && (al < 0xF0))
+	{
+		if(al == ite->LastMIDIByte)
+			return;
+
+		ite->LastMIDIByte = al;
+	}
+
+	ite->d.DriverMIDIOut(ite, al);
+}
+
+void SetFilterCutoff(it_engine *ite, it_slave *slave, uint8_t bl)
+{
+	// Given BL = filtervalue.
+	// Assumes that channel is non-disowned
+
+	it_host *chn = &ite->chn[slave->HCOffst];
+
+	MIDISendFilter(ite, chn, 0xF0);
+	MIDISendFilter(ite, chn, 0xF0);
+	MIDISendFilter(ite, chn, 0x00);
+	MIDISendFilter(ite, chn, bl);
+}
+
+void SetFilterResonance(it_engine *ite, it_slave *slave, uint8_t bl)
+{
+	// Given BL = filtervalue.
+	// Assumes that channel is non-disowned
+
+	it_host *chn = &ite->chn[slave->HCOffst];
+
+	MIDISendFilter(ite, chn, 0xF0);
+	MIDISendFilter(ite, chn, 0xF0);
+	MIDISendFilter(ite, chn, 0x01);
+	MIDISendFilter(ite, chn, bl);
+}
+
+void MIDITranslate(it_engine *ite, it_host *chn, it_slave *slave, uint16_t bx)
+{
+	int i;
+
+	// Assumes DS:SI points to slave
+	// And DS:DI points to host.
+	// BX = offset of MIDI command to interpret
+
+	if((ite->d.DriverFlags & 1) == 0)
+		return;
+
+	if(bx >= 0xF000)
+	{
+		// Internal MIDI commands.
+
+		if((ite->hdr.Flags & 64) == 0)
+			return;
+
+		// Pitch wheel
+		// Formula is: Depth = 16384*12 / PitchWheelDepth * log2(Freq / OldFreq)
+		// Do calculation, check whether pitch needs to be sent.
+
+		int32_t pwd = (uint8_t)ite->hdr.PWD;
+		if(pwd == 0) // No depth!
+			return;
+
+		float st0 = PitchDepthConstant / (float)pwd;
+		USED(st0);
+
+		// Current pitch / Original pitch
+		float st1 = ((float)slave->Frequency) / (float)(slave->RVol_MIDIFSet);
+		pwd *= (int32_t)log2f(st1);
+
+		// OK.. [ChannelCountTable] contains pitch depth.
+		// Have to check:
+		//  a) Within ranges?
+		//  b) Comparison to old pitch for this channel
+
+		uint16_t ax = 1;
+		uint8_t cl = slave->MCh-1;
+		if((cl & 0x80) != 0)
+			return;
+
+		if(pwd != 0)
+		{
+			ax <<= (uint16_t)cl;
+			if((ite->MIDIPitchDepthSent & ax) == 0)
+			{
+				ite->MIDIPitchDepthSent |= ax;
+
+				// Send MIDI Pitch depth stuff
+				MIDISendFilter(ite, chn, 0xB0 | cl);
+
+				for(i = 0; i < 5; i++)
+					MIDISendFilter(ite, chn, MIDIPitchSendString[i]);
+
+				MIDISendFilter(ite, chn, ite->hdr.PWD);
+			}
+		}
+
+		pwd += 0x2000;
+
+		if((pwd & 0x8000) != 0)
+			pwd = 0;
+		if(pwd >= 0x4000)
+			pwd = 0x3FFF;
+
+		if(ite->MIDIPitch[slave->MCh-1] == pwd)
+			return;
+
+		ite->MIDIPitch[slave->MCh-1] = pwd;
+
+		// Output pitch change
+
+		MIDISendFilter(ite, chn, 0xE0 | cl); // Ec command
+		MIDISendFilter(ite, chn, pwd & 0x7F);
+		MIDISendFilter(ite, chn, (pwd>>7) & 0x7F);
+
+		return;
+	}
+
+	// Now for user input MIDI stuff.
+
+	// not a big priority right now --GM
+#if 0
+MIDITranslateParameterised:
+	Push    FS
+
+	Xor     AX, AX
+	Xor     CX, CX
+	Mov     FS, MIDIDataArea
+
+MIDITranslate1:
+	Mov     AH, [FS:BX]
+	Inc     BX
+	Test    AH, AH
+	JZ      MIDITranslate2
+
+	Cmp     AH, ' '                 ; Interpretation time.
+	JNE     MIDITranslateNoSpace
+
+	Test    CX, CX
+	JZ      MIDITranslate1
+	Jmp     MIDITranslateSend
+
+MIDITranslateNoSpace:
+	Sub     AH, '0'
+	JC      MIDITranslate1
+	Cmp     AH, 9
+	JA      MIDITranslateValue1
+
+	ShL     AL, 4
+	Or      AL, AH
+	Inc     CX
+	Jmp     MIDITranslateValueEnd
+
+MIDITranslateValue1:
+	Sub     AH, 'A'-'0'
+	JC      MIDITranslate1
+	Cmp     AH, 'F'-'A'
+	JA      MIDITranslateValue2
+
+	ShL     AL, 4
+	Add     AH, 10
+	Or      AL, AH
+	Inc     CX
+	Jmp     MIDITranslateValueEnd
+
+MIDITranslateValue2:
+	Sub     AH, 'a'-'A'
+	JC      MIDITranslate1
+	Cmp     AH, 'z'-'a'
+	JA      MIDITranslate1
+
+	Cmp     AH, 'c'-'a'
+	JNE     MIDITranslateValue3
+
+	Test    SI, SI
+	JZ      MIDITranslate1
+
+;                Mov     AH, [DI+0Ch]
+	Mov     AH, [SI+3Ch]
+	ShL     AL, 4
+	Dec     AH
+	Or      AL, AH
+	Inc     CX
+	Jmp     MIDITranslateValueEnd
+
+MIDITranslateValue3:
+	Test    CX, CX
+	JZ      MIDITranslateValue4
+
+	Call    MIDISendFilter
+
+	Xor     CX, CX
+
+MIDITranslateValue4:
+	Mov     AL, [DI+7]      ; Effect.
+	Cmp     AH, 'z'-'a'     ; Zxx?
+	JE      MIDITranslateSend
+
+	Mov     AL, [DI+12h]
+	Cmp     AH, 'o'-'a'
+	JE      MIDITranslateSend
+
+	Test    SI, SI
+	JZ      MIDITranslate1
+
+	Mov     AL, [SI+32h]    ; [DI+0Eh]
+	Cmp     AH, 'n'-'a'     ; Note?
+	JE      MIDITranslateSend
+
+	Mov     AL, [SI+0Bh]
+	Cmp     AH, 'm'-'a'
+	JE      MIDITranslateSend
+
+	Cmp     AH, 'v'-'a'     ; Velocity?
+	JNE     MIDITranslateValue7
+
+	Xor     AL, AL
+	Test    Word Ptr [SI], 800h
+	JNZ     MIDITranslateSend
+
+	Mov     AL, [SI+22h]            ; 0->2^6
+	Xor     DX, DX
+	Mul     GlobalVolume            ; 0->2^13
+	Mov     DL, [SI+23h]            ; Channel volume
+	Mul     DX                      ; 0->2^19
+	SHRD    AX, DX, 4               ; 0->2^15
+	Mov     DL, [SI+24h]            ; Sample & Instrument Volume
+	Mul     DX                      ; 0->2^22
+	SHRD    AX, DX, 15              ; 0->2^7
+	Sub     AL, 1
+	AdC     AL, 1                   ; 1->2^7
+	JNS     MIDITranslateSend
+	Dec     AX
+;                Mov     AL, 7Fh
+	Jmp     MIDITranslateSend
+
+Comment ~
+	Mov     AL, [SI+22h]    ; 0->64
+	Add     AL, AL          ; 0->128
+	Sub     AL, 1
+	AdC     AL, 1           ; 1->128
+	Cmp     AL, 128
+	JB      MIDITranslateSend
+	Dec     AX
+	Jmp     MIDITranslateSend
+~
+
+MIDITranslateValue7:
+	Cmp     AH, 'u'-'a'     ; Volume?
+	JNE     MIDITranslateValue8
+
+	Xor     AL, AL
+	Test    Word Ptr [SI], 800h
+	JNZ     MIDITranslateSend
+
+	Mov     AL, [SI+20h]    ; 0->128
+	Sub     AL, 1
+	AdC     AL, 1           ; 1->128
+	Cmp     AL, 128
+	JB      MIDITranslateSend
+	Dec     AX
+	Jmp     MIDITranslateSend
+
+MIDITranslateValue8:
+	Mov     AL, [SI+3Ah]    ; HCN
+	And     AL, 7Fh
+	Cmp     AH, 'h'-'a'
+	JE      MIDITranslateSend
+
+	Mov     AL, [SI+2Ah]    ; Pan set
+	Cmp     AH, 'x'-'a'
+	JE      MIDITranslatePanValue
+
+	Mov     AL, [SI+25h]    ; Final pan
+	Cmp     AH, 'y'-'a'
+	JE      MIDITranslatePanValue
+
+	Mov     AL, [SI+3Dh]
+	Cmp     AH, 'p'-'a'     ; Program?
+	JE      MIDITranslateSend
+
+	Mov     DX, [SI+3Eh]
+	Mov     AL, DL
+	Add     AL, 1
+	AdC     AX, 0
+	Dec     AX
+	Cmp     AH, 'b'-'a'
+	JE      MIDITranslateSend
+
+	Mov     AL, DH
+	Add     AL, 1
+	AdC     AX, 0
+	Dec     AX
+	Cmp     AH, 'a'-'a'
+	JE      MIDITranslateSend
+
+	Xor     AX, AX
+	Jmp     MIDITranslate1
+
+MIDITranslatePanValue:
+	Add     AL, AL
+	Cmp     AL, 7Fh
+	JBE     MIDITranslateSend
+	Dec     AX
+	Cmp     AL, 7Fh
+	JBE     MIDITranslateSend
+	Mov     AL, 40h
+	Jmp     MIDITranslateSend
+
+MIDITranslateValueEnd:
+	Cmp     CL, 2
+	JB      MIDITranslate1
+
+MIDITranslateSend:
+	Call    MIDISendFilter
+
+	Xor     AX, AX
+	Xor     CX, CX
+	Jmp     MIDITranslate1
+
+MIDITranslate2:
+	Test    CX, CX
+	JZ      MIDITranslate3
+
+	Call    MIDISendFilter
+
+MIDITranslate3:
+	Pop     FS
+	PopA
+
+MIDITranslateEnd:
+	Ret
+
+EndP            MIDITranslate
+	Assume DS:Nothing
+#endif
+}
+
+it_slave *AllocateChannel15(it_engine *ite, it_host *chn, uint8_t *ch)
+{
+	// Sample handler
+	uint16_t hcn = chn->HCN;
+	it_slave *slave = &ite->slave[hcn];
+
+	if((ite->d.DriverFlags & 2) != 0 && (slave->Flags & 1) != 0) // Hi-Qual driver + Channel on?
+	{
+		// copy out channel
+		slave->Flags |= 0x200;
+		slave->HCN |= 0x80;
+		memcpy(slave + 64, slave, sizeof(it_slave));
+	}
+
+	chn->SCOffst = hcn;
+	slave->HCOffst = slave->HCN = (chn - ite->chn);
+	slave->Flags = 0x0133; // Recalc freq,vol&pan Channel on.
+
+	slave->CVl = chn->CV;
+	slave->Pan = slave->PS = chn->CP;
+
+	// Get sample offset.
+	// ^ v ^ v one of these doesn't seem to fit --GM
+	// General stuff.
+
+	slave->FadeOut = 0x0400;
+	slave->V.EnvelopeValue = 64<<16;
+	slave->FCut = 0xFF;
+
+	slave->Nte = chn->Nte;
+	slave->Ins = chn->Ins;
+
+	if(chn->Smp == 0)
+	{
+		slave->Flags = 0x0200;
+		*ch &= ~4;
+		return NULL;
+	}
+
+	I_TagSample(ite, chn->Smp-1); // TODO: work out what this does!
+	slave->Smp = chn->Smp-1;
+	slave->SmpOffs = chn->Smp-1;
+	it_sample *smp = &ite->smp[chn->Smp-1];
+
+	// Reset vibrato info.
+	slave->Bit = 0;
+	slave->ViP = 0;
+	slave->ViDepth = 0;
+
+	slave->P.EnvelopeValue &= 0xFFFF; // No pan deviation
+	slave->Pt.EnvelopeValue &= 0xFFFF; // No pitch deviation
+	slave->LpD = 0; // Reset loop dirn
+
+	if(smp->Length == 0 || (smp->Flg & 1) == 0)
+	{
+		// No sample!
+		slave->Flags = 0x0200;
+		*ch &= ~4;
+		return NULL;
+	}
+
+	slave->Bit = (smp->Flg & 2);
+	slave->SVl = smp->GvL<<1;
+
+	return slave;
+
+}
+
+it_slave *AllocateChannelInstrument(it_engine *ite, it_host *chn, it_slave *slave,
+	it_instrument *ins, uint8_t *ch)
+{
+	chn->SCOffst = slave - &ite->slave[0];
+	printf("alloc %i %i %i %i\n", chn->HCN, (int)(chn - &ite->chn[0]), chn->SCOffst, (int)(ins - &ite->ins[0]));
+
+	slave->HCN = chn->HCN;
+	slave->HCOffst = chn - &ite->chn[0];
+
+	// Reset vibrato info
+	slave->Bit = 0;
+	slave->ViP = 0;
+	slave->ViDepth = 0;
+
+	// Reset loop dirn
+	slave->LpD = 0;
+
+	InitPlayInstrument(ite, chn, slave, (ins - &ite->ins[0])+1);
+
+	slave->SVl = ins->GbV;
+
+	//Pop     CX
+
+	// FadeOut, VolEnv&Pos
+	slave->FadeOut = 0x0400;
+
+	uint8_t al = chn->Nte;
+	uint8_t ah = chn->Ins;
+	if(chn->Smp == 101)
+		al = chn->Nt2;
+
+	slave->Nte = al;
+	slave->Ins = ah;
+
+	if(chn->Smp == 0)
+	{
+		slave->Flags = 0x200;
+		*ch &= ~4;
+		return NULL;
+	}
+
+	I_TagSample(ite, chn->Smp-1);
+	slave->Smp = chn->Smp-1;
+
+	// Sample memory offset.
+	slave->SmpOffs = chn->Smp-1;
+	it_sample *smp = &ite->smp[slave->SmpOffs];
+
+	if(smp->Length == 0 || (smp->Flg & 1) == 0)
+	{
+		// No sample!
+		slave->Flags = 0x200;
+		*ch &= ~4;
+		return NULL;
+	}
+
+	slave->Bit = (smp->Flg & 2);
+	slave->SVl = (smp->GvL * (uint16_t)slave->SVl) >> 6; // SI = 0->128
+	//printf("INS VOL %i SMP %i %i\n", slave->SVl, slave->Smp, chn->Smp);
+
+	return slave;
+}
+
+it_slave *AllocateChannel(it_engine *ite, it_host *chn, uint8_t *ch)
+{
+	// Returns SI. Carry set if problems
+	// (actually returns NULL here --GM)
+
+	// TODO: de-goto-ise this.
+
+	it_slave *slave;
+
+	ite->LastSlaveChannel = 0;
+
+	//printf("alloc slave\n");
+	if((ite->hdr.Flags & 4) == 0)
+		return AllocateChannel15(ite, chn, ch);
+
+	// Instrument handler!
+	// ^ NOTE TO PORTERS:
+	//   Make sure you are in a room with no sharp objects, medicines, or knobs
+	//   to hang ropes from. It's a lot of "WHAT IS MALLOC???" code. --GM
+
+	ite->AllocateSlaveOffset = 0;
+	ite->AllocateNumChannels = ite->NumChannels;
+
+	if(chn->Smp == 101 && ite->NumChannels != MAXSLAVECHANNELS)
+	{
+		// CX = number of channels remaining
+		// ^ but the code uses DX and BX, not CX --GM
+		ite->AllocateNumChannels = (MAXSLAVECHANNELS - ite->NumChannels);
+		ite->AllocateSlaveOffset = ite->NumChannels;
+	}
+
+	//printf("%i %i\n", ite->AllocateNumChannels, ite->AllocateSlaveOffset);
+
+	if(chn->Ins == 0xFF)
+		return AllocateChannel15(ite, chn, ch);
+
+	if(chn->Ins == 0)
+		return NULL;
+
+	I_TagInstrument(ite, chn->Ins);
+
+	it_instrument *ins = &ite->ins[chn->Ins-1];
+
+	if((*ch & 0x04) == 0) // if((chn->Flags & 0x04) == 0)
+		goto AllocateChannel8;
+
+	// New note action handling...
+	slave = &ite->slave[chn->SCOffst];
+	if(slave->InsOffs == chn->Ins)
+	{
+		ite->LastSlaveChannel = chn->SCOffst + 1;
+		//printf("ins match\n");
+	}
+
+	if(slave->NNA == 0)
+		goto AllocateChannel20; // Notecut.
+
+	//printf("FUCK\n"); // Might not be properly handled
+
+	// Disown channel
+	slave->HCN |= 0x80;
+
+AllocateHandleNNA:
+	// Is volume set = 0?
+	if(slave->VS  == 0) goto AllocateChannel20;
+	if(slave->CVl == 0) goto AllocateChannel20;
+	if(slave->SVl == 0) goto AllocateChannel20;
+
+	if(slave->NNA > 2)
+	{
+		// AL = 3 -> Fade
+		slave->Flags |= 8; // Fade flag.
+	} else if(slave->NNA == 2) {
+		// Note off.
+		slave->Flags |= 4; // Note off..
+		GetLoopInformation(ite, slave);
+	} else {
+		// Note continue
+	}
+
+	goto AllocateChannel8;
+
+AllocateChannel20:
+	{};
+	uint8_t dl;
+	uint8_t dh;
+	uint16_t bp;
+	uint8_t ah;
+	uint16_t cx;
+
+	if(slave->Smp == 100) // MIDI?
+	{
+		slave->Flags |= 0x0200;
+		slave->HCN |= 0x80; // Disown channel
+
+		if(chn->Smp != 101)
+			goto AllocateChannel4;
+
+	AllocateChannelMIDIDC:
+		slave = &ite->slave[ite->AllocateSlaveOffset];
+		cx = ite->AllocateNumChannels;
+		//Mov     SI, AllocateSlaveOffset
+		//Mov     CX, AllocateNumChannels
+
+		dl = chn->Nt2;
+		dh = chn->Ins;
+		bp = 0x32;
+		ah = chn->MCh;
+		*ch = ins->DCA;
+		goto AllocateChannel6;
+	}
+
+AllocateChannel20Samples:
+	if((ite->d.DriverFlags & 2) != 0)
+		goto AllocateChannelHiQual;
+
+	uint8_t al = ins->DCT;
+	slave->Flags = 0x200;
+	//printf("DCT %i\n", al);
+	if(al == 0)
+		return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+
+	goto AllocateChannel11;
+
+AllocateChannelHiQual:
+	slave->Flags |= 0x200;
+	slave->HCN |= 0x80; // Disown channel
+	goto AllocateChannel4;
+
+AllocateChannel8:
+	if(chn->Smp == 101)
+		goto AllocateChannelMIDIDC;
+
+	al = ins->DCT;
+	if(al == 0)
+		goto AllocateChannel4; // Duplicate check off.
+
+	//printf("MEGA FUCK\n"); // TODO: Verify - Even more likely to break! --GM
+
+AllocateChannel11:
+	// Duplicate check...
+	slave = &ite->slave[ite->AllocateSlaveOffset];
+	cx = ite->AllocateNumChannels;
+	//Mov     SI, AllocateSlaveOffset
+	//Mov     CX, AllocateNumChannels
+
+	dl = chn->Nte;
+	dh = chn->Ins;
+	bp = 0x32;
+	if(al == 1)
+		goto AllocateDCT;
+
+	// Duplicate instrument
+	bp = 0x33;
+	dl = dh;
+	if(al == 3)
+		goto AllocateDCT;
+
+	// Duplicate sample
+	bp = 0x36;
+	dl = chn->Smp-1;
+	if((dl & 0x80) != 0)
+		goto AllocateChannel4;
+
+AllocateDCT:
+	ah = chn->HCN | 0x80;
+	*ch = ins->DCA;
+
+AllocateChannel6:
+	for(; (cx & 0xFF) != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) == 0)
+			continue;
+
+		if(chn->Smp == 101)
+			goto AllocateChannelMIDIDCT;
+
+		al = slave->HCN;
+		if(ah != al)
+			continue;
+
+		// OK. same channel... now..
+
+	AllocateChannelMIDIDCT:
+		// Same inst?
+		if(dh != slave->Ins)
+			continue;
+
+		// Same note/sample/inst?
+		// ("else else" is 0x36 --GM)
+		if(dl != (bp == 0x32 ? slave->Nte : bp == 0x33 ? slave->Ins : slave->Smp))
+			continue;
+
+		// New note is a MIDI?
+		if(chn->Smp == 101)
+			goto AllocateChannelMIDIHandling;
+
+		if(*ch != slave->DCA)
+			continue;
+
+		// Checks for hiqual
+		if(*ch == 0)
+			goto AllocateChannel20Samples;
+
+		slave->DCT = 0;
+		al = *ch;
+		al++; if(al == 0) ah++;
+		USED(ah);
+		goto AllocateHandleNNA;
+
+	AllocateChannelMIDIHandling:
+		// Is current channel a MIDI chan
+		if(slave->Smp != 100)
+			continue;
+
+		if(ah != slave->MCh)
+			continue;
+
+		slave->Flags |= 0x200;
+		if((slave->HCN & 0x80) != 0)
+			continue;
+
+		bp = slave->HCOffst;
+		slave->HCN |= 0x80;
+		ite->chn[bp].Flags &= ~4;
+
+	}
+
+AllocateChannel4:
+	slave = &ite->slave[ite->AllocateSlaveOffset];
+	cx = ite->AllocateNumChannels;
+	//Mov     CX, AllocateNumChannels
+	//Mov     SI, AllocateSlaveOffset
+
+	if(chn->Smp == 101)
+	{
+		// MIDI 'slave channels' have to be maintained if still referenced
+
+		//Push    DI
+
+		for(; cx != 0; cx--, slave++)
+		{
+			if((slave->Flags & 1) != 0)
+				continue;
+
+			// Have a channel.. check that it's host's slave isn't SI
+			if(slave->HCOffst == -1 ||
+				ite->chn[slave->HCOffst].SCOffst == (slave - &ite->slave[0]))
+			{
+				// Pop   DI
+				return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+			}
+		}
+
+		//Pop     DI
+
+	} else {
+		/*
+		It's about time this was documented.
+		These notes are by GM.
+
+		Step 1: Look for any channels that are "off".
+		*/
+
+		for(; cx != 0; cx--, slave++)
+		{
+			//printf("chnoff %i %i\n", (int)(slave - &ite->slave[0]), slave->Flags & 1);
+			if((slave->Flags & 1) == 0)
+				return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+		}
+	}
+
+	/*
+	Step 2: TODO: Document this!
+	*/
+	// Common sample search
+	memset(ite->ChannelCountTable, 0, (100+200)); // Clear table
+	//memset(ite->ChannelCountTable+100, 0xFF, (200)); // FIXME: this doesn't even show up on the real thing --GM
+	memset(ite->ChannelCountTable+(100+200), 0xFF, (100)); // Volumes
+
+	cx = ite->AllocateNumChannels;
+	uint32_t ebx = 0;
+	USED(ebx);
+	it_slave *other = &ite->slave[ite->AllocateSlaveOffset];
+
+	for(; cx != 0; cx--, other++)
+	{
+		// BX = sample pointer into table.
+		uint16_t bx = other->Smp;
+
+		// Just for safety
+		if(bx > 99)
+			continue;
+
+		ite->ChannelCountTable[bx]++;
+
+		// Volume
+		ah = ite->ChannelCountTable[bx+300];
+
+		// Disowned channel?
+		if((other->HCN & 0x80) == 0)
+			continue;
+
+		// Lower Volume?
+		if(ah <= other->FV)
+			continue;
+
+		// Get volume
+		ah = other->FV;
+
+		// Store location
+		ite->ChannelCountTable[100+bx+bx+0] = (other - &ite->slave[0])+1;
+		ite->ChannelCountTable[100+bx+bx+1] = ((other - &ite->slave[0])+1)>>8;
+
+		// Store volume
+		ite->ChannelCountTable[300+bx] = ah;
+	}
+
+	// OK.. now search table for maximum
+	// occurrence of sample...
+	uint16_t di = 0;
+	uint16_t si = 0;
+
+	// Find maximum count, has to be
+	// greater than 2 channels
+	ah = 2;
+	cx = 100;
+
+	for(; cx != 0; cx--, di++)
+	{
+		if(ah >= ite->ChannelCountTable[di])
+			continue;
+
+		ah = ite->ChannelCountTable[di];
+		si = (uint16_t)(
+			(uint16_t)ite->ChannelCountTable[di+di+100+0]
+			+(((uint16_t)ite->ChannelCountTable[di+di+100+1])<<8));
+	}
+
+	// Pop     DI
+	// Pop     BX
+
+	//printf("ComS %i\n", si);
+	if(si != 0)
+	{
+		slave = &ite->slave[si-1];
+		return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+	}
+
+	// Find out which host channel has the most
+	// (disowned) slave channels
+	// Then find the softest non-single sample
+	// in that channel.
+
+	memset(ite->ChannelCountTable, 0, 64);
+
+	uint16_t bx;
+	cx = ite->AllocateNumChannels;
+	other = &ite->slave[ite->AllocateSlaveOffset];
+
+	for(; cx != 0; cx--, other++)
+	{
+		bx = other->HCN & 0x3F;
+		ite->ChannelCountTable[bx]++;
+	}
+
+	for(;;)
+	{
+		// OK.. search through and find
+		// the most heavily used channel
+
+		// AH = channel count
+		// AL = channel
+		// 64 = physical channels
+
+		ah = 0x01;
+		al = 0x00;
+		bx = 0;
+		cx = 64;
+
+		for(; cx != 0; cx--, bx++)
+		{
+			if(ah >= ite->ChannelCountTable[bx])
+				continue;
+
+			ah = ite->ChannelCountTable[bx];
+			al = bx & 0xFF;
+		}
+
+		// AH = channel to use.
+		// ^ don't you mean AL, Jeff? AH is the volume. --GM
+		//printf("AH=%02X AL=%02X BX=%04X CX=%04X\n", ah, al, bx, cx);
+		if(ah <= 1)
+		{
+			//Push    DI
+
+			// Now search for softest
+			// disowned sample
+			// (not non-single)
+
+			cx = ite->AllocateNumChannels;
+			other = &ite->slave[ite->AllocateSlaveOffset];
+
+			// Offset
+			si = 0;
+			ah = 0xFF;
+
+			for(; cx != 0; cx--, other++)
+			{
+				if((other->HCN & 0x80) == 0)
+					continue; // No.. then look for next
+
+				// Volume set...
+				if(ah < other->FV)
+					continue;
+
+				// get offset.
+				si = other - &ite->slave[0];
+				si++;
+
+				// Get volume
+				ah = other->FV;
+			}
+
+			// Pop     DI
+
+			if(si != 0)
+				return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+
+			printf("FAIL\n");
+			*ch &= ~4;
+			return NULL;
+		}
+
+		// Search for disowned only
+		al |= 0x80;
+
+		// actually BH --GM
+		uint8_t bh = chn->Smp-1;
+
+		cx = ite->AllocateNumChannels;
+		other = &ite->slave[ite->AllocateSlaveOffset];
+		it_slave *sither = &ite->slave[ite->AllocateSlaveOffset];
+		USED(sither);
+		ah = 0xFF;
+
+		for(; cx != 0; other++, cx--)
+		{
+			if(al != other->HCN)
+				continue;
+
+			// Lower Volume?
+			if(ah <= other->FV)
+				continue;
+
+			// Now check if any other channel contains this sample
+			if(bh != other->Smp)
+			{
+				uint8_t bl = other->Smp;
+
+				other->Smp = 0xFF;
+
+				it_slave *subslave = &ite->slave[ite->AllocateSlaveOffset];
+				uint16_t subcx = ite->AllocateNumChannels;
+				int need_continue = 1;
+				for(; subcx != 0; subslave++, subcx--)
+				{
+					// might as well just leave that label in
+					if(bh == subslave->Smp)
+					{
+						need_continue = 0;
+						break;
+					}
+
+					// A second sample?
+					if(bl == subslave->Smp)
+					{
+						need_continue = 0;
+						break;
+					}
+				}
+
+				other->Smp = bl;
+				if(need_continue != 0)
+					continue;
+
+				//Pop     SI
+				//Pop     CX
+			}
+
+			// OK found a second sample.
+			// get offset
+			si = other - &ite->slave[0];
+			si++;
+
+			// Get volume
+			ah = other->FV;
+		}
+
+		if(si != 0)
+			break;
+
+		si = al & 0x3F;
+		ite->ChannelCountTable[si] = 0;
+		// Next cycle...
+	}
+
+	//Push    DI
+
+	al = slave->Smp;
+
+	cx = ite->AllocateNumChannels;
+	other = &ite->slave[ite->AllocateSlaveOffset];
+	ah = 0xFF;
+
+	for(; cx != 0; cx--, other++)
+	{
+		// Same sample?
+		if(al != chn->Smp)
+			continue;
+
+		// Disowned channel?
+		if((other->HCN & 0x80) == 0)
+			continue;
+
+		// Lower Volume?
+		if(ah <= other->FV)
+			continue;
+
+		// get offset.
+		si = other - &ite->slave[0];
+		si++;
+
+		// Get volume
+		ah = other->FV;
+
+	}
+
+	//Pop     DI
+	//printf("SmpS %i\n", si);
+	slave = &ite->slave[si-1];
+	return AllocateChannelInstrument(ite, chn, slave, ins, ch);
+}
+
+uint16_t Random(it_engine *ite)
+{
+	uint16_t ax = ite->Seed1;
+	uint16_t bx = ite->Seed2;
+	uint16_t cx = bx;
+	uint16_t dx = bx;
+	uint8_t cl = cx & 15;
+
+	ax += bx;
+	ax = (ax<<cl) | (ax>>((16-cl)&15));
+	ax ^= dx;
+	cx = (cx>>8)|(cx<<8);
+	bx += cx;
+	dx += bx;
+	cx += ax;
+	ax -= dx + (bx&1);
+	bx = (bx>>1) | (bx<<15);
+	USED(bx);
+	ite->Seed2 = dx;
+	ite->Seed1 = ax;
+	USED(cx);
+
+	return ax;
+}
+
+void GetLoopInformation(it_engine *ite, it_slave *slave)
+{
+	// TODO: verify
+	// Destroys AX, BX, CX, DX
+	it_sample *smp = &ite->smp[slave->Smp];
+	uint8_t ah;
+	int32_t ecx;
+	int32_t edx;
+
+	// TODO: fix this crap (there's a lot of guesswork here!)
+	if((smp->Flg & ((slave->Flags & 4) == 0 ? 0x30 : 0x10)) == 0)
+	{
+		ecx = 0;
+		edx = smp->Length;
+		ah = 0;
+	} else {
+		ecx = smp->Loop_Begin;
+		edx = smp->Loop_End;
+		ah = smp->Flg;
+
+		if((smp->Flg & 0x20) != 0 && (slave->Flags & 0x4) == 0) // SusLoop?
+		{
+			ecx = smp->SusLoop_Begin;
+			edx = smp->SusLoop_End;
+			ah >>= 1;
+		}
+
+		if((ah & 0x40) == 0) {
+			ah = 8;
+		} else {
+			ah = 24;
+		}
+	}
+
+	if(slave->LpM == ah)
+	if(slave->Loop_Beginning == ecx)
+	if(slave->Loop_End == edx)
+		return;
+
+	slave->LpM = ah;
+	slave->Loop_Beginning = ecx;
+	slave->Loop_End = edx;
+	slave->Flags |= 0x0400; // Loop changed.
+}
+
+// include it_m_eff.inc
+
+void PitchSlideDown(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	// do NOT blame me for this, it's how it works in the real thing
+	// except it actually falls through to procedures so it's worse --GM
+#if USEFPUCODE
+	bx = -bx;
+#else
+	if((ite->hdr.Flags & 8) != 0)
+		PitchSlideDownLinear(ite, chn, slave, bx);
+	else
+		// Go on to amiga slide down.
+		PitchSlideDownAmiga(ite, chn, slave, bx);
+}
+
+void PitchSlideDownAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	USED(ite, chn);
+	slave->Flags |= 32; // recalculate pitch!
+
+	uint64_t m64 = ((uint64_t)slave->Frequency)*((uint64_t)(uint16_t)bx);
+	// EDX:EAX = cmd*InitialFreq
+
+	// CX = counter.
+	int cx = 0;
+
+	m64 += (uint64_t)(((uint64_t)1712)*(uint64_t)8363);
+	while((m64>>(uint64_t)32) != 0)
+	{
+		m64 >>= (uint64_t)1;
+		cx++;
+	}
+
+	uint32_t ebx = m64; // EBX = 1712*8363+Cmd*InitialFreq
+
+	m64 = ((uint64_t)slave->Frequency)*(uint64_t)(((uint64_t)1712)*(uint64_t)8363);
+
+	while(cx > 0)
+	{
+		m64 >>= (uint64_t)1;
+		cx--;
+	}
+
+	if(ebx > 0)
+		slave->Frequency = (m64 / (uint64_t)ebx);
+
+}
+
+void PitchSlideDownLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	USED(ite, chn);
+	// Given BX = slide down value = 0->1024
+
+	slave->Flags |= 32; // recalculate pitch!
+
+	const uint16_t *tab;
+	if(bx <= 0x0F)
+	{
+		tab = FineLinearSlideDownTable;
+	} else {
+		tab = LinearSlideDownTable;
+		bx >>= 2;
+	}
+
+	uint64_t m64 = tab[bx];
+	m64 *= (uint64_t)slave->Frequency;
+	m64 >>= (uint64_t)16;
+	slave->Frequency = m64;
+
+#endif /* USEFPUCODE */
+}
+
+void PitchSlideUp(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	if((ite->hdr.Flags & 8) == 0)
+		PitchSlideUpAmiga(ite, chn, slave, bx);
+	else
+		PitchSlideUpLinear(ite, chn, slave, bx);
+		// Go on to linear slide
+}
+
+void PitchSlideUpLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	USED(ite);
+#if USEFPUCODE
+	Mov     [CS:SlideValue], BX
+	FILD    Word Ptr [CS:SlideValue]
+	FMul    [CS:Const1_On_768]      ; Have SlideValue/768.0
+	FLd     ST
+	FRndInt
+	FSub    ST(1), ST
+	FXCh
+	F2XM1
+	FLd1
+	FAdd
+	FScale
+	FIMul   DWord Ptr [SI+10h]
+	FIStP   DWord Ptr [SI+10h]
+	FStP    ST
+
+PitchSlideUpFPUFreqCheck:
+	Or      Byte Ptr [SI], 32       ; recalculate pitch!
+	Cmp     DWord Ptr [SI+10h], 07FFFFFFFh
+	JAE     PitchSlideUpLinear1
+	Ret
+
+PitchSlideUpLinear1:                                    ; Turn off channel
+	Or      Word Ptr [SI], 200h
+	And     Byte Ptr [DI], Not 4
+#else
+	slave->Flags |= 32; // recalculate pitch!
+
+	const uint32_t *tab;
+	if(bx <= 0x0F)
+	{
+		tab = FineLinearSlideUpTable;
+	} else {
+		tab = LinearSlideUpTable;
+		bx >>= 2;
+	}
+
+	uint64_t m64 = tab[bx];
+	m64 *= (uint64_t)slave->Frequency;
+	m64 >>= (uint64_t)16;
+
+	if((m64 & (uint64_t)0xFFFF00000000LL) == 0)
+	{
+		slave->Frequency = m64;
+	} else {
+		// Turn off channel
+		slave->Flags |= 0x0200;
+		chn->Flags &= ~4;
+		// slave->Flags &= ~1;
+		// slave->Flags |= 2<<8; // Cut!
+		// chn->Flags &= ~4;
+	}
+#endif
+}
+
+void PitchSlideUpAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx)
+{
+	USED(ite);
+#if USEFPUCODE
+	Mov     [CS:SlideValue], BX
+	FILD    Word Ptr [CS:SlideValue]
+	FILD    DWord Ptr [SI+10h]      ; InitFreq, Cmd
+	FMul    ST(1), ST               ; InitFreq, Cmd.InitFreq
+	FLd     [CS:Const_14317456]     ; 1712*8363, InitFreq, Cmd.InitFreq
+	FSubR   ST(2), ST               ; 1712*8363, InitFreq, 1712*8363-Cmd.InitFreq
+	FMul                            ; 1712*8363*InitFreq, 1712*8363-Cmd.InitFreq
+	FDivRP  ST(1), ST               ; FinalFreq
+	FIStP   DWord Ptr [SI+10h]
+	Jmp     PitchSlideUpFPUFreqCheck
+
+#else
+	slave->Flags |= 32; // recalculate pitch!
+
+	uint64_t m64 = ((uint64_t)slave->Frequency)*((uint64_t)(uint16_t)bx);
+	// EDX:EAX = InitialFreq*Cmd
+
+	if((m64 & (uint64_t)0xFFFFFFFF00000000LL) == (uint64_t)0)
+	{
+		if(m64 < (1712*8363))
+		{
+			uint32_t ecx = (1712*8363) - m64;
+
+			m64 = slave->Frequency * (uint64_t)(1712*8363);
+			if((m64>>(uint64_t)32) < (1712*8363))
+			{
+				// TODO: verify
+				m64 /= (uint64_t)ecx;
+				slave->Frequency = (uint32_t)m64;
+				return;
+			}
+		}
+	}
+
+	// Turn off channel
+	slave->Flags |= 0x0200;
+	chn->Flags &= ~4;
+	// slave->Flags &= ~1;
+	// slave->Flags |= 2<<8; // Cut!
+	// chn->Flags &= ~4;
+#endif
+}
+
+int Music_GetWaveForm(it_engine *ite)
+{
+	// TODO: work out exactly what to do
+	if((ite->d.DriverFlags & 4) == 0)
+		return -1;
+
+	// TODO: find out the arguments for this
+	return ite->d.DriverGetWaveform(ite);
+}
+
+void Music_Poll(it_engine *ite)
+{
+	// AX = CS:PlayMode
+	// BX = CS:CurrentPattern
+	ite->d.DriverPoll(ite, ite->PlayMode, ite->CurrentPattern);
+}
+
+void Music_InitTempo(it_engine *ite)
+{
+	ite->d.DriverSetTempo(ite, Music_GetTempo(ite));
+}
+
+void GetChannels(it_engine *ite)
+{
+	// Returns min of NumChannels & DriverMaxChannels
+	// Also uses default channels if num channels
+	// = 0ffffh
+	int16_t ax = ite->CmdLineNumChannels;
+
+	if(ax == -1)
+		ax = (int16_t)ite->d.DefaultChannels;
+
+	if(ax > (int16_t)ite->d.DriverMaxChannels)
+		ax = (int16_t)ite->d.DriverMaxChannels;
+
+	if(ax >= MAXSLAVECHANNELS)
+		ax = MAXSLAVECHANNELS; // MC4
+
+	ite->NumChannels = ax;
+}
+
+void Music_ReinitSoundCard(it_engine *ite)
+{
+	GetChannels(ite);
+	ite->d.DriverReinitSound(ite);
+	Music_SoundCardLoadAllSamples(ite);
+}
+
+void Music_UnInitSoundCard(it_engine *ite)
+{
+	ite->d.DriverUninitSound(ite);
+}
+
+void Music_InitMusic(it_engine *ite)
+{
+#if ENABLEINT3
+#else
+	// This puts Music_UpdateSampleLocation in the INT3 TSR.
+	// Because we are not in x86 Real Mode, we cannot do this.
+#endif
+
+	Trace(" - Initialising SoundDriver Tables");
+
+	Music_ClearDriverTables(ite);
+	D_GotoStartingDirectory(ite);
+
+	Trace(" - Loading MIDI configuration");
+
+	// Open MIDI Config file.
+	int fp = -1;//open(MIDIConfigFileName, OREAD);
+
+	if(fp >= 0)
+	{
+		// TODO: serialise this properly
+		// TODO: actually create a MIDIDataArea
+		//readn(fp, ite->MIDIDataArea, (128+16+9)*32);
+
+		close(fp);
+	}
+
+	Trace(" - Initialising playback tables");
+
+	Music_Stop(ite);
+}
+
+void Music_ReleasePattern(it_engine *ite, uint16_t ax)
+{
+	// AX = pattern number
+
+	// Complete rewrite.
+	if(ite->pat[ax] != NULL)
+	{
+		free(ite->pat[ax]);
+		ite->pat[ax] = NULL;
+	}
+}
+
+it_pattern *Music_GetPattern(it_engine *ite, uint16_t ax)
+{
+	// This is much simpler.
+	it_pattern *pat = ite->pat[ax]; // SI
+
+	if(pat == NULL)
+		pat = (it_pattern *)EmptyPattern;
+
+	return pat;
+
+	// Here's what happens in the actual code.
+	/*
+	if(AL < 1)
+	{
+		// Empty pattern
+		Push    CS
+		Pop     DS
+		Mov     SI, Offset EmptyPattern
+
+		Pop     AX
+		Ret
+
+	} else if(AL == 1) {
+		// Start of segment, conventional memory
+		LodsW
+		Mov     DS, AX
+		Xor     SI, SI
+
+		Pop     AX
+		Ret
+
+	} else if(AL < 3) {
+		Push    CX
+		Push    DX
+
+		MovZX   CX, AH
+		Mov     DX, [SI]
+		Call    E_MapEMSMemory
+		Call    E_GetEMSPageFrame
+		Mov     DS, AX
+		Xor     SI, SI
+
+		Pop     DX
+		Pop     CX
+
+		Pop     AX
+		Ret
+	} else {
+		// Start of EMS block
+		LodsW
+		Call    E_MapAlignedBlockEMS
+
+		Pop     AX
+		Ret
+	}
+	*/
+
+	// Basically, a bunch of conventional-vs-EMS crap.
+}
+
+it_pattern *Music_GetPatternLocationNoCount(it_engine *ite, uint16_t ax)
+{
+	// Because we don't use the x86 realmode segment:offset crap,
+	// this is identical to Music_GetPattern.
+	it_pattern *pat = ite->pat[ax];
+
+	if(pat == NULL)
+		pat = (it_pattern *)EmptyPattern;
+
+	return pat;
+}
+
+it_pattern *Music_GetPatternLocation(it_engine *ite, uint16_t ax, uint16_t *len)
+{
+	// AX = pattern number
+	// Returns AX = handle
+	//         EBX = page/offset or
+	//               seg/offset
+	//         CX = length
+
+	it_pattern *pat = Music_GetPattern(ite, ax);
+	*len = pat->Length;
+
+	return Music_GetPatternLocationNoCount(ite, ax);
+}
+
+it_pattern *Music_AllocatePattern(it_engine *ite, uint16_t dx)
+{
+	USED(ite);
+	// DX = length.
+	// SI = Pattern
+	// ES:DI points to pattern area
+
+	// I suspect this is the C version:
+	return malloc(dx);
+
+	// Yeah.
+}
+
+uint8_t *Music_AllocateSample(it_engine *ite, uint16_t ax, size_t edx)
+{
+	// AX = Sample number, 0 based
+	// EDX = length
+	// Returns ES:DI, ES = 0 if not.
+
+	it_sample *smp = &ite->smp[ax];
+	int real_len = smp->Length;
+
+	Music_ReleaseSample(ite, ax, 2);
+	edx += 8; // Extra 8 bytes allocated..
+
+	if(edx < 1048576)
+	{
+		smp->Flg |= 1;
+
+		// not in the actual code, just trying to prevent leaks --GM
+		if(ite->SamplePointer[ax] != NULL)
+		{
+			free(ite->SamplePointer[ax]);
+			ite->SamplePointer[ax] = NULL;
+		}
+
+		// XXX: not sure where this actually goes --GM
+		smp->Length = real_len;
+
+		ite->SamplePointer[ax] = malloc(edx);
+		return ite->SamplePointer[ax];
+	}
+
+	smp->Flg &= 0xF0;
+	return NULL;
+}
+
+void Music_ReleaseSample(it_engine *ite, uint8_t al, uint8_t ah)
+{
+	// AX = sample number, 0 based
+	// AH = 1 = called from network
+	//    = 2 = called from allocate
+
+	it_sample *smp = &ite->smp[al]; // DS:BX points to sample
+
+	ite->d.DriverReleaseSample(ite, smp);
+
+	if((smp->Flg & 1) != 0)
+	{
+		if(ite->SamplePointer[al] != NULL)
+		{
+			free(ite->SamplePointer[al]);
+			ite->SamplePointer[al] = NULL;
+		}
+	}
+
+	smp->Length = 0;
+	//smp->SamplePointer = 0; // TODO: find out what to do with this --GM
+
+	if(ah > 1)
+		return;
+
+	if(ah != 1)
+	{
+#if NETWORKENABLED
+		Network_AddWordToQueue(ite, (ah<<8) | NETWORK_DELETESAMPLEOBJECT);
+#endif
+	}
+
+	smp->Flg &= 0xFE;
+
+	// this is where we do a good thing and actually use our struct properly
+	// IT itself just does memset(smp->Name, 0, 0x3C). --GM
+
+	// also, why isn't the filename scrubbed? --GM
+	memset(smp->Name, 0, 26);
+	smp->Cvt = 0;
+	smp->DfP = 0;
+	smp->Length = 0;
+	smp->Loop_Begin = 0;
+	smp->Loop_End = 0;
+	smp->C5Speed = 0;
+	smp->SusLoop_Begin = 0;
+	smp->SusLoop_End = 0;
+	smp->ViS = 0;
+	smp->ViD = 0;
+	smp->ViR = 0;
+	smp->ViT = 0;
+}
+
+void Music_ClearSampleName(it_engine *ite, uint16_t ax)
+{
+	// AX = Sample number (0 based)
+
+	memcpy(&ite->smp[ax], SampleHeader, 80);
+
+	// not in the actual code, just here to prevent a leak --GM
+	if(ite->SamplePointer[ax] != NULL)
+	{
+		free(ite->SamplePointer[ax]);
+		ite->SamplePointer[ax] = NULL;
+	}
+}
+
+void Music_ClearAllSampleNames(it_engine *ite)
+{
+	int i;
+
+	for(i = 99; i >= 0; i--)
+		Music_ClearSampleName(ite, i);
+}
+
+void Music_ReleaseAllSamples(it_engine *ite)
+{
+	int i;
+
+	for(i = 99; i >= 0; i--)
+		Music_ReleaseSample(ite, i, 0);
+}
+
+void Music_ReleaseAllPatterns(it_engine *ite)
+{
+	int i;
+
+	for(i = 99; i >= 0; i--)
+		Music_ReleasePattern(ite, i);
+}
+
+void Music_ClearInstrument(it_engine *ite, uint16_t ax)
+{
+	// AX = Instrument number
+	// (0 based)
+
+	memcpy(&ite->ins[ax], InstrumentHeader, 554);
+}
+
+void Music_ClearAllInstruments(it_engine *ite)
+{
+	int i;
+
+	for(i = 99; i >= 0; i--)
+		Music_ClearInstrument(ite, i);
+}
+
+void Music_UnInitMusic(it_engine *ite)
+{
+	Music_UnInitSoundCard(ite);
+	Music_UnloadDriver(ite);
+
+	Music_ReleaseAllPatterns(ite);
+	Music_ReleaseAllSamples(ite);
+}
+
+// NOT IMPLEMENTING: Music_GetSongSegment
+// BECAUSE: it's legacy DOS crap that doesn't apply --GM
+
+void Music_UnloadDriver(it_engine *ite)
+{
+	USED(ite);
+	// really not sure how to go about this one.
+	// might just opt for dlclose() or something like that --GM
+
+	/*
+	Xor     AX, AX
+	XChg    AX, CS:SoundDriverSegment
+	Test    AX, AX
+	JZ      Music_UnloadDriver1
+
+	Mov     ES, AX
+	Mov     AH, 49h
+	Int     21h
+
+Music_UnloadDriver1:
+	Ret
+	*/
+}
+
+// these have been reworked a bit
+int NoFunction(it_engine *ite)
+{
+	USED(ite);
+	return -1;
+}
+
+int NoFunctionB(it_engine *ite, uint8_t w1)
+{
+	USED(ite, w1);
+	return -1;
+}
+
+int NoFunctionW(it_engine *ite, uint16_t w1)
+{
+	USED(ite, w1);
+	return -1;
+}
+
+int NoFunctionWW(it_engine *ite, uint16_t w1, uint16_t w2)
+{
+	USED(ite, w1, w2);
+	return -1;
+}
+
+int NoFunctionPs(it_engine *ite, it_sample *ps1)
+{
+	USED(ite, ps1);
+	return -1;
+}
+
+const char *NoFunction2(it_engine *ite)
+{
+	USED(ite);
+	return NoSoundCardMsg;
+}
+
+const char *NoFunction2PccWW(it_engine *ite, const char *c1, uint16_t w2, uint16_t w3)
+{
+	USED(ite, c1, w2, w3);
+	return NoSoundCardMsg;
+}
+
+void Music_ClearDriverTables(it_engine *ite)
+{
+	// Makes all of them point to
+	// Xor AX, AX, StC, RetF
+	ite->d.DriverDetectCard = NoFunction2PccWW;
+	ite->d.DriverInitSound = NoFunction2;
+
+	// we have to serialise this, really,
+	// thus, no OLDDRIVER distinction --GM
+	ite->d.DriverReinitSound = NoFunction;
+	ite->d.DriverUninitSound = NoFunction;
+	ite->d.DriverPoll = NoFunctionWW;
+	ite->d.DriverSetTempo = NoFunctionW;
+	ite->d.DriverSetMixVolume = NoFunctionW;
+	ite->d.DriverSetStereo = NoFunctionW;
+	ite->d.DriverLoadSample = NoFunctionW;
+	ite->d.DriverReleaseSample = NoFunctionPs;
+	ite->d.DriverResetMemory = NoFunction;
+	ite->d.DriverGetStatus = NoFunction;
+
+	ite->d.DriverSoundCardScreen = NoFunction;
+	ite->d.DriverGetVariable = NoFunctionW;
+	ite->d.DriverSetVariable = NoFunctionWW;
+
+	ite->d.DriverMIDIOut = NoFunctionB;
+	ite->d.DriverGetWaveform = NoFunction;
+
+	ite->d.DriverMaxChannels = 32;
+}
+
+int Music_LoadDriver(it_engine *ite, const char *fname)
+{
+	// pretty much a complete rewrite! let's go! --GM
+
+	// dlopen() is handled on every modern OS that isn't Windows.
+	// of course, it's easier to just statically link in a driver.
+	// so, no dlopen(). yet.
+
+	it_drvdata *drv = DriverSys_GetByName(ite, fname);
+	if(drv == NULL)
+	{
+		Music_ClearDriverTables(ite);
+		return -1;
+	}
+
+	memcpy(&ite->d, drv, sizeof(it_drvdata));
+
+	// There's no need to worry about "required variables"
+	// or "required functions" - driver has full it_engine access.
+
+	return 0;
+}
+
+const char *Music_AutoDetectSoundCard(it_engine *ite)
+{
+	int dord = 0;
+	const char *reply = NULL;
+
+	// Returns DS:SI = string
+	// AX, BX, CX, DX, DI = parameters
+
+	D_GotoStartingDirectory(ite);
+
+	if(ite->DriverName != NULL)
+	{
+		Trace(" - Loading specific soundcard driver");
+
+		if(ite->DriverName[0] != '\x00')
+		{
+			if(!Music_LoadDriver(ite, ite->DriverName))
+			{
+				reply = ite->d.DriverDetectCard(
+					ite, ite->DriverName, 1, TRACKERVERSION); // Forced
+
+				// Unfortunately these drivers don't have to return 'Jeff'.
+				if(reply != NULL)
+				{
+					Music_UnloadDriver(ite);
+					Music_ClearDriverTables(ite);
+				}
+			}
+		}
+	} else for(;;) {
+	//Music_AutoDetectSoundCard1:
+		Trace(" - Testing soundcard driver");
+
+		int didx = DriverDetectionOrder[dord++];
+		if(didx == 0xFFFF)
+			break;
+
+		if(!Music_LoadDriver(ite, DriverNameTable[didx]))
+		{
+			const char *reply = ite->d.DriverDetectCard(
+				ite, DriverNameTable[didx], 1, TRACKERVERSION); // Not forced
+
+			if(reply == NULL)
+				break;
+
+			Music_UnloadDriver(ite);
+			Music_ClearDriverTables(ite);
+		}
+	}
+
+	GetChannels(ite);
+	ite->d.DriverInitSound(ite);
+
+	// Not sure what these are for!
+	/*
+	Mov     [CS:ADSCParams+2], DS
+	Mov     [ADSCParams], SI
+	Mov     [ADSCParams+12], DI
+	Mov     [ADSCParams+10], DX
+	Mov     [ADSCParams+8], CX
+	Mov     [ADSCParams+6], BX
+	Mov     [ADSCParams+4], AX
+	*/
+
+	Music_InitStereo(ite);
+	Music_InitMixTable(ite);
+
+	return reply;
+}
+
+void Music_ShowAutoDetectSoundCard(it_engine *ite)
+{
+	USED(ite);
+	// HELP --GM
+	/*
+	Push    DWord Ptr [ADSCParams+10]
+	Push    DWord Ptr [ADSCParams+6]
+	Push    [ADSCParams+4]
+
+	LDS     SI, DWord Ptr [ADSCParams]
+
+	Mov     AH, 20h
+	Mov     DI, (26+28*80)*2
+	Call    S_DrawString
+
+	Add     SP, 10
+	Ret
+	*/
+}
+
+uint16_t Music_GetInstrumentMode(it_engine *ite)
+{
+	return (ite->hdr.Flags & 4);
+}
+
+void UpdateGOTONote(it_engine *ite)
+{
+	// Get offset & arrayed flag.
+
+	uint16_t patnum;
+	uint16_t maxrow;
+
+	it_pattern *pat = PE_GetCurrentPattern(ite, &patnum, &maxrow);
+	USED(pat);
+	// AX = Pattern number
+	// BX = MaxRow
+	// DS = PatternDataSegment
+
+	if(patnum == ite->CurrentPattern)
+	{
+		ite->DecodeExpectedPattern = patnum;
+		//Mov     CS:PatternSegment, DS
+
+		ite->NumberOfRows = maxrow;
+
+		if(ite->ProcessRow >= ite->NumberOfRows)
+		{
+			ite->ProcessRow = 0;
+			ite->CurrentRow = 0;
+		}
+
+		ite->DecodeExpectedRow = ite->ProcessRow;
+
+		ite->PatternOffset = ite->ProcessRow * (64*5);
+		ite->PatternArray = 1;
+
+		return;
+	}
+
+	ite->DecodeExpectedPattern = ite->CurrentPattern;
+#if NETWORKENABLED
+	Network_UpdatePatternIfIdle(ite);
+#endif
+	pat = Music_GetPattern(ite, ite->CurrentPattern);
+	// DS:SI points to pattern.
+
+	// AX = number of rows
+	if(ite->ProcessRow >= pat->Rows)
+		ite->CurrentRow = 0;
+	else
+		ite->CurrentRow = ite->ProcessRow;
+
+	ite->ProcessRow = ite->CurrentRow;
+	ite->DecodeExpectedRow = ite->CurrentRow;
+	ite->NumberOfRows = pat->Rows;
+
+	uint8_t *data = pat->data;
+	uint16_t cx = ite->ProcessRow;
+	uint8_t al;
+	cx++;
+
+	while((--cx) != 0)
+	{
+		for(;;)
+		{
+			// OK.. now to find the right
+			// offset & update tables.
+			al = *(data++);
+			if(al == 0)
+				break;
+
+			uint8_t dl = al;
+			al = (al & 0x7F) - 1;
+			it_host *chn = &ite->chn[al];
+			// CS:DI points.
+
+			if((dl & 0x80) != 0)
+				chn->Msk = *(data++);
+
+			if((chn->Msk & 1) != 0)
+				chn->Nte = *(data++);
+			if((chn->Msk & 2) != 0)
+				chn->Ins = *(data++);
+			if((chn->Msk & 4) != 0)
+				chn->Vol = *(data++);
+			if((chn->Msk & 8) != 0)
+			{
+				// warning, when reading the code this can catch you out.
+				// it loads then stores a *word*.
+				// the IT code does this kind of stuff a lot. --GM
+				chn->Cmd = *(data++);
+				chn->CVal = *(data++);
+			}
+		}
+	}
+
+	ite->PatternOffset = data - pat->data;
+	ite->PatternArray = 0;
+}
+
+void PreInitCommand(it_engine *ite, it_host *chn)
+{
+	if((chn->Msk & 0x33) != 0)
+	{
+		if((ite->hdr.Flags & 4) == 0 || chn->Nte >= 120 || chn->Ins == 0)
+		{
+			chn->Nt2 = chn->Nte;
+			chn->Smp = chn->Ins;
+		} else {
+			// Have to xlat.
+
+			it_instrument *ins = &ite->ins[chn->Ins-1];
+
+			// XXX: work around a modplugism
+			if(ins->MCh == 0 || ins->MCh >= 128)
+			{
+				if(ins->MCh >= 128)
+					printf("MODPLUGISM WARNING: midi channel %02X used for effect slot - WILL PROBABLY CRASH IT\n", ins->MCh);
+
+				chn->Nt2 = ins->NoteSamp[chn->Nte][0];
+				chn->Smp = ins->NoteSamp[chn->Nte][1];
+				// This part is fine.
+				//printf("XLAT %i -> %i %i\n", chn->Nte, chn->Nt2, chn->Smp);
+			} else {
+
+				if(ins->MCh == 17)
+					chn->MCh = chn->HCN+1;
+				else
+					chn->MCh = ins->MCh;
+
+				chn->MPr = ins->MPr;
+
+				chn->Nt2 = ins->NoteSamp[chn->Nte][0];
+				chn->Smp = 101;
+			}
+
+			if(chn->Smp == 0) // No sample?
+				return;
+		}
+	}
+
+	InitCommandTable[chn->Cmd & 0x1F](ite, chn); // Init note
+	chn->Flags |= 64;
+
+	// Check whether chn is on
+	if((chn->HCN & 0x80) == 0)
+		return;
+
+	if((chn->Flags & 32) != 0)
+		return;
+
+	if((chn->Flags & 4) == 0)
+		return; // Channel was off.
+
+	it_slave *slave = &ite->slave[chn->SCOffst];
+	slave->Flags |= 0x0800;
+}
+
+void UpdateNoteData(it_engine *ite)
+{
+	ite->PatternLooping = 0;
+
+	if(ite->CurrentPattern == ite->DecodeExpectedPattern)
+	{
+		UpdateGOTONote(ite);
+	} else {
+		ite->DecodeExpectedRow++;
+		if(ite->CurrentRow != ite->DecodeExpectedRow)
+			UpdateGOTONote(ite);
+	}
+
+	uint16_t cx = 64; // 64 channels
+	// Xor     AX, AX                  // Just to get rid of "jumps"
+	it_host *chn = &ite->chn[0]; // DI --GM
+
+	if(ite->PatternArray != 1)
+	{
+		// First clear all old command&value.
+		// Mov     CX, 64                 // Done above
+
+		for(; cx != 0; cx--)
+		{
+			chn->Flags &= ~(3+32+64+256);
+			chn++;
+		}
+
+		//Mov     AX, CurrentPattern
+#if NETWORKENABLED
+		// Network_UpdatePatternIfIdle(ite);
+#endif
+		it_pattern *pat = Music_GetPattern(ite, ite->CurrentPattern); // Gets DS
+		uint8_t *data = pat->data + ite->PatternOffset;
+
+		for(;;)
+		{
+			uint8_t al;
+			al = *(data++);
+			if(al == 0) break; // No more!
+
+			// else... go through decoding.
+
+			uint8_t dl = al;
+			assert((al & 0x7F) != 0);
+			al = (al & 0x7F) - 1;
+			chn = &ite->chn[al];
+
+			uint8_t dh = chn->Msk; // mask.
+			if((dl & 0x80) != 0)
+			{
+				dh = *(data++);
+				chn->Msk = dh;
+			}
+
+			if((dh & 1) != 0)
+				chn->Nte = *(data++);
+			if((dh & 2) != 0)
+				chn->Ins = *(data++);
+			if((dh & 4) != 0)
+				chn->Vol = *(data++);
+
+			uint8_t ah;
+			if((dh & 8) != 0)
+			{
+				al = chn->OCm = *(data++);
+				ah = chn->OVal = *(data++);
+			} else if((dh & 0x80) != 0) {
+				al = chn->OCm;
+				ah = chn->OVal;
+			} else {
+				al = ah = 0;
+			}
+
+			chn->Cmd  = al;
+			chn->CVal = ah;
+			PreInitCommand(ite, chn);
+		}
+
+		ite->PatternOffset = data - pat->data;
+	} else {
+
+		uint8_t *data = (&ite->patspace[0]) + ite->PatternOffset;
+
+		// Mov     CX, 64                  ; 64 channels
+		// Mov     DI, Offset HostChannelInformationTable
+
+		for(; cx != 64; cx--)
+		{
+			uint8_t dl = 0; // DL = mask.
+			chn->Flags &= ~(3+32+64+256); // Turn off all calling...
+
+			uint8_t al;
+			al = *(data++);
+			if(al != NONOTE)
+			{
+				chn->Nte = al;
+				dl |= 1;
+			}
+
+			al = *(data++);
+			if(al != 0)
+			{
+				chn->Ins = al;
+				dl |= 2;
+			}
+
+			al = *(data++);
+			if(al != 0xFF)
+			{
+				chn->Vol = al;
+				dl |= 4;
+			}
+
+			al = *(data++);
+			uint8_t ah = *(data++);
+			chn->Cmd  = chn->OCm  = al;
+			chn->CVal = chn->OVal = ah;
+
+			if(al != 0 || ah != 0)
+				dl |= 8;
+
+			if(dl != 0)
+			{
+				chn->Msk = dl;
+				PreInitCommand(ite, chn);
+			}
+
+			chn++;
+		}
+
+		ite->PatternOffset = data - ite->patspace;
+	}
+}
+
+void UpdateVibrato(it_engine *ite, it_slave *slave)
+{
+	// DS:SI points to slavechannelstruct.
+
+	it_sample *smp = &ite->smp[slave->SmpOffs]; // ES:BX points to sample
+	if(smp->ViD == 0)
+		return;
+
+	// ITTECH.TXT lied to you - check the original source.
+	// it misses a crucial step... and IT uses CX not AX --GM
+	slave->ViDepth += smp->ViR;
+	if((slave->ViDepth>>8) > smp->ViD)
+		slave->ViDepth = (slave->ViDepth & 0xFF) | (((uint16_t)smp->ViD)<<8);
+
+	if(smp->ViS == 0)
+		return;
+
+	int8_t al = 0;
+	if(smp->ViT != 3)
+	{
+		slave->ViP += smp->ViS; // Update pointer.
+
+		// probably not wise to try to emulate out-of-range vibrato types.
+		// well, at least for now. we can research these later. --GM
+		switch(smp->ViT)
+		{
+			case 0:
+				al = FineSineData[slave->ViP];
+				break;
+			case 1:
+				al = FineRampDownData[slave->ViP];
+				break;
+			case 2:
+				al = FineSquareWave[slave->ViP];
+				break;
+			default:
+				printf("PANIC: out of range vibrato types not emulated!\n");
+				abort();
+		}
+	} else {
+		al = ((Random(ite) & 0x7F) - 64);
+	}
+
+	int16_t ax = ((int16_t)al) * (int16_t)(slave->ViDepth>>8);
+	ax >>= 6; // SAL 2, take high (SAR 8) .: SAR 6
+
+#if !USEFPUCODE
+	if(ax < 0)
+	{
+		// strictly speaking this branch doesn't look for the host channel,
+		// but it's included for completeness --GM
+		PitchSlideDownLinear(ite, &ite->chn[slave->HCOffst], slave, -ax);
+	} else {
+#endif
+		PitchSlideUpLinear(ite, &ite->chn[slave->HCOffst], slave, ax);
+#if !USEFPUCODE
+	}
+#endif
+}
+
+void Update(it_engine *ite, uint16_t *rcx, it_slave **si, uint16_t *ax)
+{
+	uint16_t cx = MAXSLAVECHANNELS;
+	it_slave *slave = &ite->slave[0];
+	it_host *chn = &ite->chn[slave->HCOffst];
+
+	MIDITranslate(ite, chn, slave, MIDICOMMAND_TICK);
+
+	for(; cx != 0; cx--)
+	{
+		if((slave->Flags & 1) != 0)
+		{
+			// reset volume
+			if(slave->VS != slave->Vol)
+			{
+				slave->Vol = slave->VS;
+				slave->Flags |= 0x10;
+			}
+
+			// Freq
+			if(slave->Frequency_Set != slave->Frequency)
+			{
+				slave->Frequency = slave->Frequency_Set;
+				slave->Flags |= 0x20; // Recalc freq.
+			}
+		}
+
+		slave++;
+	}
+
+	UpdateData(ite);
+
+	if((ite->hdr.Flags & 4) != 0)
+	{
+		UpdateInstruments(ite);
+	} else {
+		UpdateSamples(ite);
+	}
+
+	*rcx = ite->NumChannels;
+	*si = &ite->slave[0];
+	*ax = ite->PlayMode;
+}
+
+void UpdateSamples(it_engine *ite)
+{
+	uint16_t i = ite->NumChannels;
+	it_slave *slave = &ite->slave[0];
+
+	for(; i != 0 ; i--, slave++)
+	{
+		if((slave->Flags & 1) == 0)
+			continue;
+
+		// OK... if recalc volume is on.
+		// then recalc volume! :)
+		if((slave->Flags & 16) != 0)
+		{
+			slave->Flags &= ~16;
+			slave->Flags |= 64; // Recalc final vol
+
+			if(ite->SoloSample != 0xFF && ite->SoloSample != slave->Smp)
+				slave->Flags |= 0x0800;
+
+			uint32_t ax = ((uint32_t)slave->Vol)*((uint32_t)slave->CVl)
+				*((uint32_t)slave->SVl); // AX = 0->64*64*128
+			ax >>= 4; // AX = 0->32768
+
+			ax *= (uint32_t)ite->GlobalVolume;
+			// AX = 0->32768*128
+
+			ax >>= 7;
+
+			// Final vol stored.
+			slave->FV = ax>>8;
+			slave->_16bVol = ax;
+		}
+
+		if((slave->Flags & 2) != 0)
+		{
+			slave->Flags &= ~2;
+			slave->Flags |= 0x8000;
+
+			slave->FP = slave->Pan;
+			if(slave->Pan != 100)
+			{
+				int16_t ax = slave->Pan;
+				ax -= 32;
+				// Pan = (Pan-32)* Separation/128 + 32
+
+				ax *= (int16_t)ite->hdr.Sep;
+				// 0->64 (Separation) => AX = -2048->+2048
+				// ie. AH = -8->+8
+
+				ax >>= 7;
+				// AL = -32->+32
+
+				if(ite->d.ReverseChannels != 0)
+					ax = -ax;
+
+				ax += 32;
+
+				slave->FPP = ax;
+			} else {
+				slave->FPP = slave->Pan;
+			}
+		}
+
+		UpdateVibrato(ite, slave);
+	}
+}
+
+int UpdateEnvelope(it_engine *ite, it_slen *slen, it_envelope *env, uint16_t bp)
+{
+	USED(ite);
+	// Returns Carry if envelope needs
+	// to be turned off
+	// Reqs ES:DI points to envelope
+	// DS:SI points to slave channel envelope structure
+	// Called only if envelope is ON
+	// BP != 0 if sustain points released, 0 otherwise
+
+	int32_t dx = slen->EnvPos; // DX = current pos
+	if(dx < slen->NextET)
+	{
+		// Increase position;
+		slen->EnvPos = ++dx;
+
+		// Update value
+		slen->EnvelopeValue += slen->EnvelopeDelta;
+
+		return 0;
+	}
+
+	// Procedure:
+	// 1) Get current pos' value
+	// 2) Figure out next pos (inc/loop)
+	// 3) Figure out delta to next pos
+	// 4) Terminate if no loop (with carry)
+	//      or place new check in [SI+0Ch]
+
+	uint16_t bx = slen->CurEnN; // BX = cur env node;
+	uint16_t ax = bx+1;
+
+	dx = (int8_t)env->Nodes[bx][0];
+	dx <<= 16;
+	slen->EnvelopeValue = dx; // Current pos value done.
+	// AX = next cur env node
+
+	if((env->Flg & 6) != 0) // Any loop at all?
+	{
+		// Normal Loop
+		uint8_t bl = env->LpB;
+		uint8_t bh = env->LpE;
+
+		// the flow here is very weird in the original source --GM
+		int use_sus = ((env->Flg & 4) != 0) && (bp == 0);
+		int has_loop = (use_sus ? (env->Flg & 4) : (env->Flg & 2)) != 0;
+
+		if(use_sus)
+		{
+			bl = env->SLB;
+			bh = env->SLE;
+		}
+
+		// Loop
+		if(has_loop && ax > bh)
+		{
+			// BL = New node
+			slen->CurEnN = bl;
+			uint16_t x = ((uint16_t)env->Nodes[bl][1])
+				| (((uint16_t)env->Nodes[bl][2])<<8);
+
+			slen->EnvPos = x;
+			slen->NextET = x;
+
+			return 0;
+		}
+	}
+
+	// AX = new node
+	if(ax >= env->Num)
+		return 1;
+
+	//slen->CurEnN = ax; // New node
+	slen->CurEnN = (slen->CurEnN & 0xFF00) | (ax & 0x00FF); // New node
+
+	// New node's tick
+	uint16_t x1 = ((uint16_t)env->Nodes[ax][1])
+		| (((uint16_t)env->Nodes[ax][2])<<8);
+	slen->NextET = x1;
+	uint16_t x0 = ((uint16_t)env->Nodes[ax-1][1])
+		| (((uint16_t)env->Nodes[ax-1][2])<<8);
+
+	uint16_t delta = x1 - x0;
+	uint16_t base = x0+1;
+	slen->EnvPos = base; // For security.
+
+	int32_t yd = (int32_t)(((int8_t)env->Nodes[ax][0]) - ((int8_t)env->Nodes[ax-1][0]));
+	int isneg = (yd < 0);
+
+	if(isneg) yd = -yd;
+
+	// Just to prevent div 0 errors
+	if(delta == 0) delta = 1;
+
+	// this was hell to translate --GM
+	uint32_t eax = ((uint32_t)yd)/(uint32_t)delta;
+	uint32_t edx = ((uint32_t)yd)%(uint32_t)delta;
+	yd = (eax<<16) | ((edx<<16)/(uint32_t)delta);
+
+	if(isneg) yd = -yd;
+
+	slen->EnvelopeDelta = yd; // Delta done
+
+	return 0;
+}
+
+void UpdateMIDI(it_engine *ite)
+{
+	uint16_t cx;
+	it_slave *slave;
+	it_host *chn;
+
+	// Stop cycle
+	for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 0x200) == 0) continue;
+		if(slave->Smp != 100) continue;
+		if((slave->Flags & 1) == 0) continue;
+
+		slave->Flags = 0;
+		chn = &ite->chn[slave->HCOffst];
+
+		MIDITranslate(ite, chn, slave, MIDICOMMAND_STOPNOTE);
+
+		if((slave->HCN & 0x80) != 0) continue;
+
+		slave->HCN |= 0x80;
+		chn->Flags &= ~4; // Signify channel off
+	}
+
+	// Play cycle
+	for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++)
+	{
+		if(slave->Smp != 100) continue; // MIDI Instrument?
+
+		if((slave->Flags & 1) == 0) continue;
+
+		slave->OldSampleOffset = (slave->OldSampleOffset & ~0xFF)
+			| (slave->Sample_Offset & 0xFF);
+		slave->Sample_Offset = (slave->Sample_Offset & ~0xFF)
+			| 1;
+
+		if((slave->Smp & 0x0800) != 0) continue; // Muted?
+
+		chn = &ite->chn[slave->HCOffst];
+		slave->Flags &= 0x788D;
+		// 0111100010001101b
+		// 0111 1000 1000 1101
+		// 7    8    8    D
+
+		if((slave->Flags & 0x0100) != 0)
+		{
+			// Check if there's a bank select.
+			uint16_t MBank = ((uint16_t)slave->FCut)
+				| (((uint16_t)slave->FRes)<<8);
+
+			if(MBank != 0xFFFF)
+			{
+				if(ite->MIDIBanks[slave->MCh-1] != MBank)
+				{
+					ite->MIDIBanks[slave->MCh-1] = MBank;
+					ite->MIDIPrograms[slave->MCh-1] = 0xFF; // Reset program
+					MIDITranslate(ite, chn, slave, MIDICOMMAND_BANKSELECT);
+				}
+			}
+
+			// Check for a program specification
+			if(((int8_t)slave->MPr) >= 0)
+			{
+				if(ite->MIDIPrograms[slave->MCh-1] != slave->MPr)
+				{
+					ite->MIDIPrograms[slave->MCh-1] = slave->MPr;
+					MIDITranslate(ite, chn, slave, MIDICOMMAND_PROGRAMSELECT);
+				}
+			}
+
+			// Check for MIDI pitch wheel..
+			if((ite->hdr.Flags & 64) != 0)
+			{
+				if(ite->MIDIPitch[slave->MCh-1] != 0x2000)
+				{
+					ite->MIDIPitch[slave->MCh-1] = 0x2000;
+					MIDISendFilter(ite, chn, (slave->MCh-1) | 0xE0);
+					MIDISendFilter(ite, chn, 0); // Reset pitch wheel
+					MIDISendFilter(ite, chn, 0x40);
+				}
+			}
+
+			slave->RVol_MIDIFSet = slave->Frequency_Set;
+			MIDITranslate(ite, chn, slave, MIDICOMMAND_PLAYNOTE);
+		} else {
+			// Change in volume?
+			if((slave->Flags & 64) != 0)
+				MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEVOLUME);
+		}
+
+		// Pan changed?
+		if((slave->Flags & 0x8000) != 0)
+		{
+			if(ite->MIDIPan[slave->MCh-1] != slave->FPP)
+			{
+				slave->FPP = ite->MIDIPan[slave->MCh-1];
+				MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEPAN);
+			}
+		}
+
+		// Pitch changed?
+		if((slave->Flags & 32) != 0)
+			MIDITranslate(ite, chn, slave, MIDICOMMAND_CHANGEPITCH);
+	}
+}
+
+void UpdateInstruments(it_engine *ite)
+{
+	// Things to update:
+	//  1) Volume envelope
+	//  2) Fadeout
+	//  3) FinalVolume
+	//  4) Vibrato.
+	// Turn off channel if
+	//  1) Volume envelope is off & VEV = 0 or
+	//  2) Fadeout = 0
+
+	uint16_t cx;
+	it_slave *slave;
+	ite->DoMIDICycle = 0;
+
+	for(cx = MAXSLAVECHANNELS, slave = &ite->slave[0]; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) != 0)
+			UpdateInstruments16(ite, slave); // Channel on!!
+	}
+
+	if(ite->DoMIDICycle != 0)
+		UpdateMIDI(ite);
+}
+
+void UpdateInstruments16(it_engine *ite, it_slave *slave)
+{
+	// Mov     CX, [SI]
+
+	it_instrument *ins = &ite->ins[slave->InsOffs-1];
+
+	if(slave->Ins == 0xFF) { // No instrument?
+		UpdateInstruments5(ite, slave);
+		return;
+	}
+
+	uint16_t bp = (slave->Flags & 4); // BP = sustain for envelope calls
+
+	//printf("InsOffs %i %i %i %04X\n", slave->InsOffs, slave->Ins, bp, slave->Flags);
+
+	if((slave->Flags & 0x4000) != 0)
+		if(UpdateEnvelope(ite, &slave->Pt, &ins->PitchEnv, bp) != 0)
+			slave->Flags &= ~0x4000;
+
+	int do_filter = ((ins->PitchEnv.Flg & 0x80) != 0);
+
+	if(do_filter && slave->Smp != 100)
+	{
+		int16_t fval = (slave->Pt.EnvelopeValue>>8);
+		fval >>= 6; // Range -128 to +128
+		fval += 128; // Range 0 -> 256
+
+		if(fval >= 0x100) fval--;
+		slave->FCut = fval;
+
+		slave->Flags |= 64; // Recalculate final volume
+
+	} else if(!do_filter) {
+		int16_t ptval = (slave->Pt.EnvelopeValue>>8);
+		ptval >>= 3;
+
+		if(ptval != 0)
+		{
+#if !USEFPUCODE
+			if(ptval >= 0)
+			{
+#endif
+				PitchSlideUpLinear(ite, &ite->chn[slave->HCOffst], slave, ptval);
+#if !USEFPUCODE
+			} else {
+				PitchSlideDownLinear(ite, &ite->chn[slave->HCOffst], slave, -ptval);
+			}
+#endif
+			slave->Flags |= 32; // Recalculate freq
+		}
+	}
+
+	if((slave->Flags & 0x2000) != 0)
+	{
+		slave->Flags |= 2; // Recalculate pan
+
+		if(UpdateEnvelope(ite, &slave->P, &ins->PanEnv, bp) != 0)
+			slave->Flags &= ~0x2000;
+	}
+
+	// Volume envelope on?
+	int jump_ins17 = 0;
+	if((slave->Flags & 0x1000) != 0)
+	{
+		slave->Flags |= 16; // Recalculate volume
+
+		if(UpdateEnvelope(ite, &slave->V, &ins->VolEnv, bp) == 0)
+		{
+			// Note fade on?
+			if((slave->Flags & 8) != 0)
+			{
+				// Now, check if loop + sustain
+				// off
+				// TODO: verify - i may have missed a bp modification --GM
+				if(bp == 0) {
+					UpdateInstruments5(ite, slave);
+					return;
+				}
+
+				// Normal vol env loop?
+				if((ins->VolEnv.Flg & 2) == 0) {
+					UpdateInstruments5(ite, slave); // Volume calculation
+					return;
+				}
+			}
+
+		} else {
+			// Envelope turned off...
+			slave->Flags &= ~0x1000; // Turn off envelope flag
+
+			// Turn off if end of loop is reached
+			// TODO: verify - this looks weird
+			if(((slave->V.EnvelopeValue>>16)&0xFF) == 0)
+				jump_ins17 = 1;
+		}
+
+	} else {
+		if((slave->Flags & 8) == 0) // Note fade??
+		{
+			// Also apply fade if No vol env
+			// AND sustain off
+
+			// Note off issued?
+			if((slave->Flags & 4) == 0) {
+				UpdateInstruments5(ite, slave);
+				return;
+			}
+		}
+	}
+
+	if(!jump_ins17)
+	{
+		// in the original, this line is skipped when the bit is set
+		// maintaining that would just make for a horrible flow --GM
+		slave->Flags |= 8;
+
+		slave->FadeOut -= ins->FadeOut;
+		if(slave->FadeOut <= 0)
+		{
+			slave->FadeOut = 0;
+		} else {
+			slave->Flags |= 16; // Recalc volume flag.
+			UpdateInstruments5(ite, slave);
+			return;
+		}
+	}
+
+	// Turn off channel
+	if((slave->HCN & 0x80) == 0)
+	{
+		slave->HCN |= 0x80;
+		// Host channel exists
+		ite->chn[slave->HCOffst].Flags &= ~4;
+	}
+
+	slave->Flags |= 0x0200;
+	slave->Flags |= 16; // Recalc volume flag.
+
+	UpdateInstruments5(ite, slave);
+}
+
+void UpdateInstruments5(it_engine *ite, it_slave *slave)
+{
+	if((slave->Flags & 16) != 0)
+	{
+		// Calculate volume
+		slave->Flags &= ~16;
+		//Mov     DX, Word Ptr [SoloSample]       ; DL = sample, DH = inst
+
+		slave->Flags |= 64; // Recalc final volume
+		uint32_t eax = slave->Vol; // Note volume...
+		eax *= (uint32_t)slave->CVl; // Channel volume
+		// AX = (0->4096)
+
+		if(ite->SoloSample != 0xFF && ite->SoloSample != slave->Smp)
+			slave->Flags |= 0x0800;
+		else if(ite->SoloInstrument != 0xFF && ite->SoloInstrument != slave->Ins)
+			slave->Flags |= 0x0800;
+
+		eax *= slave->FadeOut;
+		// Fadeout Vol (0->1024)
+		// DX:AX = 0->4194304
+
+		eax >>= 7; // AX = (0->32768)
+
+		eax *= slave->SVl; // Sample volume
+		// DX:AX = 0->4194304
+
+		eax >>= 7; // AX = 0->32768
+
+		eax *= (0xFFFF & (slave->V.EnvelopeValue>>8)); // VEV, 0->64*256
+		// DX:AX = 0->536870912
+
+		eax >>= 14; // AX = 0->32768
+
+		eax *= ite->GlobalVolume; // DX:AX = 0->4194304
+
+		eax >>= 7;
+
+		//printf("VOL %04X %02X\n", slave->_16bVol, slave->FV);
+		slave->FV = eax>>8;
+		slave->_16bVol = eax;
+	}
+
+	// Change in panning?
+	if((slave->Flags & 2) != 0)
+	{
+		slave->Flags &= ~2;
+		slave->Flags |= 0x8000;
+
+		int16_t ax = slave->Pan; // actual pan
+		if(ax != 100)
+		{
+			// some really funky stuff going on here.
+			// i could possibly simplify this but i'd like to be accurate.
+			// so, here we go for now --GM
+			ax = 32 - ax;
+			ax ^= (ax>>8);
+			ax = (ax & 0xFF00) | ((ax - (ax>>8)) & 0xFF);
+			// AL = |32-ActualPan|
+
+			ax = (ax & 0xFF00) | ((32 - ax) & 0xFF);
+
+			ax = (ax & 0xFF) * (int16_t)(int8_t)(
+				slave->Pt.EnvelopeValue>>16); // Pan envelope..
+			ax >>= 5;
+			ax = (ax & 0xFF00) + ((ax + slave->Pan) & 0xFF);
+
+			// Value to show..
+			slave->Pan = ax;
+
+			ax = (ax & 0xFF00) + ((ax - 32) & 0xFF);
+
+			// Pan = (Pan-32)* Separation/128 + 32
+			uint8_t sep = ite->hdr.Sep; // 0->64 (Separation)
+			sep >>= 1;
+			ax = (ax & 0xFF) & (int8_t)sep;
+			// AX = -2048->+2048
+			// ie. AH = -8->+8
+			ax >>= 6; // AL = -32->+32
+
+			if(ite->d.ReverseChannels != 0)
+				ax = -ax;
+
+			ax += 32;
+
+		} else {
+			slave->FP = ax;
+		}
+
+		slave->FPP = ax;
+	}
+
+	UpdateVibrato(ite, slave);
+
+	if(slave->Smp == 100) // MIDI?
+		ite->DoMIDICycle = 1;
+}
+
+void UpdateData(it_engine *ite)
+{
+	if(ite->PlayMode == 1)
+		UpdateData_PlayMode1(ite);
+	else if(ite->PlayMode < 1)
+		UpdateData_PlayMode0(ite);
+	else
+		UpdateData_PlayMode2(ite);
+}
+
+void UpdateData_PlayMode0(it_engine *ite)
+{
+	uint16_t cx;
+	it_host *chn;
+
+	// Step through each channel.
+	// Check if on/updatemode OK
+	for(cx = 64, chn = &ite->chn[0]; cx != 0; cx--, chn++)
+	{
+		if(chn->CUC == 0) continue;
+
+		chn->CUC--; // Handle counter
+
+		if(chn->CUC != 0xFF)
+			chn->Flags &= ~0x303; // Turn off mode.
+
+		// Mov     AX, [DI]
+
+		// Channel on? / Don't update effect?
+		if((chn->Flags & 4) != 0 && (chn->Flags & 0x100) != 0)
+			VolumeEffectTable[chn->VCm & 7](ite, chn);
+
+		if((chn->Flags & 2) != 0 // Update effect regardless..
+			|| ((chn->Flags & 4) != 0 && (chn->Flags & 1) != 0))
+		{
+			// OK. now handle counter.
+			CommandTable[chn->Cmd & 31](ite, chn);
+		}
+
+		// Progress to next channel
+	}
+
+	// That's all!
+}
+
+void UpdateData_NoNewRow(it_engine *ite)
+{
+	it_host *chn;
+	uint16_t cx;
+
+	// OK. call update command.
+
+	for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++)
+	{
+		if((chn->Flags & 4) != 0 && (chn->Flags & 0x100) != 0)
+			VolumeEffectTable[chn->VCm & 7](ite, chn);
+
+		if((chn->Flags & 3) == 0) continue;
+		if((chn->Flags & 2) == 0 && (chn->Flags & 4) == 0) continue;
+
+		//printf("%i %04X\n", chn->Flags);
+		CommandTable[chn->Cmd & 31](ite, chn);
+	}
+}
+
+void UpdateData_PlayMode1(it_engine *ite)
+{
+	// Pattern stuff..
+	ite->ProcessTick--;
+	ite->CurrentTick--;
+	if(ite->CurrentTick != 0)
+	{
+		UpdateData_NoNewRow(ite);
+		return;
+	}
+
+	// OK... have to update row.
+	ite->CurrentTick = ite->ProcessTick = ite->CurrentSpeed;
+
+	ite->RowDelay--;
+	if(ite->RowDelay != 0) {
+		UpdateEffectData(ite);
+		return;
+	}
+
+	ite->RowDelay = 1;
+
+	uint16_t ax = ite->ProcessRow+1; // Progress to new row.
+	if(ax >= ite->NumberOfRows)
+	{
+		if(ite->d.StopEndOfPlaySection != 0)
+		{
+			Music_Stop(ite); // Optionally.. loop!
+			return;
+		}
+
+		// Wrap row.
+		ax = ite->BreakRow;
+		ite->BreakRow = 0;
+	}
+
+	ite->ProcessRow = ite->CurrentRow = ax;
+
+	// Gotta get note data.
+	UpdateNoteData(ite);
+	return;
+}
+
+void UpdateEffectData(it_engine *ite)
+{
+	uint16_t cx;
+	it_host *chn;
+
+	for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++)
+	{
+		if((chn->Flags & 64) == 0) continue;
+		if((chn->Msk & 0x88) == 0) continue;
+
+		uint8_t oldmsk = chn->Msk;
+		chn->Msk &= 0x88;
+
+		InitCommandTable[chn->Cmd & 0x1F](ite, chn); // Init note
+
+		chn->Msk = oldmsk;
+	}
+}
+
+void UpdateData_PlayMode2(it_engine *ite)
+{
+	ite->ProcessTick--;
+	ite->CurrentTick--;
+	if(ite->CurrentTick != 0)
+	{
+		UpdateData_NoNewRow(ite);
+		return;
+	}
+
+	// Play song stuff...
+
+	ite->CurrentTick = ite->ProcessTick = ite->CurrentSpeed;
+
+	ite->RowDelay--;
+	if(ite->RowDelay != 0) {
+		UpdateEffectData(ite);
+		return;
+	}
+
+	ite->RowDelay = 1;
+
+	uint16_t ax = ite->ProcessRow+1;
+	if(ax < ite->NumberOfRows)
+	{
+		ite->ProcessRow = ite->CurrentRow = ax;
+		UpdateNoteData(ite);
+		return;
+	}
+
+	if((ite->OrderLockFlag & 1) == 0)
+	{
+		int dx = 0;
+
+		// Get new pattern.
+		int16_t bx = ite->ProcessOrder + 1;
+		uint8_t cl; // might as well emulate any side-effects we get --GM
+
+		for(;;)
+		{
+			if(bx < 0x100)
+			{
+				// next pattern
+				cl = ite->ord[bx]; // CL = next pattern.
+				if(cl < 200)
+					break;
+
+				bx++;
+
+				if(cl == 0xFE) continue;
+
+				ite->StopSong = 1;
+				if(ite->d.StopEndOfPlaySection != 0)
+				{
+					Music_Stop(ite); // Optionally.. loop!
+					return;
+				}
+			}
+
+			if(dx != 0)
+			{
+				Music_Stop(ite); // Optionally.. loop!
+				return;
+			}
+
+			dx++;
+			bx = 0;
+		}
+
+		ite->ProcessOrder = ite->CurrentOrder = bx;
+		ite->CurrentPattern = cl;
+	}
+
+	ite->ProcessRow = ite->CurrentRow = ite->BreakRow;
+	ite->BreakRow = 0;
+	UpdateNoteData(ite);
+}
+
+uint16_t Music_GetNumberOfSamples(it_engine *ite)
+{
+	// Returns AX
+	uint16_t ax = 99;
+	it_sample *smp = &ite->smp[99-1];
+
+	while(ax > 0)
+	{
+		if(memcmp(smp, SampleHeader, 80))
+			break;
+
+		ax--;
+		smp--;
+	}
+
+	return ax;
+}
+
+uint16_t Music_GetNumberOfInstruments(it_engine *ite)
+{
+	// Returns AX
+	uint16_t ax = 99;
+	it_instrument *ins = &ite->ins[99-1];
+
+	while(ax > 0)
+	{
+		if(memcmp(ins, InstrumentHeader, 80))
+			break;
+
+		ax--;
+		ins--;
+	}
+
+	return ax;
+}
+
+it_sample *Music_GetSampleHeader(it_engine *ite, uint16_t ax)
+{
+	// AX = sample, 1 based
+	return &ite->smp[ax-1];
+}
+
+uint8_t *Music_GetSampleLocation(it_engine *ite, uint16_t ax, uint32_t *rcx, int *is8bit)
+{
+	// AX = sample (1based)
+	// CH = page.
+	// Returns DS:ESI
+	//   ECX = length
+	// Carry if no sample. - (using NULL --GM)
+	// Zero set if 8 bit     (using a flag --GM)
+
+	ite->LastSample = ax;
+
+	it_sample *smp = &ite->smp[ax-1];
+	if((smp->Flg & 1) == 0)
+		return NULL;
+
+	if(ite->SamplePointer[ax-1] == NULL)
+		return NULL;
+
+	uint8_t *data = ite->SamplePointer[ax-1];
+
+	*rcx = smp->Length;
+	*is8bit = ((~smp->Flg) & 1);
+
+	return data;
+}
+
+// Accessed via Int 3
+uint8_t *Music_UpdateSampleLocation(it_engine *ite, uint32_t esi, int *is8bit)
+{
+	USED(esi);
+	// Reqs ESI.
+	// TODO: decipher this crap *properly* --GM
+
+	uint16_t ax = ite->LastSample;
+	uint32_t dummy_ecx;
+	return Music_GetSampleLocation(ite, ax, &dummy_ecx, is8bit);
+}
+
+uint8_t *Music_FarUpdateSampleLocation(it_engine *ite, uint32_t esi, int *is8bit)
+{
+	return Music_UpdateSampleLocation(ite, esi, is8bit);
+}
+
+void Music_GetPlayMode(it_engine *ite, uint16_t *PlayMode, uint16_t *CurrentRow,
+	uint16_t *CurrentPattern, uint16_t *CurrentOrder, uint16_t *NumberOfRows)
+{
+	*PlayMode = ite->PlayMode;
+	*CurrentRow = ite->CurrentRow;
+	*CurrentPattern = ite->CurrentPattern;
+	*CurrentOrder = ite->CurrentOrder;
+	*NumberOfRows = ite->NumberOfRows;
+}
+
+void Music_GetPlayMode2(it_engine *ite, uint16_t *PlayMode, uint16_t *CurrentRow,
+	uint16_t *CurrentTick, uint32_t *CurrentOrder)
+{
+	*PlayMode = ite->PlayMode;
+	*CurrentOrder = ite->CurrentOrder;
+	*CurrentRow = ite->CurrentRow;
+	*CurrentTick = ite->CurrentTick;
+}
+
+void Music_PlayPattern(it_engine *ite, uint16_t pidx, uint16_t numrows, uint16_t startrow)
+{
+	// AX = pattern, BX = number of rows
+	// CX = row to start
+
+	Music_Stop(ite);
+
+	ite->MIDIPitchDepthSent = 0;
+	ite->LastMIDIByte = 0xFF;
+	ite->CurrentPattern = pidx;
+	ite->CurrentRow = startrow;
+	ite->NumberOfRows = numrows;
+	ite->ProcessRow = startrow-1;
+	ite->PlayMode = 1;
+}
+
+void Music_PlaySong(it_engine *ite, uint16_t oidx)
+{
+	// AX = Order
+
+	Music_Stop(ite);
+
+	ite->MIDIPitchDepthSent = 0;
+	ite->LastMIDIByte = 0xFF;
+	ite->CurrentOrder = oidx;
+	ite->ProcessOrder = oidx-1;
+	ite->ProcessRow = 0xFFFE;
+	ite->PlayMode = 2;
+
+	StartClock(ite);
+
+	MIDITranslate(ite, ite->chn, ite->slave, MIDICOMMAND_START);
+}
+
+void Music_PlayPartSong(it_engine *ite, uint16_t oidx, uint16_t row)
+{
+	// AX = order, BX = row.
+
+	Music_Stop(ite);
+
+	ite->NumberOfRows = 200;
+	ite->ProcessOrder = oidx;
+	ite->CurrentOrder = oidx;
+	ite->CurrentRow = row;
+	ite->ProcessRow = row-1;
+
+	ite->CurrentPattern = ite->ord[oidx];
+
+	ite->PlayMode = 2;
+
+	StartClock(ite);
+}
+
+void Music_KBPlaySong(it_engine *ite)
+{
+	if(ite->PlayMode != 2)
+		Music_PlaySong(ite, 0);
+}
+
+void Music_StopChannels(it_engine *ite)
+{
+	uint16_t cx;
+	it_host *chn;
+	it_slave *slave;
+
+	for(chn = &ite->chn[0], cx = 64; cx != 0; cx--, chn++)
+	{
+		chn->Flags = 0;
+		chn->PLR = 0;
+		chn->PLC = 0;
+	}
+
+	for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) != 0 && slave->Smp == 100)
+			MIDITranslate(ite, &ite->chn[slave->HCOffst], slave, MIDICOMMAND_STOPNOTE);
+
+		slave->Flags = 0x200;
+	}
+}
+
+void Music_Stop(it_engine *ite)
+{
+	uint16_t cx;
+	it_host *chn;
+	it_slave *slave;
+
+	// Turn off MIDI channels first.
+	if((ite->OrderLockFlag & 1) != 0)
+		Music_ToggleOrderUpdate(ite);
+
+	for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) != 0 && slave->Smp == 100)
+			MIDITranslate(ite, &ite->chn[slave->HCOffst], slave, MIDICOMMAND_STOPNOTE);
+	}
+
+	// Stop
+	MIDITranslate(ite, &ite->chn[0], &ite->slave[0], MIDICOMMAND_STOP);
+
+	ite->PlayMode = 0;
+
+	ite->DecodeExpectedPattern = 0xFFFE;
+	ite->DecodeExpectedRow = 0xFFFE;
+	ite->RowDelay = 1;
+	ite->CurrentRow = 0;
+	ite->CurrentOrder = 0;
+	ite->CurrentTick = 1;
+	ite->BreakRow = 0;
+
+	memset(ite->MIDIPrograms, 0xFF, 32*2);
+
+	uint8_t dl;
+	uint8_t dh;
+	for(chn = &ite->chn[0], dl = 64, dh = 0; dl != 0; dl--, chn++, dh++)
+	{
+		chn->Flags = 0; chn->Msk = 0; chn->Nte = 0;
+		chn->Ins = 0; chn->Vol = 0; chn->Cmd = 0; chn->CVal = 0;
+		chn->OCm = 0; chn->OVal = 0; chn->VCm = 0; chn->VVal = 0;
+		chn->MCh = 0; chn->MPr = 0; chn->Nt2 = 0; chn->Smp = 0;
+
+		chn->DKL = 0; chn->EFG = 0; chn->O00 = 0; chn->I00 = 0;
+		chn->J00 = 0; chn->M00 = 0; chn->N00 = 0; chn->P00 = 0;
+		chn->Q00 = 0; chn->T00 = 0; chn->S00 = 0; chn->OxH = 0;
+		chn->W00 = 0; chn->VCE = 0; chn->GOE = 0; chn->SFx = 0;
+
+		chn->HCN = dh; chn->CUC = 0; chn->VSe = 0; chn->LTr = 0;
+		chn->SCOffst = 0; chn->PLR = 0; chn->PLC = 0;
+		chn->PWF = 0; chn->PPo = 0; chn->PDp = 0; chn->PSp = 0;
+		chn->LPn = 0; chn->LVi = 0;
+		chn->CP = ite->hdr.Chnl_Pan[dh] & 0x7F; chn->CV = ite->hdr.Chnl_Vol[dh];
+
+		chn->VCh = 0; chn->TCD = 0; chn->Too = 0; chn->RTC = 0;
+		chn->Porta_Frequency = 0;
+		chn->VWF = 0; chn->VPo = 0; chn->VDp = 0; chn->VSp = 0;
+		chn->TWF = 0; chn->TPo = 0; chn->TDp = 0; chn->TSp = 0;
+
+		chn->_40 = 0; chn->_42 = 0;
+		chn->_44 = 0; chn->_46 = 0; chn->_47 = 0;
+		chn->_48 = 0; chn->_49 = 0; chn->_4A = 0; chn->_4B = 0;
+		chn->_4C = 0; chn->_4D = 0; chn->_4E = 0; chn->_4F = 0;
+	}
+
+	// Now clear SlaveChannel
+	//Mov     DX, MAXSLAVECHANNELS
+	for(slave = &ite->slave[0], cx = MAXSLAVECHANNELS; cx != 0; cx--, slave++)
+	{
+		// too much bloody effort, using memset --GM
+		//memset(slave, 0, 128);
+		memset(slave, 0, sizeof(it_slave));
+		slave->Flags = 0x0200;
+	}
+
+	ite->GlobalVolume = ite->hdr.GV;
+	ite->CurrentSpeed = ite->hdr.IS;
+	ite->ProcessTick = ite->hdr.IS;
+	ite->Tempo = ite->hdr.IT;
+
+	Music_InitTempo(ite);
+	MIDI_ClearTable(ite);
+}
+
+void Music_UpdatePatternOffset(it_engine *ite)
+{
+	ite->DecodeExpectedPattern = 0xFFFE;
+}
+
+void Music_PlayNote(it_engine *ite, uint8_t *data, uint16_t cidx, uint8_t dh)
+{
+	//DS:SI points to 5-note struct
+	// AX = channel
+	// DH = +32 means ignore mute
+	//      settings
+	// DH = +128 means to use central
+	//       pan and max volume.
+
+	it_host *chn = &ite->chn[cidx&0xFF];
+
+	uint8_t dl = 0; // DL = mask
+	uint8_t al;
+
+	al = *(data++);
+	if(al != NONOTE)
+	{
+		dl |= 1;
+		chn->Nte = al;
+	}
+
+	al = *(data++);
+	if(al != 0)
+	{
+		dl |= 2;
+		chn->Ins = al;
+	}
+
+	al = *(data++);
+	if(al != 0xFF)
+	{
+		dl |= 4;
+		chn->Vol = al;
+	}
+
+	al = *(data++);
+	uint8_t ah = *(data++);
+	USED(data);
+	if(al != 0 || ah != 0)
+		dl |= 8;
+
+	chn->Cmd  = chn->OCm  = al;
+	chn->CVal = chn->OVal = ah;
+	chn->Msk = dl;
+	chn->Flags &= ~(3+32+64+256);
+	chn->Flags |= dh & 0x7F; // Now for command update count
+
+	chn->CUC = ite->CurrentSpeed;
+	PreInitCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0 && (dh & 128) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+
+		slave->Pan = slave->PS = 0x20; // Pan and pan set.
+		slave->CVl = 0x40; // Full channel volume.
+	}
+
+	ite->DecodeExpectedRow = 0xFFFE;
+}
+
+void Music_PlaySample(it_engine *ite, uint8_t note, uint8_t sidx, uint16_t cidx)
+{
+	// AL = Note
+	// AH = sample number
+	// CX = channel.
+
+	it_host *chn = &ite->chn[cidx];
+
+	chn->Msk = 3; // Note & Sample
+	chn->Nte = note;
+	chn->Vol = 0xFF;
+	chn->Ins = 0xFF;
+	chn->Nt2 = note;
+	chn->Smp = sidx;
+	chn->Flags |= 0x8020;
+
+	InitNoCommand(ite, chn);
+
+	if((chn->Flags & 4) != 0)
+	{
+		it_slave *slave = &ite->slave[chn->SCOffst];
+		slave->Pan = slave->PS = 0x20;
+		slave->CVl = 0x40; // Full channel volume.
+		slave->NNA = 0; // Note cut.
+		slave->DCT = 0;
+	}
+
+	chn->Flags &= ~0x8000;
+
+	ite->DecodeExpectedRow = 0xFFFE;
+}
+
+it_host *Music_GetHostChannelInformationTable(it_engine *ite)
+{
+	return &ite->chn[0];
+}
+
+it_slave *Music_GetSlaveChannelInformationTable(it_engine *ite, uint16_t *count)
+{
+	if(count != NULL) *count = MAXSLAVECHANNELS;
+	return &ite->slave[0];
+}
+
+void Music_NextOrder(it_engine *ite)
+{
+	if(ite->PlayMode != 2)
+		return;
+
+	ite->PlayMode = 0;
+	Music_StopChannels(ite);
+	ite->ProcessRow = 0xFFFE;
+	ite->CurrentTick = 1;
+	ite->RowDelay = 1;
+	ite->PlayMode = 2;
+}
+
+void Music_LastOrder(it_engine *ite)
+{
+	if(ite->PlayMode != 2) return;
+	if(((int16_t)ite->ProcessOrder) <= 0) return;
+
+	ite->PlayMode = 0;
+	Music_StopChannels(ite);
+	ite->ProcessOrder = ite->ProcessOrder - 2;
+	ite->ProcessRow = 0xFFFE;
+	ite->CurrentTick = 1;
+	ite->RowDelay = 1;
+	ite->PlayMode = 2;
+}
+
+void Music_SetGlobalVolume(it_engine *ite, uint8_t al)
+{
+	ite->GlobalVolume = al;
+	RecalculateAllVolumes(ite);
+}
+
+void Music_MuteChannel(it_engine *ite, uint16_t ax)
+{
+	// AX = channel number
+	uint16_t cx = ite->NumChannels;
+	it_slave *slave = &ite->slave[0];
+
+	for(; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) == 0) continue;
+		if((slave->HCN & 0x7F) != (ax & 0xFF)) continue;
+		slave->Flags |= 0x0840;
+	}
+}
+
+void Music_UnmuteChannel(it_engine *ite, uint16_t ax)
+{
+	// AX = channel number
+	uint16_t cx = ite->NumChannels;
+	ite->SoloSample = 0xFF;
+	ite->SoloInstrument = 0xFF;
+	it_slave *slave = &ite->slave[0];
+
+	for(; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) == 0) continue;
+		if((slave->HCN & 0x7F) != (ax & 0xFF)) continue;
+		slave->Flags &= ~0x0800;
+		slave->Flags |= 64;
+	}
+}
+
+void Music_ToggleChannel(it_engine *ite, uint16_t ax)
+{
+	// AX = channel number.
+
+	if((ite->hdr.Chnl_Vol[ax] & 0x80) != 0)
+	{
+		ite->hdr.Chnl_Vol[ax] &= 0x7F;
+		Music_UnmuteChannel(ite, ax);
+		ite->MuteChannelTable[ax] = 0;
+	} else {
+		// Mute channel
+		ite->MuteChannelTable[ax] ^= 1;
+		ite->hdr.Chnl_Vol[ax] |= 0x80;
+		Music_MuteChannel(ite, ax);
+	}
+}
+
+void Music_UnmuteAll(it_engine *ite)
+{
+	// solo pressed on already soloed
+	// channel -> turn everything on.
+	uint16_t cx = 64;
+
+	for(; cx != 0; cx--)
+		if(ite->MuteChannelTable[cx-1] == 1)
+			Music_ToggleChannel(ite, cx-1);
+}
+
+void Music_SoloChannel(it_engine *ite, uint16_t ax)
+{
+	// AX = channel
+
+	// Check & count whether any playing.
+	uint8_t *si = &ite->hdr.Chnl_Vol[0];
+	uint16_t cx = 64;
+	uint16_t dx = 64;
+	for(; cx != 0; cx--)
+		dx -= ((*(si++))>>7)&1; // no way to represent this properly in C --GM
+
+	// DX = num playing.
+	// check whether it's the current
+	if(dx == 1 && (ite->hdr.Chnl_Vol[ax] & 0x80) == 0) {
+		Music_UnmuteAll(ite);
+		return;
+	}
+
+	// 64 channel to step through
+	// turn 'em all off.
+	cx = 64;
+	for(; cx != 0; cx--)
+	{
+		if(cx-1 == ax)
+		{
+			if((ite->hdr.Chnl_Vol[cx-1] & 0x80) == 0)
+				Music_ToggleChannel(ite, ax);
+		} else {
+			if((ite->hdr.Chnl_Vol[cx-1] & 0x80) != 0)
+				Music_ToggleChannel(ite, ax);
+		}
+	}
+}
+
+void Music_InitMuteTable(it_engine *ite)
+{
+	memset(&ite->MuteChannelTable[0], 0, 64);
+	ite->SoloSample = 0xFF;
+	ite->SoloInstrument = 0xFF;
+	ite->AllocateNumChannels = 0;
+}
+
+void Music_InitStereo(it_engine *ite)
+{
+	ite->d.DriverSetStereo(ite, ite->hdr.Flags & 1);
+	RecalculateAllVolumes(ite);
+}
+
+// note, increase means go faster, not increase number! --GM
+uint16_t Music_IncreaseSpeed(it_engine *ite)
+{
+	// Returns AX = speed
+	uint16_t ax = ite->CurrentSpeed;
+	if(ax != 1)
+	{
+		ax--;
+		ite->CurrentSpeed = ax;
+		ite->hdr.IS = ax;
+	}
+
+	return ax;
+}
+
+uint16_t Music_DecreaseSpeed(it_engine *ite)
+{
+	// Returns AX = speed
+	uint16_t ax = ite->CurrentSpeed;
+	if(ax != 0xFF)
+	{
+		ax++;
+		ite->CurrentSpeed = ax;
+		ite->hdr.IS = ax;
+	}
+
+	return ax;
+}
+
+void Music_SetSoundCard(it_engine *ite, uint8_t al)
+{
+	// AL contains sound card num
+	if(ite->DriverName != NULL) free(ite->DriverName);
+	ite->DriverName = strdup(DriverNameTable[DriverSoundCard[al]]);
+}
+
+void Music_SetSoundCardDriver(it_engine *ite, const char *dssi)
+{
+	// improvised a bit here --GM
+	if(ite->DriverName != NULL) free(ite->DriverName);
+	ite->DriverName = strdup(dssi);
+}
+
+// exactly why am I doing this stuff? --GM
+void Music_SetDMA(it_engine *ite, uint8_t al)
+{
+	ite->d.DMA = al;
+}
+
+void Music_SetMixSpeed(it_engine *ite, uint16_t cx)
+{
+	ite->d.CmdLineMixSpeed = cx;
+}
+
+void Music_SetIRQ(it_engine *ite, uint16_t cx)
+{
+	ite->d.IRQ = cx;
+}
+
+void Music_SetAddress(it_engine *ite, uint16_t dx)
+{
+	ite->d.BasePort = dx;
+}
+
+void Music_GetDisplayVariables(it_engine *ite, uint16_t *CurrentSpeed,
+	uint16_t *Tempo, uint16_t *GlobalVolume)
+{
+	*CurrentSpeed = ite->CurrentSpeed;
+	*Tempo = ite->Tempo;
+	*GlobalVolume = ite->GlobalVolume;
+}
+
+int16_t Music_AssignSampleToInstrument(it_engine *ite, uint16_t bx)
+{
+	uint16_t cx;
+	int i;
+
+	// BX = sample num
+	// returns AX
+	// (returns -1 instead of carry on error --GM)
+
+	// Check for sample-number's instrument first.
+	// FIXME: probably an off-by-one here --GM
+	it_instrument *ins = &ite->ins[bx];
+
+	uint16_t ax = bx+1;
+
+	if(memcmp(ins, InstrumentHeader, 554))
+	{
+		// Search
+		cx = 99;
+
+		ins = &ite->ins[0]; // Points to first inst.
+		ax = 1;
+
+		for(; cx != 0; cx--, ax++, ins++)
+			if(!memcmp(ins, InstrumentHeader, 554))
+				break;
+
+		if(cx == 0)
+			return -1;
+	}
+
+#if NETWORKENABLED
+	uint8_t *nsq = Network_GetSendQueue(ite);
+
+	if(nsq != NULL)
+	{
+		*(nsq++) = 0x00;
+		*(nsq++) = 0x04;
+		*(nsq++) = ax-1;
+	}
+
+	Network_FinishedSendQueue(ite, nsq);
+#endif
+
+	ins = &ite->ins[ax-1];
+	it_sample *smp = &ite->smp[bx];
+
+	memcpy(ins->Name, smp->Name, 26);
+
+	// Now to fill in table.
+	bx++;
+	for(cx = 120, i = 0; cx != 0; cx--, i++)
+		ins->NoteSamp[i][1] = bx;
+
+	return ax;
+}
+
+void Music_SetLimit(it_engine *ite, uint16_t cx)
+{
+	ite->CmdLineNumChannels = cx;
+}
+
+void Music_ReverseChannels(it_engine *ite, uint16_t cx)
+{
+	USED(cx);
+	ite->d.ReverseChannels = 1;
+}
+
+void Music_IncreaseVolume(it_engine *ite)
+{
+	if(ite->GlobalVolume < 128)
+	{
+		ite->GlobalVolume++;
+		RecalculateAllVolumes(ite);
+	}
+}
+void Music_DecreaseVolume(it_engine *ite)
+{
+	if(ite->GlobalVolume != 0)
+	{
+		ite->GlobalVolume--;
+		RecalculateAllVolumes(ite);
+	}
+}
+
+void Music_RegetLoopInformation(it_engine *ite)
+{
+	uint16_t cx;
+	it_slave *slave;
+
+	for(cx = ite->NumChannels, slave = &ite->slave[0]; cx != 0; cx--, slave++)
+	{
+		if((slave->Flags & 1) == 0) continue;
+
+		GetLoopInformation(ite, slave);
+		slave->Flags |= 0x40;
+	}
+}
+
+void ResetSoundCardMemory(it_engine *ite)
+{
+	ite->d.DriverResetMemory(ite);
+}
+
+int Music_SoundCardLoadSample(it_engine *ite, uint16_t sidx)
+{
+	// AX = sample number
+	// (1 based)
+	// Carry set if insuf mem
+
+	// incorrect - it's CLEAR on insufficient memory
+	// anyway, we're just returning -1 on fail, 0 on pass --GM
+
+	if(ite->d.DriverLoadSample(ite, sidx) == -1)
+	{
+		M_Object1List(ite, &O1_OutOfSoundCardMemoryList, 2);
+		return -1;
+	}
+
+	return 0;
+}
+
+uint16_t Music_SoundCardLoadAllSamples(it_engine *ite)
+{
+	Music_Stop(ite);
+	S_SaveScreen(ite);
+
+	S_DrawBox(ite, 3, 30, 50, 28, 30);
+	S_DrawString(ite, 32, 29, PrepareSamplesMsg, 0x20);
+	S_UpdateScreen(ite);
+
+	ResetSoundCardMemory(ite);
+
+	uint16_t ax;
+	for(ax = 1; ax <= 100; ax++)
+		if(Music_SoundCardLoadSample(ite, ax) != 0)
+			return 1;
+
+	S_RestoreScreen(ite);
+
+	return 1;
+}
+
+int Music_GetFreeSoundCardMemory(it_engine *ite)
+{
+	// TODO: find out how this works --GM
+	return ite->d.DriverGetStatus(ite);
+}
+
+uint16_t Music_GetNumChannels(it_engine *ite)
+{
+	return ite->NumChannels;
+}
+
+const uint32_t *Music_GetPitchTable(it_engine *ite)
+{
+	USED(ite);
+	// Returns ES:DI to pitch table
+	return PitchTable;
+}
+
+void Music_ToggleReverse(it_engine *ite)
+{
+	ite->d.ReverseChannels ^= 1;
+	RecalculateAllVolumes(ite);
+	SetInfoLine(ite, ReverseMsg);
+}
+
+void Music_PatternStorage(it_engine *ite, uint8_t al)
+{
+	// this won't mean a damn thing --GM
+	ite->PatternStorage = al;
+}
+
+void Music_InitMixTable(it_engine *ite)
+{
+	ite->d.DriverSetMixVolume(ite, ite->hdr.MV); // AL = 0->128
+}
+
+uint16_t Music_GetTempo(it_engine *ite)
+{
+	// returns in BX --GM
+	return ite->Tempo;
+}
+
+uint16_t Music_GetLastChannel(it_engine *ite)
+{
+	// Returns AX
+	uint16_t cx = 64;
+	uint16_t ax = 0;
+	uint16_t dx = 0;
+
+	for(; cx != 0; cx--, dx++)
+		if(((ite->hdr.Chnl_Vol[dx]>>7)^(ite->MuteChannelTable[dx])) == 0)
+			ax = dx;
+
+	return ax;
+}
+
+int Music_GetDriverScreen(it_engine *ite)
+{
+	return ite->d.DriverSoundCardScreen(ite);
+}
+
+int Music_GetDriverVariable(it_engine *ite, uint16_t Var)
+{
+	return ite->d.DriverGetVariable(ite, Var);
+}
+
+int Music_SetDriverVariable(it_engine *ite, uint16_t Var, uint16_t Thing)
+{
+	return ite->d.DriverSetVariable(ite, Var, Thing);
+}
+
+void Music_SetNextOrder(it_engine *ite, uint16_t order)
+{
+	ite->ProcessOrder = order-1;
+}
+
+uint16_t Music_GetDelay(it_engine *ite)
+{
+	// returns in DX --GM
+
+	// in case you're wondering,
+	// this generates SDx delay effect information for liveplay --GM
+
+	if(ite->PlayMode == 0)
+		return 0;
+
+	uint16_t dx = ite->CurrentSpeed;
+
+	if(dx == ite->CurrentRow)
+	{
+		dx -= ite->ProcessTick;
+		if(dx == 0) return 0;
+
+		if(dx >= 0x0F)
+			dx = 0x0F;
+	} else {
+		dx--;
+
+		if(dx >= 0x0F)
+			dx = 0x0F;
+	}
+
+	dx <<= 8;
+	dx |= 'S'-'@' + 0xD000;
+
+	return dx;
+}
+
+int InternalTimer(it_engine *ite, uint16_t bx)
+{
+	// Ticks = (1193181/(2*0.4))/Tempo
+	uint32_t eax = 0x16C214 / (uint32_t)bx;
+	ite->TimerCounter = eax*2;
+
+	return 0;
+}
+
+int Music_TimeSong(it_engine *ite)
+{
+	// Time song!
+
+	S_SaveScreen(ite);
+
+	S_SetDirectMode(ite, 1);
+	S_DrawSmallBox(ite);
+
+	S_DrawString(ite, 33, 26, PleaseWaitMsg, 0x20);
+
+	Music_Stop(ite);
+
+	/*
+	Mov     CX, 0FFFFh
+
+Music_TimeSong1:
+	In      AL, 21h                 // Delay mechanism
+	In      AL, 0A1h                 // Delay mechanism
+	Loop    Music_TimeSong1
+	*/
+
+	ite->StopSong = 0;
+	ite->TotalTimer = 0;
+	ite->TotalTimerHigh = 0;
+
+	// appears to disable interrupts --GM
+	/*
+	ClI
+	In      AL, 0A1h
+	Mov     AH, AL
+	In      AL, 21h
+	Push    AX
+
+	Mov     AL, 0FFh
+	Out     0A1h, AL
+	Out     21h, AL
+	*/
+
+	int (*OldDriverSetTempo)(it_engine *ite, uint16_t Tempo) = ite->d.DriverSetTempo;
+	ite->d.DriverSetTempo = InternalTimer;
+	uint16_t OldNumChannels = ite->NumChannels;
+	uint16_t OldFlags = ite->hdr.Flags;
+	ite->hdr.Flags &= ~4;
+
+	Music_PlaySong(ite, 0);
+
+	for(;;)
+	{
+		uint16_t cx;
+		it_slave *si;
+		uint16_t ax;
+		Update(ite, &cx, &si, &ax);
+
+		if(ite->StopSong != 0) break;
+
+		ite->TotalTimer += ite->TimerCounter;
+		if(ite->TotalTimer < ite->TimerCounter) // cheeky way to do AdC 0 --GM
+			ite->TotalTimerHigh++;
+	}
+
+	Music_Stop(ite);
+
+	ite->hdr.Flags = OldFlags;
+	ite->NumChannels = OldNumChannels;
+	ite->d.DriverSetTempo = OldDriverSetTempo;
+
+	/*
+	Pop     AX
+	Out     21h, AL
+	Mov     AL, AH
+	Out     0A1h, AL
+
+	StI
+	*/
+
+	S_SetDirectMode(ite, 0);
+
+	S_RestoreScreen(ite);
+
+	M_Object1List(ite, &O1_ShowTime, -1);
+
+	return 1;
+}
+
+void Music_ShowTime(it_engine *ite)
+{
+	S_GetDestination(ite); // TODO: work this out --GM
+
+	uint32_t xtime = (ite->TotalTimerHigh<<16) | (ite->TotalTimer>>16);
+	USED(xtime);
+
+	D_ShowTime(ite, 43, 27, xtime);
+}
+
+uint16_t Music_GetPatternLength(it_engine *ite)
+{
+	return ite->NumberOfRows;
+}
+
+uint16_t Music_SaveMIDIConfig(it_engine *ite)
+{
+	USED(ite);
+	D_GotoStartingDirectory(ite);
+
+	// TODO!
+	/*
+	Mov     AH, 3Ch
+	Xor     CX, CX
+	Mov     DX, Offset MIDIConfigFileName
+	Int     21h
+	JC      Music_SaveMIDIConfig1
+
+	Mov     BX, AX
+
+	Mov     AH, 40h
+	Mov     DS, CS:MIDIDataArea
+	Xor     DX, DX
+	Mov     CX, (128+16+9)*32
+	Int     21h
+
+	Mov     AH, 3Eh
+	Int     21h
+
+Music_SaveMIDIConfig1:
+	Xor     AX, AX
+	*/
+	return 0;
+}
+
+uint8_t *Music_GetMIDIDataArea(it_engine *ite)
+{
+	USED(ite);
+	// TODO!
+	//Mov     DS, CS:MIDIDataArea
+	return NULL;
+}
+
+void Music_ToggleOrderUpdate(it_engine *ite)
+{
+	const char *msg = OrderUpdateEnabledMsg;
+	ite->OrderLockFlag ^= 1;
+	if(ite->OrderLockFlag != 0)
+		msg = OrderUpdateDisabledMsg;
+	USED(msg);
+
+	SetInfoLine(ite, msg);
+}
+
+uint16_t Music_ToggleSoloInstrument(it_engine *ite)
+{
+	return Music_ToggleSolo(ite, SoloInstrumentMsg, &ite->SoloInstrument, 1);
+}
+
+uint16_t Music_ToggleSoloSample(it_engine *ite)
+{
+	return Music_ToggleSolo(ite, SoloSampleMsg, &ite->SoloSample, 0);
+}
+
+uint16_t Music_ToggleSolo(it_engine *ite, const char *msg, uint8_t *v, uint16_t bp)
+{
+	uint16_t bx = PE_GetLastInstrument(ite);
+	uint16_t ax = bx;
+	bx += bp;
+
+	if(*v != (uint8_t)bx)
+	{
+		ite->SoloSample = 0xFF;
+		ite->SoloInstrument = 0xFF;
+		ax++;
+		USED(ax);
+		*v = bx;
+	} else {
+		ite->SoloSample = 0xFF;
+		ite->SoloInstrument = 0xFF;
+		msg = UnsoloMsg;
+		USED(msg);
+	}
+
+	SetInfoLine(ite, msg);
+
+	uint16_t cx;
+	it_slave *slave;
+
+	for(cx = ite->NumChannels, slave = &ite->slave[0]; cx != 0; cx--, slave++)
+	{
+		slave->Flags |= 18;
+		bx = slave->HCN; // BX = channel
+
+		if((ite->hdr.Chnl_Vol[bx] & 0x80) == 0)
+			slave->Flags &= ~0x800;
+	}
+
+	RecalculateAllVolumes(ite);
+
+	return 1;
+}
+
--- /dev/null
+++ b/it_obj.h
@@ -1,0 +1,144 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <u.h>
+#include <libc.h>
+
+typedef s8int int8_t;
+typedef u8int uint8_t;
+typedef s16int int16_t;
+typedef u16int uint16_t;
+typedef s32int int32_t;
+typedef u32int uint32_t;
+typedef s64int int64_t;
+typedef u64int uint64_t;
+typedef ulong size_t;
+
+#define NULL nil
+#define printf(...) do {} while(0)
+#define __attribute__(...)
+#define log2f(x) (log(x) / 0.693147180559945309)
+
+typedef union it_object_u it_object;
+union it_object_u
+{
+	int typ;
+
+	// 0: Box
+	struct {
+		int typ;
+		int left, top, right, bottom;
+		int style;
+	} box;
+
+	// 1: Text
+	struct {
+		int typ;
+		int x, y;
+		int color;
+		const char *text;
+	} text;
+
+	// 2: Button
+	struct {
+		int typ;
+		int a_up, a_down, a_left, a_right;
+		int usage;
+		int usage_radio_min, usage_radio_max;
+		int button_effect;
+		int eff_v1, eff_v2;
+		void *eff_p1, *eff_p2;
+		int left, top, right, bottom;
+		int style;
+		int flags;
+		const char *text;
+	} button;
+
+	// 3: Empty
+	// 4: Empty
+
+	// 5: Select Direct Screen
+	struct {
+		int typ;
+		int mode;
+	} select_direct_screen;
+
+	// 6: Redefine Characters
+	struct {
+		int typ;
+		int first, num;
+		const uint8_t *table;
+	} redefine_characters;
+
+	// 7: Empty
+
+	// 8: Call Far Function
+	struct {
+		int typ;
+		void *function;
+	} call_far_function;
+
+	// 9: Thumb bar
+	struct {
+		int typ;
+		int x, y;
+		int min_range, max_range;
+		int wd1, wd2;
+		int a_up, a_down, a_tab, a_shtab;
+		int a_pgup, a_pgdn;
+	} thumb_bar;
+
+	// 10: Infoline
+	struct {
+		int typ;
+		const char *text;
+	} infoline;
+
+	// 11: Set help context
+	struct {
+		int typ;
+		int number;
+	} set_help_context;
+
+	// TODO: The rest
+};
+
+typedef struct it_objlist_2_s
+{
+	int typ;
+	int unk1;
+	void *ptrs[];
+} it_objlist_2;
+
+typedef struct it_objlist_6_s
+{
+	int typ;
+	int unk1;
+	void *ptrs[];
+} it_objlist_6;
+
--- /dev/null
+++ b/it_struc.h
@@ -1,0 +1,574 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "switch.h"
+#include "it_obj.h"
+
+#define Trace(s)
+
+#define CREATENEWLOGFILE             0
+#define HOSTCHANNELSIZE              80
+#define SLAVECHANNELSIZE             128
+#define MAXSLAVECHANNELS             256
+#define NONOTE                       0xFD
+
+#define MIDICOMMAND_START                    0x0000
+#define MIDICOMMAND_STOP                     0x0020
+#define MIDICOMMAND_TICK                     0x0040
+#define MIDICOMMAND_PLAYNOTE                 0x0060
+#define MIDICOMMAND_STOPNOTE                 0x0080
+#define MIDICOMMAND_CHANGEVOLUME             0x00A0
+#define MIDICOMMAND_CHANGEPAN                0x00C0
+#define MIDICOMMAND_BANKSELECT               0x00E0
+#define MIDICOMMAND_PROGRAMSELECT            0x0100
+#define MIDICOMMAND_CHANGEPITCH              0xFFFF
+
+#pragma pack on
+
+typedef struct it_host_s
+{
+	// 0x0000
+	uint16_t Flags; uint8_t Msk, Nte;
+	uint8_t Ins, Vol, Cmd, CVal;
+	uint8_t OCm, OVal, VCm, VVal;
+	uint8_t MCh, MPr, Nt2, Smp;
+
+	// 0x0010
+	uint8_t DKL, EFG, O00, I00;
+	uint8_t J00, M00, N00, P00;
+	uint8_t Q00, T00, S00, OxH;
+	uint8_t W00, VCE, GOE, SFx;
+
+	// 0x0020
+	uint8_t HCN, CUC, VSe, LTr;
+	uint16_t SCOffst; uint8_t PLR, PLC;
+	uint8_t PWF, PPo, PDp, PSp;
+	uint8_t LPn, LVi, CP, CV;
+
+	// 0x0030
+	uint8_t VCh, TCD, Too, RTC;
+	uint32_t Porta_Frequency;
+	uint8_t VWF, VPo, VDp, VSp;
+	uint8_t TWF, TPo, TDp, TSp;
+
+	// 0x0040
+	// Misc Effect Data......
+	int16_t _40, _42;
+	int16_t _44; uint8_t _46, _47;
+	uint8_t _48, _49, _4A, _4B;
+	uint8_t _4C, _4D, _4E, _4F;
+
+} __attribute__((__packed__)) it_host;
+
+typedef struct it_slen_s
+{
+	uint32_t EnvelopeValue;
+	uint32_t EnvelopeDelta;
+	uint16_t EnvPos, CurEnN;
+	uint16_t NextET, filter;
+} __attribute__((__packed__)) it_slen;
+
+typedef struct it_slave_s
+{
+	// 0x0000
+	uint16_t Flags;
+	uint8_t DeviceSpecific[8];
+	uint8_t LpM, LpD;
+	uint32_t Left_Volume;
+
+	// 0x0010
+	uint32_t Frequency;
+	uint32_t Frequency_Set;
+	uint8_t Bit, ViP; uint16_t ViDepth;
+	uint32_t RVol_MIDIFSet;
+
+	// 0x0020
+	uint8_t FV, Vol, VS, CVl;
+	uint8_t SVl, FP; int16_t FadeOut;
+	uint8_t DCT, DCA, Pan, PS;
+	uint32_t OldSampleOffset;
+
+	// 0x0030
+	uint16_t InsOffs; uint8_t Nte, Ins;
+	uint16_t SmpOffs; uint8_t Smp, FPP;
+	int16_t HCOffst;  uint8_t HCN, NNA;
+	uint8_t MCh, MPr; uint8_t FCut, FRes; // 0x003E also 16-bit MBank
+
+	// 0x0040
+	uint32_t Loop_Beginning;
+	uint32_t Loop_End;
+	uint16_t SmpErr, _16bVol;
+	uint32_t Sample_Offset;
+
+	// 0x0050
+	it_slen V;
+
+	// 0x0060
+	it_slen P;
+
+	// 0x0070
+	it_slen Pt;
+} __attribute__((__packed__)) it_slave;
+
+typedef struct it_header_s
+{
+	// 0x0000
+	uint8_t magic[4]; // "IMPM"
+	uint8_t SongName[26];
+
+	// 0x001E
+	uint16_t PHiligt;
+
+	// 0x0020
+	uint16_t OrdNum, InsNum;
+	uint16_t SmpNum, PatNum;
+	uint16_t Cwt_v, Cmwt;
+	uint16_t Flags, Special;
+
+	// 0x0030
+	uint8_t GV, MV, IS, IT;
+	uint8_t Sep, PWD; uint16_t MsgLgth;
+	uint32_t Message_Offset;
+	uint32_t Time_Stamp;
+
+	// 0x0040
+	uint8_t Chnl_Pan[64];
+
+	// 0x0080
+	uint8_t Chnl_Vol[64];
+} __attribute__((__packed__)) it_header;
+
+typedef struct it_envelope_s
+{
+	// 0x0000
+	uint8_t Flg, Num, LpB, LpE;
+	uint8_t SLB, SLE;
+
+	// 0x0006
+	uint8_t Nodes[25][3];
+
+	//
+	uint8_t pad1;
+
+} __attribute__((__packed__)) it_envelope;
+
+typedef struct it_instrument_s
+{
+	// 0x0000
+	uint8_t magic[4]; // "IMPI"
+	uint8_t FileName[13];
+
+	// 0x0011
+	uint8_t NNA, DCT, DCA;
+	uint16_t FadeOut; int8_t PPS; uint8_t PPC;
+	uint8_t GbV, DfP, RV, RP;
+	uint16_t TrkVers; uint8_t NoS, _1F;
+
+	// 0x0020
+	uint8_t Name[26];
+
+	// 0x003A
+	uint8_t IFC, IFR;
+	uint8_t MCh, MPr; uint16_t MIDIBnk;
+
+	// 0x0040
+	uint8_t NoteSamp[120][2];
+
+	// 0x0130
+	it_envelope VolEnv;
+
+	// 0x0182
+	it_envelope PanEnv;
+
+	// 0x01D4
+	it_envelope PitchEnv;
+
+	//
+	uint8_t pad1[7];
+} __attribute__((__packed__)) it_instrument;
+
+typedef struct it_sample_s
+{
+	// 0x0000
+	uint8_t magic[4]; // "IMPS"
+	uint8_t FileName[13];
+
+	// 0x0011
+	uint8_t GvL, Flg, Vol;
+	uint8_t Name[26];
+
+	// 0x002E
+	uint8_t Cvt, DfP;
+
+	// 0x0030
+	uint32_t Length;
+	uint32_t Loop_Begin;
+	uint32_t Loop_End;
+	uint32_t C5Speed;
+
+	// 0x0040
+	uint32_t SusLoop_Begin;
+	uint32_t SusLoop_End;
+	uint32_t SamplePointer;
+	uint8_t ViS, ViD, ViR, ViT;
+
+} __attribute__((__packed__)) it_sample;
+
+typedef struct it_pattern_s
+{
+	uint16_t Length;
+	uint16_t Rows;
+	uint8_t pad1[4];
+	uint8_t data[];
+} __attribute__((__packed__)) it_pattern;
+
+#pragma pack off
+
+typedef struct it_engine_s it_engine;
+
+typedef struct it_drvdata_s
+{
+	uint16_t BasePort                 ;//= 0xFFFF;          // * ORDER IS IMPORTANT
+	uint16_t IRQ                      ;//= 0xFFFF;          // * ORDER IS IMPORTANT
+	uint16_t DMA                      ;//= 0xFFFF;          // * ORDER IS IMPORTANT
+	uint16_t CmdLineMixSpeed          ;//= 0;               // * ORDER IS IMPORTANT
+	//uint16_t SongDataArea             = SongData;        // * ORDER IS IMPORTANT
+	//uint16_t MIDIDataArea             = SongData + 4076;
+
+	uint16_t CmdLineDMASize           ;//= 1024;           // default
+	uint8_t  ReverseChannels          ;//= 0;
+	uint16_t DriverMaxChannels;       // = 32;
+	uint16_t StopEndOfPlaySection;    // = 0;
+	uint16_t DefaultChannels;         // = 32;
+	uint16_t DriverFlags;             // = 0;
+	// Bit 1 = MIDI Out supported
+	// Bit 2 = Hiqual
+	// Bit 3 = Output waveform data available
+
+	// Driver vectors
+	const char *(*DriverDetectCard)(it_engine *ite, const char *fname, uint16_t AL, uint16_t version);
+	const char *(*DriverInitSound)(it_engine *ite);
+	int (*DriverReinitSound)(it_engine *ite);
+	int (*DriverUninitSound)(it_engine *ite);
+
+	int (*DriverPoll)(it_engine *ite, uint16_t PlayMode, uint16_t CurrentPattern);
+	int (*DriverSetTempo)(it_engine *ite, uint16_t Tempo);
+	int (*DriverSetMixVolume)(it_engine *ite, uint16_t MixVolume);
+	int (*DriverSetStereo)(it_engine *ite, uint16_t Stereo);
+
+	int (*DriverLoadSample)(it_engine *ite, uint16_t sidx);
+	int (*DriverReleaseSample)(it_engine *ite, it_sample *smp);
+	int (*DriverResetMemory)(it_engine *ite);
+	int (*DriverGetStatus)(it_engine *ite);
+
+	int (*DriverSoundCardScreen)(it_engine *ite);
+	int (*DriverGetVariable)(it_engine *ite, uint16_t Var);
+	int (*DriverSetVariable)(it_engine *ite, uint16_t Var, uint16_t Thing); // TODO!
+
+	// 14?
+	int (*DriverMIDIOut)(it_engine *ite, uint8_t al);
+	int (*DriverGetWaveform)(it_engine *ite);
+} it_drvdata;
+
+// This does not pertain to any specific IT structure
+struct it_engine_s
+{
+	it_header hdr;
+	it_host chn[64];
+	it_slave slave[256];
+	it_instrument ins[100];
+	it_sample smp[100];
+	it_pattern *pat[200];
+	uint8_t ord[0x100];
+	//uint8_t patspace[64000];
+	//uint8_t patspace[256*64*5];
+	uint8_t patspace[128000];
+	uint8_t *SamplePointer[100];
+
+	uint16_t LastSample       ;//= 0;
+	uint16_t PlayMode         ;//= 0;
+
+	uint8_t  SaveFormat       ;//= DEFAULTFORMAT;
+	uint16_t TimerData        ;//= 0;
+	uint16_t NumTimerData     ;//= 0;
+	uint16_t TopTimerData     ;//= 0;
+
+	uint32_t EditTimer        ;//= 0;
+
+	// Playmode 0 = Freeplay
+	// Playmode 1 = Pattern
+	// Playmode 2 = Song
+	uint16_t CurrentOrder     ;//= 0;      // } Must follow
+	uint16_t CurrentPattern   ;//= 0;      // }
+	uint16_t CurrentRow       ;//= 0;      // }
+	uint16_t ProcessOrder     ;//= 0;
+	uint16_t ProcessRow       ;//= 0;
+	uint16_t BytesToMix       ;//= 0;      // = Bytes per frame
+	uint16_t PatternOffset    ;//= 0;
+	uint16_t PatternSegment   ;//= 0;
+	uint16_t BreakRow         ;//= 0;
+	uint8_t  RowDelay         ;//= 0;      // } Must join on.
+	uint8_t  RowDelayOn       ;//= 0;      // }
+	uint8_t  PatternArray     ;//= 0;
+
+	uint16_t PatternDataSegment;
+	uint16_t CurrentEditPattern;
+	uint16_t PatternEditMaxRow;
+
+	uint16_t DecodeExpectedPattern    ;//= 0xFFFE;
+	uint16_t DecodeExpectedRow        ;//= 0xFFFE;
+
+	int16_t CmdLineNumChannels       ;//= 0xFFFF;
+
+	uint16_t NumberOfRows             ;//= 64;      // Non zero globals
+	uint16_t CurrentTick              ;//= 6;
+	uint16_t CurrentSpeed             ;//= 6;
+	uint16_t ProcessTick              ;//= 0;
+	uint8_t  Tempo                    ;//= 125;
+	uint8_t  GlobalVolume             ;//= 128;
+	uint16_t NumChannels              ;//= 256;
+	uint8_t  SoloSample               ;//= 0xFF;            // * ORDER IS IMPORTANT
+	uint8_t  SoloInstrument           ;//= 0xFF;            // * ORDER IS IMPORTANT
+	uint16_t AllocateNumChannels      ;//= 0;
+	uint16_t AllocateSlaveOffset      ;//= 0;
+	uint16_t LastSlaveChannel         ;//= 0;
+
+	uint8_t  PatternLooping           ;//= 0;
+	uint8_t  PatternStorage           ;//= 1;
+	// 0 = conventional only
+	// 1 = selective
+	// 2 = EMS only.
+
+	uint8_t  OrderLockFlag            ;//= 0;
+
+	uint8_t  MuteChannelTable[64];
+	uint8_t  ChannelCountTable[400];
+
+	char    *DriverName               ;//= NULL;
+
+
+	uint8_t  LastMIDIByte             ;//= 0xFF;
+	uint16_t MIDIPitchDepthSent       ;//= 0x0000;
+
+	uint16_t Seed1                    ;//= 0x1234;
+	uint16_t Seed2                    ;//= 0x5678;
+
+	uint8_t  DoMIDICycle              ;//= 0;
+
+	uint8_t  MIDIPrograms[16]         ;//= 0xFF; // Do NOT change order!
+	uint16_t MIDIBanks[16]            ;//= 0xFFFF;
+	uint8_t  MIDIPan[16]              ;//= 0xFF;
+	uint16_t MIDIPitch[16]            ;//= 0x2000;
+
+	uint16_t ADSCParams[7]            ;//= 0;
+
+	uint8_t  StopSong                 ;//= 0;
+	uint32_t TimerCounter             ;//= 0;
+	uint32_t TotalTimer               ;//= 0;
+	uint32_t TotalTimerHigh           ;//= 0;
+
+	// PE stuff
+	uint16_t TopOrder                ;//= 0
+	uint16_t Order                   ;//= 0
+	uint16_t OrderCursor             ;//= 0
+	uint16_t PatternNumber           ;//= 0
+	uint16_t TopRow                  ;//= 0
+	uint16_t Row                     ;//= 0
+	uint16_t MaxRow                  ;//= 63
+	//uint16_t NumberOfRows            ;//= 64
+
+	uint8_t LastNote                ;//= 60
+	uint8_t LastInstrument          ;//= 1
+	uint8_t LastVolume              ;//= 0FFh
+	uint8_t LastCommand             ;//= 0
+	uint8_t LastCommandValue        ;//= 0
+
+	it_drvdata d;
+};
+
+// it_obj1.c
+#ifdef EDITOR
+void M_Object1List(it_engine *ite, void *di, int cx);
+extern it_objlist_6 O1_AutoDetectList;
+extern it_objlist_2 O1_OutOfSoundCardMemoryList;
+extern it_objlist_2 O1_ShowTime;
+#else
+#define M_Object1List(ite, list, typ)
+static void *O1_AutoDetectList;
+static void *O1_OutOfSoundCardMemoryList;
+static void *O1_ShowTime;
+#endif
+
+// it_music.c
+it_engine *ITEngineNew(void);
+void RecalculateAllVolumes(it_engine *ite);
+void InitPlayInstrument(it_engine *ite, it_host *chn, it_slave *slave, int bx);
+void ApplyRandomValues(it_engine *ite, it_host *chn);
+void SetFilterCutoff(it_engine *ite, it_slave *slave, uint8_t bl);
+void SetFilterResonance(it_engine *ite, it_slave *slave, uint8_t bl);
+void MIDITranslate(it_engine *ite, it_host *chn, it_slave *slave, uint16_t bx);
+it_slave *AllocateChannel(it_engine *ite, it_host *chn, uint8_t *ch);
+uint16_t Random(it_engine *ite);
+void GetLoopInformation(it_engine *ite, it_slave *slave);
+void PitchSlideDown(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+void PitchSlideDownAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+void PitchSlideDownLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+void PitchSlideUp(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+void PitchSlideUpLinear(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+void PitchSlideUpAmiga(it_engine *ite, it_host *chn, it_slave *slave, int16_t bx);
+int Music_GetWaveForm(it_engine *ite);
+void Music_Poll(it_engine *ite);
+void Music_InitTempo(it_engine *ite);
+void Music_ReinitSoundCard(it_engine *ite);
+void Music_UnInitSoundCard(it_engine *ite);
+void Music_InitMusic(it_engine *ite);
+it_pattern *Music_AllocatePattern(it_engine *ite, uint16_t dx);
+uint8_t *Music_AllocateSample(it_engine *ite, uint16_t ax, size_t edx);
+void Music_ReleaseSample(it_engine *ite, uint8_t al, uint8_t ah);
+void Music_ClearAllSampleNames(it_engine *ite);
+void Music_ReleaseAllSamples(it_engine *ite);
+void Music_ReleaseAllPatterns(it_engine *ite);
+void Music_ClearAllInstruments(it_engine *ite);
+void Music_UnInitMusic(it_engine *ite);
+void Music_UnloadDriver(it_engine *ite);
+void Music_ClearDriverTables(it_engine *ite);
+int Music_LoadDriver(it_engine *ite, const char *fname);
+const char *Music_AutoDetectSoundCard(it_engine *ite);
+void Music_ShowAutoDetectSoundCard(it_engine *ite);
+void Update(it_engine *ite, uint16_t *rcx, it_slave **si, uint16_t *ax);
+void UpdateSamples(it_engine *ite);
+void UpdateInstruments(it_engine *ite);
+void UpdateInstruments16(it_engine *ite, it_slave *slave);
+void UpdateInstruments5(it_engine *ite, it_slave *slave);
+void UpdateData(it_engine *ite);
+void UpdateData_PlayMode0(it_engine *ite);
+void UpdateData_PlayMode1(it_engine *ite);
+void UpdateEffectData(it_engine *ite);
+void UpdateData_PlayMode2(it_engine *ite);
+uint8_t *Music_GetSampleLocation(it_engine *ite, uint16_t ax, uint32_t *rcx, int *is8bit);
+void Music_PlayPattern(it_engine *ite, uint16_t pidx, uint16_t numrows, uint16_t startrow);
+void Music_PlaySong(it_engine *ite, uint16_t oidx);
+void Music_PlayPartSong(it_engine *ite, uint16_t oidx, uint16_t row);
+void Music_StopChannels(it_engine *ite);
+void Music_Stop(it_engine *ite);
+void Music_InitStereo(it_engine *ite);
+uint16_t Music_SoundCardLoadAllSamples(it_engine *ite);
+void Music_InitMixTable(it_engine *ite);
+uint16_t Music_GetTempo(it_engine *ite);
+void Music_ShowTime(it_engine *ite);
+void Music_ToggleOrderUpdate(it_engine *ite);
+uint16_t Music_ToggleSolo(it_engine *ite, const char *msg, uint8_t *v, uint16_t bp);
+
+extern const uint32_t PitchTable[];
+extern const int8_t FineSineData[];
+extern const int8_t FineRampDownData[];
+extern const int8_t FineSquareWave[];
+extern const uint32_t *LinearSlideUpTable;
+
+// it_m_eff.c
+void InitNoCommand(it_engine *ite, it_host *chn);
+void InitCommandA(it_engine *ite, it_host *chn);
+void InitCommandB(it_engine *ite, it_host *chn);
+void InitCommandC(it_engine *ite, it_host *chn);
+void InitCommandD(it_engine *ite, it_host *chn);
+void InitCommandE(it_engine *ite, it_host *chn);
+void InitCommandF(it_engine *ite, it_host *chn);
+void InitCommandG(it_engine *ite, it_host *chn);
+void InitCommandH(it_engine *ite, it_host *chn);
+void InitCommandI(it_engine *ite, it_host *chn);
+void InitCommandJ(it_engine *ite, it_host *chn);
+void InitCommandK(it_engine *ite, it_host *chn);
+void InitCommandL(it_engine *ite, it_host *chn);
+void InitCommandM(it_engine *ite, it_host *chn);
+void InitCommandN(it_engine *ite, it_host *chn);
+void InitCommandO(it_engine *ite, it_host *chn);
+void InitCommandP(it_engine *ite, it_host *chn);
+void InitCommandQ(it_engine *ite, it_host *chn);
+void InitCommandR(it_engine *ite, it_host *chn);
+void InitCommandS(it_engine *ite, it_host *chn);
+void InitCommandT(it_engine *ite, it_host *chn);
+void InitCommandU(it_engine *ite, it_host *chn);
+void InitCommandV(it_engine *ite, it_host *chn);
+void InitCommandW(it_engine *ite, it_host *chn);
+void InitCommandX(it_engine *ite, it_host *chn);
+void InitCommandY(it_engine *ite, it_host *chn);
+void InitCommandZ(it_engine *ite, it_host *chn);
+
+void NoCommand(it_engine *ite, it_host *chn);
+void CommandD(it_engine *ite, it_host *chn);
+void CommandE(it_engine *ite, it_host *chn);
+void CommandF(it_engine *ite, it_host *chn);
+void CommandG(it_engine *ite, it_host *chn);
+void CommandH(it_engine *ite, it_host *chn);
+void CommandI(it_engine *ite, it_host *chn);
+void CommandJ(it_engine *ite, it_host *chn);
+void CommandK(it_engine *ite, it_host *chn);
+void CommandL(it_engine *ite, it_host *chn);
+void CommandN(it_engine *ite, it_host *chn);
+void CommandP(it_engine *ite, it_host *chn);
+void CommandQ(it_engine *ite, it_host *chn);
+void CommandR(it_engine *ite, it_host *chn);
+void CommandS(it_engine *ite, it_host *chn);
+void CommandT(it_engine *ite, it_host *chn);
+void CommandW(it_engine *ite, it_host *chn);
+void CommandY(it_engine *ite, it_host *chn);
+void VolumeCommandC(it_engine *ite, it_host *chn);
+void VolumeCommandD(it_engine *ite, it_host *chn);
+void VolumeCommandE(it_engine *ite, it_host *chn);
+void VolumeCommandF(it_engine *ite, it_host *chn);
+void VolumeCommandG(it_engine *ite, it_host *chn);
+
+// it_disk.c & co
+int D_LoadIT(it_engine *ite, int fd);
+
+// unsorted
+
+// these can be filled in later, they're really simple and afaik purely for the editor
+// - look in IT_I.ASM for the code --GM
+#define I_TagSample(ite, idx)
+#define I_TagInstrument(ite, idx)
+
+// here's the screen stuff that needs implementing --GM
+#define S_SaveScreen(ite)
+#define S_DrawBox(ite, col, x1, y1, w, h) /* args unknown right now */
+#define S_DrawString(ite, x, y, str, col)
+#define S_UpdateScreen(ite)
+#define S_RestoreScreen(ite)
+#define S_SetDirectMode(ite, b)
+#define S_DrawSmallBox(ite)
+#define S_GetDestination(ite)
+
+#define SetInfoLine(ite, str) printf("%s\n", (str));
+
+#define D_GotoStartingDirectory(ite)
+#define D_ShowTime(ite, x, y, time)
+
+#define MIDI_ClearTable(ite)
+
+#define StartClock(ite)
+
--- /dev/null
+++ b/itdec.c
@@ -1,0 +1,320 @@
+#include "it_struc.h"
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+static int tempo = 125;
+static double pos;
+static s32int mixbuf[44100*2];
+static s16int outbuf[44100*2];
+
+static const char *
+DriverDetectCard(it_engine *, const char *, uint16_t, uint16_t)
+{
+	return nil;
+}
+
+static const char *
+DriverInitSound(it_engine *)
+{
+	return nil;
+}
+
+int
+DriverUninitSound(it_engine *)
+{
+	return 0;
+}
+
+static void
+kill_channel(it_engine *ite, it_slave *slave)
+{
+	slave->Flags = 0x0200;
+	slave->LpD = 0;
+
+	if((slave->HCN & 0x80) == 0)
+		ite->chn[slave->HCN].Flags &= ~4;
+}
+
+static int
+update_offs(it_engine *ite, it_slave *slave, int32_t *offs, int32_t *oferr, int32_t *nfreq, const int32_t lpbeg, const int32_t lpend)
+{
+	*oferr += *nfreq;
+	*offs += *oferr>>16;
+	*oferr &= 0xFFFF;
+
+	if((slave->LpM & 24) == 24 && slave->LpD != 0 && (*offs < lpbeg)){
+		*offs = lpbeg - *offs;
+		if(lpend <= lpbeg){
+			kill_channel(ite, slave);
+			return 1;
+		}
+
+		*offs %= (lpend-lpbeg)*2;
+		if(*offs < (lpend-lpbeg)){
+			slave->LpD = 0;
+			if(*nfreq < 0) *nfreq = -*nfreq;
+			*offs += lpbeg;
+		}else{
+			*offs = (lpend - lpbeg) - *offs;
+			*offs += lpend - 1;
+		}
+	}
+
+	if(*offs < 0){
+		if((slave->LpM & 24) != 24){
+			kill_channel(ite, slave);
+			slave->LpD = 0;
+			return 1;
+		}
+
+		*offs = 0;
+		if(*nfreq < 0) *nfreq = -*nfreq;
+		slave->LpD = 0;
+	}
+
+	if(*offs >= lpend){
+		if((slave->LpM & 8) == 0){
+			kill_channel(ite, slave);
+			return 1;
+		}
+
+		if((slave->LpM & 24) == 24){
+			if(lpend <= lpbeg){
+				kill_channel(ite, slave);
+				return 1;
+			}
+
+			*offs -= lpend;
+			*offs %= (lpend-lpbeg)*2;
+			if(*offs >= (lpend-lpbeg)){
+				*offs -= (lpend - lpbeg);
+				*offs += lpbeg;
+			}else{
+				slave->LpD = 1;
+
+				*offs = lpend - *offs - 1;
+
+				if(*nfreq > 0)
+					*nfreq = -*nfreq;
+			}
+		}else{
+			if(lpend <= lpbeg){
+				kill_channel(ite, slave);
+				return 1;
+			}
+
+			*offs -= lpend;
+			*offs %= (lpend-lpbeg);
+			*offs += lpbeg;
+		}
+	}
+
+	if(*offs < 0 || *offs >= lpend){
+		kill_channel(ite, slave);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+DriverPoll(it_engine *ite, uint16_t PlayMode, uint16_t CurrentPattern)
+{
+	it_slave *slave;
+	u16int ax, cx;
+	it_slave *si;
+	int i, j;
+
+	USED(PlayMode, CurrentPattern);
+	Update(ite, &cx, &si, &ax);
+
+	int tper = (44100*10)/(tempo*4);
+
+	memset(mixbuf, 0, tper*2*4);
+	memset(outbuf, 0, tper*2*2);
+
+	for(i = 0; i < ite->NumChannels; i++){
+		slave = &ite->slave[i];
+		if((slave->Flags & 1) != 0 && (slave->Flags & 0x0200) == 0){
+			int offs = slave->Sample_Offset;
+			int oferr = slave->SmpErr;
+			int lpbeg = slave->Loop_Beginning;
+			int lpend = slave->Loop_End;
+			int nfreq = slave->Frequency;
+
+			int32_t vol = slave->_16bVol;
+			vol *= ite->hdr.MV;
+			vol >>= 8;
+
+			int lvol = vol, rvol = vol;
+
+			if(slave->FPP == 100)
+				rvol = -rvol;
+			else if(slave->FPP < 32)
+				rvol = (rvol * slave->FPP) >> 5;
+			else
+				lvol = (lvol * (64 - slave->FPP)) >> 5;
+
+			nfreq = (((s64int)nfreq) << 16) / 44100ULL;
+
+			if(slave->LpD == 1) nfreq = -nfreq;
+
+			if(ite->SamplePointer[slave->Smp] == nil){
+				kill_channel(ite, slave);
+				break;
+			}else if(slave->Bit != 0){
+				s16int *data = (s16int*)ite->SamplePointer[slave->Smp];
+
+				for(j = 0; j < tper*2; j+=2){
+					if(update_offs(ite, slave, &offs, &oferr, &nfreq, lpbeg, lpend) != 0)
+						break;
+
+					mixbuf[j+0] -= (lvol*(int32_t)data[offs])>>14;
+					mixbuf[j+1] -= (rvol*(int32_t)data[offs])>>14;
+				}
+			}else{
+				s8int *data = (s8int*)ite->SamplePointer[slave->Smp];
+				for(j = 0; j < tper*2; j+=2){
+					if(update_offs(ite, slave, &offs, &oferr, &nfreq, lpbeg, lpend) != 0)
+						break;
+
+					mixbuf[j+0] -= (lvol*(int32_t)data[offs])>>(14-8);
+					mixbuf[j+1] -= (rvol*(int32_t)data[offs])>>(14-8);
+				}
+			}
+			slave->Sample_Offset = offs;
+			slave->SmpErr = oferr;
+		}
+
+		slave->Flags &= 0x788D;
+	}
+
+	for(i = 0; i < tper*2; i++){
+		int32_t v = mixbuf[i];
+		if(v >  0x7FFF)
+			v =  0x7FFF;
+		else if(v < -0x7FFF)
+			v = -0x7FFF;
+		outbuf[i] = v;
+	}
+
+	int got = tper*2*2;
+	int skip = 0;
+	if(pos > 0.001){
+		double n = MIN(pos, (double)got / 44100.0);
+		int skip = (int)(n * 44100)*2*2;
+		pos -= n;
+		if(skip >= got)
+			return 0;
+	}
+	write(1, outbuf+skip, got-skip);
+
+	return 0;
+}
+
+static int
+DriverSetTempo(it_engine *, uint16_t Tempo)
+{
+	tempo = Tempo;
+	return 0;
+}
+
+static int
+DriverSetMixVolume(it_engine *ite, uint16_t MixVolume)
+{
+	USED(ite, MixVolume);
+	return 0;
+}
+
+static int
+DriverSetStereo(it_engine *ite, uint16_t Stereo)
+{
+	/* whoops */
+	USED(ite, Stereo);
+	return 0;
+}
+
+static int
+DriverReleaseSample(it_engine *, it_sample *)
+{
+	return 0;
+}
+
+static int
+DriverMIDIOut(it_engine *, uint8_t)
+{
+	return 0;
+}
+
+static int
+DriverGetWaveform(it_engine *)
+{
+	return 0;
+}
+
+it_drvdata *
+drv_oss_init(it_engine *)
+{
+	static it_drvdata d = {
+		.BasePort = 0xFFFF,
+		.IRQ = 0xFFFF,
+		.DMA = 0xFFFF,
+		.CmdLineMixSpeed = 0,
+		.CmdLineDMASize = 1024,
+		.ReverseChannels = 0,
+		.DriverMaxChannels = 256,
+		.StopEndOfPlaySection = 1,
+		.DefaultChannels = 256,
+		.DriverFlags = 0,
+		.DriverDetectCard = DriverDetectCard,
+		.DriverInitSound = DriverInitSound,
+		.DriverUninitSound = DriverUninitSound,
+		.DriverPoll = DriverPoll,
+		.DriverSetTempo = DriverSetTempo,
+		.DriverSetMixVolume = DriverSetMixVolume,
+		.DriverSetStereo = DriverSetStereo,
+		.DriverReleaseSample = DriverReleaseSample,
+		.DriverMIDIOut = DriverMIDIOut,
+		.DriverGetWaveform = DriverGetWaveform,
+	};
+
+	return &d;
+}
+
+it_drvdata *
+drv_sdl_init(it_engine *)
+{
+	return nil;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-s SECONDS]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	it_engine *ite;
+
+	ARGBEGIN{
+	case 's':
+		pos = atof(EARGF(usage()));
+		assert(pos >= 0.0);
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	ite = ITEngineNew();
+	Music_InitMusic(ite);
+	Music_AutoDetectSoundCard(ite);
+	D_LoadIT(ite, 0);
+	Music_PlaySong(ite, 0);
+	if(pos > 0.0)
+		fprint(2, "time: %g\n", pos);
+	while(ite->PlayMode != 0)
+		Music_Poll(ite);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/audio
+TARG=itdec
+CFLAGS=$CFLAGS -p
+
+HFILES=\
+	it_obj.h\
+	it_struc.h\
+	switch.h\
+
+OFILES=\
+	it_disk.$O\
+	it_m_eff.$O\
+	it_music.$O\
+	itdec.$O\
+
+default:V: all
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/switch.h
@@ -1,0 +1,74 @@
+/*
+Copyright (C) 2014, Jeffrey Lim. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define TRACEENABLED          0
+
+#define TUTORIAL         0
+
+#define  EMSUSE41         0
+
+#define  SHOWVERSION          0
+#define  SHOWREGISTERNAME     1
+
+#define  USE32BITSCREENCOPY   0
+
+#define  SORTENABLED          1
+#define  DDCOMPRESS           1
+#define  ORDERSORT            1
+#define  FILTERENVELOPES      1
+#define  CHORDENTRY           1
+#define  SPECTRUMANALYSER     1
+#define  SAVESAMPLEWAV        1
+#define  ENABLEPRESETENVELOPES  1
+#define  ENABLESOLO           1
+
+#define    DEFAULTFORMAT      3 /* 0  IT214, 1  S3M, 2  IT2xx, 3  IT215 */
+
+// USEFPUCODE disabled for the time being --GM
+#define    USEFPUCODE         0 /* For IT_MUSIC, this will change from LUT to FPU code */
+
+#define    OLDDRIVER          0
+
+#define    MUSICDEBUG         0
+#define    EMSDEBUG           0
+#define    MEMORYDEBUG        0
+#define ENABLEINT3         0 /* For debugging. */
+
+#define    TIMERSCREEN        1
+
+//define    NETWORKENABLED     1
+#define    NETWORKENABLED     0
+#define    SHOWPATTERNLENGTH  0
+
+#if TUTORIAL
+#define SORTENABLED      1
+#define DDCOMPRESS       1
+#endif
+
+#define TRACKERVERSION  0x217 /* Still have to change text in IT.ASM, IT_F.ASM */
+