code:
.386P

EXTRN _kdmasm1 : dword
EXTRN _kdmasm2 : dword
EXTRN _kdmasm3 : dword
EXTRN _kdmasm4 : dword

EXTRN _chainbackcnt : dword
EXTRN _chainbackstart : dword
EXTRN _pcsndptr : dword
EXTRN _samplecount : dword
EXTRN _pcsndbufsiz : dword
EXTRN _oldpctimerhandler : far
EXTRN _bufferside : byte

EXTRN _qualookup : near
EXTRN _pcsndlookup : near
EXTRN _chain_intr_ : near
EXTRN __GETDS : near
EXTRN preparesndbuf_ : near

_DATA SEGMENT BYTE PUBLIC USE32 'DATA'
_DATA ENDS
DGROUP GROUP _DATA

CODE SEGMENT BYTE PUBLIC USE32 'CODE'
ASSUME cs:CODE

PUBLIC monolocomb_
monolocomb_:
	;eax=temp2,ebx=temp1,ecx=cnt,edx=dat,esi=dasplc,edi=stemp,ebp=dasoff
	push ebp

	mov dword ptr ds:[mach1lm+3], ebx        ;voloffs1

	mov ebx, _kdmasm4
	mov dword ptr ds:[mach5lm+2], ebx

	mov ebx, edx
	shl ebx, 32-12
	sar edx, 12
	mov dword ptr ds:[mach3lm+2], ebx        ;dasinc
	mov dword ptr ds:[mach4lm+2], edx        ;dasinc

	mov ebp, esi
	shl ebp, 32-12
	sar esi, 12

	xor edx, edx
	jmp begitlm

prebegitlm:
	cmp _kdmasm1, 0
	jz enditlm
	mov ebx, _kdmasm2
	mov dword ptr ds:[mach5lm+2], ebx
	mov _kdmasm4, ebx
	mov ebx, _kdmasm3
	mov eax, ebx
	shl ebx, 32-12
	sar eax, 12
	sub ebp, ebx
	sbb esi, eax
	test ecx, ecx
	jz enditlm

begitlm:
		;dl = [(eax>>12)+dasoff];     (QUICK NO MULTIPLY METHOD)
mach5lm: mov dl, byte ptr [esi+88888888h]      ;dasoff

	mov ebx, dword ptr [edi]
	add edi, 4
mach1lm: add ebx, dword ptr [edx*4+88888888h]  ;voloffs1
mach3lm: add ebp, 88888888h                    ;dasinc
mach4lm: adc esi, 88888888h                    ;dasinc
	mov dword ptr [edi-4], ebx
	dec ecx

	ja begitlm          ;jump if (no carry (add)) and (not zero (dec))!
	jc prebegitlm
enditlm:

	shl esi, 12
	shr ebp, 32-12
	lea eax, [esi+ebp]

	pop ebp
	ret

PUBLIC monohicomb_
monohicomb_:
	;eax=temp2,ebx=temp1,ecx=cnt,edx=dat,esi=dasplc,edi=stemp,ebp=dasoff
	push ebp
	mov ebp, ecx

	mov dword ptr ds:[mach1hm+3], ebx        ;voloffs1

	mov ebx, _kdmasm4
	mov dword ptr ds:[mach5hm+3], ebx

	mov ebx, edx
	shl ebx, 32-12
	sar edx, 12
	mov dword ptr ds:[mach4hm+2], edx        ;dasinc
	;mov edx, ebx       ;Fake 4-bit counter to save shift
	;shr edx, 20        ;
	;mov bx, dx         ;
	mov dword ptr ds:[mach3hm+2], ebx        ;dasinc

	mov ecx, esi
	shl ecx, 32-12
	sar esi, 12
	;mov edx, ecx       ;Fake 4-bit counter to save shift
	;shr edx, 20        ;
	;mov cx, dx         ;

	xor edx, edx
	sub edi, 4
	jmp begithm

prebegithm:
	cmp _kdmasm1, 0
	jz endithm
	mov ebx, _kdmasm2
	mov dword ptr ds:[mach5hm+3], ebx
	mov _kdmasm4, ebx
	mov ebx, _kdmasm3
	mov eax, ebx
	shl ebx, 32-12
	sar eax, 12
	sub ecx, ebx
	sbb esi, eax
	test ebp, ebp
	jz endithm

begithm:
	;edx = _qualookup[((ebp>>19)&0x00001e00)+(bh-bl)]+bl
mach5hm: mov bx, word ptr [esi+88888888h]
	mov eax, ecx
	shr eax, 20
	add edi, 4
	mov dl, bl
	mov al, bl
	sub al, bh
	adc ah, ah
	add dl, byte ptr _qualookup[eax]

	mov ebx, dword ptr [edi]
mach1hm: add ebx, dword ptr [edx*4+88888888h]  ;voloffs1
mach3hm: add ecx, 88888888h                    ;dasinc
mach4hm: adc esi, 88888888h                    ;dasinc
	dec ebp

	mov dword ptr [edi], ebx

	ja begithm          ;jump if (no carry (add)) and (not zero (dec))!
	jc prebegithm
endithm:

	shl esi, 12
	shr ecx, 32-12
	lea eax, [esi+ecx]

	pop ebp
	ret

PUBLIC stereolocomb_
stereolocomb_:
	;eax=temp2,ebx=temp1,ecx=cnt,edx=dat,esi=dasplc,edi=stemp,ebp=dasoff
	push ebp

	mov dword ptr ds:[mach1ls+3], ebx        ;voloffs1
	add ebx, 4
	mov dword ptr ds:[mach2ls+3], ebx        ;voloffs2

	mov ebx, _kdmasm4
	mov dword ptr ds:[mach5ls+2], ebx

	mov ebx, edx
	shl ebx, 32-12
	sar edx, 12
	mov dword ptr ds:[mach3ls+2], ebx        ;dasinc
	mov dword ptr ds:[mach4ls+2], edx        ;dasinc

	mov ebp, esi
	shl ebp, 32-12
	sar esi, 12

	xor edx, edx
	sub edi, 8
	jmp begitls

prebegitls:
	cmp _kdmasm1, 0
	jz enditls
	mov ebx, _kdmasm2
	mov dword ptr ds:[mach5ls+2], ebx
	mov _kdmasm4, ebx
	mov ebx, _kdmasm3
	mov eax, ebx
	shl ebx, 32-12
	sar eax, 12
	sub ebp, ebx
	sbb esi, eax
	test ecx, ecx
	jz enditls
	jmp begitls

begitls:
	add edi, 8

		;dl = [(eax>>12)+dasoff];     (QUICK NO MULTIPLY METHOD)
mach5ls: mov dl, byte ptr [esi+88888888h]      ;dasoff

	mov ebx, dword ptr [edi]
	mov eax, dword ptr [edi+4]

mach1ls: add ebx, dword ptr [edx*8+88888888h]  ;voloffs1
mach2ls: add eax, dword ptr [edx*8+88888888h]  ;voloffs2

mach3ls: add ebp, 88888888h                    ;dasinc
	mov dword ptr [edi], ebx
mach4ls: adc esi, 88888888h                    ;dasinc
	dec ecx

	mov dword ptr [edi+4], eax

	ja begitls          ;jump if (no carry (add)) and (not zero (dec))!
	jc prebegitls
enditls:

	shl esi, 12
	shr ebp, 32-12
	lea eax, [esi+ebp]

	pop ebp
	ret

PUBLIC stereohicomb_
stereohicomb_:
	;eax=temp2,ebx=temp1,ecx=cnt,edx=dat,esi=dasplc,edi=stemp,ebp=dasoff
	push ebp
	mov ebp, ecx

	mov dword ptr ds:[mach1hs+3], ebx        ;voloffs1
	add ebx, 4
	mov dword ptr ds:[mach2hs+3], ebx        ;voloffs2

	mov ebx, _kdmasm4
	mov dword ptr ds:[mach5hs+3], ebx

	mov ebx, edx
	shl ebx, 32-12
	sar edx, 12
	mov dword ptr ds:[mach4hs+2], edx        ;dasinc
	;mov edx, ebx       ;Fake 4-bit counter to save shift
	;shr edx, 20        ;
	;mov bx, dx         ;
	mov dword ptr ds:[mach3hs+2], ebx        ;dasinc

	mov ecx, esi
	shl ecx, 32-12
	sar esi, 12
	;mov edx, ecx       ;Fake 4-bit counter to save shift
	;shr edx, 20        ;
	;mov cx, dx         ;

	xor edx, edx
	sub edi, 8
	jmp begiths

prebegiths:
	cmp _kdmasm1, 0
	jz endiths
	mov ebx, _kdmasm2
	mov dword ptr ds:[mach5hs+3], ebx
	mov _kdmasm4, ebx
	mov ebx, _kdmasm3
	mov eax, ebx
	shl ebx, 32-12
	sar eax, 12
	sub ecx, ebx
	sbb esi, eax
	test ebp, ebp
	jz endiths

begiths:
	;edx = _qualookup[((ebp>>19)&0x00001e00)+(bh-bl)]+bl
mach5hs: mov bx, word ptr [esi+88888888h]
	mov eax, ecx
	shr eax, 20
	add edi, 8
	mov dl, bl
	mov al, bl
	sub al, bh
	adc ah, ah
	add dl, byte ptr _qualookup[eax]

	mov eax, dword ptr [edi]
mach1hs: mov ebx, dword ptr [edx*8+88888888h]  ;voloffs1
	add ebx, eax
	mov eax, dword ptr [edi+4]
	mov dword ptr [edi], ebx
mach2hs: mov ebx, dword ptr [edx*8+88888888h]  ;voloffs2
	add eax, ebx

mach3hs: add ecx, 88888888h                    ;dasinc
mach4hs: adc esi, 88888888h                    ;dasinc

	dec ebp

	mov dword ptr [edi+4], eax

	ja begiths          ;jump if (no carry (add)) and (not zero (dec))!
	jc prebegiths
endiths:

	shl esi, 12
	shr ecx, 32-12
	lea eax, [esi+ecx]

	pop ebp
	ret

PUBLIC setuppctimerhandler_
setuppctimerhandler_:
	mov dword ptr ds:[pcmach3+2], eax

	cmp ebx, 65000d
	jl short skipsetuppc1
	mov word ptr ds:[pcmach1+0], 0c381h     ;81c300000000 = add ebx, 00000000h
	mov dword ptr ds:[pcmach1+2], 00000000h
	mov word ptr ds:[pcmach2+0], 0c98bh     ;8bc9 = mov ecx, ecx
skipsetuppc1:
	ret

PUBLIC pctimerhandler_
pctimerhandler_:
	push eax
	mov ax, DGROUP
	push ds
	mov ds, ax
	push edx

	mov edx, dword ptr _pcsndptr
	mov al, byte ptr [edx]
	out 42h, al
	inc edx
	mov dword ptr _pcsndptr, edx
pcmach1: dec _chainbackcnt              ;samplediv
pcmach2: jz short pc3
	mov al, 20h
	out 20h, al
pcmach3: cmp edx, 88888888h             ;pcsndptrend
	jge short pc1

	pop edx
	pop ds
	pop eax
	iretd

pc1: xor byte ptr _bufferside, 1
	mov eax, _pcsndbufsiz
	mov edx, _pcsndptr
	jnz short pc2
	sub edx, eax
	sub edx, eax
	mov _pcsndptr, edx
pc2: add eax, edx
	mov dword ptr ds:[pcmach3+2], eax
	sti
	call preparesndbuf_
	pop edx
	pop ds
	pop eax
	iretd

pc3: sti
	cmp edx, dword ptr ds:[pcmach3+2]
	jl short pc5

	xor byte ptr _bufferside, 1
	mov eax, _pcsndbufsiz
	mov edx, _pcsndptr
	jnz short pc4
	sub edx, eax
	sub edx, eax
	mov _pcsndptr, edx
pc4: add eax, edx
	mov dword ptr ds:[pcmach3+2], eax
	sti
	call preparesndbuf_
pc5:
	mov eax, _chainbackstart
	mov _chainbackcnt, eax
	pop edx
	pop ds
	pop eax
		;Must start interrupt handler like watcom c
	pushad
	push ds
	push es
	push fs
	push gs
	mov ebp, esp
	cld
	call __GETDS
	mov dx, word ptr [_oldpctimerhandler+4]
	mov eax, dword ptr [_oldpctimerhandler]
	call _chain_intr_

PUBLIC pcbound2char_
pcbound2char_:
	push ebp

	add ecx, ecx
	lea eax, [ecx+edi]
	mov dword ptr ds:[pcmachchar+3], eax
	xor edi, edi
	sub edi, ecx
	xor ecx, ecx

	xor edx, edx
	mov ebp, 0ffff0000h
startpcbound2char:
	mov ebx, dword ptr [esi]
	mov eax, dword ptr [esi+4]
	test ebx, ebp
	jnz short pcboundit1
pcboundit1back:
	mov dl, bh
	mov dword ptr [esi], 32768
	test eax, ebp
	mov al, byte ptr _pcsndlookup[edx]
	jnz short pcboundit2
pcboundit2back:
	mov dl, ah
	mov dword ptr [esi+4], 32768
	mov ah, byte ptr _pcsndlookup[edx]
	add esi, 8
pcmachchar: mov word ptr [edi+88888888h], ax
	add edi, 2
	jnc short startpcbound2char
	pop ebp
	ret
pcboundit1:
	cmp ebx, 80000000h
	sbb bh, bh
	jmp short pcboundit1back
pcboundit2:
	cmp eax, 80000000h
	sbb ah, ah
	jmp short pcboundit2back

	;for(i=0;i<bytespertic;i++)
	;{
	;   j = (stemp[i]>>8);
	;   if (j < 0) j = 0;
	;   if (j > 255) j = 255;
	;   *charptr++ = (char)j;
	;}
PUBLIC bound2char_
bound2char_:
	add ecx, ecx
	lea eax, [ecx+edi]
	mov dword ptr ds:[machchar+3], eax
	xor edi, edi
	sub edi, ecx
	xor ecx, ecx

	mov edx, 0ffff0000h
startbound2char:
	mov ebx, dword ptr [esi]
	mov eax, dword ptr [esi+4]
	test ebx, edx
	jnz short boundchar1
boundchar1back:
	mov dword ptr [esi], 32768
	test eax, edx
	jnz short boundchar2
boundchar2back:
	mov dword ptr [esi+4], 32768
	mov al, bh
	add esi, 8
machchar: mov word ptr [edi+88888888h], ax
	add edi, 2
	jnc short startbound2char
	ret
boundchar1:
	cmp ebx, 80000000h
	sbb bh, bh
	jmp short boundchar1back
boundchar2:
	cmp eax, 80000000h
	sbb ah, ah
	jmp short boundchar2back

	;for(i=0;i<bytespertic;i++)
	;{
	;   j = stemp[i];
	;   if (j < 0) j = 0;
	;   if (j > 65535) j = 65535;
	;   *shortptr++ = (short)(j^8000h);
	;}
