From ca1ae1de12aa7aafa909a073987b5659186ecdc7 Mon Sep 17 00:00:00 2001 From: Jan Goldacker Date: Fri, 23 Aug 2019 21:55:42 +0200 Subject: [PATCH] added initial source and readme --- README.md | 12 ++ cic_c.c | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 489 insertions(+) create mode 100644 README.md create mode 100644 cic_c.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ccbd02 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# UltraCIC_C +Nintendo64 CIC clone written in C + +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 \ No newline at end of file diff --git a/cic_c.c b/cic_c.c new file mode 100644 index 0000000..1f3fd91 --- /dev/null +++ b/cic_c.c @@ -0,0 +1,477 @@ +/************************************************************** + * 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(); + } + } +} +