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 */
+