aboutsummaryrefslogtreecommitdiff
path: root/src/AREngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/AREngine.cpp')
-rw-r--r--src/AREngine.cpp490
1 files changed, 490 insertions, 0 deletions
diff --git a/src/AREngine.cpp b/src/AREngine.cpp
new file mode 100644
index 0000000..2b6df65
--- /dev/null
+++ b/src/AREngine.cpp
@@ -0,0 +1,490 @@
+/*
+ Copyright 2016-2020 Arisotura
+
+ 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 "AREngine.h"
+
+
+namespace AREngine
+{
+
+typedef struct
+{
+ u32 Code[2 * 64]; // TODO: more sensible size for this? allocate on demand?
+ bool Enabled;
+
+} CheatEntry;
+
+// TODO: more sensible size for this? allocate on demand?
+CheatEntry CheatCodes[64];
+u32 NumCheatCodes;
+
+
+void ParseTextCode(char* text, int tlen, u32* code, int clen) // or whatever this should be named?
+{
+ u32 cur_word = 0;
+ u32 ndigits = 0;
+ u32 nin = 0;
+ u32 nout = 0;
+
+ char c;
+ while ((c = *text++) != '\0')
+ {
+ u32 val;
+ if (c >= '0' && c <= '9')
+ val = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val = c - 'a' + 0xA;
+ else if (c >= 'A' && c <= 'F')
+ val = c - 'A' + 0xA;
+ else
+ continue;
+
+ cur_word <<= 4;
+ cur_word |= val;
+
+ ndigits++;
+ if (ndigits >= 8)
+ {
+ if (nout >= clen)
+ {
+ printf("AR: code too long!\n");
+ return;
+ }
+
+ *code++ = cur_word;
+ nout++;
+
+ ndigits = 0;
+ cur_word = 0;
+ }
+
+ nin++;
+ if (nin >= tlen) break;
+ }
+
+ if (nout & 1)
+ {
+ printf("AR: code was missing one word\n");
+ if (nout >= clen)
+ {
+ printf("AR: code too long!\n");
+ return;
+ }
+ *code++ = 0;
+ }
+}
+
+
+bool Init()
+{
+ return true;
+}
+
+void DeInit()
+{
+ //
+}
+
+void Reset()
+{
+ memset(CheatCodes, 0, sizeof(CheatCodes));
+ NumCheatCodes = 0;
+
+ // TODO: acquire codes from a sensible source!
+ CheatEntry* entry = &CheatCodes[0];
+ u32* ptr = &entry->Code[0];
+
+ /*char* test = R"(9209D09A 00000000
+6209B468 00000000
+B209B468 00000000
+10000672 000003FF
+D2000000 00000000
+9209D09A 00000000
+94000130 FCBF0000
+6209B468 00000000
+B209B468 00000000
+200006B3 00000001
+200006B4 00000001
+D2000000 00000000
+9209D09A 00000000
+94000130 FC7F0000
+6209B468 00000000
+B209B468 00000000
+10000672 00000000
+D2000000 00000000)";
+ ParseTextCode(test, entry->Code, 2*64);
+ printf("PARSED CODE:\n");
+ for (int i = 0; i < 2*64; i+=2)
+ {
+ printf("%08X %08X\n", entry->Code[i], entry->Code[i+1]);
+ }
+ entry->Enabled = true;
+ NumCheatCodes++;*/
+}
+
+
+#define case16(x) \
+ case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
+ case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
+ case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
+ case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
+
+void RunCheat(CheatEntry* entry)
+{
+ u32* code = &entry->Code[0];
+
+ u32 offset = 0;
+ u32 datareg = 0;
+ u32 cond = 1;
+ u32 condstack = 0;
+
+ u32* loopstart = code;
+ u32 loopcount = 0;
+ u32 loopcond = 1;
+ u32 loopcondstack = 0;
+
+ // TODO: does anything reset this??
+ u32 c5count = 0;
+
+ for (;;)
+ {
+ u32 a = *code++;
+ u32 b = *code++;
+ if ((a|b) == 0) break;
+
+ u8 op = a >> 24;
+
+ if ((op < 0xD0 && op != 0xC5) || op > 0xD2)
+ {
+ if (!cond)
+ {
+ if ((op & 0xF0) == 0xE0)
+ {
+ for (u32 i = 0; i < b; i += 8)
+ *code += 2;
+ }
+
+ continue;
+ }
+ }
+
+ switch (op)
+ {
+ case16(0x00): // 32-bit write
+ NDS::ARM7Write32((a & 0x0FFFFFFF) + offset, b);
+ break;
+
+ case16(0x10): // 16-bit write
+ NDS::ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
+ break;
+
+ case16(0x20): // 8-bit write
+ NDS::ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
+ break;
+
+ case16(0x30): // IF b > u32[a]
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
+
+ cond = (b > chk) ? 1:0;
+ }
+ break;
+
+ case16(0x40): // IF b < u32[a]
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
+
+ cond = (b < chk) ? 1:0;
+ }
+ break;
+
+ case16(0x50): // IF b == u32[a]
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
+
+ cond = (b == chk) ? 1:0;
+ }
+ break;
+
+ case16(0x60): // IF b != u32[a]
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u32 chk = NDS::ARM7Read32(a & 0x0FFFFFFF);
+
+ cond = (b != chk) ? 1:0;
+ }
+ break;
+
+ case16(0x70): // IF b.l > ((~b.h) & u16[a])
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
+ u16 chk = ~(b >> 16);
+ chk &= val;
+
+ cond = ((b & 0xFFFF) > chk) ? 1:0;
+ }
+ break;
+
+ case16(0x80): // IF b.l < ((~b.h) & u16[a])
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
+ u16 chk = ~(b >> 16);
+ chk &= val;
+
+ cond = ((b & 0xFFFF) < chk) ? 1:0;
+ }
+ break;
+
+ case16(0x90): // IF b.l == ((~b.h) & u16[a])
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
+ u16 chk = ~(b >> 16);
+ chk &= val;
+
+ cond = ((b & 0xFFFF) == chk) ? 1:0;
+ }
+ break;
+
+ case16(0xA0): // IF b.l != ((~b.h) & u16[a])
+ {
+ condstack <<= 1;
+ condstack |= cond;
+
+ u16 val = NDS::ARM7Read16(a & 0x0FFFFFFF);
+ u16 chk = ~(b >> 16);
+ chk &= val;
+
+ cond = ((b & 0xFFFF) != chk) ? 1:0;
+ }
+ break;
+
+ case16(0xB0): // offset = u32[a + offset]
+ offset = NDS::ARM7Read32((a & 0x0FFFFFFF) + offset);
+ break;
+
+ case 0xC0: // FOR 0..b
+ loopstart = code; // points to the first opcode after the FOR
+ loopcount = b;
+ loopcond = cond; // checkme
+ loopcondstack = condstack; // (GBAtek is not very clear there)
+ break;
+
+ case 0xC4: // offset = pointer to C4000000 opcode
+ // theoretically used for safe storage, by accessing [offset+4]
+ // in practice could be used for a self-modifying AR code
+ // could be implemented with some hackery, but, does anything even
+ // use it??
+ printf("AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
+ return;
+
+ case 0xC5: // count++ / IF (count & b.l) == b.h
+ {
+ // with weird condition checking, apparently
+ // oh well
+
+ c5count++;
+ if (!cond) break;
+
+ condstack <<= 1;
+ condstack |= cond;
+
+ u16 mask = b & 0xFFFF;
+ u16 chk = b >> 16;
+
+ cond = ((c5count & mask) == chk) ? 1:0;
+ }
+ break;
+
+ case 0xC6: // u32[b] = offset
+ NDS::ARM7Write32(b, offset);
+ break;
+
+ case 0xD0: // ENDIF
+ cond = condstack & 0x1;
+ condstack >>= 1;
+ break;
+
+ case 0xD1: // NEXT
+ if (loopcount > 0)
+ {
+ loopcount--;
+ code = loopstart;
+ }
+ else
+ {
+ cond = loopcond;
+ condstack = loopcondstack;
+ }
+ break;
+
+ case 0xD2: // NEXT+FLUSH
+ if (loopcount > 0)
+ {
+ loopcount--;
+ code = loopstart;
+ }
+ else
+ {
+ offset = 0;
+ datareg = 0;
+ condstack = 0;
+ cond = 1;
+ }
+ break;
+
+ case 0xD3: // offset = b
+ offset = b;
+ break;
+
+ case 0xD4: // datareg += b
+ datareg += b;
+ break;
+
+ case 0xD5: // datareg = b
+ datareg = b;
+ break;
+
+ case 0xD6: // u32[b+offset] = datareg / offset += 4
+ NDS::ARM7Write32(b + offset, datareg);
+ offset += 4;
+ break;
+
+ case 0xD7: // u16[b+offset] = datareg / offset += 2
+ NDS::ARM7Write16(b + offset, datareg & 0xFFFF);
+ offset += 2;
+ break;
+
+ case 0xD8: // u8[b+offset] = datareg / offset += 1
+ NDS::ARM7Write8(b + offset, datareg & 0xFF);
+ offset += 1;
+ break;
+
+ case 0xD9: // datareg = u32[b+offset]
+ datareg = NDS::ARM7Read32(b + offset);
+ break;
+
+ case 0xDA: // datareg = u16[b+offset]
+ datareg = NDS::ARM7Read16(b + offset);
+ break;
+
+ case 0xDB: // datareg = u8[b+offset]
+ datareg = NDS::ARM7Read8(b + offset);
+ break;
+
+ case 0xDC: // offset += b
+ offset += b;
+ break;
+
+ case16(0xE0): // copy b param bytes to address a+offset
+ {
+ // TODO: check for bad alignment of dstaddr
+
+ u32 dstaddr = (a & 0x0FFFFFFF) + offset;
+ u32 bytesleft = b;
+ while (bytesleft >= 8)
+ {
+ NDS::ARM7Write32(dstaddr, *code++); dstaddr += 4;
+ NDS::ARM7Write32(dstaddr, *code++); dstaddr += 4;
+ bytesleft -= 8;
+ }
+ if (bytesleft > 0)
+ {
+ u8* leftover = (u8*)code;
+ *code += 2;
+ if (bytesleft >= 4)
+ {
+ NDS::ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
+ leftover += 4;
+ bytesleft -= 4;
+ }
+ while (bytesleft > 0)
+ {
+ NDS::ARM7Write8(dstaddr, *leftover++); dstaddr++;
+ bytesleft--;
+ }
+ }
+ }
+ break;
+
+ case16(0xF0): // copy b bytes from address offset to address a
+ {
+ // TODO: check for bad alignment of srcaddr/dstaddr
+
+ u32 srcaddr = offset;
+ u32 dstaddr = (a & 0x0FFFFFFF);
+ u32 bytesleft = b;
+ while (bytesleft >= 4)
+ {
+ NDS::ARM7Write32(dstaddr, NDS::ARM7Read32(srcaddr));
+ srcaddr += 4;
+ dstaddr += 4;
+ bytesleft -= 4;
+ }
+ while (bytesleft > 0)
+ {
+ NDS::ARM7Write8(dstaddr, NDS::ARM7Read8(srcaddr));
+ srcaddr++;
+ dstaddr++;
+ bytesleft--;
+ }
+ }
+ break;
+
+ default:
+ printf("!! bad AR opcode %08X %08X\n", a, b);
+ return;
+ }
+ }
+}
+
+void RunCheats()
+{
+ // TODO: make it disableable in general
+
+ for (u32 i = 0; i < NumCheatCodes; i++)
+ {
+ CheatEntry* entry = &CheatCodes[i];
+ if (entry->Enabled)
+ RunCheat(entry);
+ }
+}
+
+}