PUBLIC bound2short_
bound2short_:
	shl ecx, 2
	lea eax, [ecx+edi]
	mov dword ptr ds:[machshort+2], eax
	xor edi, edi
	sub edi, ecx
	xor ecx, ecx

	mov edx, 0ffff0000h
startbound2short:
	mov ebx, dword ptr [esi]
	mov eax, dword ptr [esi+4]
	test ebx, edx
	jnz short boundshort1
boundshort1back:
	mov dword ptr [esi], 32768
	test eax, edx
	jnz short boundshort2
boundshort2back:
	shl eax, 16
	mov dword ptr [esi+4], 32768
	mov ax, bx
	add esi, 8
	xor eax, 80008000h
machshort: mov dword ptr [edi+88888888h], eax
	add edi, 4
	jnc short startbound2short
	ret
boundshort1:
	cmp ebx, 80000000h
	sbb ebx, ebx
	jmp short boundshort1back
boundshort2:
	cmp eax, 80000000h
	sbb eax, eax
	jmp short boundshort2back

CODE ENDS
END


code:
#include <malloc.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>
#include <fcntl.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>

#define NUMCHANNELS 16
#define MAXWAVES 256
#define MAXTRACKS 256
#define MAXNOTES 8192
#define MAXEFFECTS 16

	//Actual sound parameters after initsb was called
long samplerate, numspeakers, bytespersample, intspersec, kdmqual;

	//KWV wave variables
long numwaves;
char instname[MAXWAVES][16];
long wavleng[MAXWAVES];
long repstart[MAXWAVES], repleng[MAXWAVES];
long finetune[MAXWAVES];

	//Other useful wave variables
long totsndbytes, totsndmem, wavoffs[MAXWAVES];

	//Effects array
long eff[MAXEFFECTS][256];

	//KDM song variables:
long kdmversionum, numnotes, numtracks;
char trinst[MAXTRACKS], trquant[MAXTRACKS];
char trvol1[MAXTRACKS], trvol2[MAXTRACKS];
long nttime[MAXNOTES];
char nttrack[MAXNOTES], ntfreq[MAXNOTES];
char ntvol1[MAXNOTES], ntvol2[MAXNOTES];
char ntfrqeff[MAXNOTES], ntvoleff[MAXNOTES], ntpaneff[MAXNOTES];

	//Other useful song variables:
long timecount, notecnt, musicstatus, musicrepeat;

long kdmasm1, kdmasm2, kdmasm3, kdmasm4;

static char digistat = 0, musistat = 0;

char *snd = NULL, kwvname[20] = {""};

#define MAXBYTESPERTIC 1024+128
static long stemp[MAXBYTESPERTIC];

	//Sound reading information
long splc[NUMCHANNELS], sinc[NUMCHANNELS], soff[NUMCHANNELS];
long svol1[NUMCHANNELS], svol2[NUMCHANNELS];
long volookup[NUMCHANNELS<<9];
long swavenum[NUMCHANNELS];
long frqeff[NUMCHANNELS], frqoff[NUMCHANNELS];
long voleff[NUMCHANNELS], voloff[NUMCHANNELS];
long paneff[NUMCHANNELS], panoff[NUMCHANNELS];

static long globposx, globposy, globxvect, globyvect;
static long xplc[NUMCHANNELS], yplc[NUMCHANNELS], vol[NUMCHANNELS];
static long vdist[NUMCHANNELS], sincoffs[NUMCHANNELS];
static char chanstat[NUMCHANNELS];

long frqtable[256];

static long mixerval = 0;

static long kdminprep = 0, kdmintinprep = 0;
static long dmacheckport, dmachecksiz;

void (__interrupt __far *oldsbhandler)();
void __interrupt __far sbhandler(void);

long samplediv, oldtimerfreq, chainbackcnt, chainbackstart;
char *pcsndptr, pcsndlookup[256], bufferside;
long samplecount, pcsndbufsiz, pcrealmodeint;
static short kdmvect = 0x8;
static unsigned short kdmpsel, kdmrseg, kdmroff;
static unsigned long kdmpoff;

void (__interrupt __far *oldpctimerhandler)();
#define KDMCODEBYTES 256
static char pcrealbuffer[KDMCODEBYTES] =        //See pckdm.asm
{
	0x50,0x53,0x51,0x52,0x32,0xC0,0xE6,0x42,0xB0,0x20,
	0xE6,0x20,0x5A,0x59,0x5B,0x58,0xCF,
};

	//Sound interrupt information
long sbport = 0x220, sbirq = 0x7, sbdma8 = 0x1, sbdma16 = 0x1;
char dmapagenum[8] = {0x87,0x83,0x81,0x82,0x8f,0x8b,0x89,0x8a};
long sbdma, sbver;
unsigned short sndselector;
volatile long sndoffs, sndoffsplc, sndoffsxor, sndplc, sndend;
static long bytespertic, sndbufsiz;

char qualookup[512*16];
long ramplookup[64];

unsigned short sndseg = 0;

extern long monolocomb(long,long,long,long,long,long);
#pragma aux monolocomb parm [eax][ebx][ecx][edx][esi][edi];
extern long monohicomb(long,long,long,long,long,long);
#pragma aux monohicomb parm [eax][ebx][ecx][edx][esi][edi];
extern long stereolocomb(long,long,long,long,long,long);
#pragma aux stereolocomb parm [eax][ebx][ecx][edx][esi][edi];
extern long stereohicomb(long,long,long,long,long,long);
#pragma aux stereohicomb parm [eax][ebx][ecx][edx][esi][edi];
extern long setuppctimerhandler(long,long,long,long,long,long);
#pragma aux setuppctimerhandler parm [eax][ebx][ecx][edx][esi][edi];
extern void interrupt pctimerhandler();
extern long pcbound2char(long,long,long);
#pragma aux pcbound2char parm [ecx][esi][edi];
extern long bound2char(long,long,long);
#pragma aux bound2char parm [ecx][esi][edi];
extern long bound2short(long,long,long);
#pragma aux bound2short parm [ecx][esi][edi];

static long oneshr10 = 0x3a800000, oneshl14 = 0x46800000;
#pragma aux fsin =\
	"fldpi",\
	"fimul dword ptr [eax]",\
	"fmul dword ptr [oneshr10]",\
	"fsin",\
	"fmul dword ptr [oneshl14]",\
	"fistp dword ptr [eax]",\
	parm [eax]\

#pragma aux convallocate =\
	"mov ax, 0x100",\
	"int 0x31",\
	"jnc nocarry",\
	"mov ax, 0",\
	"nocarry: mov sndselector, dx",\
	parm [bx]\
	modify [eax edx]\

#pragma aux convdeallocate =\
	"mov ax, 0x101",\
	"mov dx, sndselector",\
	"int 0x31",\
	parm [dx]\
	modify [eax edx]\

#pragma aux resetsb =\
	"mov edx, sbport",\
	"add edx, 0x6",\
	"mov al, 1",\
	"out dx, al",\
	"xor al, al",\
	"delayreset: dec al",\
	"jnz delayreset",\
	"out dx, al",\
	"mov ecx, 65536",\
	"empty: mov edx, sbport",\
	"add edx, 0xe",\
	"in al, dx",\
	"test al, al",\
	"jns nextattempt",\
	"sub dl, 4",\
	"in al, dx",\
	"cmp al, 0aah",\
	"je resetok",\
	"dec ecx",\
	"nextattempt: jnz empty",\
	"mov eax, -1",\
	"jmp resetend",\
	"resetok: xor eax, eax",\
	"resetend:",\
	modify [ebx ecx edx]\

#pragma aux sbin =\
	"xor eax, eax",\
	"mov dx, word ptr sbport[0]",\
	"add dl, 0xe",\
	"busy: in al, dx",\
	"or al, al",\
	"jns busy",\
	"sub dl, 4",\
	"in al, dx",\
	modify [edx]\

#pragma aux sbout =\
	"mov dx, word ptr sbport[0]",\
	"add dl, 0xc",\
	"mov ah, al",\
	"busy: in al, dx",\
	"or al, al",\
	"js busy",\
	"mov al, ah",\
	"out dx, al",\
	parm [eax]\
	modify [edx]\

#pragma aux sbmixin =\
	"mov dx, word ptr sbport[0]",\
	"add dl, 0x4",\
	"out dx, al",\
	"inc dx",\
	"xor eax, eax",\
	"in al, dx",\
	parm [eax]\
	modify [edx]\

#pragma aux sbmixout =\
	"mov dx, word ptr sbport[0]",\
	"add dl, 0x4",\
	"out dx, al",\
	"inc dx",\
	"mov al, bl",\
	"out dx, al",\
	parm [eax][ebx]\
	modify [edx]\

#pragma aux findpas =\
	"mov eax, 0x0000bc00",\
	"mov ebx, 0x00003f3f",\
	"int 0x2f",\
	"xor bx, cx",\
	"xor bx, dx",\
	"cmp bx, 0x4d56",\
	"stc",\
	"jne nopasfound",\
	"mov eax, 0x0000bc04",\
	"int 0x2f",\
	"mov edx, 0x0000ffff",\
	"and ebx, edx",\
	"and ecx, edx",\
	"mov sbdma, ebx",\
	"mov sbirq, ecx",\
	"clc",\
	"nopasfound:",\
	"sbb eax, eax",\
	modify [eax ebx ecx edx]\

#pragma aux calcvolookupmono =\
	"mov ecx, 64",\
	"lea edx, [eax+ebx]",\
	"add ebx, ebx",\
	"begit: mov dword ptr [edi], eax",\
	"mov dword ptr [edi+4], edx",\
	"add eax, ebx",\
	"add edx, ebx",\
	"mov dword ptr [edi+8], eax",\
	"mov dword ptr [edi+12], edx",\
	"add eax, ebx",\
	"add edx, ebx",\
	"add edi, 16",\
	"dec ecx",\
	"jnz begit",\
	parm [edi][eax][ebx]\
	modify [ecx edx]\

#pragma aux calcvolookupstereo =\
	"mov esi, 128",\
	"begit: mov dword ptr [edi], eax",\
	"mov dword ptr [edi+4], ecx",\
	"add eax, ebx",\
	"add ecx, edx",\
	"mov dword ptr [edi+8], eax",\
	"mov dword ptr [edi+12], ecx",\
	"add eax, ebx",\
	"add ecx, edx",\
	"add edi, 16",\
	"dec esi",\
	"jnz begit",\
	parm [edi][eax][ebx][ecx][edx]\
	modify [esi]\

#pragma aux gettimerval =\
	"xor eax, eax",\
	"xor ebx, ebx",\
	"mov ecx, 65536",\
	"xor edx, edx",\
	"loopit: mov al, 0x4",\
	"out 0x43, al",\
	"in al, 0x40",\
	"mov dl, al",\
	"in al, 0x40",\
	"mov dh, al",\
	"cmp ebx, edx",\
	"dec ecx",\
	"ja loopit",\
	"jz endit",\
	"mov ebx, edx",\
	"jmp loopit",\
	"endit: mov eax, ebx",\
	modify [eax ebx ecx edx]\

#pragma aux klabs =\
	"test eax, eax",\
	"jns skipnegate",\
	"neg eax",\
	"skipnegate:",\
	parm [eax]\

#pragma aux mulscale16 =\
	"imul ebx",\
	"shrd eax, edx, 16",\
	parm nomemory [eax][ebx]\
	modify exact [eax edx]\

#pragma aux mulscale24 =\
	"imul ebx",\
	"shrd eax, edx, 24",\
	parm nomemory [eax][ebx]\
	modify exact [eax edx]\

#pragma aux mulscale30 =\
	"imul ebx",\
	"shrd eax, edx, 30",\
	parm nomemory [eax][ebx]\
	modify exact [eax edx]\

#pragma aux dmulscale28 =\
	"imul edx",\
	"mov ebx, eax",\
	"mov eax, esi",\
	"mov esi, edx",\
	"imul edi",\
	"add eax, ebx",\
	"adc edx, esi",\
	"shrd eax, edx, 28",\
	parm nomemory [eax][edx][esi][edi]\
	modify exact [eax ebx edx esi]\

#pragma aux clearbuf =\
	"snot: mov dword ptr [edi], eax",\
	"add edi, 4",\
	"loop snot",\
	parm [edi][ecx][eax]\

#pragma aux copybuf =\
	"snot: mov eax, dword ptr [esi]",\
	"mov dword ptr [edi], eax",\
	"add esi, 4",\
	"add edi, 4",\
	"loop snot",\
	parm [esi][edi][ecx]\
	modify [eax]\

#pragma aux koutp =\
	"out dx, al",\
	parm [edx][eax]\

#pragma aux koutpw =\
	"out dx, ax",\
	parm [edx][eax]\

#pragma aux kinp =\
	"xor eax, eax",\
	"in al, dx",\
	parm [edx]\

#pragma aux msqrtasm =\
	"mov eax, 0x40000000",\
	"mov ebx, 0x20000000",\
	"begit: cmp ecx, eax",\
	"jl skip",\
	"sub ecx, eax",\
	"lea eax, [eax+ebx*4]",\
	"skip: sub eax, ebx",\
	"shr eax, 1",\
	"shr ebx, 2",\
	"jnz begit",\
	"cmp ecx, eax",\
	"sbb eax, -1",\
	"shr eax, 1",\
	parm nomemory [ecx]\
	modify exact [eax ebx ecx]\

