#include "stdafx.h"



// fm tables
uint32_t CDiskEncoding::fminit = 0;
uint32_t *CDiskEncoding::fmcode = nullptr;
uint32_t *CDiskEncoding::fmdecode = nullptr;

// mfm tables
uint32_t CDiskEncoding::mfminit = 0;
uint32_t CDiskEncoding::mfmcodebit = 0;
uint32_t *CDiskEncoding::mfmcode = nullptr;
uint32_t *CDiskEncoding::mfmdecode = nullptr;

// primary and secondary gcr tables
int CDiskEncoding::gcrinit = gcridNone;
uint32_t *CDiskEncoding::gcrcode = nullptr;
uint32_t *CDiskEncoding::gcrdecode = nullptr;
int CDiskEncoding::gcrinit_s = gcridNone;
uint32_t *CDiskEncoding::gcrcode_s = nullptr;
uint32_t *CDiskEncoding::gcrdecode_s = nullptr;

// gcr apple header tables
int CDiskEncoding::gcrahinit = 0;
uint32_t *CDiskEncoding::gcrahcode = nullptr;
uint32_t *CDiskEncoding::gcrahdecode = nullptr;

// gcr apple 5 bit tables
int CDiskEncoding::gcra5init = 0;
uint32_t *CDiskEncoding::gcra5code = nullptr;
uint32_t *CDiskEncoding::gcra5decode = nullptr;

// gcr apple 6 bit tables
int CDiskEncoding::gcra6init = 0;
uint32_t *CDiskEncoding::gcra6code = nullptr;
uint32_t *CDiskEncoding::gcra6decode = nullptr;

// gcr cbm vorpal 6 bit tables
int CDiskEncoding::gcrvorpalinit = 0;
uint32_t *CDiskEncoding::gcrvorpalcode = nullptr;
uint32_t *CDiskEncoding::gcrvorpaldecode = nullptr;

// gcr cbm vorpal 2, 5 bit tables
int CDiskEncoding::gcrvorpal2init = 0;
uint32_t *CDiskEncoding::gcrvorpal2code = nullptr;
uint32_t *CDiskEncoding::gcrvorpal2decode = nullptr;

// gcr cbm vmax 6 bit tables
int CDiskEncoding::gcrvmaxinit = vmaxidNone;
uint32_t *CDiskEncoding::gcrvmaxcode = nullptr;
uint32_t *CDiskEncoding::gcrvmaxdecode = nullptr;

// gcr cbm teque 4 bit tables
int CDiskEncoding::gcr4bitinit = 0;
uint32_t *CDiskEncoding::gcr4bitcode = nullptr;
uint32_t *CDiskEncoding::gcr4bitdecode = nullptr;

// GCR table used by CBM DOS
const uint32_t CDiskEncoding::gcr_cbm[] = {
	0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17,
	0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15
};

// 4 bit GCR table used by CBM Big Five
const uint32_t CDiskEncoding::gcr_bigfive[] = {
	0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x12, 0x13,
	0x15, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1d, 0x1e
};

// 5 bit GCR table used by Apple DOS
const uint32_t CDiskEncoding::gcr_apple5[] = {
	0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
	0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
	0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
	0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff
};

