2016-12-05 17:02:29 +00:00
|
|
|
/*
|
2022-01-09 01:15:50 +00:00
|
|
|
Copyright 2016-2022 melonDS team
|
2016-12-05 17:02:29 +00:00
|
|
|
|
|
|
|
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-04 02:20:50 +00:00
|
|
|
#include <stdio.h>
|
2017-01-20 00:18:30 +00:00
|
|
|
#include <string.h>
|
2017-05-11 21:43:57 +00:00
|
|
|
#include <stdlib.h>
|
2023-09-18 19:09:11 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <utility>
|
2016-12-04 02:20:50 +00:00
|
|
|
#include "NDS.h"
|
2021-08-30 18:26:49 +00:00
|
|
|
#include "DSi.h"
|
2016-12-04 02:20:50 +00:00
|
|
|
#include "SPI.h"
|
2019-08-04 09:44:36 +00:00
|
|
|
#include "DSi_SPI_TSC.h"
|
2019-03-27 12:34:26 +00:00
|
|
|
#include "Platform.h"
|
2016-12-04 02:20:50 +00:00
|
|
|
|
2023-08-18 20:50:57 +00:00
|
|
|
using namespace Platform;
|
2016-12-04 02:20:50 +00:00
|
|
|
|
|
|
|
namespace SPI_Firmware
|
|
|
|
{
|
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
std::unique_ptr<Firmware> Firmware;
|
2016-12-04 02:20:50 +00:00
|
|
|
|
|
|
|
u32 Hold;
|
|
|
|
u8 CurCmd;
|
|
|
|
u32 DataPos;
|
|
|
|
u8 Data;
|
|
|
|
|
2016-12-05 17:02:29 +00:00
|
|
|
u8 StatusReg;
|
2016-12-04 02:20:50 +00:00
|
|
|
u32 Addr;
|
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
u16 CRC16(const u8* data, u32 len, u32 start)
|
2017-01-16 03:47:37 +00:00
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
constexpr u16 blarg[8] = {0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001};
|
2017-01-16 03:47:37 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset];
|
|
|
|
u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start);
|
2017-01-16 03:47:37 +00:00
|
|
|
return (crc_stored == crc_calced);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-07 21:23:46 +00:00
|
|
|
bool Init()
|
2016-12-04 02:20:50 +00:00
|
|
|
{
|
2017-02-07 21:23:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
RemoveFirmware();
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 18:05:07 +00:00
|
|
|
u32 FixFirmwareLength(u32 originalLength)
|
|
|
|
{
|
|
|
|
if (originalLength != 0x20000 && originalLength != 0x40000 && originalLength != 0x80000)
|
|
|
|
{
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "Bad firmware size %d, ", originalLength);
|
2021-01-22 18:05:07 +00:00
|
|
|
|
|
|
|
// pick the nearest power-of-two length
|
|
|
|
originalLength |= (originalLength >> 1);
|
|
|
|
originalLength |= (originalLength >> 2);
|
|
|
|
originalLength |= (originalLength >> 4);
|
|
|
|
originalLength |= (originalLength >> 8);
|
|
|
|
originalLength |= (originalLength >> 16);
|
|
|
|
originalLength++;
|
|
|
|
|
|
|
|
// ensure it's a sane length
|
|
|
|
if (originalLength > 0x80000) originalLength = 0x80000;
|
|
|
|
else if (originalLength < 0x20000) originalLength = 0x20000;
|
|
|
|
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Debug, "assuming %d\n", originalLength);
|
2021-01-22 18:05:07 +00:00
|
|
|
}
|
|
|
|
return originalLength;
|
|
|
|
}
|
|
|
|
|
2021-10-02 18:16:27 +00:00
|
|
|
void Reset()
|
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
if (!Firmware)
|
2021-10-02 18:16:27 +00:00
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n");
|
|
|
|
Firmware = std::make_unique<class Firmware>(NDS::ConsoleType);
|
2021-10-02 18:16:27 +00:00
|
|
|
}
|
2021-11-18 17:17:48 +00:00
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
// fix touchscreen coords
|
|
|
|
for (UserData& u : Firmware->UserData())
|
2017-01-31 23:24:36 +00:00
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
u.TouchCalibrationADC1[0] = 0;
|
|
|
|
u.TouchCalibrationADC1[1] = 0;
|
|
|
|
u.TouchCalibrationPixel1[0] = 0;
|
|
|
|
u.TouchCalibrationPixel1[1] = 0;
|
|
|
|
u.TouchCalibrationADC2[0] = 255<<4;
|
|
|
|
u.TouchCalibrationADC2[1] = 191<<4;
|
|
|
|
u.TouchCalibrationPixel2[0] = 255;
|
|
|
|
u.TouchCalibrationPixel2[1] = 191;
|
2017-01-31 23:24:36 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
Firmware->UpdateChecksums();
|
2021-08-30 18:26:49 +00:00
|
|
|
|
|
|
|
// disable autoboot
|
|
|
|
//Firmware[userdata+0x64] &= 0xBF;
|
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
MacAddress mac = Firmware->Header().MacAddress;
|
|
|
|
Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
2017-05-11 21:43:57 +00:00
|
|
|
|
2017-01-16 03:47:37 +00:00
|
|
|
// verify shit
|
2023-09-18 19:09:11 +00:00
|
|
|
u32 mask = Firmware->Mask();
|
|
|
|
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
|
|
|
|
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD");
|
|
|
|
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD");
|
|
|
|
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD");
|
|
|
|
Log(LogLevel::Debug, "FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FE00&mask, 0x70, 0x7FE72&mask)?"GOOD":"BAD");
|
|
|
|
Log(LogLevel::Debug, "FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FF00&mask, 0x70, 0x7FF72&mask)?"GOOD":"BAD");
|
2017-01-16 03:47:37 +00:00
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
Hold = 0;
|
|
|
|
CurCmd = 0;
|
|
|
|
Data = 0;
|
2016-12-05 17:02:29 +00:00
|
|
|
StatusReg = 0x00;
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 00:54:48 +00:00
|
|
|
void DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
file->Section("SPFW");
|
|
|
|
|
|
|
|
// CHECKME/TODO: trust the firmware to stay the same?????
|
|
|
|
// embedding the whole firmware in the savestate would be derpo tho??
|
|
|
|
|
|
|
|
file->Var32(&Hold);
|
|
|
|
file->Var8(&CurCmd);
|
|
|
|
file->Var32(&DataPos);
|
|
|
|
file->Var8(&Data);
|
|
|
|
|
|
|
|
file->Var8(&StatusReg);
|
|
|
|
file->Var32(&Addr);
|
|
|
|
}
|
|
|
|
|
2021-08-30 18:26:49 +00:00
|
|
|
void SetupDirectBoot(bool dsi)
|
2017-03-20 23:53:04 +00:00
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
const FirmwareHeader& header = Firmware->Header();
|
|
|
|
const UserData& userdata = Firmware->EffectiveUserData();
|
2021-08-30 18:26:49 +00:00
|
|
|
if (dsi)
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < 6; i += 2)
|
2023-09-18 19:09:11 +00:00
|
|
|
DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddress[i]); // MAC address
|
2017-03-20 23:53:04 +00:00
|
|
|
|
2021-08-30 18:26:49 +00:00
|
|
|
// checkme
|
2023-09-18 19:09:11 +00:00
|
|
|
DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels
|
2017-03-20 23:53:04 +00:00
|
|
|
|
2021-08-30 18:26:49 +00:00
|
|
|
for (u32 i = 0; i < 0x70; i += 4)
|
2023-09-18 19:09:11 +00:00
|
|
|
DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]);
|
2021-08-30 18:26:49 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NDS::ARM9Write32(0x027FF864, 0);
|
2023-09-18 19:09:11 +00:00
|
|
|
NDS::ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset
|
2021-08-30 18:26:49 +00:00
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
NDS::ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx
|
|
|
|
NDS::ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // CRC16 for GUI/wifi code
|
2021-08-30 18:26:49 +00:00
|
|
|
|
|
|
|
for (u32 i = 0; i < 0x70; i += 4)
|
2023-09-18 19:09:11 +00:00
|
|
|
NDS::ARM9Write32(0x027FFC80+i, *(u32*)&userdata.Bytes[i]);
|
2021-08-30 18:26:49 +00:00
|
|
|
}
|
2017-03-20 23:53:04 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 19:09:11 +00:00
|
|
|
const class Firmware* GetFirmware()
|
|
|
|
{
|
|
|
|
return Firmware.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsLoadedFirmwareBuiltIn()
|
|
|
|
{
|
|
|
|
return Firmware->Header().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InstallFirmware(class Firmware&& firmware)
|
|
|
|
{
|
|
|
|
if (!firmware.Buffer())
|
|
|
|
{
|
|
|
|
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Firmware = std::make_unique<class Firmware>(std::move(firmware));
|
|
|
|
|
|
|
|
FirmwareIdentifier id = Firmware->Header().Identifier;
|
|
|
|
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InstallFirmware(std::unique_ptr<class Firmware>&& firmware)
|
|
|
|
{
|
|
|
|
if (!firmware)
|
|
|
|
{
|
|
|
|
Log(LogLevel::Error, "SPI firmware: firmware is null!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!firmware->Buffer())
|
|
|
|
{
|
|
|
|
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Firmware = std::move(firmware);
|
|
|
|
|
|
|
|
FirmwareIdentifier id = Firmware->Header().Identifier;
|
|
|
|
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RemoveFirmware()
|
|
|
|
{
|
|
|
|
Firmware.reset();
|
|
|
|
Log(LogLevel::Debug, "Removed installed firmware (if any)\n");
|
|
|
|
}
|
2017-04-07 15:37:49 +00:00
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
u8 Read()
|
|
|
|
{
|
|
|
|
return Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Write(u8 val, u32 hold)
|
|
|
|
{
|
|
|
|
if (!hold)
|
|
|
|
{
|
2017-06-25 22:35:19 +00:00
|
|
|
if (!Hold) // commands with no paramters
|
|
|
|
CurCmd = val;
|
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
Hold = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hold && (!Hold))
|
|
|
|
{
|
|
|
|
CurCmd = val;
|
|
|
|
Hold = 1;
|
2016-12-05 17:02:29 +00:00
|
|
|
Data = 0;
|
2016-12-04 02:20:50 +00:00
|
|
|
DataPos = 1;
|
|
|
|
Addr = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (CurCmd)
|
|
|
|
{
|
|
|
|
case 0x03: // read
|
|
|
|
{
|
|
|
|
if (DataPos < 4)
|
|
|
|
{
|
|
|
|
Addr <<= 8;
|
|
|
|
Addr |= val;
|
|
|
|
Data = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
Data = Firmware->Buffer()[Addr & Firmware->Mask()];
|
2016-12-04 02:20:50 +00:00
|
|
|
Addr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-12-05 17:02:29 +00: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;
|
|
|
|
|
2017-06-25 22:35:19 +00:00
|
|
|
case 0x0A: // write
|
|
|
|
{
|
|
|
|
// TODO: what happens if you write too many bytes? (max 256, they say)
|
|
|
|
if (DataPos < 4)
|
2023-09-18 19:09:11 +00:00
|
|
|
{ // If we're in the middle of writing the address...
|
2017-06-25 22:35:19 +00:00
|
|
|
Addr <<= 8;
|
|
|
|
Addr |= val;
|
|
|
|
Data = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-09-18 19:09:11 +00:00
|
|
|
Firmware->Buffer()[Addr & Firmware->Mask()] = val;
|
2017-06-25 22:35:19 +00:00
|
|
|
Data = val;
|
|
|
|
Addr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-12-05 17:02:29 +00:00
|
|
|
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-04 02:20:50 +00:00
|
|
|
default:
|
2023-03-23 17:04:38 +00:00
|
|
|
Log(LogLevel::Warn, "unknown firmware SPI command %02X\n", CurCmd);
|
2022-03-14 17:08:29 +00:00
|
|
|
Data = 0xFF;
|
2016-12-04 02:20:50 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-06-25 22:35:19 +00:00
|
|
|
|
|
|
|
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
|
2023-09-18 19:09:11 +00:00
|
|
|
{ // If the SPI firmware chip just finished a write...
|
|
|
|
// We only notify the frontend of changes to the Wi-fi/userdata settings region
|
|
|
|
// (although it might still decide to flush the whole thing)
|
|
|
|
u32 wifioffset = Firmware->WifiAccessPointOffset();
|
|
|
|
|
|
|
|
// Request that the start of the Wi-fi/userdata settings region
|
|
|
|
// through the end of the firmware blob be flushed to disk
|
|
|
|
Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset);
|
2017-06-25 22:35:19 +00:00
|
|
|
}
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-20 00:18:30 +00:00
|
|
|
namespace SPI_Powerman
|
|
|
|
{
|
|
|
|
|
|
|
|
u32 Hold;
|
|
|
|
u32 DataPos;
|
|
|
|
u8 Index;
|
|
|
|
u8 Data;
|
|
|
|
|
|
|
|
u8 Registers[8];
|
|
|
|
u8 RegMasks[8];
|
|
|
|
|
|
|
|
|
2017-02-07 21:23:46 +00:00
|
|
|
bool Init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
2017-01-20 00:18:30 +00: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;
|
2022-03-06 21:47:21 +00:00
|
|
|
RegMasks[1] = 0x00;
|
2017-01-20 00:18:30 +00:00
|
|
|
RegMasks[2] = 0x01;
|
|
|
|
RegMasks[3] = 0x03;
|
|
|
|
RegMasks[4] = 0x0F;
|
|
|
|
}
|
|
|
|
|
2022-02-18 15:32:46 +00:00
|
|
|
bool GetBatteryLevelOkay() { return !Registers[1]; }
|
|
|
|
void SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; }
|
|
|
|
|
2018-10-18 00:54:48 +00:00
|
|
|
void DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
file->Section("SPPW");
|
|
|
|
|
|
|
|
file->Var32(&Hold);
|
|
|
|
file->Var32(&DataPos);
|
|
|
|
file->Var8(&Index);
|
|
|
|
file->Var8(&Data);
|
|
|
|
|
|
|
|
file->VarArray(Registers, 8);
|
|
|
|
file->VarArray(RegMasks, 8); // is that needed??
|
|
|
|
}
|
|
|
|
|
2017-01-20 00:18:30 +00:00
|
|
|
u8 Read()
|
|
|
|
{
|
|
|
|
return Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Write(u8 val, u32 hold)
|
2017-09-21 01:59:12 +00:00
|
|
|
{
|
2017-01-20 00:18:30 +00:00
|
|
|
if (!hold)
|
|
|
|
{
|
|
|
|
Hold = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hold && (!Hold))
|
|
|
|
{
|
|
|
|
Index = val;
|
|
|
|
Hold = 1;
|
|
|
|
Data = 0;
|
|
|
|
DataPos = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DataPos == 1)
|
|
|
|
{
|
2022-03-06 21:47:21 +00:00
|
|
|
// TODO: DSi-specific registers in DSi mode
|
2017-08-05 17:13:55 +00:00
|
|
|
u32 regid = Index & 0x07;
|
|
|
|
|
2017-01-20 00:18:30 +00:00
|
|
|
if (Index & 0x80)
|
|
|
|
{
|
2017-08-05 17:13:55 +00:00
|
|
|
Data = Registers[regid];
|
2017-01-20 00:18:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-05 17:13:55 +00:00
|
|
|
Registers[regid] = (Registers[regid] & ~RegMasks[regid]) | (val & RegMasks[regid]);
|
|
|
|
|
|
|
|
switch (regid)
|
|
|
|
{
|
|
|
|
case 0:
|
2023-08-18 20:50:57 +00:00
|
|
|
if (val & 0x40) NDS::Stop(StopReason::PowerOff); // shutdown
|
2017-09-21 01:59:12 +00:00
|
|
|
//printf("power %02X\n", val);
|
2017-08-05 17:13:55 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2017-09-21 01:59:12 +00:00
|
|
|
//printf("brightness %02X\n", val);
|
2017-08-05 17:13:55 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-01-20 00:18:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Data = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
|
2017-01-31 23:24:36 +00:00
|
|
|
namespace SPI_TSC
|
|
|
|
{
|
|
|
|
|
|
|
|
u32 DataPos;
|
|
|
|
u8 ControlByte;
|
|
|
|
u8 Data;
|
|
|
|
|
|
|
|
u16 ConvResult;
|
|
|
|
|
|
|
|
u16 TouchX, TouchY;
|
|
|
|
|
2018-12-12 15:33:40 +00:00
|
|
|
s16 MicBuffer[1024];
|
|
|
|
int MicBufferLen;
|
|
|
|
|
2017-01-31 23:24:36 +00:00
|
|
|
|
2017-02-07 21:23:46 +00:00
|
|
|
bool Init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
2017-01-31 23:24:36 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
ControlByte = 0;
|
|
|
|
Data = 0;
|
|
|
|
|
|
|
|
ConvResult = 0;
|
2018-12-12 15:33:40 +00:00
|
|
|
|
|
|
|
MicBufferLen = 0;
|
2017-01-31 23:24:36 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 00:54:48 +00:00
|
|
|
void DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
file->Section("SPTS");
|
|
|
|
|
|
|
|
file->Var32(&DataPos);
|
|
|
|
file->Var8(&ControlByte);
|
|
|
|
file->Var8(&Data);
|
|
|
|
|
|
|
|
file->Var16(&ConvResult);
|
|
|
|
}
|
|
|
|
|
2017-01-31 23:24:36 +00:00
|
|
|
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 23:12:08 +00:00
|
|
|
TouchX <<= 4;
|
|
|
|
TouchY <<= 4;
|
2017-01-31 23:24:36 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 15:33:40 +00:00
|
|
|
void MicInputFrame(s16* data, int samples)
|
|
|
|
{
|
2018-12-14 04:15:57 +00:00
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
MicBufferLen = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-12 15:33:40 +00:00
|
|
|
if (samples > 1024) samples = 1024;
|
|
|
|
memcpy(MicBuffer, data, samples*sizeof(s16));
|
|
|
|
MicBufferLen = samples;
|
|
|
|
}
|
|
|
|
|
2017-01-31 23:24:36 +00: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;
|
2018-12-12 15:33:40 +00:00
|
|
|
|
|
|
|
case 0x60:
|
|
|
|
{
|
|
|
|
if (MicBufferLen == 0)
|
|
|
|
ConvResult = 0x800;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 560190 cycles per frame
|
|
|
|
u32 cyclepos = (u32)NDS::GetSysClockCycles(2);
|
|
|
|
u32 samplepos = (cyclepos * MicBufferLen) / 560190;
|
2018-12-12 16:23:54 +00:00
|
|
|
if (samplepos >= MicBufferLen) samplepos = MicBufferLen-1;
|
2018-12-12 15:33:40 +00:00
|
|
|
s16 sample = MicBuffer[samplepos];
|
|
|
|
|
|
|
|
// make it louder
|
|
|
|
//if (sample > 0x3FFF) sample = 0x7FFF;
|
|
|
|
//else if (sample < -0x4000) sample = -0x8000;
|
|
|
|
//else sample <<= 1;
|
|
|
|
|
|
|
|
// make it unsigned 12-bit
|
|
|
|
sample ^= 0x8000;
|
|
|
|
ConvResult = sample >> 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-01-31 23:24:36 +00:00
|
|
|
default: ConvResult = 0xFFF; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ControlByte & 0x08)
|
|
|
|
ConvResult &= 0x0FF0; // checkme
|
|
|
|
}
|
|
|
|
else
|
|
|
|
DataPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
namespace SPI
|
|
|
|
{
|
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
u16 Cnt;
|
2016-12-04 02:20:50 +00:00
|
|
|
|
2019-06-15 14:58:02 +00:00
|
|
|
u32 CurDevice; // remove me
|
2016-12-04 02:20:50 +00:00
|
|
|
|
|
|
|
|
2017-02-07 21:23:46 +00:00
|
|
|
bool Init()
|
2016-12-04 02:20:50 +00:00
|
|
|
{
|
2023-11-02 20:04:09 +00:00
|
|
|
NDS::RegisterEventFunc(NDS::Event_SPITransfer, 0, TransferDone);
|
|
|
|
|
2017-02-07 21:23:46 +00:00
|
|
|
if (!SPI_Firmware::Init()) return false;
|
|
|
|
if (!SPI_Powerman::Init()) return false;
|
|
|
|
if (!SPI_TSC::Init()) return false;
|
2019-08-04 09:44:36 +00:00
|
|
|
if (!DSi_SPI_TSC::Init()) return false;
|
2017-02-07 21:23:46 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeInit()
|
|
|
|
{
|
|
|
|
SPI_Firmware::DeInit();
|
|
|
|
SPI_Powerman::DeInit();
|
|
|
|
SPI_TSC::DeInit();
|
2019-08-04 09:44:36 +00:00
|
|
|
DSi_SPI_TSC::DeInit();
|
2023-11-02 20:04:09 +00:00
|
|
|
|
|
|
|
NDS::UnregisterEventFunc(NDS::Event_SPITransfer, 0);
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
2017-01-31 16:34:17 +00:00
|
|
|
Cnt = 0;
|
2016-12-04 02:20:50 +00:00
|
|
|
|
|
|
|
SPI_Firmware::Reset();
|
2017-01-20 00:18:30 +00:00
|
|
|
SPI_Powerman::Reset();
|
2017-05-25 20:46:01 +00:00
|
|
|
SPI_TSC::Reset();
|
2020-06-01 18:36:30 +00:00
|
|
|
if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset();
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 00:54:48 +00:00
|
|
|
void DoSavestate(Savestate* file)
|
|
|
|
{
|
|
|
|
file->Section("SPIG");
|
|
|
|
|
|
|
|
file->Var16(&Cnt);
|
|
|
|
file->Var32(&CurDevice);
|
|
|
|
|
|
|
|
SPI_Firmware::DoSavestate(file);
|
|
|
|
SPI_Powerman::DoSavestate(file);
|
|
|
|
SPI_TSC::DoSavestate(file);
|
2020-06-01 18:36:30 +00:00
|
|
|
if (NDS::ConsoleType == 1) DSi_SPI_TSC::DoSavestate(file);
|
2018-10-18 00:54:48 +00:00
|
|
|
}
|
|
|
|
|
2016-12-04 02:20:50 +00:00
|
|
|
|
|
|
|
void WriteCnt(u16 val)
|
|
|
|
{
|
2017-12-18 23:20:36 +00:00
|
|
|
// turning it off should clear chipselect
|
|
|
|
// TODO: confirm on hardware. libnds expects this, though.
|
|
|
|
if ((Cnt & (1<<15)) && !(val & (1<<15)))
|
|
|
|
{
|
|
|
|
switch (Cnt & 0x0300)
|
|
|
|
{
|
|
|
|
case 0x0000: SPI_Powerman::Hold = 0; break;
|
|
|
|
case 0x0100: SPI_Firmware::Hold = 0; break;
|
2020-06-01 18:36:30 +00:00
|
|
|
case 0x0200:
|
|
|
|
if (NDS::ConsoleType == 1)
|
|
|
|
DSi_SPI_TSC::DataPos = 0;
|
|
|
|
else
|
|
|
|
SPI_TSC::DataPos = 0;
|
|
|
|
break;
|
2017-12-18 23:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-13 23:27:03 +00:00
|
|
|
// TODO: presumably the transfer speed can be changed during a transfer
|
|
|
|
// like with the NDSCart SPI interface
|
2017-01-31 16:34:17 +00:00
|
|
|
Cnt = (Cnt & 0x0080) | (val & 0xCF03);
|
2023-03-23 17:04:38 +00:00
|
|
|
if (val & 0x0400) Log(LogLevel::Warn, "!! CRAPOED 16BIT SPI MODE\n");
|
|
|
|
if (Cnt & (1<<7)) Log(LogLevel::Warn, "!! CHANGING SPICNT DURING TRANSFER: %04X\n", val);
|
2017-07-15 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TransferDone(u32 param)
|
|
|
|
{
|
|
|
|
Cnt &= ~(1<<7);
|
|
|
|
|
|
|
|
if (Cnt & (1<<14))
|
|
|
|
NDS::SetIRQ(1, NDS::IRQ_SPI);
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 ReadData()
|
|
|
|
{
|
2017-01-31 16:34:17 +00:00
|
|
|
if (!(Cnt & (1<<15))) return 0;
|
2017-07-15 17:29:10 +00:00
|
|
|
if (Cnt & (1<<7)) return 0; // checkme
|
2016-12-04 02:20:50 +00:00
|
|
|
|
2017-01-31 16:34:17 +00:00
|
|
|
switch (Cnt & 0x0300)
|
2016-12-04 02:20:50 +00:00
|
|
|
{
|
2017-01-20 00:18:30 +00:00
|
|
|
case 0x0000: return SPI_Powerman::Read();
|
2016-12-04 02:20:50 +00:00
|
|
|
case 0x0100: return SPI_Firmware::Read();
|
2020-06-01 18:36:30 +00:00
|
|
|
case 0x0200:
|
|
|
|
if (NDS::ConsoleType == 1)
|
|
|
|
return DSi_SPI_TSC::Read();
|
|
|
|
else
|
|
|
|
return SPI_TSC::Read();
|
2016-12-04 02:20:50 +00:00
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteData(u8 val)
|
|
|
|
{
|
2017-01-31 16:34:17 +00:00
|
|
|
if (!(Cnt & (1<<15))) return;
|
2022-03-13 23:27:03 +00:00
|
|
|
if (Cnt & (1<<7)) return;
|
2016-12-04 02:20:50 +00:00
|
|
|
|
2017-07-15 17:29:10 +00:00
|
|
|
Cnt |= (1<<7);
|
2017-01-31 16:34:17 +00:00
|
|
|
switch (Cnt & 0x0300)
|
2016-12-04 02:20:50 +00:00
|
|
|
{
|
2017-01-31 16:34:17 +00:00
|
|
|
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
|
|
|
|
case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break;
|
2020-06-01 18:36:30 +00:00
|
|
|
case 0x0200:
|
|
|
|
if (NDS::ConsoleType == 1)
|
|
|
|
DSi_SPI_TSC::Write(val, Cnt&(1<<11));
|
|
|
|
else
|
|
|
|
SPI_TSC::Write(val, Cnt&(1<<11));
|
|
|
|
break;
|
2023-03-23 17:04:38 +00:00
|
|
|
default: Log(LogLevel::Warn, "SPI to unknown device %04X %02X\n", Cnt, val); break;
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 17:29:10 +00:00
|
|
|
// SPI transfers one bit per cycle -> 8 cycles per byte
|
|
|
|
u32 delay = 8 * (8 << (Cnt & 0x3));
|
2023-11-02 20:04:09 +00:00
|
|
|
NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, 0, 0);
|
2016-12-04 02:20:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|