initsb(char dadigistat, char damusistat, long dasamplerate, char danumspeakers, char dabytespersample, char daintspersec, char daquality)
{
	long i, j, k;

	digistat = dadigistat;
	musistat = damusistat;

	if ((digistat == 0) && (musistat != 1))
		return;

	samplerate = dasamplerate;
	if (samplerate < 6000) samplerate = 6000;
	if (samplerate > 48000) samplerate = 48000;
	numspeakers = danumspeakers;
	if (numspeakers < 1) numspeakers = 1;
	if (numspeakers > 2) numspeakers = 2;
	bytespersample = dabytespersample;
	if (bytespersample < 1) bytespersample = 1;
	if (bytespersample > 2) bytespersample = 2;
	intspersec = daintspersec;
	if (intspersec < 1) intspersec = 1;
	if (intspersec > 120) intspersec = 120;
	kdmqual = daquality;
	if (kdmqual < 0) kdmqual = 0;
	if (kdmqual > 1) kdmqual = 1;

	switch(digistat)
	{
		case 1:
			getsbset();
			if (resetsb() != 0) { digistat = musistat = 0; break; }

			sbout(0xe1);
			sbver = (sbin()<<8);
			sbver += sbin();

			if (sbver < 0x0201) samplerate = min(samplerate,22050);
			if (sbver < 0x0300) numspeakers = 1;
			if (sbver < 0x0400)
			{
				samplerate = min(samplerate,44100>>(numspeakers-1));
				bytespersample = 1;
			}

			if (bytespersample == 2) sbdma = sbdma16; else sbdma = sbdma8;

			break;
		case 2:
			findpas();        // If == -1 then print not found & quit
			koutp(0xf8a,128);
			break;
		case 13:
			if (numspeakers == 2) numspeakers = 1;
			if (bytespersample == 2) bytespersample = 1;
			break;
	}

	bytespertic = (((samplerate/120)+1)&~1);
	sndbufsiz = bytespertic*(120/intspersec);

	if (sndseg == 0)    //Allocate DMA buffer in conventional memory
	{
		if ((sndseg = convallocate(((sndbufsiz<<(bytespersample+numspeakers))+15)>>4)) == 0)
		{
			printf("Could not allocation conventional memory for digitized music\n");
			exit(0);
		}
		sndoffs = (((long)sndseg)<<4);

		if ((sndoffs&65535)+(sndbufsiz<<(bytespersample+numspeakers-1)) >= 65536)   //64K DMA page check
			sndoffs += (sndbufsiz<<(bytespersample+numspeakers-1));

		sndoffsplc = sndoffs;
		sndoffsxor = sndoffsplc ^ (sndoffsplc+(sndbufsiz<<(bytespersample+numspeakers-2)));
	}

	j = (((11025L*2093)/samplerate)<<13);
	for(i=1;i<76;i++)
	{
		frqtable[i] = j;
		j = mulscale30(j,1137589835);  //(pow(2,1/12)<<30) = 1137589835
	}
	for(i=0;i>=-14;i--) frqtable[i&255] = (frqtable[(i+12)&255]>>1);

	loadwaves("WAVES.KWV");

	timecount = notecnt = musicstatus = musicrepeat = 0;

	clearbuf(FP_OFF(stemp),sizeof(stemp)>>2,32768L);
	for(i=0;i<256;i++)
		for(j=0;j<16;j++)
		{
			qualookup[(j<<9)+i] = (((-i)*j+8)>>4);
			qualookup[(j<<9)+i+256] = (((256-i)*j+8)>>4);
		}
	for(i=0;i<(samplerate>>11);i++)
	{
		j = 1536 - (i<<10)/(samplerate>>11);
		fsin(&j);
		ramplookup[i] = ((16384-j)<<1);
	}

	for(i=0;i<256;i++)
	{
		j = i*90; fsin(&j);
		eff[0][i] = 65536+j/9;
		eff[1][i] = min(58386+((i*(65536-58386))/30),65536);
		eff[2][i] = max(69433+((i*(65536-69433))/30),65536);
		j = (i*2048)/120; fsin(&j);
		eff[3][i] = 65536+(j<<2);
		j = (i*2048)/30; fsin(&j);
		eff[4][i] = 65536+j;
		switch((i>>1)%3)
		{
			case 0: eff[5][i] = 65536; break;
			case 1: eff[5][i] = 65536*330/262; break;
			case 2: eff[5][i] = 65536*392/262; break;
		}
		eff[6][i] = min((i<<16)/120,65536);
		eff[7][i] = max(65536-(i<<16)/120,0);
	}

	switch(digistat)
	{
		case 1: case 2:
			if (sbirq < 8)
			{
				oldsbhandler = _dos_getvect(sbirq+0x8);           //Set new IRQ7 vector
				_disable(); _dos_setvect(sbirq+0x8, sbhandler); _enable();
				koutp(0x21,kinp(0x21) & ~(1<<(sbirq&7)));
			}
			else
			{
				oldsbhandler = _dos_getvect(sbirq+0x68);    //Set new SB IRQ vector
				_disable(); _dos_setvect(sbirq+0x68, sbhandler); _enable();
				koutp(0xa1,kinp(0xa1) & ~(1<<(sbirq&7)));
			}
			break;
	}

	musicoff();

	if (digistat != 255)
	{
		preparesndbuf();
		if (digistat != 13) preparesndbuf();

		if ((digistat == 1) || (digistat == 2))
		{
			if (sbdma < 4)
			{
				dmacheckport = (sbdma<<1)+1;
				dmachecksiz = (sndbufsiz<<(bytespersample+numspeakers-1))-1;

				koutp(0xa,sbdma+4);             //Set up DMA REGISTERS
				koutp(0xc,0);
				koutp(0xb,0x58+sbdma);          //&H58 - auto-init, &H48 - 1 block only
				koutp(sbdma<<1,sndoffs&255);
				koutp(sbdma<<1,(sndoffs>>8)&255);
				koutp(dmacheckport,dmachecksiz&255);
				koutp(dmacheckport,(dmachecksiz>>8)&255);
				koutp(dmapagenum[sbdma],((sndoffs>>16)&255));
				koutp(0xa,sbdma);
			}
			else
			{
				dmacheckport = ((sbdma&3)<<2)+0xc2;
				dmachecksiz = ((sndbufsiz<<(bytespersample+numspeakers-1))>>1)-1;

				koutp(0xd4,sbdma);               //Set up DMA REGISTERS
				koutp(0xd8,0);
				koutp(0xd6,0x58+(sbdma&3));      //&H58 - auto-init, &H48 - 1 block only
				koutp(dmacheckport-2,(sndoffs>>1)&255);
				koutp(dmacheckport-2,((sndoffs>>1)>>8)&255);
				koutp(dmacheckport,dmachecksiz&255);
				koutp(dmacheckport,(dmachecksiz>>8)&255);
				koutp(dmapagenum[sbdma],((sndoffs>>16)&255));
				koutp(0xd4,sbdma&3);
			}
		}

		switch(digistat)
		{
			case 1:
				sbout(0xd1);                                    //SB Speaker on

				if (sbver < 0x0400)
				{
					sbout(0x40);                            //SB Set speed
					sbout(256-(1000000/(samplerate<<(numspeakers-1))));
				}

				if (sbver < 0x0200)
				{
					sbout(0x14);                                 //SB 1-shot mode
					i = sndbufsiz-1;
					sbout(i&255);
					sbout((i>>8)&255);
				}
				else
				{
					if (sbver < 0x0400)
					{
							//Set length for auto-init mode
						i = (sndbufsiz<<(numspeakers+bytespersample-2))-1;
						sbout(0x48);
						sbout(i&255);
						sbout((i>>8)&255);
						if (numspeakers == 2)                   //SB set stereo
						{
							sbmixout(0L,0L);
							sbmixout(0xe,sbmixin(0xe)|2);
						}
						if ((samplerate<<(numspeakers-1)) <= 22050)
							sbout(0x1c);                         //Start SB interrupts!
						else
							sbout(0x90);                         //High Speed DMA
					}
					else
					{
						sbout(0x41);
						sbout((samplerate>>8)&255);
						sbout(samplerate&255);

						sbout(0xc6-((bytespersample-1)<<4));
						sbout(((bytespersample-1)<<4)+((numspeakers-1)<<5));
						i = (sndbufsiz<<(numspeakers-1))-1;
						sbout(i&255);
						sbout((i>>8)&255);
					}
				}
				break;
			case 2:
				koutp(0xf88,128);
				koutp(0xb8a,0);

				i = (1193180L>>(numspeakers-1)) / samplerate;
				koutp(0x138b,0x36); koutp(0x1388,i&255); koutp(0x1388,i>>8);
				i = (sndbufsiz<<(bytespersample+numspeakers-2));
				koutp(0x138b,0x74); koutp(0x1389,i&255); koutp(0x1389,i>>8);

				koutp(0x8389,0x3+((bytespersample-1)<<2)); //0x3=8bit/0x7=16bit
				koutp(0xb89,0xdf);
				koutp(0xb8b,0x8);
				koutp(0xf8a,0xd9+((2-numspeakers)<<5));    //0xd9=play/0xc9=record
				koutp(0xb8a,0xe1);
				break;
			case 13:
				samplecount = sndbufsiz;
				pcsndptr = (char *)sndoffs;
				bufferside = 0;
				pcsndbufsiz = sndbufsiz;
				pcrealmodeint = 0;

				samplediv = 1193280L / samplerate;

				j = 0;
				for(i=0;i<256;i++)
				{
						 //Scale (65536 normal)
					k = mulscale24(j-(samplediv<<7),160000) + (samplediv>>1);
					if (k < 0) k = 0;
					if (k > samplerate) k = samplerate;
					pcsndlookup[i] = (char)(k+1);
					j += samplediv;
				}

				oldtimerfreq = gettimerval();
				chainbackstart = oldtimerfreq/samplediv;
				chainbackcnt = chainbackstart;
				setuppctimerhandler(sndoffs+sndbufsiz,oldtimerfreq,0L,0L,0L,0L);

				_disable();
				oldpctimerhandler = _dos_getvect(0x8);
				installbikdmhandlers();
				koutp(0x43,0x34); koutp(0x40,samplediv&255); koutp(0x40,(samplediv>>8)&255);
				koutp(0x43,0x90);
				koutp(97,kinp(97)|3);
				_enable();
				break;
		}
	}
}

getsbset()
{
	char *sbset;
	long i;

	sbset = getenv("BLASTER");

	i = 0;
	while (sbset[i] > 0)
	{
		switch(sbset[i])
		{
			case 'A': case 'a':
				i++;
				sbport = 0;
				while (((sbset[i] >= 48) && (sbset[i] <= 57)) ||
						 ((sbset[i] >= 'A') && (sbset[i] <= 'F')) ||
						 ((sbset[i] >= 'a') && (sbset[i] <= 'f')))
				{
					sbport *= 16;
					if ((sbset[i] >= 48) && (sbset[i] <= 57)) sbport += sbset[i]-48;
					if ((sbset[i] >= 'A') && (sbset[i] <= 'F')) sbport += sbset[i]-55;
					if ((sbset[i] >= 'a') && (sbset[i] <= 'f')) sbport += sbset[i]-55-32;
					i++;
				}
				break;
			case 'I': case 'i':
				i++;
				sbirq = 0;
				while ((sbset[i] >= 48) && (sbset[i] <= 57))
				{
					sbirq *= 10;
					sbirq += sbset[i]-48;
					i++;
				}
				break;
			case 'D': case 'd':
				i++;
				sbdma8 = 0;
				while ((sbset[i] >= 48) && (sbset[i] <= 57))
				{
					sbdma8 *= 10;
					sbdma8 += sbset[i]-48;
					i++;
				}
				break;
			case 'H': case 'h':
				i++;
				sbdma16 = 0;
				while ((sbset[i] >= 48) && (sbset[i] <= 57))
				{
					sbdma16 *= 10;
					sbdma16 += sbset[i]-48;
					i++;
				}
				break;
			default:
				i++;
				break;
		}
	}
}

void __interrupt __far sbhandler()
{
	switch(digistat)
	{
		case 1:
			if (sbver < 0x0200)
			{
				sbout(0x14);                           //SB 1-shot mode
				sbout((sndbufsiz-1)&255);
				sbout(((sndbufsiz-1)>>8)&255);
				kinp(sbport+0xe);                      //Acknowledge SB
			}
			else
			{
				mixerval = sbmixin(0x82);
				if (mixerval&1) kinp(sbport+0xe);      //Acknowledge 8-bit DMA
				if (mixerval&2) kinp(sbport+0xf);      //Acknowledge 16-bit DMA
			}
			break;
		case 2:
			if ((kinp(0xb89)&8) > 0) koutp(0xb89,0);
			break;
	}
	if (sbirq >= 8) koutp(0xa0,0x20);
	koutp(0x20,0x20);
	_enable(); preparesndbuf();
}

uninitsb()
{
	if ((digistat == 0) && (musistat != 1))
		return;

	if (digistat != 255)
	{
		if ((digistat == 1) || (digistat == 2))  //Mask off DMA
		{
			if (sbdma < 4) koutp(0xa,sbdma+4); else koutp(0xd4,sbdma);
		}

		switch(digistat)
		{
			case 1:
				if (sbver >= 0x0400) sbout(0xda-(bytespersample-1));
				resetsb();
				sbout(0xd3);                           //Turn speaker off
				break;
			case 2:
				koutp(0xb8a,32);                       //Stop interrupts
				koutp(0xf8a,0x9);                      //DMA stop
				break;
			case 13:
				koutp(97,kinp(97)&252);
				koutp(0x43,0x34); koutp(0x40,0); koutp(0x40,0);
				koutp(0x43,0xbc);
				uninstallbikdmhandlers();
				break;
		}
	}

	if (snd != 0) free(snd), snd = 0;
	if (sndseg != 0) convdeallocate(sndseg), sndseg = 0;

	switch(digistat)
	{
		case 1: case 2:
			if (sbirq < 8)
			{
				koutp(0x21,kinp(0x21) | (1<<(sbirq&7)));
				_disable(); _dos_setvect(sbirq+0x8, oldsbhandler); _enable();
			}
			else
			{
				koutp(0xa1,kinp(0xa1) | (1<<(sbirq&7)));
				_disable(); _dos_setvect(sbirq+0x68, oldsbhandler); _enable();
			}
			break;
	}
}

startwave(long wavnum, long dafreq, long davolume1, long davolume2, long dafrqeff, long davoleff, long dapaneff)
{
	long i, j, chanum;

	if ((davolume1|davolume2) == 0) return;

	chanum = 0;
	for(i=NUMCHANNELS-1;i>0;i--)
		if (splc[i] > splc[chanum])
			chanum = i;

	splc[chanum] = 0;     //Disable channel temporarily for clean switch

	if (numspeakers == 1)
		calcvolookupmono(FP_OFF(volookup)+(chanum<<(9+2)),-(davolume1+davolume2)<<6,(davolume1+davolume2)>>1);
	else
		calcvolookupstereo(FP_OFF(volookup)+(chanum<<(9+2)),-(davolume1<<7),davolume1,-(davolume2<<7),davolume2);

	sinc[chanum] = dafreq;
	svol1[chanum] = davolume1;
	svol2[chanum] = davolume2;
	soff[chanum] = wavoffs[wavnum]+wavleng[wavnum];
	splc[chanum] = -(wavleng[wavnum]<<12);              //splc's modified last
	swavenum[chanum] = wavnum;
	frqeff[chanum] = dafrqeff; frqoff[chanum] = 0;
	voleff[chanum] = davoleff; voloff[chanum] = 0;
	paneff[chanum] = dapaneff; panoff[chanum] = 0;
	chanstat[chanum] = 0; sincoffs[chanum] = 0;
}

setears(long daposx, long daposy, long daxvect, long dayvect)
{
	globposx = daposx;
	globposy = daposy;
	globxvect = daxvect;
	globyvect = dayvect;
}