// 6 bit GCR table used by Apple DOS
const uint32_t CDiskEncoding::gcr_apple6[] = {
	0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
	0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
	0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
	0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
	0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
	0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
	0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
	0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

// 6 bit GCR table used by CBM Vorpal
const uint32_t CDiskEncoding::gcr_vorpal[] = {
	0x49, 0x56, 0x4b, 0x5a, 0x99, 0xaa, 0x9b, 0xad,
	0x4e, 0x5d, 0x53, 0x65, 0x9e, 0xb2, 0xa6, 0xb5,
	0x69, 0x76, 0x6b, 0x7a, 0xb9, 0xce, 0xbb, 0xd3,
	0x6e, 0x92, 0x73, 0x95, 0xc9, 0xd6, 0xcb, 0xda,
	0x4a, 0x59, 0x4d, 0x5b, 0x9a, 0xab, 0x9d, 0xae,
	0x52, 0x5e, 0x55, 0x66, 0xa5, 0xb3, 0xa9, 0xb6,
	0x6a, 0x79, 0x6d, 0x7b, 0xba, 0xd2, 0xbd, 0xd5,
	0x72, 0x93, 0x75, 0x96, 0xca, 0xd9, 0xcd, 0xdb
};

// 5 bit GCR tables used by CBM Vorpal 2, 2 bits cleared in alternate values for 3 or 4 consecutive 1 bits; 0 is an illegal entry
const uint32_t CDiskEncoding::gcr_vorpal2[] = {
	0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x12, 0x13, 0x15, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1d, 0x1e,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x06
};

// 6 bit GCR table used by CBM V-MAX! for normal encoding, alternate values are not used
const uint32_t CDiskEncoding::gcr_vmax[] = {
	0xf7, 0xf6, 0xf4, 0xf5, 0xf3, 0xf2, 0xef, 0xee,
	0xec, 0xed, 0xea, 0xeb, 0xe9, 0xe7, 0xe6, 0xe4,
	0xe5, 0xde, 0xdc, 0xdd, 0xd9, 0xdb, 0xd3, 0xd7,
	0xdf, 0xcf, 0xce, 0xcc, 0xcd, 0xcb, 0xca, 0xc9,
	0xbe, 0xbc, 0xb2, 0xbd, 0xb9, 0xb4, 0xbb, 0xba,
	0xb6, 0xb7, 0xb5, 0xaf, 0xad, 0xae, 0xac, 0xab,
	0xa7, 0x9f, 0x9e, 0x9d, 0x9b, 0x97, 0xaa, 0xa9,
	0x9c, 0x99, 0x93, 0x92, 0x96, 0xa6, 0xa4, 0xa5
};

// 6 bit GCR table used by CBM V-MAX! for old, illegal encoding
const uint32_t CDiskEncoding::gcr_vmaxold[] = {
	0xf7, 0xf6, 0xf4, 0xf5, 0xf3, 0xf2, 0xef, 0xee,
	0xec, 0xed, 0xe2, 0xeb, 0xe9, 0xe7, 0xe6, 0xe4,
	0xe5, 0xde, 0xdc, 0xdd, 0xd9, 0xdb, 0xd3, 0xd7,
	0xdf, 0xcf, 0xce, 0xcc, 0xcd, 0xcb, 0xca, 0xc9,
	0xbe, 0xbc, 0xb2, 0xbd, 0xb9, 0xb4, 0xbb, 0xba,
	0xb6, 0xb7, 0xb5, 0xaf, 0xa3, 0xae, 0xac, 0xab,
	0xa7, 0x9f, 0x9e, 0x9d, 0x9b, 0x97, 0xaa, 0xa9,
	0x9c, 0x99, 0x93, 0x92, 0x96, 0xa6, 0xa4, 0xa5
};

// 4 bit GCR table used by CBM Teque
const uint32_t CDiskEncoding::gcr_teque[] = {
	0xab, 0xb7, 0xad, 0xb5, 0x6b, 0x77, 0x6d, 0x75,
	0xdb, 0xd7, 0xdd, 0xd5, 0x5b, 0x57, 0x5d, 0x55
};

// 4 bit GCR table used by CBM OziSoft
const uint32_t CDiskEncoding::gcr_ozisoft[] = {
	0x5d, 0x65, 0x66, 0x67, 0x6d, 0x77, 0x79, 0x7b,
	0x99, 0xa5, 0xa6, 0xaa, 0xb7, 0xb9, 0xba, 0xbe
};



CDiskEncoding::CDiskEncoding()
{
}

CDiskEncoding::~CDiskEncoding()
{
	delete[] fmcode;
	delete[] fmdecode;

	delete[] mfmcode;
	delete[] mfmdecode;

	delete[] gcrcode;
	delete[] gcrdecode;

	delete[] gcrcode_s;
	delete[] gcrdecode_s;

	delete[] gcrahcode;
	delete[] gcrahdecode;

	delete[] gcra5code;
	delete[] gcra5decode;

	delete[] gcra6code;
	delete[] gcra6decode;

	delete[] gcrvorpalcode;
	delete[] gcrvorpaldecode;

	delete[] gcrvorpal2code;
	delete[] gcrvorpal2decode;

	delete[] gcrvmaxcode;
	delete[] gcrvmaxdecode;

	delete[] gcr4bitcode;
	delete[] gcr4bitdecode;

	Clear();
}

// reset variables
void CDiskEncoding::Clear()
{
	fminit = 0;
	fmcode = nullptr;
	fmdecode = nullptr;

	mfminit = 0;
	mfmcodebit = 0;
	mfmcode = nullptr;
	mfmdecode = nullptr;

	gcrinit = gcridNone;
	gcrcode = nullptr;
	gcrdecode = nullptr;

	gcrinit_s = gcridNone;
	gcrcode_s = nullptr;
	gcrdecode_s = nullptr;

	gcrahinit = 0;
	gcrahcode = nullptr;
	gcrahdecode = nullptr;

	gcra5init = 0;
	gcra5code = nullptr;
	gcra5decode = nullptr;

	gcra6init = 0;
	gcra6code = nullptr;
	gcra6decode = nullptr;

	gcrvorpalinit = 0;
	gcrvorpalcode = nullptr;
	gcrvorpaldecode = nullptr;

	gcrvorpal2init = 0;
	gcrvorpal2code = nullptr;
	gcrvorpal2decode = nullptr;

	gcrvmaxinit = 0;
	gcrvmaxcode = nullptr;
	gcrvmaxdecode = nullptr;

	gcr4bitinit = 0;
	gcr4bitcode = nullptr;
	gcr4bitdecode = nullptr;
}

// initialize FM tables
void CDiskEncoding::InitFM()
{
	// stop if correct tables are available
	if (fminit)
		return;

	// create tables
	if (!fmcode)
		fmcode = new uint32_t[256];

	if (!fmdecode)
		fmdecode = new uint32_t[65536];

	uint32_t sval;

	// create FM word code table
	for (sval = 0x0000; sval < 256; sval++) {
		uint32_t code = 0;

		// generate clock and data bits
		for (uint32_t bit = 0x80; bit; bit >>= 1) {
			code <<= 2;

			// always set clock to 1, and add data
			code |= (sval & bit) ? 3 : 2;
		}

		// store encoding
		fmcode[sval] = code;
	}

	// create FM decode table
	for (sval = 0x0000; sval < 0x10000; sval++) {
		uint32_t code = 0;

		// all bits
		for (uint32_t bit = 0x4000; bit; bit >>= 2) {
			code <<= 1;
			if (sval & bit)
				code |= 1;
		}

		// store decoding, mark coding erros
		uint32_t cd = fmcode[code] & 0xffff;
		if (cd != sval)
			code |= DF_31;
		fmdecode[sval] = code;
	}

	fminit = 1;
}

// initialize MFM tables
void CDiskEncoding::InitMFM(uint32_t mfmsize)
{
	// cancel if correct tables are available
	if (mfmsize && mfminit >= mfmsize)
		return;

	// clear tables
	delete[] mfmcode;
	mfmcode = nullptr;
	delete[] mfmdecode;
	mfmdecode = nullptr;

	mfminit = 0;
	mfmcodebit = 0;

	// stop if empty tables
	if (!mfmsize)
		return;

	// create tables
	mfmcode = new uint32_t[mfmsize];
	mfmdecode = new uint32_t[mfmsize];

	// code only ever uses 8 or 16 bit indexing
	mfmcodebit = (mfmsize > 0x100) ? 16 : 8;

	uint32_t sval;

	// create MFM word code table (last code bit assumed 0)
	for (sval = 0x0000; sval < mfmsize; sval++) {
		uint32_t code = 0;

		// all bits
		for (uint32_t bit = 0x8000; bit; bit >>= 1) {
			code <<= 2;
			// bit 0 encoded as 00, if last bit is 1
			// bit 0 encoded as 10, if last bit is 0
			// bit 1 encoded as 01
			if (sval & bit)
				code |= 1;
			else
				if (!(code & 4))
					code |= 2;
		}

		// store encoding
		mfmcode[sval] = code;
	}

	// create MFM decode table, possible errors marked for analyser only
	if (mfmsize > 0x100) {
		for (sval = 0x0000; sval < mfmsize; sval++) {
			uint32_t code = 0;

			// all bits
			for (uint32_t bit = 0x4000; bit; bit >>= 2) {
				code <<= 1;
				if (sval & bit)
					code |= 1;
			}

			// store decoding, mark coding erros
			uint32_t cd = mfmcode[code] & 0xffff;
			if (cd != sval && (cd & 0x7fff) != sval)
				code |= DF_31;
			mfmdecode[sval] = code;
		}
	} else {
		for (sval = 0x0000; sval < mfmsize; sval++) {
			uint32_t code = 0;

			// all bits
			for (uint32_t bit = 0x4000; bit; bit >>= 2) {
				code <<= 1;
				if (sval & bit)
					code |= 1;
			}

			// store decoding
			mfmdecode[sval] = code;
		}
	}

	mfminit = mfmsize;
}

// initialize GCR tables
void CDiskEncoding::InitGCRCBM(const uint32_t *gcrtable, int gcrid)
{
	// cancel if table initialized with the same gcr version
	if (gcrid == gcrinit)
		return;

	// create tables
	if (!gcrcode)
		gcrcode = new uint32_t[256];

	if (!gcrdecode)
		gcrdecode = new uint32_t[1024];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 1024; sval++)
		gcrdecode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 256; sval++) {
		uint32_t ghi = gcrtable[sval >> 4];
		uint32_t glo = gcrtable[sval & 0xf];
		uint32_t code = ghi << 5 | glo;
		gcrcode[sval] = code;
		gcrdecode[code] = sval;
	}

	gcrinit = gcrid;
}

