#include #include #include #include <3ds.h> #include #include #include #include #include #include "cdc_bin.h" #define COMMON_TYPE_3DS #include "../../src/test.h" PrintConsole topScreen, bottomScreen; void MoveCursor(unsigned row, unsigned col) { printf("\x1b[%u;%uH", row + 1, col + 1); } enum Color { Reset = 0, Black = 30, Red = 31, Green = 32, Yellow = 33, Blue = 34, Magnenta = 35, Cyan = 36, White = 37, }; void SetColor(Color color, Color background) { printf("\x1b[%dm\x1b[%dm", (int)color, (int)background + 10); } class IReg { public: IReg(const std::string& name, const std::string& flags) : name(name), flags(flags) {} virtual ~IReg() = default; virtual unsigned GetLength() = 0; virtual unsigned GetSrcDigit(unsigned pos) = 0; virtual unsigned GetDstDigit(unsigned pos) = 0; virtual void SetSrcDigit(unsigned pos, unsigned value) = 0; virtual unsigned GetDigitRange() = 0; void Print(unsigned row, unsigned col, unsigned selected) { SetColor(Cyan, Black); MoveCursor(row, col - name.size()); printf("%s", name.c_str()); unsigned len = GetLength(); for (unsigned i = 0; i < len; ++i) { unsigned src = GetSrcDigit(len - i - 1); unsigned dst = GetDstDigit(len - i - 1); MoveCursor(row, col + i); if (len - i - 1 == selected) SetColor(Black, White); else SetColor(White, Black); if (flags.empty()) printf("%X", src); else printf("%c", src ? flags[i] : '-'); MoveCursor(row + 1, col + i); SetColor(src == dst ? Magnenta : Green, Black); if (flags.empty()) printf("%X", dst); else printf("%c", dst ? flags[i] : '-'); } } private: std::string name, flags; }; class HexReg : public IReg { public: HexReg(const std::string& name, u16& src, u16& dst) : IReg(name, ""), src(src), dst(dst) { src = dst; } unsigned GetLength() override { return 4; } unsigned GetSrcDigit(unsigned pos) override { return (src >> (pos * 4)) & 0xF; } unsigned GetDstDigit(unsigned pos) override { return (dst >> (pos * 4)) & 0xF; } void SetSrcDigit(unsigned pos, unsigned value) override { src &= ~(0xF << (pos * 4)); src |= value << (pos * 4); } unsigned GetDigitRange() override { return 16; } private: u16& src; u16& dst; }; class BinReg : public IReg { public: BinReg(const std::string& name, u16& src, u16& dst, const std::string flags) : IReg(name, flags), src(src), dst(dst) { src = dst; } unsigned GetLength() override { return 16; } unsigned GetSrcDigit(unsigned pos) override { return (src >> pos) & 1; } unsigned GetDstDigit(unsigned pos) override { return (dst >> pos) & 1; } void SetSrcDigit(unsigned pos, unsigned value) override { src &= ~(1 << pos); src |= value << pos; } unsigned GetDigitRange() override { return 2; } private: u16& src; u16& dst; }; u16* dspP = (u16*)0x1FF00000; u16* dspD = (u16*)0x1FF40000; u16* srcBase = &dspD[0x2000]; u16* dstBase = &dspD[0x2FD0]; constexpr u32 reg_region_size = 0x60; std::unordered_map reg_map; IReg* MakeHexReg(const std::string& name, unsigned offset) { reg_map[name] = offset; return new HexReg(name, srcBase[offset], dstBase[offset]); } IReg* MakeBinReg(const std::string& name, unsigned offset, const std::string flags = "") { reg_map[name] = offset; return new BinReg(name, srcBase[offset], dstBase[offset], flags); } constexpr unsigned t_row = 15; constexpr unsigned t_col = 4; IReg* grid[t_row][t_col] = {}; unsigned c_row = 0, c_col = 0, c_pos = 0; void PrintGrid(unsigned row, unsigned col) { IReg* r = grid[row][col]; if (!r) return; r->Print(row * 2, 4 + col * 9, row == c_row && col == c_col ? c_pos : 0xFFFFFFFF); } void PrintAll() { for (unsigned row = 0; row < t_row; ++row) for (unsigned col = 0; col < t_col; ++col) PrintGrid(row, col); } void FlushCache(void* ptr, u32 size) { svcFlushProcessDataCache(CUR_PROCESS_HANDLE, ptr, size); } void InvalidateCache(void* ptr, u32 size) { svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, ptr, size); } void StartDspProgram() { dspD[1] = 1; FlushCache(&dspD[1], 2); while (dspD[1]) InvalidateCache(&dspD[1], 2); } void StopDspProgram() { dspD[2] = 1; FlushCache(&dspD[2], 2); while (dspD[2]) InvalidateCache(&dspD[2], 2); } void SetOnshotDspProgram() { dspD[3] = 1; FlushCache(&dspD[3], 2); } void PulseDspProgram() { SetOnshotDspProgram(); StartDspProgram(); StopDspProgram(); } void ExecuteTestCases() { consoleSelect(&bottomScreen); printf("--------\nExecuting test cases...!\n"); FILE* fi = fopen("teaklite2_tests", "rb"); if (!fi) printf("failed to open input file\n"); FILE* fo = fopen("teaklite2_tests_result", "wb"); if (!fo) printf("failed to open output file\n"); StopDspProgram(); int i = 0; while (true) { TestCase test_case; if (!fread(&test_case, sizeof(test_case), 1, fi)) break; srcBase[reg_map.at("a0l")] = test_case.before.a[0] & 0xFFFF; srcBase[reg_map.at("a0h")] = (test_case.before.a[0] >> 16) & 0xFFFF; srcBase[reg_map.at("a0e")] = (test_case.before.a[0] >> 32) & 0xFFFF; srcBase[reg_map.at("a1l")] = test_case.before.a[1] & 0xFFFF; srcBase[reg_map.at("a1h")] = (test_case.before.a[1] >> 16) & 0xFFFF; srcBase[reg_map.at("a1e")] = (test_case.before.a[1] >> 32) & 0xFFFF; srcBase[reg_map.at("b0l")] = test_case.before.b[0] & 0xFFFF; srcBase[reg_map.at("b0h")] = (test_case.before.b[0] >> 16) & 0xFFFF; srcBase[reg_map.at("b0e")] = (test_case.before.b[0] >> 32) & 0xFFFF; srcBase[reg_map.at("b1l")] = test_case.before.b[1] & 0xFFFF; srcBase[reg_map.at("b1h")] = (test_case.before.b[1] >> 16) & 0xFFFF; srcBase[reg_map.at("b1e")] = (test_case.before.b[1] >> 32) & 0xFFFF; srcBase[reg_map.at("p0l")] = test_case.before.p[0] & 0xFFFF; srcBase[reg_map.at("p0h")] = (test_case.before.p[0] >> 16) & 0xFFFF; srcBase[reg_map.at("p1l")] = test_case.before.p[1] & 0xFFFF; srcBase[reg_map.at("p1h")] = (test_case.before.p[1] >> 16) & 0xFFFF; srcBase[reg_map.at("r0")] = test_case.before.r[0]; srcBase[reg_map.at("r1")] = test_case.before.r[1]; srcBase[reg_map.at("r2")] = test_case.before.r[2]; srcBase[reg_map.at("r3")] = test_case.before.r[3]; srcBase[reg_map.at("r4")] = test_case.before.r[4]; srcBase[reg_map.at("r5")] = test_case.before.r[5]; srcBase[reg_map.at("r6")] = test_case.before.r[6]; srcBase[reg_map.at("r7")] = test_case.before.r[7]; srcBase[reg_map.at("x0")] = test_case.before.x[0]; srcBase[reg_map.at("y0")] = test_case.before.y[0]; srcBase[reg_map.at("x1")] = test_case.before.x[1]; srcBase[reg_map.at("y1")] = test_case.before.y[1]; srcBase[reg_map.at("sti0")] = test_case.before.stepi0; srcBase[reg_map.at("stj0")] = test_case.before.stepj0; srcBase[reg_map.at("mixp")] = test_case.before.mixp; srcBase[reg_map.at("sv")] = test_case.before.sv; srcBase[reg_map.at("repc")] = test_case.before.repc; srcBase[reg_map.at("lc")] = test_case.before.lc; srcBase[reg_map.at("cfgi")] = test_case.before.cfgi; srcBase[reg_map.at("cfgj")] = test_case.before.cfgj; srcBase[reg_map.at("stt0")] = test_case.before.stt0; srcBase[reg_map.at("stt1")] = test_case.before.stt1; srcBase[reg_map.at("stt2")] = test_case.before.stt2; srcBase[reg_map.at("mod0")] = test_case.before.mod0; srcBase[reg_map.at("mod1")] = test_case.before.mod1; srcBase[reg_map.at("mod2")] = test_case.before.mod2; srcBase[reg_map.at("ar0")] = test_case.before.ar[0]; srcBase[reg_map.at("ar1")] = test_case.before.ar[1]; srcBase[reg_map.at("arp0")] = test_case.before.arp[0]; srcBase[reg_map.at("arp1")] = test_case.before.arp[1]; srcBase[reg_map.at("arp2")] = test_case.before.arp[2]; srcBase[reg_map.at("arp3")] = test_case.before.arp[3]; for (u16 i = 0; i < TestSpaceSize; ++i) { dspD[TestSpaceX + i] = test_case.before.test_space_x[i]; dspD[TestSpaceY + i] = test_case.before.test_space_y[i]; } InvalidateCache(&dspP[0x2000], 0x2000); dspP[0x2000] = test_case.opcode; dspP[0x2001] = test_case.expand; dspP[0x2002] = 0x0000; // nop dspP[0x2003] = 0x0000; // nop dspP[0x2004] = 0x0000; // nop dspP[0x2005] = 0x4180; // br 0x1800 dspP[0x2006] = 0x1800; FlushCache(&dspP[0x2000], 0x2000); FlushCache(&dspD[0], 0x20000); InvalidateCache(&dspD[0], 0x20000); PulseDspProgram(); FlushCache(&dspD[0], 0x20000); InvalidateCache(&dspD[0], 0x20000); test_case.after.a[0] = dstBase[reg_map.at("a0l")] | ((u64)dstBase[reg_map.at("a0h")] << 16) | ((u64)dstBase[reg_map.at("a0e")] << 32); test_case.after.a[1] = dstBase[reg_map.at("a1l")] | ((u64)dstBase[reg_map.at("a1h")] << 16) | ((u64)dstBase[reg_map.at("a1e")] << 32); test_case.after.b[0] = dstBase[reg_map.at("b0l")] | ((u64)dstBase[reg_map.at("b0h")] << 16) | ((u64)dstBase[reg_map.at("b0e")] << 32); test_case.after.b[1] = dstBase[reg_map.at("b1l")] | ((u64)dstBase[reg_map.at("b1h")] << 16) | ((u64)dstBase[reg_map.at("b1e")] << 32); test_case.after.p[0] = dstBase[reg_map.at("p0l")] | ((u64)dstBase[reg_map.at("p0h")] << 16); test_case.after.p[1] = dstBase[reg_map.at("p1l")] | ((u64)dstBase[reg_map.at("p1h")] << 16); test_case.after.r[0] = dstBase[reg_map.at("r0")]; test_case.after.r[1] = dstBase[reg_map.at("r1")]; test_case.after.r[2] = dstBase[reg_map.at("r2")]; test_case.after.r[3] = dstBase[reg_map.at("r3")]; test_case.after.r[4] = dstBase[reg_map.at("r4")]; test_case.after.r[5] = dstBase[reg_map.at("r5")]; test_case.after.r[6] = dstBase[reg_map.at("r6")]; test_case.after.r[7] = dstBase[reg_map.at("r7")]; test_case.after.x[0] = dstBase[reg_map.at("x0")]; test_case.after.y[0] = dstBase[reg_map.at("y0")]; test_case.after.x[1] = dstBase[reg_map.at("x1")]; test_case.after.y[1] = dstBase[reg_map.at("y1")]; test_case.after.stepi0 = dstBase[reg_map.at("sti0")]; test_case.after.stepj0 = dstBase[reg_map.at("stj0")]; test_case.after.mixp = dstBase[reg_map.at("mixp")]; test_case.after.sv = dstBase[reg_map.at("sv")]; test_case.after.repc = dstBase[reg_map.at("repc")]; test_case.after.lc = dstBase[reg_map.at("lc")]; test_case.after.cfgi = dstBase[reg_map.at("cfgi")]; test_case.after.cfgj = dstBase[reg_map.at("cfgj")]; test_case.after.stt0 = dstBase[reg_map.at("stt0")]; test_case.after.stt1 = dstBase[reg_map.at("stt1")]; test_case.after.stt2 = dstBase[reg_map.at("stt2")]; test_case.after.mod0 = dstBase[reg_map.at("mod0")]; test_case.after.mod1 = dstBase[reg_map.at("mod1")]; test_case.after.mod2 = dstBase[reg_map.at("mod2")]; test_case.after.ar[0] = dstBase[reg_map.at("ar0")]; test_case.after.ar[1] = dstBase[reg_map.at("ar1")]; test_case.after.arp[0] = dstBase[reg_map.at("arp0")]; test_case.after.arp[1] = dstBase[reg_map.at("arp1")]; test_case.after.arp[2] = dstBase[reg_map.at("arp2")]; test_case.after.arp[3] = dstBase[reg_map.at("arp3")]; for (u16 i = 0; i < TestSpaceSize; ++i) { test_case.after.test_space_x[i] = dspD[TestSpaceX + i]; test_case.after.test_space_y[i] = dspD[TestSpaceY + i]; } fwrite(&test_case, sizeof(test_case), 1, fo); ++i; if (i % 100 == 0) printf("case %d\n", i); } fclose(fi); fclose(fo); printf("Finished!\n"); consoleSelect(&topScreen); } void UploadDspProgram(const std::vector& code, bool oneshot) { StopDspProgram(); InvalidateCache(&dspP[0x2000], 0x2000); memcpy(&dspP[0x2000], code.data(), code.size() * 2); unsigned end = 0x2000 + code.size(); dspP[end + 0] = 0x0000; // nop dspP[end + 1] = 0x0000; // nop dspP[end + 2] = 0x0000; // nop dspP[end + 3] = 0x4180; // br 0x1800 dspP[end + 4] = 0x1800; FlushCache(&dspP[0x2000], 0x2000); if (oneshot) { PulseDspProgram(); } else { StartDspProgram(); } } int udp_s; int udp_s_broadcast; void UdpInit() { #define SOC_ALIGN 0x1000 #define SOC_BUFFERSIZE 0x100000 static u32* SOC_buffer; // allocate buffer for SOC service SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE); if (SOC_buffer == NULL) { printf("memalign: failed to allocate\n"); return; } Result ret; if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) { printf("socInit: 0x%08lX\n", ret); return; } sockaddr_in si_me; // create a UDP socket if ((udp_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { printf("socket() failed\n"); return; } // zero out the structure memset(&si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(8888); si_me.sin_addr.s_addr = htonl(INADDR_ANY); // bind socket to port if (bind(udp_s, (sockaddr*)&si_me, sizeof(si_me)) == -1) { printf("bind() failed\n"); return; } // create a UDP broadcast socket if ((udp_s_broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { printf("socket()(broadcast) failed\n"); return; } } void CheckPackage() { constexpr unsigned BUFLEN = 512; char buf[BUFLEN]; sockaddr_in si_other; socklen_t slen = sizeof(si_other); int recv_len; if ((recv_len = recvfrom(udp_s, buf, BUFLEN, MSG_DONTWAIT, (sockaddr*)&si_other, &slen)) < 4) return; u16 magic; memcpy(&magic, buf, 2); if (magic == 0xD590 || magic == 0xD591) { std::vector program_package((recv_len - 2) / 2); memcpy(program_package.data(), buf + 2, program_package.size() * 2); consoleSelect(&bottomScreen); printf("--------\nNew program received!\n"); for (u16 code : program_package) { printf("%04X ", code); } UploadDspProgram(program_package, magic == 0xD591); printf("\nUploaded!\n"); consoleSelect(&topScreen); } else if (magic == 0x4352) { recv_len -= 2; if ((unsigned)recv_len > reg_region_size) recv_len = reg_region_size; consoleSelect(&bottomScreen); printf("--------\nRegister sync received!\n"); memcpy(srcBase, buf + 2, recv_len); printf("\nSynced!\n"); consoleSelect(&topScreen); } } int main() { aptInit(); gfxInitDefault(); consoleInit(GFX_TOP, &topScreen); consoleInit(GFX_BOTTOM, &bottomScreen); consoleSelect(&bottomScreen); printf("Hello!\n"); UdpInit(); printf("dspInit: %08lX\n", dspInit()); bool loaded = false; printf("DSP_LoadComponent: %08lX\n", DSP_LoadComponent(cdc_bin, cdc_bin_size, 0xFF, 0xFF, &loaded)); printf("loaded = %d\n", loaded); consoleSelect(&topScreen); svcSleepThread(1000000000); grid[7][0] = MakeHexReg("r7", 0); grid[6][0] = MakeHexReg("r6", 1); grid[5][0] = MakeHexReg("r5", 2); grid[4][0] = MakeHexReg("r4", 3); grid[3][0] = MakeHexReg("r3", 4); grid[2][0] = MakeHexReg("r2", 5); grid[1][0] = MakeHexReg("r1", 6); grid[0][0] = MakeHexReg("r0", 7); grid[6][1] = MakeHexReg("mixp", 8); grid[7][1] = MakeHexReg("repc", 9); grid[5][1] = MakeHexReg("stj0", 0xA); grid[5][2] = MakeHexReg("sti0", 0xB); grid[7][2] = MakeHexReg("lc", 0xC); grid[3][1] = MakeHexReg("p1h", 0xD); grid[3][2] = MakeHexReg("p1l", 0xE); grid[2][2] = MakeHexReg("y1", 0xF); grid[2][1] = MakeHexReg("x1", 0x10); grid[1][1] = MakeHexReg("p0h", 0x11); grid[1][2] = MakeHexReg("p0l", 0x12); grid[0][2] = MakeHexReg("y0", 0x13); grid[0][1] = MakeHexReg("x0", 0x14); grid[6][2] = MakeHexReg("sv", 0x15); grid[12][1] = MakeHexReg("b1h", 0x16); grid[12][2] = MakeHexReg("b1l", 0x17); grid[12][0] = MakeHexReg("b1e", 0x18); grid[11][1] = MakeHexReg("b0h", 0x19); grid[11][2] = MakeHexReg("b0l", 0x1A); grid[11][0] = MakeHexReg("b0e", 0x1B); grid[10][1] = MakeHexReg("a1h", 0x1C); grid[10][2] = MakeHexReg("a1l", 0x1D); grid[10][0] = MakeHexReg("a1e", 0x1E); grid[9][1] = MakeHexReg("a0h", 0x1F); grid[9][2] = MakeHexReg("a0l", 0x20); grid[9][0] = MakeHexReg("a0e", 0x21); grid[13][3] = MakeBinReg("arp3", 0x22, "#RR#RRiiiiijjjjj"); grid[12][3] = MakeBinReg("arp2", 0x23, "#RR#RRiiiiijjjjj"); grid[11][3] = MakeBinReg("arp1", 0x24, "#RR#RRiiiiijjjjj"); grid[10][3] = MakeBinReg("arp0", 0x25, "#RR#RRiiiiijjjjj"); grid[9][3] = MakeBinReg("ar1", 0x26, "RRRRRRoosssoosss"); grid[8][3] = MakeBinReg("ar0", 0x27, "RRRRRRoosssoosss"); grid[7][3] = MakeBinReg("mod2", 0x28, "7654321m7654321M"); grid[6][3] = MakeBinReg("mod1", 0x29, "jicB####pppppppp"); grid[5][3] = MakeBinReg("mod0", 0x2A, "#QQ#PPooSYY###SS"); grid[4][3] = MakeBinReg("stt2", 0x2B, "LBBB####mm##V21I"); grid[3][3] = MakeBinReg("stt1", 0x2C, "QP#########R####"); grid[2][3] = MakeBinReg("stt0", 0x2D, "####C###ZMNVCELL"); grid[1][3] = MakeBinReg("cfgj", 0x2E, "mmmmmmmmmsssssss"); grid[0][3] = MakeBinReg("cfgi", 0x2F, "mmmmmmmmmsssssss"); MoveCursor(28, 0); char hostname[100]; gethostname(hostname, 100); printf("IP: %s port: 8888\n", hostname); printf("X: use sample program Y: Fill memory"); // Main loop while (aptMainLoop()) { hidScanInput(); u32 kDown = hidKeysDown(); if (kDown & KEY_START) break; if (kDown & KEY_SELECT) ExecuteTestCases(); if (kDown & KEY_DOWN) { for (int next = (int)c_row + 1; next < (int)t_row; ++next) { if (grid[next][c_col]) { c_row = next; break; } } } if (kDown & KEY_UP) { for (int next = (int)c_row - 1; next >= 0; --next) { if (grid[next][c_col]) { c_row = next; break; } } } if (kDown & KEY_LEFT) { if (c_pos == grid[c_row][c_col]->GetLength() - 1) { for (int next = (int)c_col - 1; next >= 0; --next) { if (grid[c_row][next]) { c_col = next; c_pos = 0; break; } } } else { ++c_pos; } } if (kDown & KEY_RIGHT) { if (c_pos == 0) { for (int next = (int)c_col + 1; next < (int)t_col; ++next) { if (grid[c_row][next]) { c_col = next; c_pos = grid[c_row][c_col]->GetLength() - 1; break; } } } else { --c_pos; } } if (kDown & KEY_A) { unsigned v = grid[c_row][c_col]->GetSrcDigit(c_pos); ++v; if (v == grid[c_row][c_col]->GetDigitRange()) v = 0; grid[c_row][c_col]->SetSrcDigit(c_pos, v); } if (kDown & KEY_B) { unsigned v = grid[c_row][c_col]->GetSrcDigit(c_pos); if (v == 0) v = grid[c_row][c_col]->GetDigitRange(); --v; grid[c_row][c_col]->SetSrcDigit(c_pos, v); } if (kDown & KEY_X) { UploadDspProgram({0x86A0}, false); // add r0, a0 } if (kDown & KEY_Y) { FlushCache(&dspD[0x3000], 0x40000 - 0x6000); for (u32 i = 0x3000; i < 0x20000; ++i) { dspD[i] = i; // i >> 4; } InvalidateCache(&dspD[0x3000], 0x40000 - 0x6000); } if (kDown & KEY_TOUCH) { consoleSelect(&bottomScreen); printf("--------\nSending register sync!\n"); u8 buf[reg_region_size + 2]; u16 magic = 0x4352; memcpy(buf, &magic, 2); memcpy(buf + 2, srcBase, reg_region_size); sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(8888); addr.sin_addr.s_addr = 0xFFFFFFFF; sendto(udp_s_broadcast, buf, reg_region_size + 2, 0, (sockaddr*)&addr, sizeof(addr)); printf("\nSynced!\n"); consoleSelect(&topScreen); } FlushCache(&dspD[0x2000], 0x1000); InvalidateCache(&dspD[0x2000], 0x2000); PrintAll(); CheckPackage(); // Flush and swap framebuffers gfxFlushBuffers(); gfxSwapBuffers(); // Wait for VBlank gspWaitForVBlank(); } socExit(); dspExit(); gfxExit(); aptExit(); return 0; }