wsayfollow(char *dafilename, long dafreq, long davol, long *daxplc, long *dayplc, char followstat)
{
	char ch1, ch2, bad;
	long i, wavnum, chanum;

	if (digistat == 0) return;
	if (davol <= 0) return;

	for(wavnum=numwaves-1;wavnum>=0;wavnum--)
	{
		bad = 0;

		i = 0;
		while ((dafilename[i] > 0) && (i < 16))
		{
			ch1 = dafilename[i]; if ((ch1 >= 97) && (ch1 <= 123)) ch1 -= 32;
			ch2 = instname[wavnum][i]; if ((ch2 >= 97) && (ch2 <= 123)) ch2 -= 32;
			if (ch1 != ch2) {bad = 1; break;}
			i++;
		}
		if (bad != 0) continue;

		chanum = 0;
		for(i=NUMCHANNELS-1;i>0;i--) if (splc[i] > splc[chanum]) chanum = i;

		splc[chanum] = 0;     //Disable channel temporarily for clean switch

		if (followstat == 0)
		{
			xplc[chanum] = *daxplc;
			yplc[chanum] = *dayplc;
		}
		else
		{
			xplc[chanum] = ((long)daxplc);
			yplc[chanum] = ((long)dayplc);
		}
		vol[chanum] = davol;
		vdist[chanum] = 0;
		sinc[chanum] = (dafreq*11025)/samplerate;
		svol1[chanum] = davol;
		svol2[chanum] = davol;
		sincoffs[chanum] = 0;
		soff[chanum] = wavoffs[wavnum]+wavleng[wavnum];
		splc[chanum] = -(wavleng[wavnum]<<12);              //splc's modified last
		swavenum[chanum] = wavnum;
		chanstat[chanum] = followstat+1;
		frqeff[chanum] = 0; frqoff[chanum] = 0;
		voleff[chanum] = 0; voloff[chanum] = 0;
		paneff[chanum] = 0; panoff[chanum] = 0;
		return;
	}
}

getsndbufinfo(long *dasndoffsplc, long *dasndbufsiz)
{
	*dasndoffsplc = sndoffsplc;
	*dasndbufsiz = (sndbufsiz<<(bytespersample+numspeakers-2));
}

preparesndbuf()
{
	long i, j, k, voloffs1, voloffs2, *stempptr;
	long daswave, dasinc, dacnt;
	long ox, oy, x, y;
	char *sndptr, v1, v2;

	kdmintinprep++;
	if (kdminprep != 0) return;

	if ((digistat == 1) || (digistat == 2))
	{
		i = kinp(dmacheckport); i += (kinp(dmacheckport)<<8);
		if (i <= dmachecksiz)
		{
			i = ((i > 32) && (i <= (dmachecksiz>>1)+32));
			if ((sndoffsplc<(sndoffsplc^sndoffsxor)) == i) kdmintinprep++;
		}
	}

	kdminprep = 1;
	while (kdmintinprep > 0)
	{
		sndoffsplc ^= sndoffsxor;

		for (i=NUMCHANNELS-1;i>=0;i--)
			if ((splc[i] < 0) && (chanstat[i] > 0))
			{
				if (chanstat[i] == 1)
				{
					ox = xplc[i];
					oy = yplc[i];
				}
				else
				{
					stempptr = (long *)xplc[i]; ox = *stempptr;
					stempptr = (long *)yplc[i]; oy = *stempptr;
				}
				ox -= globposx; oy -= globposy;
				x = dmulscale28(oy,globxvect,-ox,globyvect);
				y = dmulscale28(ox,globxvect,oy,globyvect);

				if ((klabs(x) >= 32768) || (klabs(y) >= 32768))
					{ splc[i] = 0; continue; }

				j = vdist[i];
				vdist[i] = msqrtasm(x*x+y*y);
				if (j)
				{
					j = (sinc[i]<<10)/(min(max(vdist[i]-j,-768),768)+1024)-sinc[i];
					sincoffs[i] = ((sincoffs[i]*7+j)>>3);
				}

				voloffs1 = min((vol[i]<<22)/(((x+1536)*(x+1536)+y*y)+1),255);
				voloffs2 = min((vol[i]<<22)/(((x-1536)*(x-1536)+y*y)+1),255);

				if (numspeakers == 1)
					calcvolookupmono(FP_OFF(volookup)+(i<<(9+2)),-(voloffs1+voloffs2)<<6,(voloffs1+voloffs2)>>1);
				else
					calcvolookupstereo(FP_OFF(volookup)+(i<<(9+2)),-(voloffs1<<7),voloffs1,-(voloffs2<<7),voloffs2);
			}

		for(dacnt=0;dacnt<sndbufsiz;dacnt+=bytespertic)
		{
			if (musicstatus > 0)    //Gets here 120 times/second
			{
				while ((notecnt < numnotes) && (timecount >= nttime[notecnt]))
				{
					j = trinst[nttrack[notecnt]];
					k = mulscale24(frqtable[ntfreq[notecnt]],finetune[j]+748);

					if (ntvol1[notecnt] == 0)   //Note off
					{
						for(i=NUMCHANNELS-1;i>=0;i--)
							if (splc[i] < 0)
								if (swavenum[i] == j)
									if (sinc[i] == k)
										splc[i] = 0;
					}
					else                        //Note on
						startwave(j,k,ntvol1[notecnt],ntvol2[notecnt],ntfrqeff[notecnt],ntvoleff[notecnt],ntpaneff[notecnt]);

					notecnt++;
					if (notecnt >= numnotes)
						if (musicrepeat > 0)
							notecnt = 0, timecount = nttime[0];
				}
				timecount++;
			}

			for(i=NUMCHANNELS-1;i>=0;i--)
			{
				if (splc[i] >= 0) continue;

				dasinc = sinc[i]+sincoffs[i];

				if (frqeff[i] != 0)
				{
					dasinc = mulscale16(dasinc,eff[frqeff[i]-1][frqoff[i]]);
					frqoff[i]++; if (frqoff[i] >= 256) frqeff[i] = 0;
				}
				if ((voleff[i]) || (paneff[i]))
				{
					voloffs1 = svol1[i];
					voloffs2 = svol2[i];
					if (voleff[i])
					{
						voloffs1 = mulscale16(voloffs1,eff[voleff[i]-1][voloff[i]]);
						voloffs2 = mulscale16(voloffs2,eff[voleff[i]-1][voloff[i]]);
						voloff[i]++; if (voloff[i] >= 256) voleff[i] = 0;
					}

					if (numspeakers == 1)
						calcvolookupmono(FP_OFF(volookup)+(i<<(9+2)),-(voloffs1+voloffs2)<<6,(voloffs1+voloffs2)>>1);
					else
					{
						if (paneff[i])
						{
							voloffs1 = mulscale16(voloffs1,131072-eff[paneff[i]-1][panoff[i]]);
							voloffs2 = mulscale16(voloffs2,eff[paneff[i]-1][panoff[i]]);
							panoff[i]++; if (panoff[i] >= 256) paneff[i] = 0;
						}
						calcvolookupstereo(FP_OFF(volookup)+(i<<(9+2)),-(voloffs1<<7),voloffs1,-(voloffs2<<7),voloffs2);
					}
				}

				daswave = swavenum[i];
				voloffs1 = FP_OFF(volookup)+(i<<(9+2));

				kdmasm1 = repleng[daswave];
				kdmasm2 = wavoffs[daswave]+repstart[daswave]+repleng[daswave];
				kdmasm3 = (repleng[daswave]<<12); //repsplcoff
				kdmasm4 = soff[i];
				if (numspeakers == 1)
				{
					if (kdmqual == 0) splc[i] = monolocomb(0L,voloffs1,bytespertic,dasinc,splc[i],FP_OFF(stemp));
									 else splc[i] = monohicomb(0L,voloffs1,bytespertic,dasinc,splc[i],FP_OFF(stemp));
				}
				else
				{
					if (kdmqual == 0) splc[i] = stereolocomb(0L,voloffs1,bytespertic,dasinc,splc[i],FP_OFF(stemp));
									 else splc[i] = stereohicomb(0L,voloffs1,bytespertic,dasinc,splc[i],FP_OFF(stemp));
				}
				soff[i] = kdmasm4;

				if ((kdmqual == 0) || (splc[i] >= 0)) continue;
				if (numspeakers == 1)
				{
					if (kdmqual == 0) monolocomb(0L,voloffs1,samplerate>>11,dasinc,splc[i],FP_OFF(stemp)+(bytespertic<<2));
									 else monohicomb(0L,voloffs1,samplerate>>11,dasinc,splc[i],FP_OFF(stemp)+(bytespertic<<2));
				}
				else
				{
					if (kdmqual == 0) stereolocomb(0L,voloffs1,samplerate>>11,dasinc,splc[i],FP_OFF(stemp)+(bytespertic<<3));
									 else stereohicomb(0L,voloffs1,samplerate>>11,dasinc,splc[i],FP_OFF(stemp)+(bytespertic<<3));
				}
			}

			if (kdmqual)
			{
				if (numspeakers == 1)
				{
					for(i=(samplerate>>11)-1;i>=0;i--)
						stemp[i] += mulscale16(stemp[i+1024]-stemp[i],ramplookup[i]);
					j = bytespertic; k = (samplerate>>11);
					copybuf(&stemp[j],&stemp[1024],k);
					clearbuf(&stemp[j],k,32768);
				}
				else
				{
					for(i=(samplerate>>11)-1;i>=0;i--)
					{
						j = (i<<1);
						stemp[j+0] += mulscale16(stemp[j+1024]-stemp[j+0],ramplookup[i]);
						stemp[j+1] += mulscale16(stemp[j+1025]-stemp[j+1],ramplookup[i]);
					}
					j = (bytespertic<<1); k = ((samplerate>>11)<<1);
					copybuf(&stemp[j],&stemp[1024],k);
					clearbuf(&stemp[j],k,32768);
				}
			}

			if (numspeakers == 1)
			{
				if (bytespersample == 1)
				{
					if (digistat == 13) pcbound2char(bytespertic>>1,FP_OFF(stemp),sndoffsplc+dacnt);
										  else bound2char(bytespertic>>1,FP_OFF(stemp),sndoffsplc+dacnt);
				} else bound2short(bytespertic>>1,FP_OFF(stemp),sndoffsplc+(dacnt<<1));
			}
			else
			{
				if (bytespersample == 1) bound2char(bytespertic,FP_OFF(stemp),sndoffsplc+(dacnt<<1));
										 else bound2short(bytespertic,FP_OFF(stemp),sndoffsplc+(dacnt<<2));
			}
		}
		kdmintinprep--;
	}
	kdminprep = 0;
}

wsay(char *dafilename, long dafreq, long volume1, long volume2)
{
	unsigned char ch1, ch2;
	long i, j, bad;

	if (digistat == 0) return;

	i = numwaves-1;
	do
	{
		bad = 0;

		j = 0;
		while ((dafilename[j] > 0) && (j < 16))
		{
			ch1 = dafilename[j]; if ((ch1 >= 97) && (ch1 <= 123)) ch1 -= 32;
			ch2 = instname[i][j]; if ((ch2 >= 97) && (ch2 <= 123)) ch2 -= 32;
			if (ch1 != ch2) {bad = 1; break;}
			j++;
		}
		if (bad == 0)
		{
			startwave(i,(dafreq*11025)/samplerate,volume1,volume2,0L,0L,0L);
			return;
		}

		i--;
	} while (i >= 0);
}

loadwaves(char *wavename)
{
	long fil, i, j, dawaversionum;
	char filename[80];

	strcpy(filename,wavename);
	if (strstr(filename,".KWV") == 0) strcat(filename,".KWV");
	if ((fil = kopen4load(filename,0)) == -1)
		if (strcmp(filename,"WAVES.KWV") != 0)
		{
			strcpy(filename,"WAVES.KWV");
			fil = kopen4load(filename,0);
		}

	totsndbytes = 0;

	if (fil != -1)
	{
		if (strcmp(kwvname,filename) == 0) { kclose(fil); return; }
		strcpy(kwvname,filename);

		kread(fil,&dawaversionum,4);
		if (dawaversionum != 0) { kclose(fil); return; }

		kread(fil,&numwaves,4);
		for(i=0;i<numwaves;i++)
		{
			kread(fil,&instname[i][0],16);
			kread(fil,&wavleng[i],4);
			kread(fil,&repstart[i],4);
			kread(fil,&repleng[i],4);
			kread(fil,&finetune[i],4);
			wavoffs[i] = totsndbytes;
			totsndbytes += wavleng[i];
		}
	}
	else
	{
		dawaversionum = 0;
		numwaves = 0;
	}

	for(i=numwaves;i<MAXWAVES;i++)
	{
		for(j=0;j<16;j++) instname[i][j] = 0;
		wavoffs[i] = totsndbytes;
		wavleng[i] = 0L;
		repstart[i] = 0L;
		repleng[i] = 0L;
		finetune[i] = 0L;
	}

	if (snd == 0)
	{
		if ((snd = (char *)malloc(totsndbytes+2)) == 0)
			{ printf("Not enough memory for digital music!\n"); exit(0); }
	}
	for(i=0;i<MAXWAVES;i++) wavoffs[i] += FP_OFF(snd);
	if (fil != -1)
	{
		kread(fil,snd,totsndbytes);
		kclose(fil);
	}
	snd[totsndbytes] = snd[totsndbytes+1] = 128;
}

loadsong(char *filename)
{
	long i, fil;

	if (musistat != 1) return(0);
	musicoff();
	filename = strupr(filename);
	if (strstr(filename,".KDM") == 0) strcat(filename,".KDM");
	if ((fil = kopen4load(filename,0)) == -1)
	{
		printf("I cannot load %s.\n",filename);
		uninitsb();
		return(-1);
	}
	kread(fil,&kdmversionum,4);
	if (kdmversionum != 0) return(-2);
	kread(fil,&numnotes,4);
	kread(fil,&numtracks,4);
	kread(fil,trinst,numtracks);
	kread(fil,trquant,numtracks);
	kread(fil,trvol1,numtracks);
	kread(fil,trvol2,numtracks);
	kread(fil,nttime,numnotes<<2);
	kread(fil,nttrack,numnotes);
	kread(fil,ntfreq,numnotes);
	kread(fil,ntvol1,numnotes);
	kread(fil,ntvol2,numnotes);
	kread(fil,ntfrqeff,numnotes);
	kread(fil,ntvoleff,numnotes);
	kread(fil,ntpaneff,numnotes);
	kclose(fil);
	return(0);
}

musicon()
{
	if (musistat != 1)
		return;

	notecnt = 0;
	timecount = nttime[notecnt];
	musicrepeat = 1;
	musicstatus = 1;
}

musicoff()
{
	long i;

	musicstatus = 0;
	for(i=0;i<NUMCHANNELS;i++)
		splc[i] = 0;
	musicrepeat = 0;
	timecount = 0;
	notecnt = 0;
}

kdmconvalloc32 (long size)
{
	union REGS r;

	r.x.eax = 0x0100;           //DPMI allocate DOS memory
	r.x.ebx = ((size+15)>>4);   //Number of paragraphs requested
	int386(0x31,&r,&r);

	if (r.x.cflag != 0)         //Failed
		return ((long)0);
	return ((long)((r.x.eax&0xffff)<<4));   //Returns full 32-bit offset
}