// initialize secondary GCR tables
void CDiskEncoding::InitGCRCBM_S(const uint32_t *gcrtable, int gcrid)
{
	// cancel if table initialized with the same gcr version
	if (gcrid == gcrinit_s)
		return;

	// create tables
	if (!gcrcode_s)
		gcrcode_s = new uint32_t[256];

	if (!gcrdecode_s)
		gcrdecode_s = new uint32_t[1024];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 1024; sval++)
		gcrdecode_s[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 256; sval++) {
		uint32_t ghi = gcrtable[sval >> 4];
		uint32_t glo = gcrtable[sval & 0xf];
		uint32_t code = ghi << 5 | glo;
		gcrcode_s[sval] = code;
		gcrdecode_s[code] = sval;
	}

	gcrinit_s = gcrid;
}

// initialize GCR Apple Header tables
void CDiskEncoding::InitGCRAppleH()
{
	// stop if correct tables are available
	if (gcrahinit)
		return;

	// create tables
	if (!gcrahcode)
		gcrahcode = new uint32_t[256];

	if (!gcrahdecode)
		gcrahdecode = new uint32_t[65536];

	uint32_t sval;

	// create code table
	for (sval = 0x0000; sval < 256; sval++) {
		// generate clock and data bits, like FM but bits are interleaved
		// C7C5C3C1 C6C4C2C0
		uint32_t ahi = (sval >> 1) | 0xaa;
		uint32_t alo = sval | 0xaa;
		uint32_t code = ahi << 8 | alo;

		// store encoding
		gcrahcode[sval] = code;
	}

	// create decode table
	for (sval = 0x0000; sval < 0x10000; sval++) {
		uint32_t dhi = (sval >> 8) & 0x55;
		uint32_t dlo = sval & 0x55;
		uint32_t code = dhi << 1 | dlo;

		// store decoding, mark coding erros
		uint32_t cd = gcrahcode[code] & 0xffff;
		if (cd != sval)
			code |= DF_31;
		gcrahdecode[sval] = code;
	}

	gcrahinit = 1;
}

