478 lines
9.9 KiB
C
478 lines
9.9 KiB
C
/**************************************************************
|
|
* Generic CIC implementation for N64 *
|
|
* ---------------------------------------------------------- *
|
|
* This should run on every MCU which is fast enough to *
|
|
* handle the IO operations. *
|
|
* You just have to implement the low level gpio functions: *
|
|
* - ReadBit() and *
|
|
* - WriteBit(). *
|
|
* *
|
|
* Hardware connections *
|
|
* Data Clock Input (DCLK): CIC Pin 14 *
|
|
* Data Line, Bidir (DIO): CIC Pin 15 *
|
|
* *
|
|
**************************************************************/
|
|
|
|
#define REGION_NTSC (0)
|
|
#define REGION_PAL (1)
|
|
|
|
#define GET_REGION() (REGION_NTSC)
|
|
|
|
/* SEEDs */
|
|
|
|
// 6102/7101
|
|
#define CIC6102_SEED 0x3F
|
|
|
|
// 6101
|
|
#define CIC6101_SEED 0x3F
|
|
|
|
// 6103/7103
|
|
#define CIC6103_SEED 0x78
|
|
|
|
// 6105/7105
|
|
#define CIC6105_SEED 0x91
|
|
|
|
// 6106/7106
|
|
#define CIC6106_SEED 0x85
|
|
|
|
// 7102
|
|
#define CIC7102_SEED 0x3F
|
|
|
|
/* CHECKSUMs */
|
|
|
|
// 6102/7101
|
|
#define CIC6102_CHECKSUM 0xa, 0x5, 0x3, 0x6, 0xc, 0x0, 0xf, 0x1, 0xd, 0x8, 0x5, 0x9
|
|
|
|
// 6101
|
|
#define CIC6101_CHECKSUM 0x4, 0x5, 0xC, 0xC, 0x7, 0x3, 0xE, 0xE, 0x3, 0x1, 0x7, 0xA
|
|
|
|
// 6103/7103
|
|
#define CIC6103_CHECKSUM 0x5, 0x8, 0x6, 0xf, 0xd, 0x4, 0x7, 0x0, 0x9, 0x8, 0x6, 0x7
|
|
|
|
// 6105/7105
|
|
#define CIC6105_CHECKSUM 0x8, 0x6, 0x1, 0x8, 0xA, 0x4, 0x5, 0xB, 0xC, 0x2, 0xD, 0x3
|
|
|
|
// 6106/7106
|
|
#define CIC6106_CHECKSUM 0x2, 0xB, 0xB, 0xA, 0xD, 0x4, 0xE, 0x6, 0xE, 0xB, 0x7, 0x4
|
|
|
|
// 7102
|
|
#define CIC7102_CHECKSUM 0x4, 0x4, 0x1, 0x6, 0x0, 0xE, 0xC, 0x5, 0xD, 0x9, 0xA, 0xF
|
|
|
|
void EncodeRound(unsigned char index);
|
|
void CicRound(unsigned char *);
|
|
void Cic6105Algo(void);
|
|
|
|
/* Select SEED and CHECKSUM here */
|
|
const unsigned char _CicSeed = CIC6102_SEED;
|
|
|
|
const unsigned char _CicChecksum[] = {
|
|
CIC6102_CHECKSUM
|
|
};
|
|
|
|
|
|
/* NTSC initial RAM */
|
|
const unsigned char _CicRamInitNtsc[] = {
|
|
0xE, 0x0, 0x9, 0xA, 0x1, 0x8, 0x5, 0xA, 0x1, 0x3, 0xE, 0x1, 0x0, 0xD, 0xE, 0xC,
|
|
0x0, 0xB, 0x1, 0x4, 0xF, 0x8, 0xB, 0x5, 0x7, 0xC, 0xD, 0x6, 0x1, 0xE, 0x9, 0x8
|
|
};
|
|
|
|
/* PAL initial RAM */
|
|
const unsigned char _CicRamInitPal[] = {
|
|
0xE, 0x0, 0x4, 0xF, 0x5, 0x1, 0x2, 0x1, 0x7, 0x1, 0x9, 0x8, 0x5, 0x7, 0x5, 0xA,
|
|
0x0, 0xB, 0x1, 0x2, 0x3, 0xF, 0x8, 0x2, 0x7, 0x1, 0x9, 0x8, 0x1, 0x1, 0x5, 0xC
|
|
};
|
|
|
|
/* Memory for the CIC algorithm */
|
|
unsigned char _CicMem[32];
|
|
|
|
/* Memory for the 6105 algorithm */
|
|
unsigned char _6105Mem[32];
|
|
|
|
/* YOU HAVE TO IMPLEMENT THE LOW LEVEL GPIO FUNCTIONS ReadBit() and WriteBit() */
|
|
|
|
/* Read a bit synchronized by DCLK */
|
|
unsigned char ReadBit(void)
|
|
{
|
|
unsigned char res;
|
|
|
|
// wait for DCLK to go low
|
|
// while (DCLK_PIN == HIGH)
|
|
// { }
|
|
|
|
// Read the data bit
|
|
res = 0;
|
|
|
|
// wait for DCLK to go high
|
|
// while (DCLK_PIN == LOW)
|
|
// { }
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Write a bit synchronized by DCLK */
|
|
void WriteBit(unsigned char b)
|
|
{
|
|
// wait for DCLK to go low
|
|
// while (DCLK_PIN == HIGH)
|
|
// { }
|
|
|
|
if (b == 0)
|
|
{
|
|
// drive the output low
|
|
// OUTPUT_TRISTATE = 0;
|
|
// OUTPUT_DATA = 0;
|
|
}
|
|
|
|
// wait for DCLK to go high
|
|
// while (DCLK_PIN == LOW)
|
|
// { }
|
|
|
|
// tristate the output
|
|
// OUTPUT_TRISTATE = 1;
|
|
}
|
|
|
|
/* Writes the lowes 4 bits of the byte */
|
|
void WriteNibble(unsigned char n)
|
|
{
|
|
WriteBit(n & 0x08);
|
|
WriteBit(n & 0x04);
|
|
WriteBit(n & 0x02);
|
|
WriteBit(n & 0x01);
|
|
}
|
|
|
|
// Write RAM nibbles until index hits a 16 Byte border
|
|
void WriteRamNibbles(unsigned char index)
|
|
{
|
|
do
|
|
{
|
|
WriteNibble(_CicMem[index]);
|
|
index++;
|
|
} while ((index & 0x0f) != 0);
|
|
}
|
|
|
|
/* Reads 4 bits and returns them in the lowes 4 bits of a byte */
|
|
unsigned char ReadNibble(void)
|
|
{
|
|
unsigned char res = 0;
|
|
unsigned char i;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
res <<= 1;
|
|
res |= ReadBit();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* Encrypt and output the seed */
|
|
void WriteSeed(void)
|
|
{
|
|
_CicMem[0x0a] = 0xb;
|
|
_CicMem[0x0b] = 0x5;
|
|
_CicMem[0x0c] = _CicSeed >> 4;
|
|
_CicMem[0x0d] = _CicSeed;
|
|
_CicMem[0x0e] = _CicSeed >> 4;
|
|
_CicMem[0x0f] = _CicSeed;
|
|
|
|
EncodeRound(0x0a);
|
|
EncodeRound(0x0a);
|
|
WriteRamNibbles(0x0a);
|
|
}
|
|
|
|
/* Encrypt and output the checksum */
|
|
void WriteChecksum(void)
|
|
{
|
|
unsigned char i;
|
|
for (i = 0; i < 12; i++)
|
|
_CicMem[i + 4] = _CicChecksum[i];
|
|
|
|
// wait for DCLK to go low
|
|
// (doesn't seem to be necessary)
|
|
|
|
// "encrytion" key
|
|
// initial value doesn't matter
|
|
//_CicMem[0x00] = 0;
|
|
//_CicMem[0x01] = 0xd;
|
|
//_CicMem[0x02] = 0;
|
|
//_CicMem[0x03] = 0;
|
|
|
|
EncodeRound(0x00);
|
|
EncodeRound(0x00);
|
|
EncodeRound(0x00);
|
|
EncodeRound(0x00);
|
|
|
|
// signal that we are done to the pif
|
|
// (test with WriteBit(1) also worked)
|
|
WriteBit(0);
|
|
|
|
// Write 16 nibbles
|
|
WriteRamNibbles(0);
|
|
}
|
|
|
|
/* seed and checksum encryption algorithm */
|
|
void EncodeRound(unsigned char index)
|
|
{
|
|
unsigned char a;
|
|
|
|
a = _CicMem[index];
|
|
index++;
|
|
|
|
do
|
|
{
|
|
a = (a + 1) & 0x0f;
|
|
a = (a + _CicMem[index]) & 0x0f;
|
|
_CicMem[index] = a;
|
|
index++;
|
|
} while ((index & 0x0f) != 0);
|
|
}
|
|
|
|
/* Exchange value of a and b */
|
|
void Exchange(unsigned char *a, unsigned char *b)
|
|
{
|
|
unsigned char tmp;
|
|
tmp = *a;
|
|
*a = *b;
|
|
*b = tmp;
|
|
}
|
|
|
|
// translated from PIC asm code (thx to Mike Ryan for the PIC implementation)
|
|
// this implementation saves program memory in LM8
|
|
|
|
/* CIC compare mode memory alternation algorithm */
|
|
void CicRound(unsigned char * m)
|
|
{
|
|
unsigned char a;
|
|
unsigned char b, x;
|
|
|
|
x = m[15];
|
|
a = x;
|
|
|
|
do
|
|
{
|
|
b = 1;
|
|
a += m[b] + 1;
|
|
m[b] = a;
|
|
b++;
|
|
a += m[b] + 1;
|
|
Exchange(&a, &m[b]);
|
|
m[b] = ~m[b];
|
|
b++;
|
|
a &= 0xf;
|
|
a += (m[b] & 0xf) + 1;
|
|
if (a < 16)
|
|
{
|
|
Exchange(&a, &m[b]);
|
|
b++;
|
|
}
|
|
a += m[b];
|
|
m[b] = a;
|
|
b++;
|
|
a += m[b];
|
|
Exchange(&a, &m[b]);
|
|
b++;
|
|
a &= 0xf;
|
|
a += 8;
|
|
if (a < 16)
|
|
a += m[b];
|
|
Exchange(&a, &m[b]);
|
|
b++;
|
|
do
|
|
{
|
|
a += m[b] + 1;
|
|
m[b] = a;
|
|
b++;
|
|
b &= 0xf;
|
|
} while (b != 0);
|
|
a = x + 0xf;
|
|
x = a & 0xf;
|
|
} while (x != 15);
|
|
}
|
|
|
|
// Big Thx to Mike Ryan, John McMaster, marshallh for publishing their work
|
|
|
|
/* CIC 6105 algorithm */
|
|
void Cic6105Algo(void)
|
|
{
|
|
unsigned char A = 5;
|
|
unsigned char carry = 1;
|
|
unsigned char i;
|
|
for (i = 0; i < 30; ++i)
|
|
{
|
|
if (!(_6105Mem[i] & 1))
|
|
A += 8;
|
|
if (!(A & 2))
|
|
A += 4;
|
|
A = (A + _6105Mem[i]) & 0xf;
|
|
_6105Mem[i] = A;
|
|
if (!carry)
|
|
A += 7;
|
|
A = (A + _6105Mem[i]) & 0xF;
|
|
A = A + _6105Mem[i] + carry;
|
|
if (A >= 0x10)
|
|
{
|
|
carry = 1;
|
|
A -= 0x10;
|
|
}
|
|
else
|
|
{
|
|
carry = 0;
|
|
}
|
|
A = (~A) & 0xf;
|
|
_6105Mem[i] = A;
|
|
}
|
|
}
|
|
|
|
/* Wait for reset */
|
|
void Die(void)
|
|
{
|
|
// never return
|
|
while (1)
|
|
{ }
|
|
}
|
|
|
|
/* CIC compare mode */
|
|
void CompareMode(unsigned char isPal)
|
|
{
|
|
unsigned char ramPtr;
|
|
// don't care about the low ram as we don't check this
|
|
// CicRound(&_CicMem[0x00]);
|
|
// CicRound(&_CicMem[0x00]);
|
|
// CicRound(&_CicMem[0x00]);
|
|
|
|
// only need to calculate the high ram
|
|
CicRound(&_CicMem[0x10]);
|
|
CicRound(&_CicMem[0x10]);
|
|
CicRound(&_CicMem[0x10]);
|
|
|
|
// 0x17 determines the start index (but never 0)
|
|
ramPtr = _CicMem[0x17] & 0xf;
|
|
if (ramPtr == 0)
|
|
ramPtr = 1;
|
|
ramPtr |= 0x10;
|
|
|
|
do
|
|
{
|
|
// read the bit from PIF (don't care about the value)
|
|
ReadBit();
|
|
|
|
// send out the lowest bit of the currently indexed ram
|
|
WriteBit(_CicMem[ramPtr] & 0x01);
|
|
|
|
// PAL or NTSC?
|
|
if (!isPal)
|
|
{
|
|
// NTSC
|
|
ramPtr++;
|
|
}
|
|
else
|
|
{
|
|
// PAL
|
|
ramPtr--;
|
|
}
|
|
|
|
// repeat until the end is reached
|
|
} while (ramPtr & 0xf);
|
|
}
|
|
|
|
/* CIC 6105 mode */
|
|
void Cic6105Mode(void)
|
|
{
|
|
unsigned char ramPtr;
|
|
|
|
// write 0xa 0xa
|
|
WriteNibble(0xa);
|
|
WriteNibble(0xa);
|
|
|
|
// receive 30 nibbles
|
|
for (ramPtr = 0; ramPtr < 30; ramPtr++)
|
|
{
|
|
_6105Mem[ramPtr] = ReadNibble();
|
|
}
|
|
|
|
// execute the algorithm
|
|
Cic6105Algo();
|
|
|
|
// send bit 0
|
|
WriteBit(0);
|
|
|
|
// send 30 nibbles
|
|
for (ramPtr = 0; ramPtr < 30; ramPtr++)
|
|
{
|
|
WriteNibble(_6105Mem[ramPtr]);
|
|
}
|
|
}
|
|
|
|
/* Load initial ram depending on region */
|
|
void InitRam(unsigned char isPal)
|
|
{
|
|
unsigned char i;
|
|
|
|
if (!isPal)
|
|
{
|
|
for (i = 0; i < 32; i++)
|
|
_CicMem[i] = _CicRamInitNtsc[i];
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 32; i++)
|
|
_CicMem[i] = _CicRamInitPal[i];
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
unsigned char isPal;
|
|
|
|
// read the region setting
|
|
isPal = GET_REGION();
|
|
|
|
// send out the corresponding id
|
|
unsigned char hello = 0x1;
|
|
if (isPal)
|
|
hello |= 0x4;
|
|
WriteNibble(hello);
|
|
|
|
// encode and send the seed
|
|
WriteSeed();
|
|
|
|
// encode and send the checksum
|
|
WriteChecksum();
|
|
|
|
// init the ram corresponding to the region
|
|
InitRam(isPal);
|
|
|
|
// read the initial values from the PIF
|
|
_CicMem[0x01] = ReadNibble();
|
|
_CicMem[0x11] = ReadNibble();
|
|
|
|
while(1)
|
|
{
|
|
// read mode (2 bit)
|
|
unsigned char cmd = 0;
|
|
cmd |= (ReadBit() << 1);
|
|
cmd |= ReadBit();
|
|
switch (cmd)
|
|
{
|
|
case 0:
|
|
// 00 (compare)
|
|
CompareMode(isPal);
|
|
break;
|
|
|
|
case 2:
|
|
// 10 (6105)
|
|
Cic6105Mode();
|
|
break;
|
|
|
|
case 3:
|
|
// 11 (reset)
|
|
WriteBit(0);
|
|
break;
|
|
|
|
case 1:
|
|
// 01 (die)
|
|
default:
|
|
Die();
|
|
}
|
|
}
|
|
}
|
|
|