installbikdmhandlers()
{
	union REGS r;
	struct SREGS sr;
	long lowp;
	void far *fh;

		//Get old protected mode handler
	r.x.eax = 0x3500+kdmvect;   /* DOS get vector (INT 0Ch) */
	sr.ds = sr.es = 0;
	int386x(0x21,&r,&r,&sr);
	kdmpsel = (unsigned short)sr.es;
	kdmpoff = r.x.ebx;

		//Get old real mode handler
	r.x.eax = 0x0200;   /* DPMI get real mode vector */
	r.h.bl = kdmvect;
	int386(0x31,&r,&r);
	kdmrseg = (unsigned short)r.x.ecx;
	kdmroff = (unsigned short)r.x.edx;

		//Allocate memory in low memory to store real mode handler
	if ((lowp = kdmconvalloc32(KDMCODEBYTES)) == 0)
	{
		printf("Can't allocate conventional memory.\n");
		exit;
	}
	memcpy((void *)lowp,(void *)pcrealbuffer,KDMCODEBYTES);

		//Set new protected mode handler
	r.x.eax = 0x2500+kdmvect;   /* DOS set vector (INT 0Ch) */
	fh = (void far *)pctimerhandler;
	r.x.edx = FP_OFF(fh);
	sr.ds = FP_SEG(fh);      //DS:EDX == &handler
	sr.es = 0;
	int386x(0x21,&r,&r,&sr);

		//Set new real mode handler (must be after setting protected mode)
	r.x.eax = 0x0201;
	r.h.bl = kdmvect;              //CX:DX == real mode &handler
	r.x.ecx = ((lowp>>4)&0xffff);  //D32realseg
	r.x.edx = 0L;                  //D32realoff
	int386(0x31,&r,&r);
}

uninstallbikdmhandlers()
{
	union REGS r;
	struct SREGS sr;

		//restore old protected mode handler
	r.x.eax = 0x2500+kdmvect;   /* DOS set vector (INT 0Ch) */
	r.x.edx = kdmpoff;
	sr.ds = kdmpsel;    /* DS:EDX == &handler */
	sr.es = 0;
	int386x(0x21,&r,&r,&sr);

		//restore old real mode handler
	r.x.eax = 0x0201;   /* DPMI set real mode vector */
	r.h.bl = kdmvect;
	r.x.ecx = (unsigned long)kdmrseg;     //CX:DX == real mode &handler
	r.x.edx = (unsigned long)kdmroff;
	int386(0x31,&r,&r);
}

repo_name: orochi/nblood

license: mit


code:
game.exe : game.obj engine.obj cache1d.obj multi.obj a.obj kdmeng.obj k.obj
	wlink f game f engine f cache1d f multi f a f kdmeng f k
build.exe : build.obj engine.obj cache1d.obj a.obj bstub.obj
	wlink f build f engine f cache1d f a f bstub

game.obj : game.c build.h names.h pragmas.h
	wcc386 game /5r /s /orb
build.obj : build.c build.h pragmas.h
	wcc386 build /5r /s /orb
bstub.obj : bstub.c build.h
	wcc386 bstub /5r /s /orb
cache1d.obj : cache1d.c pragmas.h
	wcc386 cache1d /5r /s /orb
engine.obj : engine.c build.h pragmas.h ves2.h
	wcc386 engine /5r /s /orb
a.obj : a.asm
	wasm a.asm
multi.obj : multi.c
	wcc386 multi /4r /s /or
kdmeng.obj : kdmeng.c makefile.
	wcc386 kdmeng /4r /s /or /d2
k.obj : k.asm makefile.
	wasm k.asm

path: extras/rrsrc/enginera/MAKEFILE


code:
/* -----------------------------------------------------------------------
	  This source code is the property of Ken Silverman, East Greenwich,
	  Rhode Island, and contains confidential and trade secret information.
	  It may not be transferred from the custody or control of Ken Silverman
	  except as authorized in writing by Ken Silverman.  Neither this item
	  nor the information it contains may be used, transferred, reproduced,
	  published, or disclosed, in whole or in part, and directly or
	  indirectly, except as expressly authorized by Ken Silverman,
	  pursuant to written agreement.  Copyright (c) 1995 Ken Silverman.
	  All rights reserved.  E-mail Address:  kjs@lems.brown.edu
	----------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <process.h>
#include <stdarg.h>
#include "pragmas.h"

#define MAXPLAYERS 16
#define BAKSIZ 16384
#define SIMULATEERRORS 0
#define SHOWSENDPACKETS 0
#define SHOWGETPACKETS 0
#define PRINTERRORS 0

#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])

static long incnt[MAXPLAYERS], outcntplc[MAXPLAYERS], outcntend[MAXPLAYERS];
static char errorgotnum[MAXPLAYERS];
static char errorfixnum[MAXPLAYERS];
static char errorresendnum[MAXPLAYERS];
#if (PRINTERRORS)
	static char lasterrorgotnum[MAXPLAYERS];
#endif

long crctable[256];

static char lastpacket[576], inlastpacket = 0;
static short lastpacketfrom, lastpacketleng;

extern long totalclock;  //MUST EXTERN 1 ANNOYING VARIABLE FROM GAME
static long timeoutcount = 60, resendagaincount = 4, lastsendtime[MAXPLAYERS];

static short bakpacketptr[MAXPLAYERS][256], bakpacketlen[MAXPLAYERS][256];
static char bakpacketbuf[BAKSIZ];
static long bakpacketplc = 0;

short myconnectindex, numplayers;
short connecthead, connectpoint2[MAXPLAYERS];
char syncstate = 0;

extern int _argc;
extern char **_argv;

#define MAXPACKETSIZE 2048
typedef struct
{
	short intnum;                //communication between Game and the driver
	short command;               //1-send, 2-get
	short other;                 //dest for send, set by get (-1 = no packet)
	short numbytes;
	short myconnectindex;
	short numplayers;
	short gametype;              //gametype: 1-serial,2-modem,3-net
	short filler;
	char buffer[MAXPACKETSIZE];
} gcomtype;
static gcomtype *gcom;

static union REGS regs;

initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
{
	long i;
	char *parm, delims[4] = {'\\','-','/','\0'};

	initcrc();
	for(i=0;i<MAXPLAYERS;i++)
	{
		incnt[i] = 0L;
		outcntplc[i] = 0L;
		outcntend[i] = 0L;
		bakpacketlen[i][255] = -1;
	}

	for(i=_argc-1;i>0;i--)
		if ((parm = strtok(_argv[i],&delims[0])) != NULL)
			if (!stricmp("net",parm)) break;
	if (i == 0)
	{
		numplayers = 1; myconnectindex = 0;
		connecthead = 0; connectpoint2[0] = -1;
		return;
	}
	gcom = (gcomtype *)atol(_argv[i+1]);

	numplayers = gcom->numplayers;
	myconnectindex = gcom->myconnectindex-1;
#if (SIMULATEERRORS != 0)
	srand(myconnectindex*24572457+345356);
#endif
	connecthead = 0;
	for(i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
	connectpoint2[numplayers-1] = -1;

	for(i=0;i<numplayers;i++) lastsendtime[i] = totalclock;
}

initcrc()
{
	long i, j, k, a;

	for(j=0;j<256;j++)      //Calculate CRC table
	{
		k = (j<<8); a = 0;
		for(i=7;i>=0;i--)
		{
			if (((k^a)&0x8000) > 0)
				a = ((a<<1)&65535) ^ 0x1021;   //0x1021 = genpoly
			else
				a = ((a<<1)&65535);
			k = ((k<<1)&65535);
		}
		crctable[j] = (a&65535);
	}
}

setpackettimeout(long datimeoutcount, long daresendagaincount)
{
	long i;

	timeoutcount = datimeoutcount;
	resendagaincount = daresendagaincount;
	for(i=connecthead;i>=0;i=connectpoint2[i]) lastsendtime[i] = totalclock;
}

getcrc(char *buffer, short bufleng)
{
	long i, j;

	j = 0;
	for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
	return(j&65535);
}

uninitmultiplayers()
{
}

sendlogon()
{
}

sendlogoff()
{
	long i;
	char tempbuf[2];

	tempbuf[0] = 255;
	tempbuf[1] = myconnectindex;
	for(i=connecthead;i>=0;i=connectpoint2[i])
		if (i != myconnectindex)
			sendpacket(i,tempbuf,2L);
}
getoutputcirclesize()
{
	return(0);
}

setsocket(short newsocket)
{
}

sendpacket(long other, char *bufptr, long messleng)
{
	long i, j, k, l,cnt;
	unsigned short dacrc;

	if (numplayers < 2) return;

	i = 0;
	if (bakpacketlen[other][(outcntend[other]-1)&255] == messleng)
	{
		j = bakpacketptr[other][(outcntend[other]-1)&255];
		for(i=messleng-1;i>=0;i--)
			if (bakpacketbuf[(i+j)&(BAKSIZ-1)] != bufptr[i]) break;
	}
	bakpacketlen[other][outcntend[other]&255] = messleng;

	if (i < 0)   //Point to last packet to save space on bakpacketbuf
		bakpacketptr[other][outcntend[other]&255] = j;
	else
	{
		bakpacketptr[other][outcntend[other]&255] = bakpacketplc;
		for(i=0;i<messleng;i++)
			bakpacketbuf[(bakpacketplc+i)&(BAKSIZ-1)] = bufptr[i];
		bakpacketplc = ((bakpacketplc+messleng)&(BAKSIZ-1));
	}
	outcntend[other]++;

	lastsendtime[other] = totalclock;
	dosendpackets(other);
}

dosendpackets(long other)
{
	long i, j, k, messleng;
	unsigned short dacrc;

	if (outcntplc[other] == outcntend[other]) return;

#if (PRINTERRORS)
	if (errorgotnum[other] > lasterrorgotnum[other])
	{
		lasterrorgotnum[other]++;
		printf(" MeWant %ld",incnt[other]&255);
	}
#endif

	if (outcntplc[other]+1 == outcntend[other])
	{     //Send 1 sub-packet
		k = 0;
		gcom->buffer[k++] = (outcntplc[other]&255);
		gcom->buffer[k++] = (errorgotnum[other]&7)+((errorresendnum[other]&7)<<3);
		gcom->buffer[k++] = (incnt[other]&255);

		j = bakpacketptr[other][outcntplc[other]&255];
		messleng = bakpacketlen[other][outcntplc[other]&255];
		for(i=0;i<messleng;i++)
			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
		outcntplc[other]++;
	}
	else
	{     //Send 2 sub-packets
		k = 0;
		gcom->buffer[k++] = (outcntplc[other]&255);
		gcom->buffer[k++] = (errorgotnum[other]&7)+((errorresendnum[other]&7)<<3)+128;
		gcom->buffer[k++] = (incnt[other]&255);

			//First half-packet
		j = bakpacketptr[other][outcntplc[other]&255];
		messleng = bakpacketlen[other][outcntplc[other]&255];
		gcom->buffer[k++] = (char)(messleng&255);
		gcom->buffer[k++] = (char)(messleng>>8);
		for(i=0;i<messleng;i++)
			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
		outcntplc[other]++;

			//Second half-packet
		j = bakpacketptr[other][outcntplc[other]&255];
		messleng = bakpacketlen[other][outcntplc[other]&255];
		for(i=0;i<messleng;i++)
			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
		outcntplc[other]++;

	}

	dacrc = getcrc(gcom->buffer,k);
	gcom->buffer[k++] = (dacrc&255);
	gcom->buffer[k++] = (dacrc>>8);

	gcom->other = other+1;
	gcom->numbytes = k;

#if (SHOWSENDPACKETS)
	printf("Send(%ld): ",gcom->other);
	for(i=0;i<gcom->numbytes;i++) printf("%2x ",gcom->buffer[i]);
	printf("\n");
#endif

#if (SIMULATEERRORS != 0)
	if (!(rand()&SIMULATEERRORS)) gcom->buffer[rand()%gcom->numbytes] = (rand()&255);
	if (rand()&SIMULATEERRORS)
#endif
		{ gcom->command = 1; int386(gcom->intnum,&regs,&regs); }
}

short getpacket (short *other, char *bufptr)
{
	long i, j, messleng;
	unsigned short dacrc;

	if (numplayers < 2) return(0);

	for(i=connecthead;i>=0;i=connectpoint2[i])
		if (i != myconnectindex)
		{
			if (totalclock < lastsendtime[i]) lastsendtime[i] = totalclock;
			if (totalclock > lastsendtime[i]+timeoutcount)
			{
#if (PRINTERRORS)
					printf(" TimeOut!");
#endif
					errorgotnum[i] = errorfixnum[i]+1;

					if ((outcntplc[i] == outcntend[i]) && (outcntplc[i] > 0))
						{ outcntplc[i]--; lastsendtime[i] = totalclock; }
					else
						lastsendtime[i] += resendagaincount;
					dosendpackets(i);
				//}
			}
		}

	if (inlastpacket != 0)
	{
			//2ND half of good double-packet
		inlastpacket = 0;
		*other = lastpacketfrom;
		memcpy(bufptr,lastpacket,lastpacketleng);
		return(lastpacketleng);
	}

	gcom->command = 2; int386(gcom->intnum,&regs,&regs);

#if (SHOWGETPACKETS)
	if (gcom->other != -1)
	{
		printf(" Get(%ld): ",gcom->other);
		for(i=0;i<gcom->numbytes;i++) printf("%2x ",gcom->buffer[i]);
		printf("\n");
	}
#endif

	if (gcom->other < 0) return(0);
	*other = gcom->other-1;

	messleng = gcom->numbytes;

	dacrc = ((unsigned short)gcom->buffer[messleng-2]);
	dacrc += (((unsigned short)gcom->buffer[messleng-1])<<8);
	if (dacrc != getcrc(gcom->buffer,messleng-2))        //CRC check
	{
#if (PRINTERRORS)
		printf("\n%ld CRC",gcom->buffer[0]);
#endif
		errorgotnum[*other] = errorfixnum[*other]+1;
		return(0);
	}

	while ((errorfixnum[*other]&7) != ((gcom->buffer[1]>>3)&7))
		errorfixnum[*other]++;

	if ((gcom->buffer[1]&7) != (errorresendnum[*other]&7))
	{
		errorresendnum[*other]++;
		outcntplc[*other] = (outcntend[*other]&0xffffff00)+gcom->buffer[2];
		if (outcntplc[*other] > outcntend[*other]) outcntplc[*other] -= 256;
	}

	if (gcom->buffer[0] != (incnt[*other]&255))   //CNT check
	{
		if (((incnt[*other]-gcom->buffer[0])&255) > 32)
		{
			errorgotnum[*other] = errorfixnum[*other]+1;
#if (PRINTERRORS)
			printf("\n%ld CNT",gcom->buffer[0]);
#endif
		}
#if (PRINTERRORS)
		else
		{
			if (!(gcom->buffer[1]&128))           //single else double packet
				printf("\n%ld cnt",gcom->buffer[0]);
			else
			{
				if (((gcom->buffer[0]+1)&255) == (incnt[*other]&255))
				{
								 //GOOD! Take second half of double packet
#if (PRINTERRORS)
					printf("\n%ld-%ld .� ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
#endif
					messleng = ((long)gcom->buffer[3]) + (((long)gcom->buffer[4])<<8);
					lastpacketleng = gcom->numbytes-7-messleng;
					memcpy(bufptr,&gcom->buffer[messleng+5],lastpacketleng);
					incnt[*other]++;
					return(lastpacketleng);
				}
				else
					printf("\n%ld-%ld cnt ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
			}
		}
#endif
		return(0);
	}

		//PACKET WAS GOOD!
	if ((gcom->buffer[1]&128) == 0)           //Single packet
	{
#if (PRINTERRORS)
		printf("\n%ld �  ",gcom->buffer[0]);
#endif

		messleng = gcom->numbytes-5;

		memcpy(bufptr,&gcom->buffer[3],messleng);

		incnt[*other]++;
		return(messleng);
	}

														 //Double packet
#if (PRINTERRORS)
	printf("\n%ld-%ld �� ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
#endif

	messleng = ((long)gcom->buffer[3]) + (((long)gcom->buffer[4])<<8);
	lastpacketleng = gcom->numbytes-7-messleng;
	inlastpacket = 1; lastpacketfrom = *other;

	memcpy(bufptr,&gcom->buffer[5],messleng);
	memcpy(lastpacket,&gcom->buffer[messleng+5],lastpacketleng);

	incnt[*other] += 2;
	return(messleng);
}

flushpackets()
{
	/*long i;

	if (numplayers < 2) return;

	do
	{
		gcom->command = 2; int386(gcom->intnum,&regs,&regs);
	} while (gcom->other >= 0);

	for(i=connecthead;i>=0;i=connectpoint2[i])
	{
		incnt[i] = 0L;
		outcntplc[i] = 0L;
		outcntend[i] = 0L;
		errorgotnum[i] = 0;
		errorfixnum[i] = 0;
		errorresendnum[i] = 0;
		lastsendtime[i] = totalclock;
	}*/
}