// initialize GCR Apple 5 bit tables
void CDiskEncoding::InitGCRApple5(const uint32_t *gcrtable)
{
	// stop if correct tables are available
	if (gcra5init)
		return;

	// create tables
	if (!gcra5code)
		gcra5code = new uint32_t[32];

	if (!gcra5decode)
		gcra5decode = new uint32_t[256];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 256; sval++)
		gcra5decode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 32; sval++) {
		uint32_t code = gcrtable[sval];
		gcra5code[sval] = code;
		gcra5decode[code] = sval;
	}

	gcra5init = 1;
}

// initialize GCR Apple 6 bit tables
void CDiskEncoding::InitGCRApple6(const uint32_t *gcrtable)
{
	// stop if correct tables are available
	if (gcra6init)
		return;

	// create tables
	if (!gcra6code)
		gcra6code = new uint32_t[64];

	if (!gcra6decode)
		gcra6decode = new uint32_t[256];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 256; sval++)
		gcra6decode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 64; sval++) {
		uint32_t code = gcrtable[sval];
		gcra6code[sval] = code;
		gcra6decode[code] = sval;
	}

	gcra6init = 1;
}

// initialize GCR CBM Vorpal 6 bit tables
void CDiskEncoding::InitGCRVorpal(const uint32_t *gcrtable)
{
	// stop if correct tables are available
	if (gcrvorpalinit)
		return;

	// create tables
	if (!gcrvorpalcode)
		gcrvorpalcode = new uint32_t[64];

	if (!gcrvorpaldecode)
		gcrvorpaldecode = new uint32_t[256];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 256; sval++)
		gcrvorpaldecode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 64; sval++) {
		uint32_t code = gcrtable[sval];
		gcrvorpalcode[sval] = code;
		gcrvorpaldecode[code] = sval;
	}

	gcrvorpalinit = 1;
}

