diff options
Diffstat (limited to 'src/SPI.cpp')
-rw-r--r-- | src/SPI.cpp | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/SPI.cpp b/src/SPI.cpp new file mode 100644 index 0000000..13ab2ab --- /dev/null +++ b/src/SPI.cpp @@ -0,0 +1,457 @@ +/* + Copyright 2016-2017 StapleButter + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdio.h> +#include <string.h> +#include "NDS.h" +#include "SPI.h" + + +namespace SPI_Firmware +{ + +u8* Firmware; +u32 FirmwareLength; + +u32 Hold; +u8 CurCmd; +u32 DataPos; +u8 Data; + +u8 StatusReg; +u32 Addr; + + +u16 CRC16(u8* data, u32 len, u32 start) +{ + u16 blarg[8] = {0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001}; + + for (u32 i = 0; i < len; i++) + { + start ^= data[i]; + + for (int j = 0; j < 8; j++) + { + if (start & 0x1) + { + start >>= 1; + start ^= (blarg[j] << (7-j)); + } + else + start >>= 1; + } + } + + return start & 0xFFFF; +} + +bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) +{ + u16 crc_stored = *(u16*)&Firmware[crcoffset]; + u16 crc_calced = CRC16(&Firmware[offset], len, start); + //printf("%04X vs %04X\n", crc_stored, crc_calced); + return (crc_stored == crc_calced); +} + + +bool Init() +{ + Firmware = NULL; + return true; +} + +void DeInit() +{ + if (Firmware) delete[] Firmware; +} + +void Reset() +{ + if (Firmware) delete[] Firmware; + Firmware = NULL; + + FILE* f = fopen("firmware.bin", "rb"); + if (!f) + { + printf("firmware.bin not found\n"); + + // TODO: generate default firmware + return; + } + + fseek(f, 0, SEEK_END); + FirmwareLength = (u32)ftell(f); + Firmware = new u8[FirmwareLength]; + + fseek(f, 0, SEEK_SET); + fread(Firmware, FirmwareLength, 1, f); + + fclose(f); + + u32 userdata = 0x3FE00; + if (*(u16*)&Firmware[0x3FF70] == ((*(u16*)&Firmware[0x3FE70] + 1) & 0x7F)) + { + if (VerifyCRC16(0xFFFF, 0x3FF00, 0x70, 0x3FF72)) + userdata = 0x3FF00; + } + + // fix touchscreen coords + *(u16*)&Firmware[userdata+0x58] = 0; + *(u16*)&Firmware[userdata+0x5A] = 0; + Firmware[userdata+0x5C] = 1; + Firmware[userdata+0x5D] = 1; + *(u16*)&Firmware[userdata+0x5E] = 254<<4; + *(u16*)&Firmware[userdata+0x60] = 190<<4; + Firmware[userdata+0x62] = 255; + Firmware[userdata+0x63] = 191; + + // disable autoboot + //Firmware[userdata+0x64] &= 0xBF; + + *(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF); + + // verify shit + printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD"); + printf("FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FA00, 0xFE, 0x3FAFE)?"GOOD":"BAD"); + printf("FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FB00, 0xFE, 0x3FBFE)?"GOOD":"BAD"); + printf("FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x3FC00, 0xFE, 0x3FCFE)?"GOOD":"BAD"); + printf("FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x3FE00, 0x70, 0x3FE72)?"GOOD":"BAD"); + printf("FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x3FF00, 0x70, 0x3FF72)?"GOOD":"BAD"); + + Hold = 0; + CurCmd = 0; + Data = 0; + StatusReg = 0x00; +} + +u8 Read() +{ + return Data; +} + +void Write(u8 val, u32 hold) +{ + if (!hold) + { + Hold = 0; + } + + if (hold && (!Hold)) + { + CurCmd = val; + Hold = 1; + Data = 0; + DataPos = 1; + Addr = 0; + //printf("firmware SPI command %02X\n", CurCmd); + return; + } + + switch (CurCmd) + { + case 0x03: // read + { + if (DataPos < 4) + { + Addr <<= 8; + Addr |= val; + Data = 0; + + //if (DataPos == 3) printf("firmware SPI read %08X\n", Addr); + } + else + { + if (Addr >= FirmwareLength) + Data = 0; + else + Data = Firmware[Addr]; + + Addr++; + } + + DataPos++; + } + break; + + case 0x04: // write disable + StatusReg &= ~(1<<1); + Data = 0; + break; + + case 0x05: // read status reg + Data = StatusReg; + break; + + case 0x06: // write enable + StatusReg |= (1<<1); + Data = 0; + break; + + case 0x9F: // read JEDEC ID + { + switch (DataPos) + { + case 1: Data = 0x20; break; + case 2: Data = 0x40; break; + case 3: Data = 0x12; break; + default: Data = 0; break; + } + DataPos++; + } + break; + + default: + printf("unknown firmware SPI command %02X\n", CurCmd); + break; + } +} + +} + +namespace SPI_Powerman +{ + +u32 Hold; +u32 DataPos; +u8 Index; +u8 Data; + +u8 Registers[8]; +u8 RegMasks[8]; + + +bool Init() +{ + return true; +} + +void DeInit() +{ +} + +void Reset() +{ + Hold = 0; + Index = 0; + Data = 0; + + memset(Registers, 0, sizeof(Registers)); + memset(RegMasks, 0, sizeof(RegMasks)); + + Registers[4] = 0x40; + + RegMasks[0] = 0x7F; + RegMasks[1] = 0x01; + RegMasks[2] = 0x01; + RegMasks[3] = 0x03; + RegMasks[4] = 0x0F; +} + +u8 Read() +{ + return Data; +} + +void Write(u8 val, u32 hold) +{ + if (!hold) + { + Hold = 0; + } + + if (hold && (!Hold)) + { + Index = val; + Hold = 1; + Data = 0; + DataPos = 1; + return; + } + + if (DataPos == 1) + { + if (Index & 0x80) + { + Data = Registers[Index & 0x07]; + } + else + { + Registers[Index & 0x07] = + (Registers[Index & 0x07] & ~RegMasks[Index & 0x07]) | + (val & RegMasks[Index & 0x07]); + } + } + else + Data = 0; +} + +} + + +namespace SPI_TSC +{ + +u32 DataPos; +u8 ControlByte; +u8 Data; + +u16 ConvResult; + +u16 TouchX, TouchY; + + +bool Init() +{ + return true; +} + +void DeInit() +{ +} + +void Reset() +{ + ControlByte = 0; + Data = 0; + + ConvResult = 0; +} + +void SetTouchCoords(u16 x, u16 y) +{ + // scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1) + // scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1) + // adc.x = ((scr.x * ((adc.x2-adc.x1) + (scr.x1-1))) / (scr.x2-scr.x1)) + adc.x1 + // adc.y = ((scr.y * ((adc.y2-adc.y1) + (scr.y1-1))) / (scr.y2-scr.y1)) + adc.y1 + TouchX = x; + TouchY = y; + + if (y == 0xFFF) return; + + TouchX <<= 4; + TouchY <<= 4; +} + +u8 Read() +{ + return Data; +} + +void Write(u8 val, u32 hold) +{ + if (DataPos == 1) + Data = (ConvResult >> 5) & 0xFF; + else if (DataPos == 2) + Data = (ConvResult << 3) & 0xFF; + else + Data = 0; + + if (val & 0x80) + { + ControlByte = val; + DataPos = 1; + + switch (ControlByte & 0x70) + { + case 0x10: ConvResult = TouchY; break; + case 0x50: ConvResult = TouchX; break; + default: ConvResult = 0xFFF; break; + } + + if (ControlByte & 0x08) + ConvResult &= 0x0FF0; // checkme + } + else + DataPos++; +} + +} + + +namespace SPI +{ + +u16 Cnt; + +u32 CurDevice; + + +bool Init() +{ + if (!SPI_Firmware::Init()) return false; + if (!SPI_Powerman::Init()) return false; + if (!SPI_TSC::Init()) return false; + + return true; +} + +void DeInit() +{ + SPI_Firmware::DeInit(); + SPI_Powerman::DeInit(); + SPI_TSC::DeInit(); +} + +void Reset() +{ + Cnt = 0; + + SPI_Firmware::Reset(); + SPI_Powerman::Reset(); + SPI_TSC::Init(); +} + + +void WriteCnt(u16 val) +{ + Cnt = (Cnt & 0x0080) | (val & 0xCF03); + if (val & 0x0400) printf("!! CRAPOED 16BIT SPI MODE\n"); +} + +u8 ReadData() +{ + if (!(Cnt & (1<<15))) return 0; + + switch (Cnt & 0x0300) + { + case 0x0000: return SPI_Powerman::Read(); + case 0x0100: return SPI_Firmware::Read(); + case 0x0200: return SPI_TSC::Read(); + default: return 0; + } +} + +void WriteData(u8 val) +{ + if (!(Cnt & (1<<15))) return; + + // TODO: take delays into account + + switch (Cnt & 0x0300) + { + case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break; + case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break; + case 0x0200: SPI_TSC::Write(val, Cnt&(1<<11)); break; + default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break; + } + + if (Cnt & (1<<14)) + NDS::SetIRQ(1, NDS::IRQ_SPI); +} + +} |