genericmultifunction(long other, char *bufptr, long messleng, long command)
{
	if (numplayers < 2) return;

	gcom->command = command;
	gcom->numbytes = min(messleng,MAXPACKETSIZE);
	copybuf(bufptr,gcom->buffer,(gcom->numbytes+3)>>2);
	gcom->other = other+1;
	int386(gcom->intnum,&regs,&regs);
}


code:
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <i86.h>

#define COMBUFSIZ 16384
#define COMCODEBYTES 384
#define COMCODEOFFS 14
#define NETCODEBYTES 384
#define MAXPLAYERS 16
#define ESC1 0x83
#define ESC2 0x8f
#define NETBACKPACKETS 4
#define MAXIPXSIZ 546

#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])

char syncstate = 0, hangup = 1;
static char multioption = 0, comrateoption = 0;

	//COM & NET variables
short numplayers = 0, myconnectindex = 0;
short connecthead, connectpoint2[MAXPLAYERS];
char syncbuf[MAXIPXSIZ];
long syncbufleng, outbufindex[128], outcnt;
long myconnectnum, otherconnectnum, mypriority;
long crctable[256];

	//COM ONLY variables
long comnum, comvect, comspeed, comtemp, comi, comescape, comreset;
static void interrupt far comhandler(void);
static unsigned short orig_pm_sel, orig_rm_seg, orig_rm_off;
static unsigned long orig_pm_off;
volatile unsigned char *inbuf, *outbuf, *comerror, *incnt, *comtype;
volatile unsigned char *comresend;
volatile short *inbufplc, *inbufend, *outbufplc, *outbufend, *comport;
static char rmbuffer[COMCODEBYTES] =        //See realcom.asm
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x50,0x53,0x66,0x51,0x52,0x2e,
	0x8b,0x16,0x08,0x00,0x83,0xc2,0x02,0xec,0x24,0x07,
	0x8a,0xe0,0x80,0xfc,0x02,0x74,0x08,0x80,0xfc,0x04,
	0x74,0x62,0xe9,0x89,0x00,0x2e,0x8b,0x16,0x08,0x00,
	0x2e,0x8a,0x0e,0x0a,0x00,0x80,0xe9,0x01,0x78,0x7a,
	0x2e,0x80,0x3e,0x0c,0x00,0x01,0x7c,0x10,0x74,0x04,
	0xb0,0x83,0xeb,0x02,0xb0,0x8f,0xee,0x2e,0xfe,0x0e,
	0x0c,0x00,0xeb,0xe3,0x2e,0x80,0x3e,0x0b,0x00,0x01,
	0x7c,0x12,0x74,0x04,0xb0,0x83,0xeb,0x04,0x2e,0xa0,
	0x0d,0x00,0xee,0x2e,0xfe,0x0e,0x0b,0x00,0xeb,0xc9,
	0x2e,0x8b,0x1e,0x04,0x00,0x2e,0x3b,0x1e,0x06,0x00,
	0x74,0x3c,0x2e,0x8a,0x87,0x80,0x41,0xee,0x43,0x81,
	0xe3,0xff,0x3f,0x2e,0x89,0x1e,0x04,0x00,0xeb,0xab,
	0x2e,0x8b,0x16,0x08,0x00,0xec,0x2e,0x8b,0x1e,0x02,
	0x00,0x2e,0x88,0x87,0x80,0x01,0x43,0x81,0xe3,0xff,
	0x3f,0x2e,0x89,0x1e,0x02,0x00,0x2e,0x80,0x3e,0x0a,
	0x00,0x10,0x75,0x08,0x83,0xc2,0x05,0xec,0xa8,0x01,
	0x75,0xd6,0xf6,0xc4,0x01,0x0f,0x84,0x56,0xff,0xb0,
	0x20,0xe6,0x20,0x5a,0x66,0x59,0x5b,0x58,0xcf,
};

	//NET ONLY variables
short socket = 0x4949;
char compaddr[MAXPLAYERS][12], mycompaddr[12];
char netincnt[MAXPLAYERS], netoutcnt[MAXPLAYERS];
char getmess[MAXIPXSIZ];
char omessout[MAXPLAYERS][NETBACKPACKETS][MAXIPXSIZ];
short omessleng[MAXPLAYERS][NETBACKPACKETS];
short omessconnectindex[MAXPLAYERS][NETBACKPACKETS];
short omessnum[MAXPLAYERS];
long connectnum[MAXPLAYERS], rmoffset32, rmsegment16, neti;
volatile char *ecbget, *ecbput, *ipxin, *ipxout, *messin, *messout;
volatile char *tempinbuf, *tempoutbuf, *rmnethandler, *netinbuf;
volatile short *netinbufplc, *netinbufend;
static char rmnetbuffer[NETCODEBYTES] =
{
	0xfb,0x2e,0x8a,0x26,0x62,0x00,0x2e,0xa0,0x63,0x00,
	0x83,0xe8,0x1e,0x2e,0x8b,0x1e,0xe2,0x06,0x2e,0x88,
	0x87,0xe4,0x06,0x43,0x81,0xe3,0xff,0x3f,0x2e,0x88,
	0xa7,0xe4,0x06,0x43,0x81,0xe3,0xff,0x3f,0x33,0xf6,
	0x2e,0x8a,0x8c,0xa0,0x00,0x46,0x2e,0x88,0x8f,0xe4,
	0x06,0x43,0x81,0xe3,0xff,0x3f,0x3b,0xf0,0x72,0xec,
	0x2e,0x89,0x1e,0xe2,0x06,0xbb,0x04,0x00,0x8c,0xc8,
	0x8e,0xc0,0xbe,0x00,0x00,0xcd,0x7a,0xcb,
};
static long my7a = 0;

#pragma aux koutp =\
	"out dx, al",\
	parm [edx][eax]\

#pragma aux kinp =\
	"in al, dx",\
	parm [edx]\

convalloc32 (long size)
{
	union REGS r;

	r.x.eax = 0x0100;           //DPMI allocate DOS memory
	r.x.ebx = ((size+15)>>4);   //Number of paragraphs requested
	int386(0x31,&r,&r);

	if (r.x.cflag != 0) return ((long)0);   //Failed
	return ((long)((r.x.eax&0xffff)<<4));   //Returns full 32-bit offset
}

#pragma aux fixregistersaftersimulate =\
	"cld",\
	"push ds",\
	"pop es",\

static struct rminfo
{
	long EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX;
	short flags, ES, DS, FS, GS, IP, CS, SP, SS;
} RMI;

simulateint(char intnum, long daeax, long daebx, long daecx, long daedx, long daesi, long daedi)
{
	union REGS regs;
	struct SREGS sregs;

	memset(&RMI,0,sizeof(RMI));    // Set up real-mode call structure
	memset(&sregs,0,sizeof(sregs));

	RMI.EAX = daeax;
	RMI.EBX = daebx;
	RMI.ECX = daecx;
	RMI.EDX = daedx;
	RMI.ESI = daesi-rmoffset32;
	RMI.EDI = daedi-rmoffset32;
	RMI.DS = rmsegment16;
	RMI.ES = rmsegment16;

	regs.w.ax = 0x0300;            // Use DMPI call 300h to issue the DOS interrupt
	regs.h.bl = intnum;
	regs.h.bh = 0;
	regs.w.cx = 0;
	sregs.es = FP_SEG(&RMI);
	regs.x.edi = FP_OFF(&RMI);
	int386x(0x31,&regs,&regs,&sregs);

	fixregistersaftersimulate();

	return(RMI.EAX);
}

initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
{
	long i;

	multioption = damultioption;
	comrateoption = dacomrateoption;

	connecthead = 0;
	for(i=MAXPLAYERS-1;i>=0;i--)
		connectpoint2[i] = -1, connectnum[i] = 0x7fffffff;

	mypriority = dapriority;

	initcrc();

	if ((multioption >= 1) && (multioption <= 4))
	{
		comnum = multioption;
		switch(dacomrateoption&15)
		{
			case 0: comspeed = 2400; break;
			case 1: comspeed = 4800; break;
			case 2: comspeed = 9600; break;
			case 3: comspeed = 14400; break;
			case 4: comspeed = 19200; break;
			case 5: comspeed = 28800; break;
		}
		comon();
	}
	if (multioption >= 5)
	{
		if ((i = neton()) != 0)
		{
			if (i == -1) printf("IPX driver not found\n");
			if (i == -2) printf("Socket could not be opened\n");
			exit(0);
		}
	}
	numplayers = 1;
}

uninitmultiplayers()
{
	if (numplayers > 0)
	{
		if ((multioption >= 1) && (multioption <= 4)) comoff();
		if (multioption >= 5) netoff();  //Uninstall before timer
	}
}

neton()
{
	long i, j;

	if ((simulateint(0x2f,(long)0x7a00,0L,0L,0L,0L,0L)&255) != 255) return(-1);
	if (*(long *)(0x7a<<2) == 0)
	{
		printf("Faking int 0x7a to call IPX entry at: %4x:%4x\n",RMI.ES,RMI.EDI&65535);
		my7a = convalloc32(16L);
		*(short *)((0x7a<<2)+0) = (my7a&15);
		*(short *)((0x7a<<2)+2) = (my7a>>4);

		*(char *)(my7a+0) = 0x2e;               //call far ptr [L1]
		*(char *)(my7a+1) = 0x9a;
		*(long *)(my7a+2) = 7L;
		*(char *)(my7a+6) = 0xcf;               //iret
		*(short *)(my7a+7) = (RMI.EDI&65535);   //L1: ipxoff
		*(short *)(my7a+9) = RMI.ES;            //    ipxseg
	}

		//Special stuff for WATCOM C
	if ((rmoffset32 = convalloc32(1380L+NETCODEBYTES+COMBUFSIZ)) == 0)
		{ printf("Can't allocate memory for IPX\n"); exit; }
	rmsegment16 = (rmoffset32>>4);

	i = rmoffset32;
	ecbget = (char *)i; i += 48;
	ecbput = (char *)i; i += 48;
	ipxin = (char *)i; i += 32;
	ipxout = (char *)i; i += 32;
	messin = (char *)i; i += 560;
	messout = (char *)i; i += 560;
	tempinbuf = (char *)i; i += 16;
	tempoutbuf = (char *)i; i += 80;
	rmnethandler = (char *)i; i += NETCODEBYTES;
	netinbufplc = (short *)i; i += 2;
	netinbufend = (short *)i; i += 2;
	netinbuf = (char *)i; i += COMBUFSIZ;
	memcpy((void *)rmnethandler,(void *)rmnetbuffer,NETCODEBYTES);

	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket
	if ((simulateint(0x7a,(long)0xff,0L,0L,(long)socket,0L,0L)&255) != 0) return(-2); //Opensocket

	simulateint(0x7a,0L,9L,0L,0L,(long)tempoutbuf,0L);    //Getinternetworkaddress
	memcpy((void *)&mycompaddr[0],(void *)&tempoutbuf[0],10);
	mycompaddr[10] = (socket&255);
	mycompaddr[11] = (socket>>8);
	myconnectnum = ((long)tempoutbuf[6])+(((long)tempoutbuf[7])<<8)+(((long)(tempoutbuf[8]^tempoutbuf[9]))<<16)+(((long)mypriority)<<24);

	netinitconnection(myconnectnum,mycompaddr);

	ecbget[8] = 1; ecbput[8] = 0;
	*netinbufplc = 0; *netinbufend = 0;

	for(i=MAXPLAYERS-1;i>=0;i--) netincnt[i] = 0, netoutcnt[i] = 0;

	for(i=0;i<MAXPLAYERS;i++)
	{
		omessnum[i] = 0;
		for(j=NETBACKPACKETS-1;j>=0;j--)
		{
			omessleng[i][j] = 0;
			omessconnectindex[i][j] = 0;
		}
	}

		//Netlisten
	for(i=0;i<30;i++) ipxin[i] = 0;
	for(i=0;i<48;i++) ecbget[i] = 0;
	ecbget[4] = (char)(((long)rmnethandler-rmoffset32)&255), ecbget[5] = (char)(((long)rmnethandler-rmoffset32)>>8);
	ecbget[6] = (char)(rmsegment16&255), ecbget[7] = (char)(rmsegment16>>8);
	ecbget[10] = (socket&255), ecbget[11] = (socket>>8);
	ecbget[34] = 2, ecbget[35] = 0;
	ecbget[36] = (char)(((long)ipxin-rmoffset32)&255), ecbget[37] = (char)(((long)ipxin-rmoffset32)>>8);
	ecbget[38] = (char)(rmsegment16&255), ecbget[39] = (char)(rmsegment16>>8);
	ecbget[40] = 30, ecbget[41] = 0;
	ecbget[42] = (char)(((long)messin-rmoffset32)&255), ecbget[43] = (char)(((long)messin-rmoffset32)>>8);
	ecbget[44] = (char)(rmsegment16&255), ecbget[45] = (char)(rmsegment16>>8);
	ecbget[46] = (MAXIPXSIZ&255), ecbget[47] = (MAXIPXSIZ>>8);
	simulateint(0x7a,0L,(long)0x4,0L,0L,(long)ecbget,0L);               //Receivepacket

	return(0);
}