// initialize GCR CBM Vorpal 5 bit tables
void CDiskEncoding::InitGCRVorpal2(const uint32_t *gcrtable)
{
	// stop if correct tables are available
	if (gcrvorpal2init)
		return;

	// create tables
	if (!gcrvorpal2code)
		gcrvorpal2code = new uint32_t[32];

	if (!gcrvorpal2decode)
		gcrvorpal2decode = new uint32_t[1024];

	// decoded 5 bit gcr value
	int gdtab[32];
	int i;

	// mark all values as illegal
	for (i = 0; i < 32; i++)
		gdtab[i] = -1;

	// add valid gcr and decoded values
	for (i = 0; i < 32; i++) {
		uint32_t code = gcrtable[i];
		gcrvorpal2code[i] = code;

		// skip illegal codes
		if (!code)
			continue;

		// 4 bit decoded value
		gdtab[code] = i & 0x0f;
	}

	// process all possible 10 bit combinations (5 bit hi nybble, 5 bit lo nybble)
	for (uint32_t sval = 0; sval < 1024; sval++) {
		// set value as illegal by default
		gcrvorpal2decode[sval] = DF_31;

		// decode the 5 bit gcr values
		int dechi = gdtab[sval >> 5];
		int declo = gdtab[sval & 0x1f];

		// the 10 bit encoded value is illegal if any of its nybbles decoded is an illegal code
		if (dechi < 0 || declo < 0)
			continue;

		int lbv = -1;
		int bc = 0;

		// check the 10 bit encoded value as a stream for illegal encoding
		uint32_t bm;
		for (bm = 1 << 9; bm; bm >>= 1) {
			// current bit value
			int bitval = (sval & bm) ? 1 : 0;
			if (lbv != bitval) {
				// if different from previous bit, restart counting
				lbv = bitval;
				bc = 1;
			} else {
				// same as previous bit, count it in
				bc++;

				// the stream is illegal if it contains 5 consecutive 1 bits or 3 consecutive 0 bits
				if ((!lbv && bc >= 3) || (lbv && bc >= 5))
					break;
			}
		}

		// skip illegal value; the loop ended early
		if (bm)
			continue;

		// set 8 bit decoded value
		uint32_t code = dechi << 4 | declo;
		gcrvorpal2decode[sval] = code;
	}

	gcrvorpal2init = 1;
}

