2016-12-05 10:02:29 -07:00
|
|
|
/*
|
|
|
|
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/.
|
|
|
|
*/
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
#include <stdio.h>
|
2017-01-19 17:18:30 -07:00
|
|
|
#include <string.h>
|
2016-12-03 19:20:50 -07:00
|
|
|
#include "NDS.h"
|
|
|
|
#include "SPI.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace SPI_Firmware
|
|
|
|
{
|
|
|
|
|
|
|
|
u8* Firmware;
|
|
|
|
u32 FirmwareLength;
|
|
|
|
|
|
|
|
u32 Hold;
|
|
|
|
u8 CurCmd;
|
|
|
|
u32 DataPos;
|
|
|
|
u8 Data;
|
|
|
|
|
2016-12-05 10:02:29 -07:00
|
|
|
u8 StatusReg;
|
2016-12-03 19:20:50 -07:00
|
|
|
u32 Addr;
|
|
|
|
|
2017-01-15 20:47:37 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-07 14:23:46 -07:00
|
|
|
bool Init()
|
2016-12-03 19:20:50 -07:00
|
|
|
{
|
|
|
|
Firmware = NULL;
|
2017-02-07 14:23:46 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
|
|
|
{
|
|
|
|
if (Firmware) delete[] Firmware;
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
if (Firmware) delete[] Firmware;
|
2017-02-07 14:23:46 -07:00
|
|
|
Firmware = NULL;
|
2016-12-03 19:20:50 -07:00
|
|
|
|
|
|
|
FILE* f = fopen("firmware.bin", "rb");
|
2017-02-07 14:23:46 -07:00
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
printf("firmware.bin not found\n");
|
|
|
|
|
|
|
|
// TODO: generate default firmware
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
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);
|
|
|
|
|
2017-01-31 16:24:36 -07:00
|
|
|
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;
|
2017-02-03 16:12:08 -07:00
|
|
|
*(u16*)&Firmware[userdata+0x5E] = 254<<4;
|
|
|
|
*(u16*)&Firmware[userdata+0x60] = 190<<4;
|
2017-01-31 16:24:36 -07:00
|
|
|
Firmware[userdata+0x62] = 255;
|
|
|
|
Firmware[userdata+0x63] = 191;
|
|
|
|
|
|
|
|
// disable autoboot
|
|
|
|
//Firmware[userdata+0x64] &= 0xBF;
|
|
|
|
|
|
|
|
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
|
2017-01-30 10:36:11 -07:00
|
|
|
|
2017-01-15 20:47:37 -07:00
|
|
|
// 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");
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
Hold = 0;
|
|
|
|
CurCmd = 0;
|
|
|
|
Data = 0;
|
2016-12-05 10:02:29 -07:00
|
|
|
StatusReg = 0x00;
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 Read()
|
|
|
|
{
|
|
|
|
return Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Write(u8 val, u32 hold)
|
|
|
|
{
|
|
|
|
if (!hold)
|
|
|
|
{
|
|
|
|
Hold = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hold && (!Hold))
|
|
|
|
{
|
|
|
|
CurCmd = val;
|
|
|
|
Hold = 1;
|
2016-12-05 10:02:29 -07:00
|
|
|
Data = 0;
|
2016-12-03 19:20:50 -07:00
|
|
|
DataPos = 1;
|
|
|
|
Addr = 0;
|
2016-12-05 15:17:03 -07:00
|
|
|
//printf("firmware SPI command %02X\n", CurCmd);
|
2016-12-03 19:20:50 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (CurCmd)
|
|
|
|
{
|
|
|
|
case 0x03: // read
|
|
|
|
{
|
|
|
|
if (DataPos < 4)
|
|
|
|
{
|
|
|
|
Addr <<= 8;
|
|
|
|
Addr |= val;
|
|
|
|
Data = 0;
|
|
|
|
|
2016-12-05 15:17:03 -07:00
|
|
|
//if (DataPos == 3) printf("firmware SPI read %08X\n", Addr);
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Addr >= FirmwareLength)
|
|
|
|
Data = 0;
|
|
|
|
else
|
|
|
|
Data = Firmware[Addr];
|
|
|
|
|
|
|
|
Addr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-12-05 10:02:29 -07:00
|
|
|
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;
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
default:
|
|
|
|
printf("unknown firmware SPI command %02X\n", CurCmd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-19 17:18:30 -07:00
|
|
|
namespace SPI_Powerman
|
|
|
|
{
|
|
|
|
|
|
|
|
u32 Hold;
|
|
|
|
u32 DataPos;
|
|
|
|
u8 Index;
|
|
|
|
u8 Data;
|
|
|
|
|
|
|
|
u8 Registers[8];
|
|
|
|
u8 RegMasks[8];
|
|
|
|
|
|
|
|
|
2017-02-07 14:23:46 -07:00
|
|
|
bool Init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
2017-01-19 17:18:30 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
|
2017-01-31 16:24:36 -07:00
|
|
|
namespace SPI_TSC
|
|
|
|
{
|
|
|
|
|
|
|
|
u32 DataPos;
|
|
|
|
u8 ControlByte;
|
|
|
|
u8 Data;
|
|
|
|
|
|
|
|
u16 ConvResult;
|
|
|
|
|
|
|
|
u16 TouchX, TouchY;
|
|
|
|
|
|
|
|
|
2017-02-07 14:23:46 -07:00
|
|
|
bool Init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
2017-01-31 16:24:36 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-02-03 16:12:08 -07:00
|
|
|
TouchX <<= 4;
|
|
|
|
TouchY <<= 4;
|
2017-01-31 16:24:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-03 19:20:50 -07:00
|
|
|
namespace SPI
|
|
|
|
{
|
|
|
|
|
2017-01-31 09:34:17 -07:00
|
|
|
u16 Cnt;
|
2016-12-03 19:20:50 -07:00
|
|
|
|
|
|
|
u32 CurDevice;
|
|
|
|
|
|
|
|
|
2017-02-07 14:23:46 -07:00
|
|
|
bool Init()
|
2016-12-03 19:20:50 -07:00
|
|
|
{
|
2017-02-07 14:23:46 -07:00
|
|
|
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();
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
2017-01-31 09:34:17 -07:00
|
|
|
Cnt = 0;
|
2016-12-03 19:20:50 -07:00
|
|
|
|
|
|
|
SPI_Firmware::Reset();
|
2017-01-19 17:18:30 -07:00
|
|
|
SPI_Powerman::Reset();
|
2017-01-31 16:24:36 -07:00
|
|
|
SPI_TSC::Init();
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WriteCnt(u16 val)
|
|
|
|
{
|
2017-01-31 09:34:17 -07:00
|
|
|
Cnt = (Cnt & 0x0080) | (val & 0xCF03);
|
2016-12-03 19:20:50 -07:00
|
|
|
if (val & 0x0400) printf("!! CRAPOED 16BIT SPI MODE\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 ReadData()
|
|
|
|
{
|
2017-01-31 09:34:17 -07:00
|
|
|
if (!(Cnt & (1<<15))) return 0;
|
2016-12-03 19:20:50 -07:00
|
|
|
|
2017-01-31 09:34:17 -07:00
|
|
|
switch (Cnt & 0x0300)
|
2016-12-03 19:20:50 -07:00
|
|
|
{
|
2017-01-19 17:18:30 -07:00
|
|
|
case 0x0000: return SPI_Powerman::Read();
|
2016-12-03 19:20:50 -07:00
|
|
|
case 0x0100: return SPI_Firmware::Read();
|
2017-01-31 16:24:36 -07:00
|
|
|
case 0x0200: return SPI_TSC::Read();
|
2016-12-03 19:20:50 -07:00
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteData(u8 val)
|
|
|
|
{
|
2017-01-31 09:34:17 -07:00
|
|
|
if (!(Cnt & (1<<15))) return;
|
2016-12-03 19:20:50 -07:00
|
|
|
|
|
|
|
// TODO: take delays into account
|
|
|
|
|
2017-01-31 09:34:17 -07:00
|
|
|
switch (Cnt & 0x0300)
|
2016-12-03 19:20:50 -07:00
|
|
|
{
|
2017-01-31 09:34:17 -07:00
|
|
|
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
|
|
|
|
case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break;
|
2017-01-31 16:24:36 -07:00
|
|
|
case 0x0200: SPI_TSC::Write(val, Cnt&(1<<11)); break;
|
2017-01-31 09:34:17 -07:00
|
|
|
default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break;
|
2016-12-03 19:20:50 -07:00
|
|
|
}
|
|
|
|
|
2017-01-31 09:34:17 -07:00
|
|
|
if (Cnt & (1<<14))
|
2016-12-03 19:20:50 -07:00
|
|
|
NDS::TriggerIRQ(1, NDS::IRQ_SPI);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|