comon()
{
	long divisor, cnt;
	short *ptr;

	if ((comnum < 1) || (comnum > 4)) return(-1);
	//comvect = 0xb+(comnum&1);
	comvect = ((comrateoption>>4)+0x8+2);
	installbicomhandlers();

	*incnt = 0; outcnt = 0;
	*inbufplc = 0; *inbufend = 0; *outbufplc = 0; *outbufend = 0;

	ptr = (short *)(0x400L+(long)((comnum-1)<<1));
	*comport = *ptr;
	if (*comport == 0)
	{
		switch(comnum)
		{
			case 1: *comport = 0x3f8; break;
			case 2: *comport = 0x2f8; break;
			case 3: *comport = 0x3e8; break;
			case 4: *comport = 0x2e8; break;
		}
		if ((inp((*comport)+5)&0x60) != 0x60) { *comport = 0; return(-1); }
	}
	if ((comspeed <= 0) || (comspeed > 115200)) return(-1);

	  // Baud-Setting,?,?,Parity O/E,Parity Off/On, Stop-1/2,Bits-5/6/7/8
	  // 0x0b is odd parity,1 stop bit, 8 bits

	_disable();
	koutp((*comport)+3,0x80);                  //enable latch registers
	divisor = 115200 / comspeed;
	koutp((*comport)+0,divisor&255);           //# = 115200 / bps
	koutp((*comport)+1,divisor>>8);
	koutp((*comport)+3,0x03);                  //0x03 = n,8,1

	koutp((*comport)+2,0x87);   //check for a 16550   0=1,64=4,128=8,192=14
	if ((kinp((*comport)+2)&0xf8) == 0xc0)
	{
		*comtype = 16;
	}
	else
	{
		*comtype = 1;
		koutp((*comport)+2,0);
	}
	cnt = *comtype;                        //Clear any junk already in FIFO
	while (((kinp((*comport)+5)&0x1) > 0) && (cnt > 0))
		{ kinp(*comport); cnt--; }

	koutp((*comport)+4,0x0b);             //setup for interrupts (modem control)
	koutp((*comport)+1,0);                //com interrupt disable
	koutp(0x21,kinp(0x21)&(255-(1<<(comvect&7))));          //Unmask vector
	kinp((*comport)+6);
	kinp((*comport)+5);
	kinp((*comport)+0);
	kinp((*comport)+2);
	koutp((*comport)+1,0x03);             //com interrupt enable
	koutp(0x20,0x20);

	comescape = 0; comreset = 0;
	*comerror = 0; *comresend = 0;
	_enable();

	syncbufleng = 0;

	return(0);
}

netoff()
{
	if (my7a) *(long *)(0x7a<<2) = 0L;
	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);               //Closesocket
}

comoff()
{
	long i;

	i = 1048576;
	while ((*outbufplc != *outbufend) && (i >= 0))
	{
		startcom();
		i--;
	}

	_disable();
	koutp(0x21,kinp(0x21)|(1<<(comvect&7)));          //Mask vector
	if (hangup != 0)
	{
		koutp((*comport)+1,0);
		koutp((*comport)+4,0);
	}
	_enable();
	uninstallbicomhandlers();
}

netsend (short otherconnectindex, short messleng)
{
	long i;

	i = 32767;
	while ((ecbput[8] != 0) && (i > 0)) i--;
	for(i=0;i<30;i++) ipxout[i] = 0;
	for(i=0;i<48;i++) ecbput[i] = 0;
	ipxout[5] = 4;
	if (otherconnectindex < 0)
	{
		memcpy((void *)&ipxout[6],(void *)&compaddr[0][0],4);
		ipxout[10] = 0xff, ipxout[11] = 0xff, ipxout[12] = 0xff;
		ipxout[13] = 0xff, ipxout[14] = 0xff, ipxout[15] = 0xff;
		ipxout[16] = (socket&255), ipxout[17] = (socket>>8);
	}
	else
	{
		memcpy((void *)&ipxout[6],(void *)&compaddr[otherconnectindex][0],12);
	}

	ecbput[10] = (socket&255), ecbput[11] = (socket>>8);
	if (otherconnectindex < 0)
	{
		ecbput[28] = 0xff, ecbput[29] = 0xff, ecbput[30] = 0xff;
		ecbput[31] = 0xff, ecbput[32] = 0xff, ecbput[33] = 0xff;
	}
	else
	{
		memcpy((void *)&ecbput[28],(void *)&compaddr[otherconnectindex][4],6);
	}

	ecbput[34] = 2, ecbput[35] = 0;
	ecbput[36] = (char)(((long)ipxout-rmoffset32)&255), ecbput[37] = (char)(((long)ipxout-rmoffset32)>>8);
	ecbput[38] = (char)(rmsegment16&255), ecbput[39] = (char)(rmsegment16>>8);
	ecbput[40] = 30, ecbput[41] = 0;
	ecbput[42] = (char)(((long)messout-rmoffset32)&255), ecbput[43] = (char)(((long)messout-rmoffset32)>>8);
	ecbput[44] = (char)(rmsegment16&255), ecbput[45] = (char)(rmsegment16>>8);
	ecbput[46] = (char)(messleng&255), ecbput[47] = (char)(messleng>>8);
	simulateint(0x7a,0L,(long)0x3,0L,0L,(long)ecbput,0L);               //Sendpacket
}

comsend(char ch)
{
	if (ch == ESC1)
	{
		outbuf[*outbufend] = ESC1; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
		ch = 128;
	}
	else if (ch == ESC2)
	{
		outbuf[*outbufend] = ESC1; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
		ch = 129;
	}
	outbuf[*outbufend] = ch; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
}

startcom()
{
	if ((kinp((*comport)+5)&0x40) == 0) return;

	if (*comresend != 0)
	{
		if (*comresend == 2) koutp(*comport,ESC1);
		if (*comresend == 1) koutp(*comport,ESC2);
		*comresend = (*comresend) - 1;
	}
	else if (*comerror != 0)
	{
		if (*comerror == 2) koutp(*comport,ESC1);
		if (*comerror == 1) koutp(*comport,*incnt);
		*comerror = (*comerror) - 1;
	}
	else if (*outbufplc != *outbufend)
	{
		koutp(*comport,(long)outbuf[*outbufplc]);
		*outbufplc = (((*outbufplc)+1)&(COMBUFSIZ-1));
	}
}

void interrupt far comhandler(void)
{
	do
	{
		comtemp = (kinp((*comport)+2)&7);
		if (comtemp == 2)
		{
			for(comi=(*comtype);comi>0;comi--)
			{
				if (*comresend != 0)
				{
					if (*comresend == 2) koutp(*comport,ESC1);
					if (*comresend == 1) koutp(*comport,ESC2);
					*comresend = (*comresend) - 1;
					continue;
				}
				if (*comerror != 0)
				{
					if (*comerror == 2) koutp(*comport,ESC1);
					if (*comerror == 1) koutp(*comport,*incnt);
					*comerror = (*comerror) - 1;
					continue;
				}
				if (*outbufplc != *outbufend)
				{
					koutp(*comport,(long)outbuf[*outbufplc]);
					*outbufplc = (((*outbufplc)+1)&(COMBUFSIZ-1));
					continue;
				}
				break;
			}
		}
		else if (comtemp == 4)
		{
			do
			{
				//comtemp = (rand()&255);
				//if (comtemp == 17)
				//   inbuf[*inbufend] = 17;
				//else
					inbuf[*inbufend] = (char)kinp(*comport);

				//if (comtemp != 11) *inbufend = (((*inbufend)+1)&(COMBUFSIZ-1));
				//if (comtemp == 24)
				//{
				//   inbuf[*inbufend] = 17;
					*inbufend = (((*inbufend)+1)&(COMBUFSIZ-1));
				//}
				//comtemp = 4;

			} while ((*comtype == 16) && ((kinp((*comport)+5)&1) > 0));
		}
	}
	while ((comtemp&1) == 0);
	koutp(0x20,0x20);
}

netinitconnection (long newconnectnum, char *newcompaddr)
{
	long i, j, k, newindex, templong;
	char tempchar;

		//Check to see if connection number already initialized
	for(i=0;i<MAXPLAYERS;i++)
		if (connectnum[i] == newconnectnum) return(-1);

		//Find blank place to put new connection number
	newindex = 0;
	while (connectnum[newindex] != 0x7fffffff)
	{
		newindex++;
		if (newindex >= MAXPLAYERS) return(-1);  //Out of space! (more than 16 players)
	}

		//Insert connection number on connection number list
	numplayers++;
	connectnum[newindex] = newconnectnum;

		//Getinternetworkaddress
	memcpy((void *)&compaddr[newindex][0],(void *)newcompaddr,10);
	compaddr[newindex][10] = (socket&255);
	compaddr[newindex][11] = (socket>>8);

		//Sort connection numbers
	for(i=1;i<MAXPLAYERS;i++)
		for(j=0;j<i;j++)
			if (connectnum[i] < connectnum[j])
			{
				templong = connectnum[i], connectnum[i] = connectnum[j], connectnum[j] = templong;
				for(k=0;k<12;k++) tempchar = compaddr[i][k], compaddr[i][k] = compaddr[j][k], compaddr[j][k] = tempchar;
			}

		//Rebuild linked list, MAKING SURE that the linked list goes through
		//   the players in the same order on all computers!
	connecthead = 0;
	for(i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
	connectpoint2[numplayers-1] = -1;

	for(i=0;i<numplayers;i++)
		if (connectnum[i] == myconnectnum) myconnectindex = i;

	return(1);
}

netuninitconnection(short goneindex)
{
	long i, j, k;

	connectnum[goneindex] = 0x7fffffff; numplayers--;

	j = 0;
	for(i=0;i<MAXPLAYERS;i++)
		if (connectnum[i] != 0x7fffffff)
		{
			if (j == 0) connecthead = i; else connectpoint2[k] = i;
			k = i; j++;
		}
	connectpoint2[k] = -1;
}

sendpacket (short otherconnectindex, char *bufptr, short messleng)
{
	long i, j, k, l;

	if (multioption <= 0) return;
	if (multioption < 5)
	{
			//Allow initial incnt/outcnt syncing
		if ((bufptr[0] == 253) || (bufptr[0] == 254)) outcnt = 0;

		outbufindex[outcnt] = *outbufend;

		comsend(((messleng&1)<<7)+outcnt);
		for(i=0;i<messleng;i++) comsend(bufptr[i]);
		if ((comrateoption&15) > 0)
		{
			i = getcrc(bufptr,messleng);
			updatecrc16(i,((messleng&1)<<7)+outcnt);
			comsend(i&255); comsend(i>>8);
		}
		outbuf[*outbufend] = ESC2, *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));  //Not raw

		startcom();
		outcnt = ((outcnt+1)&127);
	}
	else
	{
		i = 262144;       //Wait for last packet to be sent
		while ((i > 0) && (ecbput[8] != 0)) i--;

		messout[0] = myconnectindex; j = 1;

		if ((bufptr[0] >= 200) && (bufptr[0] <= 255))
		{
				//Allow initial incnt/outcnt syncing
			for(i=0;i<MAXPLAYERS;i++) netoutcnt[i] = 0;
			messout[j++] = 0xfe;
			for(i=0;i<messleng;i++) messout[j++] = bufptr[i];
			netsend(otherconnectindex,j);
			return;
		}

			//Copy new packet into omess fifo
		if (otherconnectindex < 0)
		{
			for(k=connecthead;k>=0;k=connectpoint2[k])
				if (k != myconnectindex)
				{
					omessconnectindex[k][omessnum[k]] = -1;
					omessleng[k][omessnum[k]] = messleng;
					for(i=0;i<messleng;i++) omessout[k][omessnum[k]][i] = bufptr[i];
				}
		}
		else
		{
			omessconnectindex[otherconnectindex][omessnum[otherconnectindex]] = otherconnectindex;
			omessleng[otherconnectindex][omessnum[otherconnectindex]] = messleng;
			for(i=0;i<messleng;i++) omessout[otherconnectindex][omessnum[otherconnectindex]][i] = bufptr[i];
		}

			//Put last 4 packets into 1 big packet
		for(l=0;l<NETBACKPACKETS;l++)
		{
				//k = omess index
			k = ((omessnum[otherconnectindex]-l)&(NETBACKPACKETS-1));

			if (omessconnectindex[otherconnectindex][k] < 0)
			{
				messout[j++] = 255;
				for(i=connecthead;i>=0;i=connectpoint2[i])
					if (i != myconnectindex)
						messout[j++] = ((netoutcnt[i]-l)&255);
			}
			else
			{
				messout[j++] = otherconnectindex;
				messout[j++] = ((netoutcnt[otherconnectindex]-l)&255);
			}
			messout[j++] = (omessleng[otherconnectindex][k]&255);
			messout[j++] = ((omessleng[otherconnectindex][k]>>8)&255);
			for(i=0;i<omessleng[otherconnectindex][k];i++)
				messout[j++] = omessout[otherconnectindex][k][i];
		}

			//SEND!!!
		netsend(otherconnectindex,j);

			//Increment outcnt and omessnum counters
		if (otherconnectindex < 0)
		{
			for(i=connecthead;i>=0;i=connectpoint2[i])
				if (i != myconnectindex)
				{
					netoutcnt[i]++;
					omessnum[i] = ((omessnum[i]+1)&(NETBACKPACKETS-1));
				}
		}
		else
		{
			netoutcnt[otherconnectindex]++;
			omessnum[otherconnectindex] = ((omessnum[otherconnectindex]+1)&(NETBACKPACKETS-1));
		}
	}
}