// initialize GCR CBM V-Max! 6 bit tables
void CDiskEncoding::InitGCRVMax(const uint32_t *gcrtable, int vmaxid)
{
	// cancel if table initialized with the same gcr version
	if (gcrvmaxinit == vmaxid)
		return;

	// create tables
	if (!gcrvmaxcode)
		gcrvmaxcode = new uint32_t[64];

	if (!gcrvmaxdecode)
		gcrvmaxdecode = new uint32_t[256];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 256; sval++)
		gcrvmaxdecode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 64; sval++) {
		uint32_t code = gcrtable[sval];
		if (!code)
			continue;

		gcrvmaxcode[sval] = code;
		gcrvmaxdecode[code] = sval;
	}

	gcrvmaxinit = vmaxid;
}

// initialize 4 bit gcr tables
void CDiskEncoding::InitGCR4Bit(const uint32_t *gcrtable)
{
	// stop if correct tables are available
	if (gcr4bitinit)
		return;

	// create tables
	if (!gcr4bitcode)
		gcr4bitcode = new uint32_t[16];

	if (!gcr4bitdecode)
		gcr4bitdecode = new uint32_t[256];

	uint32_t sval;

	// set GCR decode table to coding error as default
	for (sval = 0; sval < 256; sval++)
		gcr4bitdecode[sval] = DF_31;

	// create GCR table
	for (sval = 0; sval < 16; sval++) {
		uint32_t code = gcrtable[sval];
		gcr4bitcode[sval] = code;
		gcr4bitdecode[code] = sval;
	}

	gcr4bitinit = 1;
}

// find recording violation, that exceeds the maximum number of consecutive 0 or 1 bits
int CDiskEncoding::FindViolation(const uint8_t *buffer, int bitpos, int bitcnt, int max0, int max1, int mode)
{
	int vcnt = 0;

	// no violation if any of the parameters is incorrect or nothing to check
	if (!buffer || bitpos < 0 || bitcnt <= 0 || (max0 < 0 && max1 < 0))
		return mode ? vcnt : -1;

	// calculate the buffer position and bit mask for the first bit to check
	uint32_t bitmask = 1 << ((bitpos & 7) ^ 7);
	uint32_t pos = bitpos >> 3;
	uint32_t sval = buffer[pos++];

	// reset bit counter
	int lbv = -1;
	int bc = 0;

	// process all bits
	while (bitcnt-- > 0) {
		// if mask underflowed, reset the mask to b7 and read the next buffer value
		if (!bitmask) {
			bitmask = 0x80;
			sval = buffer[pos++];
		}

		// current bit value
		int bitval = (sval & bitmask) ? 1 : 0;

		if (lbv != bitval) {
			// if different from previous bit, restart counting
			lbv = bitval;
			bc = 1;
		} else {
			// same as previous bit, count it in
			bc++;

			// violation found if the stream contains max1 consecutive 1 bits or max0 consecutive 0 bits
			if ((!lbv && max0 >= 0 && bc >= max0) || (lbv && max1 >= 0 && bc >= max1)) {
				// count violations or return on first violation found depending on mode
				if (mode)
					vcnt++;
				else
					return bitpos;
			}
		}

		// move to next bit
		bitpos++;
		bitmask >>= 1;
	}

	// return violation count or no violation found depending on mode
	return mode ? vcnt : -1;
}