short getpacket (short *otherconnectindex, char *bufptr)
{
	char toindex, bad, totbad;
	short i, j, k, messleng, submessleng;

	if (multioption <= 0) return(0);
	if (multioption < 5)
	{
		*otherconnectindex = (myconnectindex^1);

		bad = 0;
		while (*inbufplc != *inbufend)
		{
			i = (short)inbuf[*inbufplc], *inbufplc = (((*inbufplc)+1)&(COMBUFSIZ-1));

			if (i != ESC2)
			{
				if (i == ESC1) { comescape++; continue; }
				if (comescape != 0)
				{
					comescape--;
					if ((i < 128) && (*comresend == 0) && (((i-outcnt)&127) > 4))
					{
						*comresend = 2;
						*outbufplc = outbufindex[i];
						startcom();
						continue;
					}
					if (syncbufleng < MAXIPXSIZ)
					{
						if (i == 128) { syncbuf[syncbufleng++] = ESC1; continue; }
						if (i == 129) { syncbuf[syncbufleng++] = ESC2; continue; }
					}
				}
				if (syncbufleng < MAXIPXSIZ) syncbuf[syncbufleng++] = i;
				continue;
			}

			if (comescape != 0)
			{
				comescape = 0; comreset = 0; *comerror = 0;
				syncbufleng = 0;
				continue;
			}

			messleng = syncbufleng-3+(((comrateoption&15)==0)<<1);
			if ((syncbuf[0]&127) != *incnt)
			{
				bad |= 1;      //Packetcnt error
				if ((*incnt == 1) && (syncbuf[1] == 254))  //Prevent 2 Masters!
					myconnectindex = (myconnectnum<otherconnectnum);
			}
			if (((syncbuf[0]&128)>>7) != (messleng&1)) bad |= 2;   //messleng error
			for(i=0;i<messleng;i++) bufptr[i] = syncbuf[i+1];
			if ((comrateoption&15) > 0)
			{
				i = getcrc(bufptr,messleng);
				updatecrc16(i,syncbuf[0]);
				if (((unsigned short)i) != ((long)syncbuf[syncbufleng-2])+((long)syncbuf[syncbufleng-1]<<8))
					bad |= 2;   //CRC error
			}

			syncbufleng = 0;
			if (bad != 0)
			{
					//Don't send reset again if outbufplc is not before incnt!
				if ((bad == 1) && ((((syncbuf[0]&127)-(*incnt))&127) >= 124))
				{
					bad = 0;
					continue;
				}

				bad = 0;
				if (comreset != 0) comreset--;
				if (((*comerror)|comreset) == 0)
				{
					*comerror = 2; comreset = 2;
					startcom();
				}
				continue;
			}

			*incnt = (((*incnt)+1)&127);
			if ((messleng > 0) && (bufptr[0] >= 200))  //200-255 are messages for engine's use only
				processreservedmessage(messleng,bufptr);

			comescape = 0; comreset = 0; *comerror = 0;
			return(messleng);
		}
		return(0);
	}
	else
	{
		if (*netinbufplc == *netinbufend) return(0);

		messleng = (short)netinbuf[*netinbufplc] + (((short)netinbuf[((*netinbufplc)+1)&(COMBUFSIZ-1)])<<8);
		for(i=0;i<messleng;i++)
			getmess[i] = netinbuf[((*netinbufplc)+i+2)&(COMBUFSIZ-1)];

		k = 0; *otherconnectindex = getmess[k++];

		for(totbad=0;totbad<NETBACKPACKETS;totbad++)   //Number of sub-packets per packet
		{
			toindex = getmess[k++];
			if (toindex == 0xfe)
			{
				netincnt[*otherconnectindex] = 0;  // (>= 200) && ( <= 254)
				submessleng = messleng-2;          // Submessleng not necessary
			}
			else
			{
				if (toindex == 0xff)
				{
					for(i=connecthead;i>=0;i=connectpoint2[i])
						if (i != *otherconnectindex)
						{
							if (i == myconnectindex) j = getmess[k];
							k++;
						}
				}
				else
					j = getmess[k++];

				if (j != netincnt[*otherconnectindex])
				{
					submessleng = (short)getmess[k]+(((short)getmess[k+1])<<8);
					k += submessleng+2;
					continue;
				}
				netincnt[*otherconnectindex]++;

				submessleng = (short)getmess[k]+(((short)getmess[k+1])<<8); k += 2;
			}
			for(i=0;i<submessleng;i++) bufptr[i] = getmess[k++];

			if (totbad == 0)
			{
					//Increment inbufplc only if first sub-message is read
				*netinbufplc = (((*netinbufplc)+messleng+2)&(COMBUFSIZ-1));
				if ((submessleng > 0) && (bufptr[0] >= 200)) //200-255 are messages for engine's use only
				{
					if (bufptr[0] >= 253)
					{
						processreservedmessage(submessleng,bufptr);
						if ((bufptr[0] == 253) || (bufptr[0] == 254)) return(0);
					}
					return(submessleng);
				}
			}
			if (*otherconnectindex == myconnectindex) return(0);
			return(submessleng);   //Got good packet
		}

		syncstate++;   //DON'T WANT TO GET HERE!!!
			//Increment inbufplc to make it not continuously screw up!
		*netinbufplc = (((*netinbufplc)+messleng+2)&(COMBUFSIZ-1));
	}
	return(0);
}

initcrc()
{
	long i, j, k, a;

	for(j=0;j<256;j++)      //Calculate CRC table
	{
		k = (j<<8); a = 0;
		for(i=7;i>=0;i--)
		{
			if (((k^a)&0x8000) > 0)
				a = ((a<<1)&65535) ^ 0x1021;   //0x1021 = genpoly
			else
				a = ((a<<1)&65535);
			k = ((k<<1)&65535);
		}
		crctable[j] = (a&65535);
	}
}

getcrc(char *buffer, short bufleng)
{
	long i, j;

	j = 0;
	for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
	return(j&65535);
}

installbicomhandlers()
{
	union REGS r;
	struct SREGS sr;
	long lowp;
	void far *fh;

		//Get old protected mode handler
	r.x.eax = 0x3500+comvect;   /* DOS get vector (INT 0Ch) */
	sr.ds = sr.es = 0;
	int386x(0x21,&r,&r,&sr);
	orig_pm_sel = (unsigned short)sr.es;
	orig_pm_off = r.x.ebx;

		//Get old real mode handler
	r.x.eax = 0x0200;   /* DPMI get real mode vector */
	r.h.bl = comvect;
	int386(0x31,&r,&r);
	orig_rm_seg = (unsigned short)r.x.ecx;
	orig_rm_off = (unsigned short)r.x.edx;

		//Allocate memory in low memory to store real mode handler
	if ((lowp = convalloc32(COMCODEBYTES+(COMBUFSIZ<<1))) == 0)
		{ printf("Can't allocate conventional memory.\n"); exit; }

	inbufplc = (short *)(lowp+0);
	inbufend = (short *)(lowp+2);
	outbufplc = (short *)(lowp+4);
	outbufend = (short *)(lowp+6);
	comport = (short *)(lowp+8);
	comtype = (char *)(lowp+10);
	comerror = (char *)(lowp+11);
	comresend = (char *)(lowp+12);
	incnt = (char *)(lowp+13);
	inbuf = (char *)(lowp+COMCODEBYTES);
	outbuf = (char *)(lowp+COMCODEBYTES+COMBUFSIZ);

	memcpy((void *)lowp,(void *)rmbuffer,COMCODEBYTES);

		//Set new protected mode handler
	r.x.eax = 0x2500+comvect;   /* DOS set vector (INT 0Ch) */
	fh = (void far *)comhandler;
	r.x.edx = FP_OFF(fh);
	sr.ds = FP_SEG(fh);      //DS:EDX == &handler
	sr.es = 0;
	int386x(0x21,&r,&r,&sr);

		//Set new real mode handler (must be after setting protected mode)
	r.x.eax = 0x0201;
	r.h.bl = comvect;              //CX:DX == real mode &handler
	r.x.ecx = ((lowp>>4)&0xffff);  //D32realseg
	r.x.edx = COMCODEOFFS;         //D32realoff
	int386(0x31,&r,&r);
}

uninstallbicomhandlers()
{
	union REGS r;
	struct SREGS sr;

		//restore old protected mode handler
	r.x.eax = 0x2500+comvect;   /* DOS set vector (INT 0Ch) */
	r.x.edx = orig_pm_off;
	sr.ds = orig_pm_sel;    /* DS:EDX == &handler */
	sr.es = 0;
	int386x(0x21,&r,&r,&sr);

		//restore old real mode handler
	r.x.eax = 0x0201;   /* DPMI set real mode vector */
	r.h.bl = comvect;
	r.x.ecx = (unsigned long)orig_rm_seg;     //CX:DX == real mode &handler
	r.x.edx = (unsigned long)orig_rm_off;
	int386(0x31,&r,&r);
}

processreservedmessage(short tempbufleng, char *datempbuf)
{
	long i, j, k, daotherconnectnum, templong;

	switch(datempbuf[0])
	{
		//[253] (login, if myconnectnum's lowest, then respond with packet type 254)
		case 253:
			if (multioption < 5)
			{
				otherconnectnum = ((long)datempbuf[1])+(((long)datempbuf[2])<<8)+(((long)datempbuf[3])<<16)+(((long)datempbuf[4])<<24);

				datempbuf[0] = 254;
				sendpacket(-1,datempbuf,1);

				myconnectindex = 0;
				connecthead = 0; connectpoint2[0] = 1; connectpoint2[1] = -1;
				numplayers = 2;
			}
			else if (multioption >= 5)
			{
				daotherconnectnum = ((long)datempbuf[1])+((long)(datempbuf[2]<<8))+((long)(datempbuf[3]<<16))+((long)(datempbuf[4]<<24));
				if (daotherconnectnum != myconnectnum)
				{
					netinitconnection(daotherconnectnum,&datempbuf[5]);

					if ((myconnectindex == connecthead) || ((connectnum[connecthead] == daotherconnectnum) && (myconnectindex == connectpoint2[connecthead])))
					{
						datempbuf[0] = 254;
						j = 1;
						for(i=0;i<MAXPLAYERS;i++)
							if ((connectnum[i] != 0x7fffffff) && (connectnum[i] != daotherconnectnum))
							{
								datempbuf[j++] = (connectnum[i]&255);
								datempbuf[j++] = ((connectnum[i]>>8)&255);
								datempbuf[j++] = ((connectnum[i]>>16)&255);
								datempbuf[j++] = ((connectnum[i]>>24)&255);

								for(k=0;k<10;k++)
									datempbuf[j++] = compaddr[i][k];
							}

							//While this doesn't have to be a broadcast, sending
							//this info again makes good error correction
						sendpacket(-1,datempbuf,j);

						for(i=0;i<MAXPLAYERS;i++)
							if (connectnum[i] == daotherconnectnum)
							{
								sendpacket((short)i,datempbuf,j);
								break;
							}
					}
				}
			}
			break;
		case 254:  //[254][connectnum][connectnum]...(Packet type 253 response)
			if (multioption < 5)
			{
				myconnectindex = 1;
				connecthead = 0; connectpoint2[0] = 1; connectpoint2[1] = -1;
				numplayers = 2;
			}
			else if (multioption >= 5)
			{
				j = 1;
				while (j < tempbufleng)
				{
					templong = ((long)datempbuf[j])+((long)(datempbuf[j+1]<<8))+((long)(datempbuf[j+2]<<16))+((long)(datempbuf[j+3]<<24));
					netinitconnection(templong,&datempbuf[j+4]);
					j += 14;
				}
			}
			break;
		case 255:
			if (multioption >= 5)
				netuninitconnection(datempbuf[1]);
			break;
	}
}

sendlogon()
{
	long i;
	char tempbuf[16];

	if (multioption <= 0)
		return;

	tempbuf[0] = 253;
	if (multioption < 5)
	{
		tempbuf[1] = kinp(0x40);
		tempbuf[2] = kinp(0x40);
		tempbuf[3] = kinp(0x40);
		tempbuf[4] = mypriority;
		myconnectnum = ((long)tempbuf[1])+(((long)tempbuf[2])<<8)+(((long)tempbuf[3])<<16)+(((long)mypriority)<<24);
		sendpacket(-1,tempbuf,5);
	}
	else
	{
		tempbuf[1] = (myconnectnum&255);
		tempbuf[2] = ((myconnectnum>>8)&255);
		tempbuf[3] = ((myconnectnum>>16)&255);
		tempbuf[4] = ((myconnectnum>>24)&255);
		for(i=0;i<10;i++)
			tempbuf[i+5] = mycompaddr[i];

		sendpacket(-1,tempbuf,15);
	}
}

sendlogoff()
{
	char tempbuf[16];
	long i;

	if ((numplayers <= 1) || (multioption <= 0)) return;

	tempbuf[0] = 255;
	if (multioption < 5)
	{
		sendpacket(-1,tempbuf,1);
	}
	else
	{
		tempbuf[1] = myconnectindex;
		for(i=connecthead;i>=0;i=connectpoint2[i])
			if (i != myconnectindex)
				sendpacket(i,tempbuf,2);
	}
}

getoutputcirclesize()
{
	if ((multioption >= 1) && (multioption <= 4))
	{
		startcom();
		return(((*outbufend)-(*outbufplc)+COMBUFSIZ)&(COMBUFSIZ-1));
	}
	return(0);
}

setsocket(short newsocket)
{
	long i;

	if (multioption < 5)
	{
		socket = newsocket;
		return(0);
	}

	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket

	socket = newsocket;

	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket
	if ((simulateint(0x7a,(long)0xff,0L,0L,(long)socket,0L,0L)&255) != 0) return(-2); //Opensocket
	mycompaddr[10] = (socket&255);
	mycompaddr[11] = (socket>>8);
	ecbget[10] = (socket&255);
	ecbget[11] = (socket>>8);
	for(i=0;i<MAXPLAYERS;i++)
	{
		compaddr[i][10] = (socket&255);
		compaddr[i][11] = (socket>>8);
	}
	return(0);
}


code:
//Be careful when changing this file - it is parsed by Editart and Build.
#define SWITCH1ON 15
#define SLIME 34
#define BACKGROUND 37
#define KENPICTURE 48
#define BUILDDISK 49
#define SWITCH2ON 66
#define SWITCH2OFF 69
#define ALPHABET 73
#define NO 74
#define DEMOSIGN 75
#define COIN 76
#define COINSTACK 77
#define GIFTBOX 78
#define DIAMONDS 79
#define EVILALGRAVE 83
#define STATUSBAR 87
#define DAYSKY 89
#define WATERFOUNTAIN 90
#define USEWATERFOUNTAIN 91
#define NIGHTSKY 93
#define BULLET 98
#define BOMB 100
#define CANNON 101
#define GUNONBOTTOM 102
#define BOMBEMITTER 103
#define EXPLOSION 105
#define SPLASH 106
#define BROWNMONSTER 110
#define SKELETON 113
#define AL 114
#define EVILAL 115
#define PLAYER 120
#define SWITCH3OFF 146
#define SWITCH3ON 147
#define AIRPLANE 148
#define SPIRAL 149
#define COMPASS 150
#define FOOTPRINT 156
#define STATUSBARFILL8 160
#define STATUSBARFILL4 161
#define BOUNCYMAT 162
#define MIRROR 165
#define FLOORMIRROR 166
#define GRABBER 167
#define GRABCANNON 168
#define MISSILE 169
#define LAUNCHER 171
#define MIRRORLABEL 4000