#include "core/gba/gbaRemote.h"

#if !defined(VBAM_ENABLE_DEBUGGER)
#error "This file should only be compiled when VBAM_ENABLE_DEBUGGER is defined."
#endif  // !defined(VBAM_ENABLE_DEBUGGER)

#if defined(_WIN32)

#include <io.h>
#include <winsock.h>
#define socklen_t int
#define close closesocket
#define read _read
#define write _write
#define strdup _strdup

#else  // !defined(_WIN32)

#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif // HAVE_NETINET_IN_H

#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#else // ! HAVE_ARPA_INET_H
#define socklen_t int
#endif // ! HAVE_ARPA_INET_H

#define SOCKET int

#endif  // defined(_WIN32)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iosfwd>
#include <iostream>
#include <sstream>

#include "core/gba/gba.h"
#include "core/gba/gbaElf.h"
#include "core/gba/gbaGlobals.h"
#include "core/gba/gbaRemote.h"
#include "core/gba/internal/gbaBreakpoint.h"

#if __STDC_WANT_SECURE_LIB__
#define snprintf sprintf_s
#define sscanf sscanf_s
#endif

extern int emulating;
extern void CPUUpdateCPSR();

int remotePort = 0;
int remoteSignal = 5;
SOCKET remoteSocket = (SOCKET)(-1);
SOCKET remoteListenSocket = (SOCKET)(-1);
bool remoteConnected = false;
bool remoteResumed = false;

int (*remoteSendFnc)(char*, int) = NULL;
int (*remoteRecvFnc)(char*, int) = NULL;
bool (*remoteInitFnc)() = NULL;
void (*remoteCleanUpFnc)() = NULL;

#ifndef SDL
void remoteSetSockets(SOCKET l, SOCKET r)
{
    remoteSocket = r;
    remoteListenSocket = l;
}
#endif

#define debuggerReadMemory(addr) \
    (*(uint32_t*)&map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask])

#define debuggerReadHalfWord(addr) \
    (*(uint16_t*)&map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask])

#define debuggerReadByte(addr) \
    map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask]

#define debuggerWriteMemory(addr, value) \
    *(uint32_t*)&map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask] = (value)

#define debuggerWriteHalfWord(addr, value) \
    *(uint16_t*)&map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask] = (value)

#define debuggerWriteByte(addr, value) \
    map[(addr) >> 24].address[(addr)&map[(addr) >> 24].mask] = (value)

bool dontBreakNow = false;
int debuggerNumOfDontBreak = 0;
int debuggerRadix = 0;

#define NUMBEROFDB 1000
uint32_t debuggerNoBreakpointList[NUMBEROFDB];

const char* cmdAliasTable[] = { "help", "?", "h", "?", "continue", "c", "next", "n",
    "cpyb", "copyb", "cpyh", "copyh", "cpyw", "copyw",
    "exe", "execute", "exec", "execute",
    NULL, NULL };

struct DebuggerCommand {
    const char* name;
    void (*function)(int, char**);
    const char* help;
    const char* syntax;
};

char monbuf[1000];
void monprintf(std::string line);
std::string StringToHex(std::string& cmd);
std::string HexToString(char* p);
void debuggerUsage(const char* cmd);
void debuggerHelp(int n, char** args);
void printFlagHelp();
void dbgExecute(std::string& cmd);

extern bool debuggerBreakOnWrite(uint32_t, uint32_t, int);
extern bool debuggerBreakOnRegisterCondition(uint8_t, uint32_t, uint32_t, uint8_t);
extern bool debuggerBreakOnExecution(uint32_t, uint8_t);

regBreak* breakRegList[16];
uint8_t lowRegBreakCounter[4]; //(r0-r3)
uint8_t medRegBreakCounter[4]; //(r4-r7)
uint8_t highRegBreakCounter[4]; //(r8-r11)
uint8_t statusRegBreakCounter[4]; //(r12-r15)
uint8_t* regBreakCounter[4] = {
    &lowRegBreakCounter[0],
    &medRegBreakCounter[0],
    &highRegBreakCounter[0],
    &statusRegBreakCounter[0]
};
uint32_t lastWasBranch = 0;

struct regBreak* getFromBreakRegList(uint8_t regnum, int location)
{
    if (location > regBreakCounter[regnum >> 2][regnum & 3])
        return NULL;

    struct regBreak* ans = breakRegList[regnum];
    for (int i = 0; i < location && ans; i++) {
        ans = ans->next;
    }
    return ans;
}

bool enableRegBreak = false;
reg_pair oldReg[16];
uint32_t regDiff[16];

void breakReg_check(int i)
{
    struct regBreak* brkR = breakRegList[i];
    bool notFound = true;
    uint8_t counter = regBreakCounter[i >> 2][i & 3];
    for (int bri = 0; (bri < counter) && notFound; bri++) {
        if (!brkR) {
            regBreakCounter[i >> 2][i & 3] = (uint8_t)bri;
            break;
        } else {
            if (brkR->flags != 0) {
                uint32_t regVal = (i == 15 ? (armState ? reg[15].I - 4 : reg[15].I - 2) : reg[i].I);
                if ((brkR->flags & 0x1) && (regVal == brkR->intVal)) {
                    debuggerBreakOnRegisterCondition((uint8_t)i, brkR->intVal, regVal, 1);
                    notFound = false;
                }
                if ((brkR->flags & 0x8)) {
                    if ((brkR->flags & 0x4) && ((int)regVal < (int)brkR->intVal)) {
                        debuggerBreakOnRegisterCondition((uint8_t)i, brkR->intVal, regVal, 4);
                        notFound = false;
                    }
                    if ((brkR->flags & 0x2) && ((int)regVal > (int)brkR->intVal)) {
                        debuggerBreakOnRegisterCondition((uint8_t)i, brkR->intVal, regVal, 5);
                        notFound = false;
                    }
                }
                if ((brkR->flags & 0x4) && (regVal < brkR->intVal)) {
                    debuggerBreakOnRegisterCondition((uint8_t)i, brkR->intVal, regVal, 2);
                    notFound = false;
                }
                if ((brkR->flags & 0x2) && (regVal > brkR->intVal)) {
                    debuggerBreakOnRegisterCondition((uint8_t)i, brkR->intVal, regVal, 3);
                    notFound = false;
                }
            }
            brkR = brkR->next;
        }
    }
    if (!notFound) {
        //CPU_BREAK_LOOP_2;
    }
}

void clearParticularRegListBreaks(int regNum)
{

    while (breakRegList[regNum]) {
        struct regBreak* ans = breakRegList[regNum]->next;
        free(breakRegList[regNum]);
        breakRegList[regNum] = ans;
    }
    regBreakCounter[regNum >> 2][regNum & 3] = 0;
}

void clearBreakRegList()
{
    for (int i = 0; i < 16; i++) {
        clearParticularRegListBreaks(i);
    }
}

void deleteFromBreakRegList(uint8_t regNum, int num)
{
    int counter = regBreakCounter[regNum >> 2][regNum & 3];
    if (num >= counter) {
        return;
    }
    struct regBreak* ans = breakRegList[regNum];
    struct regBreak* prev = NULL;
    for (int i = 0; i < num; i++) {
        prev = ans;
        ans = ans->next;
    }
    if (prev) {
        prev->next = ans->next;
    } else {
        breakRegList[regNum] = ans->next;
    }
    free(ans);
    regBreakCounter[regNum >> 2][regNum & 3]--;
}

void addBreakRegToList(uint8_t regnum, uint8_t flags, uint32_t value)
{
    struct regBreak* ans = (struct regBreak*)malloc(sizeof(struct regBreak));
    ans->flags = flags;
    ans->intVal = value;
    ans->next = breakRegList[regnum];
    breakRegList[regnum] = ans;
    regBreakCounter[regnum >> 2][regnum & 3]++;
}

void printBreakRegList(bool verbose)
{
    const char* flagsToOP[] = { "never", "==", ">", ">=", "<", "<=", "!=", "always" };
    bool anyPrint = false;
    for (int i = 0; i < 4; i++) {
        for (int k = 0; k < 4; k++) {
            if (regBreakCounter[i][k]) {
                if (!anyPrint) {
                    {
                        snprintf(monbuf, sizeof(monbuf), "Register breakpoint list:\n");
                        monprintf(monbuf);
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                        monprintf(monbuf);
                    }
                    anyPrint = true;
                }
                struct regBreak* tmp = breakRegList[i * 4 + k];
                for (int j = 0; j < regBreakCounter[i][k]; j++) {
                    if (tmp->flags & 8) {
                        snprintf(monbuf, sizeof(monbuf), "No. %d:\tBreak if (signed)%s %08x\n", j, flagsToOP[tmp->flags & 7], tmp->intVal);
                        monprintf(monbuf);
                    } else {
                        snprintf(monbuf, sizeof(monbuf), "No. %d:\tBreak if %s %08x\n", j, flagsToOP[tmp->flags], tmp->intVal);
                        monprintf(monbuf);
                    }
                    tmp = tmp->next;
                }
                {
                    snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                    monprintf(monbuf);
                }
            } else {
                if (verbose) {
                    if (!anyPrint) {
                        {
                            snprintf(monbuf, sizeof(monbuf), "Register breakpoint list:\n");
                            monprintf(monbuf);
                        }
                        {
                            snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                            monprintf(monbuf);
                        }
                        anyPrint = true;
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "No breaks on r%d.\n", i);
                        monprintf(monbuf);
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                        monprintf(monbuf);
                    }
                }
            }
        }
    }
    if (!verbose && !anyPrint) {
        {
            snprintf(monbuf, sizeof(monbuf), "No Register breaks found.\n");
            monprintf(monbuf);
        }
    }
}

void debuggerOutput(const char* s, uint32_t addr)
{
    if (s)
        printf("%s", s);
    else {
        char c;

        c = debuggerReadByte(addr);
        addr++;
        while (c) {
            putchar(c);
            c = debuggerReadByte(addr);
            addr++;
        }
    }
}

// checks that the given address is in the DB list
bool debuggerInDB(uint32_t address)
{

    for (int i = 0; i < debuggerNumOfDontBreak; i++) {
        if (debuggerNoBreakpointList[i] == address)
            return true;
    }

    return false;
}

void debuggerDontBreak(int n, char** args)
{
    if (n == 2) {
        uint32_t address = 0;
        sscanf(args[1], "%x", &address);
        int i = debuggerNumOfDontBreak;
        if (i > NUMBEROFDB) {
            monprintf("Can't have this many DB entries");
            return;
        }
        debuggerNoBreakpointList[i] = address;
        debuggerNumOfDontBreak++;
        {
            snprintf(monbuf, sizeof(monbuf), "Added Don't Break at %08x\n", address);
            monprintf(monbuf);
        }
    } else
        debuggerUsage("db");
}

void debuggerDontBreakClear(int n, char** args)
{
    (void)args; // unused params
    if (n == 1) {
        debuggerNumOfDontBreak = 0;
        {
            snprintf(monbuf, sizeof(monbuf), "Cleared Don't Break list.\n");
            monprintf(monbuf);
        }
    } else
        debuggerUsage("dbc");
}

void debuggerDumpLoad(int n, char** args)
{
    uint32_t address;
    char* file;
    FILE* f;
    int c;

    if (n == 3) {
        file = args[1];

        if (!dexp_eval(args[2], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }

#if __STDC_WANT_SECURE_LIB__
        fopen_s(&f, file, "rb");
#else
        f = fopen(file, "rb");
#endif
        if (f == NULL) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error opening file.\n");
                monprintf(monbuf);
            }
            return;
        }

        fseek(f, 0, SEEK_END);
        int size = ftell(f);
        fseek(f, 0, SEEK_SET);

        for (int i = 0; i < size; i++) {
            c = fgetc(f);
            if (c == -1)
                break;
            debuggerWriteByte(address, (uint8_t)c);
            address++;
        }

        fclose(f);
    } else
        debuggerUsage("dload");
}

void debuggerDumpSave(int n, char** args)
{
    uint32_t address;
    uint32_t size;
    char* file;
    FILE* f;

    if (n == 4) {
        file = args[1];
        if (!dexp_eval(args[2], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }
        if (!dexp_eval(args[3], &size)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in size");
                monprintf(monbuf);
            }
            return;
        }

#if __STDC_WANT_SECURE_LIB__
        fopen_s(&f, file, "wb");
#else
        f = fopen(file, "wb");
#endif

        if (f == NULL) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error opening file.\n");
                monprintf(monbuf);
            }
            return;
        }

        for (uint32_t i = 0; i < size; i++) {
            fputc(debuggerReadByte(address), f);
            address++;
        }

        fclose(f);
    } else
        debuggerUsage("dsave");
}

void debuggerEditByte(int n, char** args)
{
    if (n >= 3) {
        uint32_t address;
        uint32_t value;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }
        for (int i = 2; i < n; i++) {
            if (!dexp_eval(args[i], &value)) {
                {
                    snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1));
                    monprintf(monbuf);
                }
            }
            debuggerWriteByte(address, (uint8_t)value);
            address++;
        }
    } else
        debuggerUsage("eb");
}

void debuggerEditHalfWord(int n, char** args)
{
    if (n >= 3) {
        uint32_t address;
        uint32_t value;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }
        if (address & 1) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error: address must be half-word aligned\n");
                monprintf(monbuf);
            }
            return;
        }
        for (int i = 2; i < n; i++) {
            if (!dexp_eval(args[i], &value)) {
                {
                    snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1));
                    monprintf(monbuf);
                }
            }
            debuggerWriteHalfWord(address, (uint16_t)value);
            address += 2;
        }
    } else
        debuggerUsage("eh");
}

void debuggerEditWord(int n, char** args)
{
    if (n >= 3) {
        uint32_t address;
        uint32_t value;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }
        if (address & 3) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error: address must be word aligned\n");
                monprintf(monbuf);
            }
            return;
        }
        for (int i = 2; i < n; i++) {
            if (!dexp_eval(args[i], &value)) {
                {
                    snprintf(monbuf, sizeof(monbuf), "Invalid expression in %d value.Ignored.\n", (i - 1));
                    monprintf(monbuf);
                }
            }
            debuggerWriteMemory(address, (uint32_t)value);
            address += 4;
        }
    } else
        debuggerUsage("ew");
}

bool debuggerBreakOnRegisterCondition(uint8_t registerName, uint32_t compareVal, uint32_t regVal, uint8_t type)
{
    const char* typeName;
    switch (type) {
    case 1:
        typeName = "equal to";
        break;
    case 2:
        typeName = "greater (unsigned) than";
        break;
    case 3:
        typeName = "smaller (unsigned) than";
        break;
    case 4:
        typeName = "greater (signed) than";
        break;
    case 5:
        typeName = "smaller (signed) than";
        break;
    default:
        typeName = "unknown";
    }
    {
        snprintf(monbuf, sizeof(monbuf), "Breakpoint on R%02d : %08x is %s register content (%08x)\n", registerName, compareVal, typeName, regVal);
        monprintf(monbuf);
    }
    if (debuggerInDB(armState ? reg[15].I - 4 : reg[15].I - 2)) {
        {
            snprintf(monbuf, sizeof(monbuf), "But this address is marked not to break, so skipped\n");
            monprintf(monbuf);
        }
        return false;
    }
    debugger = true;
    return true;
}

void debuggerBreakRegisterList(bool verbose)
{
    printBreakRegList(verbose);
}

int getRegisterNumber(char* regName)
{
    int r = -1;
    if (toupper(regName[0]) == 'P' && toupper(regName[1]) == 'C') {
        r = 15;
    } else if (toupper(regName[0]) == 'L' && toupper(regName[1]) == 'R') {
        r = 14;
    } else if (toupper(regName[0]) == 'S' && toupper(regName[1]) == 'P') {
        r = 13;
    } else if (toupper(regName[0]) == 'R') {
        sscanf((char*)(regName + 1), "%d", &r);
    } else {
        sscanf(regName, "%d", &r);
    }

    return r;
}

void debuggerEditRegister(int n, char** args)
{
    if (n == 3) {
        int r = getRegisterNumber(args[1]);
        uint32_t val;
        if (r > 16) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error: Register must be valid (0-16)\n");
                monprintf(monbuf);
            }
            return;
        }
        if (!dexp_eval(args[2], &val)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n");
                monprintf(monbuf);
            }
            return;
        }
        reg[r].I = val;
        {
            snprintf(monbuf, sizeof(monbuf), "R%02d=%08X\n", r, val);
            monprintf(monbuf);
        }
    } else
        debuggerUsage("er");
}

void debuggerEval(int n, char** args)
{
    if (n == 2) {
        uint32_t result = 0;
        if (dexp_eval(args[1], &result)) {
            {
                snprintf(monbuf, sizeof(monbuf), " =$%08X\n", result);
                monprintf(monbuf);
            }
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression\n");
                monprintf(monbuf);
            }
        }
    } else
        debuggerUsage("eval");
}

void debuggerFillByte(int n, char** args)
{
    if (n == 4) {
        uint32_t address;
        uint32_t value;
        uint32_t reps;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        }
        if (!dexp_eval(args[2], &value)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n");
                monprintf(monbuf);
            }
        }
        if (!dexp_eval(args[3], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
        for (uint32_t i = 0; i < reps; i++) {
            debuggerWriteByte(address, (uint8_t)value);
            address++;
        }
    } else
        debuggerUsage("fillb");
}

void debuggerFillHalfWord(int n, char** args)
{
    if (n == 4) {
        uint32_t address;
        uint32_t value;
        uint32_t reps;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        } /*
		 if(address & 1) {
		 { snprintf(monbuf, sizeof(monbuf), "Error: address must be halfword aligned\n"); monprintf(monbuf); }
		 return;
		 }*/
        if (!dexp_eval(args[2], &value)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n");
                monprintf(monbuf);
            }
        }
        if (!dexp_eval(args[3], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
        for (uint32_t i = 0; i < reps; i++) {
            debuggerWriteHalfWord(address, (uint16_t)value);
            address += 2;
        }
    } else
        debuggerUsage("fillh");
}

void debuggerFillWord(int n, char** args)
{
    if (n == 4) {
        uint32_t address;
        uint32_t value;
        uint32_t reps;
        if (!dexp_eval(args[1], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in address.\n");
                monprintf(monbuf);
            }
            return;
        } /*
		 if(address & 3) {
		 { snprintf(monbuf, sizeof(monbuf), "Error: address must be word aligned\n"); monprintf(monbuf); }
		 return;
		 }*/
        if (!dexp_eval(args[2], &value)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in value.\n");
                monprintf(monbuf);
            }
        }
        if (!dexp_eval(args[3], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
        for (uint32_t i = 0; i < reps; i++) {
            debuggerWriteMemory(address, (uint32_t)value);
            address += 4;
        }
    } else
        debuggerUsage("fillw");
}

unsigned int SearchStart = 0xFFFFFFFF;
unsigned int SearchMaxMatches = 5;
uint8_t SearchData[64]; // It actually doesn't make much sense to search for more than 64 bytes, does it?
unsigned int SearchLength = 0;
unsigned int SearchResults;

unsigned int AddressToGBA(uint8_t* mem)
{
    if (mem >= &g_bios[0] && mem <= &g_bios[0x3fff])
        return (unsigned int)(0x00000000 + (mem - &g_bios[0]));
    else if (mem >= &g_workRAM[0] && mem <= &g_workRAM[0x3ffff])
        return (unsigned int)(0x02000000 + (mem - &g_workRAM[0]));
    else if (mem >= &g_internalRAM[0] && mem <= &g_internalRAM[0x7fff])
        return (unsigned int)(0x03000000 + (mem - &g_internalRAM[0]));
    else if (mem >= &g_ioMem[0] && mem <= &g_ioMem[0x3ff])
        return (unsigned int)(0x04000000 + (mem - &g_ioMem[0]));
    else if (mem >= &g_paletteRAM[0] && mem <= &g_paletteRAM[0x3ff])
        return (unsigned int)(0x05000000 + (mem - &g_paletteRAM[0]));
    else if (mem >= &g_vram[0] && mem <= &g_vram[0x1ffff])
        return (unsigned int)(0x06000000 + (mem - &g_vram[0]));
    else if (mem >= &g_oam[0] && mem <= &g_oam[0x3ff])
        return (unsigned int)(0x07000000 + (mem - &g_oam[0]));
    else if (mem >= &g_rom[0] && mem <= &g_rom[0x1ffffff])
        return (unsigned int)(0x08000000 + (mem - &g_rom[0]));
    else
        return 0xFFFFFFFF;
};

void debuggerDoSearch()
{
    unsigned int count = 0;

    while (true) {
        unsigned int final = SearchStart + SearchLength - 1;
        uint8_t* end;
        uint8_t* start;

        switch (SearchStart >> 24) {
        case 0:
            if (final > 0x00003FFF) {
                SearchStart = 0x02000000;
                continue;
            } else {
                start = g_bios + (SearchStart & 0x3FFF);
                end = g_bios + 0x3FFF;
                break;
            };
        case 2:
            if (final > 0x0203FFFF) {
                SearchStart = 0x03000000;
                continue;
            } else {
                start = g_workRAM + (SearchStart & 0x3FFFF);
                end = g_workRAM + 0x3FFFF;
                break;
            };
        case 3:
            if (final > 0x03007FFF) {
                SearchStart = 0x04000000;
                continue;
            } else {
                start = g_internalRAM + (SearchStart & 0x7FFF);
                end = g_internalRAM + 0x7FFF;
                break;
            };
        case 4:
            if (final > 0x040003FF) {
                SearchStart = 0x05000000;
                continue;
            } else {
                start = g_ioMem + (SearchStart & 0x3FF);
                end = g_ioMem + 0x3FF;
                break;
            };
        case 5:
            if (final > 0x050003FF) {
                SearchStart = 0x06000000;
                continue;
            } else {
                start = g_paletteRAM + (SearchStart & 0x3FF);
                end = g_paletteRAM + 0x3FF;
                break;
            };
        case 6:
            if (final > 0x0601FFFF) {
                SearchStart = 0x07000000;
                continue;
            } else {
                start = g_vram + (SearchStart & 0x1FFFF);
                end = g_vram + 0x1FFFF;
                break;
            };
        case 7:
            if (final > 0x070003FF) {
                SearchStart = 0x08000000;
                continue;
            } else {
                start = g_oam + (SearchStart & 0x3FF);
                end = g_oam + 0x3FF;
                break;
            };
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
            if (final <= 0x09FFFFFF) {
                start = g_rom + (SearchStart & 0x01FFFFFF);
                end = g_rom + 0x01FFFFFF;
                break;
            };
        default: {
            snprintf(monbuf, sizeof(monbuf), "Search completed.\n");
            monprintf(monbuf);
        }
            SearchLength = 0;
            return;
        };

        end -= SearchLength - 1;
        uint8_t firstbyte = SearchData[0];
        while (start <= end) {
            while ((start <= end) && (*start != firstbyte))
                start++;

            if (start > end)
                break;

            unsigned int p = 1;
            while ((start[p] == SearchData[p]) && (p < SearchLength))
                p++;

            if (p == SearchLength) {
                {
                    snprintf(monbuf, sizeof(monbuf), "Search result (%d): %08x\n", count + SearchResults, AddressToGBA(start));
                    monprintf(monbuf);
                }
                count++;
                if (count == SearchMaxMatches) {
                    SearchStart = AddressToGBA(start + p);
                    SearchResults += count;
                    return;
                };

                start += p; // assume areas don't overlap; alternative: start++;
            } else
                start++;
        };

        SearchStart = AddressToGBA(end + SearchLength - 1) + 1;
    };
};

void debuggerFindText(int n, char** args)
{
    if ((n == 4) || (n == 3)) {
        SearchResults = 0;
        if (!dexp_eval(args[1], &SearchStart)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n");
                monprintf(monbuf);
            }
            return;
        }

        if (n == 4) {
            sscanf(args[2], "%u", &SearchMaxMatches);
#if __STDC_WANT_SECURE_LIB__
            strncpy_s((char*)SearchData, sizeof(SearchData), args[3], 64);
#else
            strncpy((char*)SearchData, args[3], 64);
#endif
            SearchLength = (unsigned int)strlen(args[3]);
        } else if (n == 3) {
#if __STDC_WANT_SECURE_LIB__
            strncpy_s((char*)SearchData, sizeof(SearchData), args[2], 64);
#else
            strncpy((char*)SearchData, args[2], 64);
#endif
            SearchLength = (unsigned int)strlen(args[2]);
        };

        if (SearchLength > 64) {
            {
                snprintf(monbuf, sizeof(monbuf), "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength);
                monprintf(monbuf);
            }
            SearchLength = 64;
        };

        debuggerDoSearch();

    } else
        debuggerUsage("ft");
};

void debuggerFindHex(int n, char** args)
{
    if ((n == 4) || (n == 3)) {
        SearchResults = 0;
        if (!dexp_eval(args[1], &SearchStart)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n");
                monprintf(monbuf);
            }
            return;
        }

        char SearchHex[128];
        if (n == 4) {
            sscanf(args[2], "%u", &SearchMaxMatches);
#if __STDC_WANT_SECURE_LIB__
            strncpy_s(SearchHex, sizeof(SearchHex), args[3], 128);
#else
            strncpy(SearchHex, args[3], 128);
#endif
            SearchLength = (unsigned int)strlen(args[3]);
        } else if (n == 3) {
#if __STDC_WANT_SECURE_LIB__
            strncpy_s(SearchHex, sizeof(SearchHex), args[2], 128);
#else
            strncpy(SearchHex, args[2], 128);
#endif
            SearchLength = (unsigned int)strlen(args[2]);
        };

        if (SearchLength & 1) {
            snprintf(monbuf, sizeof(monbuf), "Unaligned bytecount: %d,5. Last digit (%c) cut.\n", SearchLength / 2, SearchHex[SearchLength - 1]);
            monprintf(monbuf);
        }

        SearchLength /= 2;

        if (SearchLength > 64) {
            {
                snprintf(monbuf, sizeof(monbuf), "Entered string (length: %d) is longer than 64 bytes and was cut.\n", SearchLength);
                monprintf(monbuf);
            }
            SearchLength = 64;
        };

        for (unsigned int i = 0; i < SearchLength; i++) {
            unsigned int cbuf = 0;
            sscanf(&SearchHex[i << 1], "%02x", &cbuf);
            SearchData[i] = (uint8_t)cbuf;
        };

        debuggerDoSearch();

    } else
        debuggerUsage("fh");
};

void debuggerFindResume(int n, char** args)
{
    if ((n == 1) || (n == 2)) {
        if (SearchLength == 0) {
            {
                snprintf(monbuf, sizeof(monbuf), "Error: No search in progress. Start a search with ft or fh.\n");
                monprintf(monbuf);
            }
            debuggerUsage("fr");
            return;
        };

        if (n == 2)
            sscanf(args[1], "%u", &SearchMaxMatches);

        debuggerDoSearch();

    } else
        debuggerUsage("fr");
};

void debuggerCopyByte(int n, char** args)
{
    uint32_t source;
    uint32_t dest;
    uint32_t number = 1;
    uint32_t reps = 1;
    if (n > 5 || n < 3) {
        debuggerUsage("copyb");
    }

    if (n == 5) {
        if (!dexp_eval(args[4], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
    }
    if (n > 3) {
        if (!dexp_eval(args[3], &number)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n");
                monprintf(monbuf);
            }
        }
    }
    if (!dexp_eval(args[1], &source)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n");
            monprintf(monbuf);
        }
        return;
    }
    if (!dexp_eval(args[2], &dest)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n");
            monprintf(monbuf);
        }
    }

    for (uint32_t j = 0; j < reps; j++) {
        for (uint32_t i = 0; i < number; i++) {
            debuggerWriteByte(dest + i, debuggerReadByte(source + i));
        }
        dest += number;
    }
}

void debuggerCopyHalfWord(int n, char** args)
{
    uint32_t source;
    uint32_t dest;
    uint32_t number = 2;
    uint32_t reps = 1;
    if (n > 5 || n < 3) {
        debuggerUsage("copyh");
    }

    if (n == 5) {
        if (!dexp_eval(args[4], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
    }
    if (n > 3) {
        if (!dexp_eval(args[3], &number)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n");
                monprintf(monbuf);
            }
        }
        number = number << 1;
    }
    if (!dexp_eval(args[1], &source)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n");
            monprintf(monbuf);
        }
        return;
    }
    if (!dexp_eval(args[2], &dest)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n");
            monprintf(monbuf);
        }
    }

    for (uint32_t j = 0; j < reps; j++) {
        for (uint32_t i = 0; i < number; i += 2) {
            debuggerWriteHalfWord(dest + i, debuggerReadHalfWord(source + i));
        }
        dest += number;
    }
}

void debuggerCopyWord(int n, char** args)
{
    uint32_t source;
    uint32_t dest;
    uint32_t number = 4;
    uint32_t reps = 1;
    if (n > 5 || n < 3) {
        debuggerUsage("copyw");
    }

    if (n == 5) {
        if (!dexp_eval(args[4], &reps)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in repetition number.\n");
                monprintf(monbuf);
            }
        }
    }
    if (n > 3) {
        if (!dexp_eval(args[3], &number)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression in number of copy units.\n");
                monprintf(monbuf);
            }
        }
        number = number << 2;
    }
    if (!dexp_eval(args[1], &source)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in source address.\n");
            monprintf(monbuf);
        }
        return;
    }
    if (!dexp_eval(args[2], &dest)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression in destination address.\n");
            monprintf(monbuf);
        }
    }

    for (uint32_t j = 0; j < reps; j++) {
        for (uint32_t i = 0; i < number; i += 4) {
            debuggerWriteMemory(dest + i, debuggerReadMemory(source + i));
        }
        dest += number;
    }
}

void debuggerIoVideo()
{
    {
        snprintf(monbuf, sizeof(monbuf), "DISPCNT  = %04x\n", DISPCNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DISPSTAT = %04x\n", DISPSTAT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "VCOUNT   = %04x\n", VCOUNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG0CNT   = %04x\n", BG0CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG1CNT   = %04x\n", BG1CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2CNT   = %04x\n", BG2CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3CNT   = %04x\n", BG3CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WIN0H    = %04x\n", WIN0H);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WIN0V    = %04x\n", WIN0V);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WIN1H    = %04x\n", WIN1H);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WIN1V    = %04x\n", WIN1V);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WININ    = %04x\n", WININ);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "WINOUT   = %04x\n", WINOUT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "MOSAIC   = %04x\n", MOSAIC);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BLDMOD   = %04x\n", BLDMOD);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "COLEV    = %04x\n", COLEV);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "COLY     = %04x\n", COLY);
        monprintf(monbuf);
    }
}

void debuggerIoVideo2()
{
    {
        snprintf(monbuf, sizeof(monbuf), "BG0HOFS  = %04x\n", BG0HOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG0VOFS  = %04x\n", BG0VOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG1HOFS  = %04x\n", BG1HOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG1VOFS  = %04x\n", BG1VOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2HOFS  = %04x\n", BG2HOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2VOFS  = %04x\n", BG2VOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3HOFS  = %04x\n", BG3HOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3VOFS  = %04x\n", BG3VOFS);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2PA    = %04x\n", BG2PA);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2PB    = %04x\n", BG2PB);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2PC    = %04x\n", BG2PC);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2PD    = %04x\n", BG2PD);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2X     = %08x\n", (BG2X_H << 16) | BG2X_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG2Y     = %08x\n", (BG2Y_H << 16) | BG2Y_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3PA    = %04x\n", BG3PA);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3PB    = %04x\n", BG3PB);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3PC    = %04x\n", BG3PC);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3PD    = %04x\n", BG3PD);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3X     = %08x\n", (BG3X_H << 16) | BG3X_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "BG3Y     = %08x\n", (BG3Y_H << 16) | BG3Y_L);
        monprintf(monbuf);
    }
}

void debuggerIoDMA()
{
    {
        snprintf(monbuf, sizeof(monbuf), "DM0SAD   = %08x\n", (DM0SAD_H << 16) | DM0SAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM0DAD   = %08x\n", (DM0DAD_H << 16) | DM0DAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM0CNT   = %08x\n", (DM0CNT_H << 16) | DM0CNT_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM1SAD   = %08x\n", (DM1SAD_H << 16) | DM1SAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM1DAD   = %08x\n", (DM1DAD_H << 16) | DM1DAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM1CNT   = %08x\n", (DM1CNT_H << 16) | DM1CNT_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM2SAD   = %08x\n", (DM2SAD_H << 16) | DM2SAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM2DAD   = %08x\n", (DM2DAD_H << 16) | DM2DAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM2CNT   = %08x\n", (DM2CNT_H << 16) | DM2CNT_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM3SAD   = %08x\n", (DM3SAD_H << 16) | DM3SAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM3DAD   = %08x\n", (DM3DAD_H << 16) | DM3DAD_L);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "DM3CNT   = %08x\n", (DM3CNT_H << 16) | DM3CNT_L);
        monprintf(monbuf);
    }
}

void debuggerIoTimer()
{
    {
        snprintf(monbuf, sizeof(monbuf), "TM0D     = %04x\n", TM0D);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM0CNT   = %04x\n", TM0CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM1D     = %04x\n", TM1D);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM1CNT   = %04x\n", TM1CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM2D     = %04x\n", TM2D);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM2CNT   = %04x\n", TM2CNT);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM3D     = %04x\n", TM3D);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "TM3CNT   = %04x\n", TM3CNT);
        monprintf(monbuf);
    }
}

void debuggerIoMisc()
{
    {
        snprintf(monbuf, sizeof(monbuf), "P1       = %04x\n", P1);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "IE       = %04x\n", IE);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "IF       = %04x\n", IF);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "IME      = %04x\n", IME);
        monprintf(monbuf);
    }
}

void debuggerIo(int n, char** args)
{
    if (n == 1) {
        debuggerIoVideo();
        return;
    }
    if (!strcmp(args[1], "video"))
        debuggerIoVideo();
    else if (!strcmp(args[1], "video2"))
        debuggerIoVideo2();
    else if (!strcmp(args[1], "dma"))
        debuggerIoDMA();
    else if (!strcmp(args[1], "timer"))
        debuggerIoTimer();
    else if (!strcmp(args[1], "misc"))
        debuggerIoMisc();
    else {
        snprintf(monbuf, sizeof(monbuf), "Unrecognized option %s\n", args[1]);
        monprintf(monbuf);
    }
}

#define ASCII(c) (c)<32 ? '.' : (c)> 127 ? '.' : (c)

bool canUseTbl = true;
bool useWordSymbol = false;
bool thereIsATable = false;
char** wordSymbol;
bool isTerminator[256];
bool isNewline[256];
bool isTab[256];
uint8_t largestSymbol = 1;

void freeWordSymbolContents()
{
    for (int i = 0; i < 256; i++) {
        if (wordSymbol[i])
            free(wordSymbol[i]);
        wordSymbol[i] = NULL;
        isTerminator[i] = false;
        isNewline[i] = false;
        isTab[i] = false;
    }
}

void freeWordSymbol()
{
    useWordSymbol = false;
    thereIsATable = false;
    free(wordSymbol);
    largestSymbol = 1;
}

void debuggerReadCharTable(int n, char** args)
{
    if (n == 2) {
        if (!canUseTbl) {
            {
                snprintf(monbuf, sizeof(monbuf), "Cannot operate over character table, as it was disabled.\n");
                monprintf(monbuf);
            }
            return;
        }
        if (strcmp(args[1], "none") == 0) {
            freeWordSymbol();
            {
                snprintf(monbuf, sizeof(monbuf), "Cleared table. Reverted to ASCII.\n");
                monprintf(monbuf);
            }
            return;
        }
#if __STDC_WANT_SECURE_LIB__
        FILE* tlb = NULL;
        fopen_s(&tlb, args[1], "r");
#else
        FILE* tlb = fopen(args[1], "r");
#endif
        if (!tlb) {
            {
                snprintf(monbuf, sizeof(monbuf), "Could not open specified file. Abort.\n");
                monprintf(monbuf);
            }
            return;
        }
        char buffer[30];
        uint32_t slot;
        char* character = (char*)calloc(10, sizeof(char));
        wordSymbol = (char**)calloc(256, sizeof(char*));
        while (fgets(buffer, 30, tlb)) {
#if __STDC_WANT_SECURE_LIB__
            sscanf_s(buffer, "%02x=%s", &slot, character, 10);
#else
            sscanf(buffer, "%02x=%s", &slot, character);
#endif

            if (character[0]) {
                if (strlen(character) == 4) {
                    if ((character[0] == '<') && (character[1] == '\\') && (character[3] == '>')) {
                        if (character[2] == '0') {
                            isTerminator[slot] = true;
                        }
                        if (character[2] == 'n') {
                            isNewline[slot] = true;
                        }
                        if (character[2] == 't') {
                            isTab[slot] = true;
                        }
                        continue;
                    } else
                        wordSymbol[slot] = character;
                } else
                    wordSymbol[slot] = character;
            } else
                wordSymbol[slot] = (char*)' ';

            if (largestSymbol < strlen(character))
                largestSymbol = (uint8_t)strlen(character);

            character = (char*)malloc(10);
        }
        useWordSymbol = true;
        thereIsATable = true;

    } else {
        debuggerUsage("tbl");
    }
}

void printCharGroup(uint32_t addr, bool useAscii)
{
    for (int i = 0; i < 16; i++) {
        if (useWordSymbol && !useAscii) {
            char* c = wordSymbol[debuggerReadByte(addr + i)];
            int j;
            if (c) {
                {
                    snprintf(monbuf, sizeof(monbuf), "%s", c);
                    monprintf(monbuf);
                }
                j = (int)strlen(c);
            } else {
                j = 0;
            }
            while (j < largestSymbol) {
                {
                    snprintf(monbuf, sizeof(monbuf), " ");
                    monprintf(monbuf);
                }
                j++;
            }
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "%c", ASCII(debuggerReadByte(addr + i)));
                monprintf(monbuf);
            }
        }
    }
}

void debuggerMemoryByte(int n, char** args)
{
    if (n == 2) {
        uint32_t addr = 0;

        if (!dexp_eval(args[1], &addr)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression\n");
                monprintf(monbuf);
            }
            return;
        }
        for (int loop = 0; loop < 16; loop++) {
            {
                snprintf(monbuf, sizeof(monbuf), "%08x ", addr);
                monprintf(monbuf);
            }
            for (int j = 0; j < 16; j++) {
                {
                    snprintf(monbuf, sizeof(monbuf), "%02x ", debuggerReadByte(addr + j));
                    monprintf(monbuf);
                }
            }
            printCharGroup(addr, true);
            {
                snprintf(monbuf, sizeof(monbuf), "\n");
                monprintf(monbuf);
            }
            addr += 16;
        }
    } else
        debuggerUsage("mb");
}

void debuggerMemoryHalfWord(int n, char** args)
{
    if (n == 2) {
        uint32_t addr = 0;

        if (!dexp_eval(args[1], &addr)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression\n");
                monprintf(monbuf);
            }
            return;
        }

        addr = addr & 0xfffffffe;

        for (int loop = 0; loop < 16; loop++) {
            {
                snprintf(monbuf, sizeof(monbuf), "%08x ", addr);
                monprintf(monbuf);
            }
            for (int j = 0; j < 16; j += 2) {
                {
                    snprintf(monbuf, sizeof(monbuf), "%02x%02x ", debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j));
                    monprintf(monbuf);
                }
            }
            printCharGroup(addr, true);
            {
                snprintf(monbuf, sizeof(monbuf), "\n");
                monprintf(monbuf);
            }
            addr += 16;
        }
    } else
        debuggerUsage("mh");
}

void debuggerMemoryWord(int n, char** args)
{
    if (n == 2) {
        uint32_t addr = 0;
        if (!dexp_eval(args[1], &addr)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression\n");
                monprintf(monbuf);
            }
            return;
        }
        addr = addr & 0xfffffffc;
        for (int loop = 0; loop < 16; loop++) {
            {
                snprintf(monbuf, sizeof(monbuf), "%08x ", addr);
                monprintf(monbuf);
            }
            for (int j = 0; j < 16; j += 4) {
                {
                    snprintf(monbuf, sizeof(monbuf), "%02x%02x%02x%02x ", debuggerReadByte(addr + j + 3), debuggerReadByte(addr + j + 2), debuggerReadByte(addr + j + 1), debuggerReadByte(addr + j));
                    monprintf(monbuf);
                }
            }
            printCharGroup(addr, true);
            {
                snprintf(monbuf, sizeof(monbuf), "\n");
                monprintf(monbuf);
            }
            addr += 16;
        }
    } else
        debuggerUsage("mw");
}

void debuggerStringRead(int n, char** args)
{
    if (n == 2) {
        uint32_t addr = 0;

        if (!dexp_eval(args[1], &addr)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression\n");
                monprintf(monbuf);
            }
            return;
        }
        for (int i = 0; i < 512; i++) {
            uint8_t slot = debuggerReadByte(addr + i);

            if (useWordSymbol) {
                if (isTerminator[slot]) {
                    {
                        snprintf(monbuf, sizeof(monbuf), "\n");
                        monprintf(monbuf);
                    }
                    return;
                } else if (isNewline[slot]) {
                    {
                        snprintf(monbuf, sizeof(monbuf), "\n");
                        monprintf(monbuf);
                    }
                } else if (isTab[slot]) {
                    {
                        snprintf(monbuf, sizeof(monbuf), "\t");
                        monprintf(monbuf);
                    }
                } else {
                    if (wordSymbol[slot]) {
                        {
                            snprintf(monbuf, sizeof(monbuf), "%s", wordSymbol[slot]);
                            monprintf(monbuf);
                        }
                    }
                }
            } else {
                {
                    snprintf(monbuf, sizeof(monbuf), "%c", ASCII(slot));
                    monprintf(monbuf);
                }
            }
        }
    } else
        debuggerUsage("ms");
}

void debuggerRegisters(int, char**)
{
    {
        snprintf(monbuf, sizeof(monbuf), "R00=%08x R04=%08x R08=%08x R12=%08x\n", reg[0].I, reg[4].I, reg[8].I, reg[12].I);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "R01=%08x R05=%08x R09=%08x R13=%08x\n", reg[1].I, reg[5].I, reg[9].I, reg[13].I);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "R02=%08x R06=%08x R10=%08x R14=%08x\n", reg[2].I, reg[6].I, reg[10].I, reg[14].I);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "R03=%08x R07=%08x R11=%08x R15=%08x\n", reg[3].I, reg[7].I, reg[11].I, reg[15].I);
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "CPSR=%08x (%c%c%c%c%c%c%c Mode: %02x)\n",
            reg[16].I,
            (N_FLAG ? 'N' : '.'),
            (Z_FLAG ? 'Z' : '.'),
            (C_FLAG ? 'C' : '.'),
            (V_FLAG ? 'V' : '.'),
            (armIrqEnable ? '.' : 'I'),
            ((!(reg[16].I & 0x40)) ? '.' : 'F'),
            (armState ? '.' : 'T'),
            armMode);
        monprintf(monbuf);
    }
}

void debuggerExecuteCommands(int n, char** args)
{
    if (n == 1) {
        {
            snprintf(monbuf, sizeof(monbuf), "%s requires at least one pathname to execute.", args[0]);
            monprintf(monbuf);
        }
        return;
    } else {
        char buffer[4096];
        n--;
        args++;
        while (n) {
#if __STDC_WANT_SECURE_LIB__
            FILE* toExec = NULL;
            fopen_s(&toExec, args[0], "r");
#else
            FILE* toExec = fopen(args[0], "r");
#endif
            if (toExec) {
                while (fgets(buffer, 4096, toExec)) {
                    std::string buf(buffer);
                    dbgExecute(buf);
                    if (!debugger || !emulating) {
                        return;
                    }
                }
            } else {
                snprintf(monbuf, sizeof(monbuf), "Could not open %s. Will not be executed.\n", args[0]);
                monprintf(monbuf);
            }

            args++;
            n--;
        }
    }
}

void debuggerSetRadix(int argc, char** argv)
{
    if (argc != 2)
        debuggerUsage(argv[0]);
    else {
        int r = atoi(argv[1]);

        bool error = false;
        switch (r) {
        case 10:
            debuggerRadix = 0;
            break;
        case 8:
            debuggerRadix = 2;
            break;
        case 16:
            debuggerRadix = 1;
            break;
        default:
            error = true;
            {
                snprintf(monbuf, sizeof(monbuf), "Unknown radix %d. Valid values are 8, 10 and 16.\n", r);
                monprintf(monbuf);
            }
            break;
        }
        if (!error) {
            snprintf(monbuf, sizeof(monbuf), "Radix set to %d\n", r);
            monprintf(monbuf);
        }
    }
}

void debuggerSymbols(int argc, char** argv)
{
    int i = 0;
    uint32_t value;
    uint32_t size;
    int type;
    bool match = false;
    int matchSize = 0;
    char* matchStr = NULL;

    if (argc == 2) {
        match = true;
        matchSize = (int)strlen(argv[1]);
        matchStr = argv[1];
    }
    {
        snprintf(monbuf, sizeof(monbuf), "Symbol               Value    Size     Type   \n");
        monprintf(monbuf);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "-------------------- -------  -------- -------\n");
        monprintf(monbuf);
    }
    const char* s = NULL;
    while ((s = elfGetSymbol(i, &value, &size, &type))) {
        if (*s) {
            if (match) {
                if (strncmp(s, matchStr, matchSize) != 0) {
                    i++;
                    continue;
                }
            }
            const char* ts = "?";
            switch (type) {
            case 2:
                ts = "ARM";
                break;
            case 0x0d:
                ts = "THUMB";
                break;
            case 1:
                ts = "DATA";
                break;
            }
            {
                snprintf(monbuf, sizeof(monbuf), "%-20s %08x %08x %-7s\n", s, value, size, ts);
                monprintf(monbuf);
            }
        }
        i++;
    }
}

void debuggerWhere(int n, char** args)
{
    (void)n; // unused params
    (void)args; // unused params
    void elfPrintCallChain(uint32_t);
    elfPrintCallChain(armNextPC);
}

void debuggerVar(int n, char** args)
{
    uint32_t val;

    if (n < 2) {
        dexp_listVars();
        return;
    }

    if (strcmp(args[1], "set") == 0) {

        if (n < 4) {
            {
                snprintf(monbuf, sizeof(monbuf), "No expression specified.\n");
                monprintf(monbuf);
            }
            return;
        }

        if (!dexp_eval(args[3], &val)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n");
                monprintf(monbuf);
            }
            return;
        }

        dexp_setVar(args[2], val);
        {
            snprintf(monbuf, sizeof(monbuf), "%s = $%08x\n", args[2], val);
            monprintf(monbuf);
        }
        return;
    }

    if (strcmp(args[1], "list") == 0) {
        dexp_listVars();
        return;
    }

    if (strcmp(args[1], "save") == 0) {
        if (n < 3) {
            {
                snprintf(monbuf, sizeof(monbuf), "No file specified.\n");
                monprintf(monbuf);
            }
            return;
        }
        dexp_saveVars(args[2]);
        return;
    }

    if (strcmp(args[1], "load") == 0) {
        if (n < 3) {
            {
                snprintf(monbuf, sizeof(monbuf), "No file specified.\n");
                monprintf(monbuf);
            }
            return;
        }
        dexp_loadVars(args[2]);
        return;
    }

    {
        snprintf(monbuf, sizeof(monbuf), "Unrecognized sub-command.\n");
        monprintf(monbuf);
    }
}

bool debuggerBreakOnExecution(uint32_t address, uint8_t state)
{
    (void)state; // unused params
    if (dontBreakNow)
        return false;
    if (debuggerInDB(address))
        return false;
    if (!doesBreak(address, armState ? 0x44 : 0x88))
        return false;

    {
        snprintf(monbuf, sizeof(monbuf), "Breakpoint (on %s) address %08x\n", (armState ? "ARM" : "Thumb"), address);
        monprintf(monbuf);
    }
    debugger = true;
    return true;
}

bool debuggerBreakOnRead(uint32_t address, int size)
{
    (void)size; // unused params
    if (dontBreakNow)
        return false;
    if (debuggerInDB(armState ? reg[15].I - 4 : reg[15].I - 2))
        return false;
    if (!doesBreak(address, 0x22))
        return false;
    //if (size == 2)
    //	monprintf("Breakpoint (on read) address %08x value:%08x\n",
    //	address, debuggerReadMemory(address));
    //else if (size == 1)
    //	monprintf("Breakpoint (on read) address %08x value:%04x\n",
    //	address, debuggerReadHalfWord(address));
    //else
    //	monprintf("Breakpoint (on read) address %08x value:%02x\n",
    //	address, debuggerReadByte(address));
    debugger = true;
    return true;
}

bool debuggerBreakOnWrite(uint32_t address, uint32_t value, int size)
{
    (void)value; // unused params
    (void)size; // unused params
    if (dontBreakNow)
        return false;
    if (debuggerInDB(armState ? reg[15].I - 4 : reg[15].I - 2))
        return false;
    if (!doesBreak(address, 0x11))
        return false;
    //uint32_t lastValue;
    //dexp_eval("old_value", &lastValue);
    //if (size == 2)
    //	monprintf("Breakpoint (on write) address %08x old:%08x new:%08x\n",
    //	address, lastValue, value);
    //else if (size == 1)
    //	monprintf("Breakpoint (on write) address %08x old:%04x new:%04x\n",
    //	address, (uint16_t)lastValue, (uint16_t)value);
    //else
    //	monprintf("Breakpoint (on write) address %08x old:%02x new:%02x\n",
    //	address, (uint8_t)lastValue, (uint8_t)value);
    debugger = true;
    return true;
}

void debuggerBreakOnWrite(uint32_t address, uint32_t oldvalue, uint32_t value, int size, int t)
{
    (void)oldvalue; // unused params
    (void)t; // unused params
    debuggerBreakOnWrite(address, value, size);
    //uint32_t lastValue;
    //dexp_eval("old_value", &lastValue);

    //const char *type = "write";
    //if (t == 2)
    //	type = "change";

    //if (size == 2)
    //	monprintf("Breakpoint (on %s) address %08x old:%08x new:%08x\n",
    //	type, address, oldvalue, value);
    //else if (size == 1)
    //	monprintf("Breakpoint (on %s) address %08x old:%04x new:%04x\n",
    //	type, address, (uint16_t)oldvalue, (uint16_t)value);
    //else
    //	monprintf("Breakpoint (on %s) address %08x old:%02x new:%02x\n",
    //	type, address, (uint8_t)oldvalue, (uint8_t)value);
    //debugger = true;
}

uint8_t getFlags(char* flagName)
{

    for (int i = 0; flagName[i] != '\0'; i++) {
        flagName[i] = (char)toupper(flagName[i]);
    }

    if (strcmp(flagName, "ALWAYS") == 0) {
        return 0x7;
    }

    if (strcmp(flagName, "NEVER") == 0) {
        return 0x0;
    }

    uint8_t flag = 0;

    bool negate_flag = false;

    for (int i = 0; flagName[i] != '\0'; i++) {
        switch (flagName[i]) {
        case 'E':
            flag |= 1;
            break;
        case 'G':
            flag |= 2;
            break;
        case 'L':
            flag |= 4;
            break;
        case 'S':
            flag |= 8;
            break;
        case 'U':
            flag &= 7;
            break;
        case 'N':
            negate_flag = (!negate_flag);
            break;
        }
    }
    if (negate_flag) {
        flag = ((flag & 8) | ((~flag) & 7));
    }
    return flag;
}

void debuggerBreakRegister(int n, char** args)
{
    if (n != 3) {
        {
            snprintf(monbuf, sizeof(monbuf), "Incorrect usage of breg. Correct usage is breg <register> {flag} {value}\n");
            monprintf(monbuf);
        }
        printFlagHelp();
        return;
    }
    uint8_t _reg = (uint8_t)getRegisterNumber(args[0]);
    uint8_t flag = getFlags(args[1]);
    uint32_t value;
    if (!dexp_eval(args[2], &value)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Invalid expression.\n");
            monprintf(monbuf);
        }
        return;
    }
    if (flag != 0) {
        addBreakRegToList(_reg, flag, value);
        {
            snprintf(monbuf, sizeof(monbuf), "Added breakpoint on register R%02d, value %08x\n", _reg, value);
            monprintf(monbuf);
        }
    }
    return;
}

void debuggerBreakRegisterClear(int n, char** args)
{
    if (n > 0) {
        int r = getRegisterNumber(args[0]);
        if (r >= 0) {
            clearParticularRegListBreaks(r);
            {
                snprintf(monbuf, sizeof(monbuf), "Cleared all Register breakpoints for %s.\n", args[0]);
                monprintf(monbuf);
            }
        }
    } else {
        clearBreakRegList();
        {
            snprintf(monbuf, sizeof(monbuf), "Cleared all Register breakpoints.\n");
            monprintf(monbuf);
        }
    }
}

void debuggerBreakRegisterDelete(int n, char** args)
{
    if (n < 2) {
        {
            snprintf(monbuf, sizeof(monbuf), "Illegal use of Break register delete:\n Correct usage requires <register> <breakpointNo>.\n");
            monprintf(monbuf);
        }
        return;
    }
    int r = getRegisterNumber(args[0]);
    if ((r < 0) || (r > 16)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Could not find a correct register number:\n Correct usage requires <register> <breakpointNo>.\n");
            monprintf(monbuf);
        }
        return;
    }
    uint32_t num;
    if (!dexp_eval(args[1], &num)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Could not parse the breakpoint number:\n Correct usage requires <register> <breakpointNo>.\n");
            monprintf(monbuf);
        }
        return;
    }
    deleteFromBreakRegList((uint8_t)r, num);
    {
        snprintf(monbuf, sizeof(monbuf), "Deleted Breakpoint %d of regsiter %s.\n", num, args[0]);
        monprintf(monbuf);
    }
}

//WARNING: Some old particle to new code conversion may convert a single command
//into two or more words. Such words are separated by space, so a new tokenizer can
//find them.
const char* replaceAlias(const char* lower_cmd, const char** aliasTable)
{
    for (int i = 0; aliasTable[i]; i = i + 2) {
        if (strcmp(lower_cmd, aliasTable[i]) == 0) {
            return aliasTable[i + 1];
        }
    }
    return lower_cmd;
}

const char* breakAliasTable[] = {

    //actual beginning
    "break", "b 0 0",
    "breakpoint", "b 0 0",
    "bp", "b 0 0",
    "b", "b 0 0",

    //break types
    "thumb", "t",
    "arm", "a",
    "execution", "x",
    "exec", "x",
    "e", "x",
    "exe", "x",
    "x", "x",
    "read", "r",
    "write", "w",
    "access", "i",
    "acc", "i",
    "io", "i",
    "register", "g",
    "reg", "g",
    "any", "*",

    //code modifiers
    "clear", "c",
    "clean", "c",
    "cls", "c",
    "list", "l",
    "lst", "l",
    "delete", "d",
    "del", "d",
    "make", "m",
    /*
	//old parts made to look like the new code parts
	"bt", "b t m",
	"ba", "b a m",
	"bd", "b * d",
	"bl", "b * l",
	"bpr","b r m",
	"bprc","b r c",
	"bpw", "b w m",
	"bpwc", "b w c",
	"bt", "b t m",
	*/
    //and new parts made to look like old parts
    "breg", "b g m",
    "bregc", "b g c",
    "bregd", "b g d",
    "bregl", "b g l",

    "blist", "b * l",
    /*
	"btc", "b t c",
	"btd", "b t d",
	"btl", "b t l",

	"bac", "b a c",
	"bad", "b a d",
	"bal", "b a l",

	"bx", "b x m",
	"bxc", "b x c",
	"bxd", "b x d",
	"bxl", "b x l",

	"bw", "b w m",
	"bwc", "b w c",
	"bwd", "b w d",
	"bwl", "b w l",

	"br", "b r m",
	"brc", "b r c",
	"brd", "b r d",
	"brl", "b r l",
	*/
    "bio", "b i m",
    "bioc", "b i c",
    "biod", "b i d",
    "biol", "b i l",

    "bpio", "b i m",
    "bpioc", "b i c",
    "bpiod", "b i d",
    "bpiol", "b i l",
    /*
	"bprd", "b r d",
	"bprl", "b r l",

	"bpwd", "b w d",
	"bpwl", "b w l",
	*/
    NULL, NULL

};

char* breakSymbolCombo(char* command, int* length)
{
    char* res = (char*)malloc(6);
    res[0] = 'b';
    res[1] = ' ';
    res[2] = '0';
    res[3] = ' ';
    res[4] = '0';
    int i = 1;
    if (command[1] == 'p') {
        i++;
    }
    while (i < *length) {
        switch (command[i]) {
        case 'l':
        case 'c':
        case 'd':
        case 'm':
            if (res[4] == '0')
                res[4] = command[i];
            else {
                free(res);
                return command;
            }
            break;
        case '*':
        case 't':
        case 'a':
        case 'x':
        case 'r':
        case 'w':
        case 'i':
            if (res[2] == '0')
                res[2] = command[i];
            else {
                free(res);
                return command;
            }
            break;
        default:
            free(res);
            return command;
        }
        i++;
    }
    if (res[2] == '0')
        res[2] = '*';
    if (res[4] == '0')
        res[4] = 'm';
    *length = 5;
    return res;
}

const char* typeMapping[] = { "'uint8_t", "'uint16_t", "'uint32_t", "'uint32_t", "'int8_t", "'int16_t", "'int32_t", "'int32_t" };

const char* compareFlagMapping[] = { "Never", "==", ">", ">=", "<", "<=", "!=", "<=>" };

struct intToString {
    int value;
    const char mapping[20];
};

struct intToString breakFlagMapping[] = {
    { 0x80, "Thumb" },
    { 0x40, "ARM" },
    { 0x20, "Read" },
    { 0x10, "Write" },
    { 0x8, "Thumb" },
    { 0x4, "ARM" },
    { 0x2, "Read" },
    { 0x1, "Write" },
    { 0x0, "None" }
};

//printers
void printCondition(struct ConditionalBreakNode* toPrint)
{
    if (toPrint) {
        const char* firstType = typeMapping[toPrint->exp_type_flags & 0x7];
        const char* secondType = typeMapping[(toPrint->exp_type_flags >> 4) & 0x7];
        const char* operand = compareFlagMapping[toPrint->cond_flags & 0x7];
        {
            snprintf(monbuf, sizeof(monbuf), "%s %s %s%s %s %s", firstType, toPrint->address,
                ((toPrint->cond_flags & 8) ? "s" : ""), operand,
                secondType, toPrint->value);
            monprintf(monbuf);
        }
        if (toPrint->next) {
            {
                snprintf(monbuf, sizeof(monbuf), " &&\n\t\t");
                monprintf(monbuf);
            }
            printCondition(toPrint->next);
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "\n");
                monprintf(monbuf);
            }
            return;
        }
    }
}

void printConditionalBreak(struct ConditionalBreak* toPrint, bool printAddress)
{
    if (toPrint) {
        if (printAddress) {
            snprintf(monbuf, sizeof(monbuf), "At %08x, ", toPrint->break_address);
            monprintf(monbuf);
        }
        if (toPrint->type_flags & 0xf0) {
            snprintf(monbuf, sizeof(monbuf), "Break Always on");
            monprintf(monbuf);
        }
        bool hasPrevCond = false;
        uint8_t flgs = 0x80;
        while (flgs != 0) {
            if (toPrint->type_flags & flgs) {
                if (hasPrevCond) {
                    snprintf(monbuf, sizeof(monbuf), ",");
                    monprintf(monbuf);
                }
                for (int i = 0; i < 9; i++) {
                    if (breakFlagMapping[i].value == flgs) {
                        {
                            snprintf(monbuf, sizeof(monbuf), "\t%s", breakFlagMapping[i].mapping);
                            monprintf(monbuf);
                        }
                        hasPrevCond = true;
                    }
                }
            }
            flgs = flgs >> 1;
            if ((flgs == 0x8) && (toPrint->type_flags & 0xf)) {
                {
                    snprintf(monbuf, sizeof(monbuf), "\n\t\tBreak conditional on");
                    monprintf(monbuf);
                }
                hasPrevCond = false;
            }
        }
        {
            snprintf(monbuf, sizeof(monbuf), "\n");
            monprintf(monbuf);
        }
        if (toPrint->type_flags & 0xf && toPrint->firstCond) {
            {
                snprintf(monbuf, sizeof(monbuf), "With conditions:\n\t\t");
                monprintf(monbuf);
            }
            printCondition(toPrint->firstCond);
        } else if (toPrint->type_flags & 0xf) {
            //should not happen
            {
                snprintf(monbuf, sizeof(monbuf), "No conditions detected, but conditional. Assumed always by default.\n");
                monprintf(monbuf);
            }
        }
    }
}

void printAllConditionals()
{

    for (int i = 0; i < 16; i++) {

        if (conditionals[i] != NULL) {
            {
                snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i);
                monprintf(monbuf);
            }
            {
                snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                monprintf(monbuf);
            }
            struct ConditionalBreak* base = conditionals[i];
            int count = 1;
            uint32_t lastAddress = base->break_address;
            {
                snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress);
                monprintf(monbuf);
            }
            while (base) {
                if (lastAddress != base->break_address) {
                    lastAddress = base->break_address;
                    count = 1;
                    {
                        snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                        monprintf(monbuf);
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress);
                        monprintf(monbuf);
                    }
                }
                {
                    snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count);
                    monprintf(monbuf);
                }
                printConditionalBreak(base, false);
                count++;
                base = base->next;
            }
        }
    }
}

uint8_t printConditionalsFromAddress(uint32_t address)
{
    uint8_t count = 1;
    if (conditionals[address >> 24] != NULL) {
        struct ConditionalBreak* base = conditionals[address >> 24];
        while (base) {
            if (address == base->break_address) {
                if (count == 1) {
                    {
                        snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", address);
                        monprintf(monbuf);
                    }
                }
                {
                    snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count);
                    monprintf(monbuf);
                }
                printConditionalBreak(base, false);
                count++;
            }
            if (address < base->break_address)
                break;
            base = base->next;
        }
    }
    if (count == 1) {
        {
            snprintf(monbuf, sizeof(monbuf), "None\n");
            monprintf(monbuf);
        }
    }
    return count;
}

void printAllFlagConditionals(uint8_t flag, bool orMode)
{
    int count = 1;
    int actualCount = 1;
    for (int i = 0; i < 16; i++) {
        if (conditionals[i] != NULL) {
            bool isCondStart = true;
            struct ConditionalBreak* base = conditionals[i];

            uint32_t lastAddress = base->break_address;

            while (base) {
                if (lastAddress != base->break_address) {
                    lastAddress = base->break_address;
                    count = 1;
                    actualCount = 1;
                }
                if (((base->type_flags & flag) == base->type_flags) || (orMode && (base->type_flags & flag))) {
                    if (actualCount == 1) {
                        if (isCondStart) {
                            {
                                snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i);
                                monprintf(monbuf);
                            }
                            {
                                snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                                monprintf(monbuf);
                            }
                            isCondStart = false;
                        }
                        {
                            snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress);
                            monprintf(monbuf);
                        }
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count);
                        monprintf(monbuf);
                    }
                    printConditionalBreak(base, false);
                    actualCount++;
                }
                base = base->next;
                count++;
            }
        }
    }
}

void printAllFlagConditionalsWithAddress(uint32_t address, uint8_t flag, bool orMode)
{
    int count = 1;
    int actualCount = 1;
    for (int i = 0; i < 16; i++) {
        if (conditionals[i] != NULL) {
            bool isCondStart = true;
            struct ConditionalBreak* base = conditionals[i];

            uint32_t lastAddress = base->break_address;

            while (base) {
                if (lastAddress != base->break_address) {
                    lastAddress = base->break_address;
                    count = 1;
                    actualCount = 1;
                }
                if ((lastAddress == address) && (((base->type_flags & flag) == base->type_flags) || (orMode && (base->type_flags & flag)))) {
                    if (actualCount == 1) {
                        if (isCondStart) {
                            {
                                snprintf(monbuf, sizeof(monbuf), "Address range 0x%02x000000 breaks:\n", i);
                                monprintf(monbuf);
                            }
                            {
                                snprintf(monbuf, sizeof(monbuf), "-------------------------\n");
                                monprintf(monbuf);
                            }
                            isCondStart = false;
                        }
                        {
                            snprintf(monbuf, sizeof(monbuf), "Address %08x\n-------------------------\n", lastAddress);
                            monprintf(monbuf);
                        }
                    }
                    {
                        snprintf(monbuf, sizeof(monbuf), "No.%d\t-->\t", count);
                        monprintf(monbuf);
                    }
                    printConditionalBreak(base, false);
                    actualCount++;
                }
                base = base->next;
                count++;
            }
        }
    }
}

void makeBreak(uint32_t address, uint8_t flags, char** expression, int n)
{
    if (n >= 1) {
        if (tolower(expression[0][0]) == 'i' && tolower(expression[0][1]) == 'f') {
            expression = expression + 1;
            n--;
            if (n != 0) {
                parseAndCreateConditionalBreaks(address, flags, expression, n);
                return;
            }
        }
    } else {
        flags = flags << 0x4;
        printConditionalBreak(addConditionalBreak(address, flags), true);
        return;
    }
}
void deleteBreak(uint32_t address, uint8_t flags, char** expression, int howToDelete)
{
    bool applyOr = true;
    if (howToDelete > 0) {
        if (((expression[0][0] == '&') && !expression[0][1]) || ((tolower(expression[0][0]) == 'o') && (tolower(expression[0][1]) == 'n')) || ((tolower(expression[0][0]) == 'l') && (tolower(expression[0][1]) == 'y'))) {
            applyOr = false;
            howToDelete--;
            expression++;
        }
        if (howToDelete > 0) {
            uint32_t number = 0;
            if (!dexp_eval(expression[0], &number)) {
                {
                    snprintf(monbuf, sizeof(monbuf), "Invalid expression for number format.\n");
                    monprintf(monbuf);
                }
                return;
            }
            removeFlagFromConditionalBreakNo(address, (uint8_t)number, (flags | (flags >> 4)));
            {
                snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address);
                monprintf(monbuf);
            }
            return;
        }
        removeConditionalWithAddressAndFlag(address, flags, applyOr);
        removeConditionalWithAddressAndFlag(address, flags << 4, applyOr);
        {
            snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address);
            monprintf(monbuf);
        }
    } else {
        removeConditionalWithAddressAndFlag(address, flags, applyOr);
        removeConditionalWithAddressAndFlag(address, flags << 4, applyOr);
        {
            snprintf(monbuf, sizeof(monbuf), "Removed all specified breaks from %08x.\n", address);
            monprintf(monbuf);
        }
    }
    return;
}
void clearBreaks(uint32_t address, uint8_t flags, char** expression, int howToClear)
{
    (void)address; // unused params
    (void)expression; // unused params
    if (howToClear == 2) {
        removeConditionalWithFlag(flags, true);
        removeConditionalWithFlag(flags << 4, true);
    } else {
        removeConditionalWithFlag(flags, false);
        removeConditionalWithFlag(flags << 4, false);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "Cleared all requested breaks.\n");
        monprintf(monbuf);
    }
}

void listBreaks(uint32_t address, uint8_t flags, char** expression, int howToList)
{
    (void)expression; // unused params
    flags |= (flags << 4);
    if (howToList) {
        printAllFlagConditionalsWithAddress(address, flags, true);
    } else {
        printAllFlagConditionals(flags, true);
    }
    {
        snprintf(monbuf, sizeof(monbuf), "\n");
        monprintf(monbuf);
    }
}

void executeBreakCommands(int n, char** cmd)
{
    char* command = cmd[0];
    int len = (int)strlen(command);
    bool changed = false;
    if (len <= 4) {
        command = breakSymbolCombo(command, &len);
        changed = (len == 5);
    }
    if (!changed) {
        command = strdup(replaceAlias(cmd[0], breakAliasTable));
        changed = (strcmp(cmd[0], command));
    }
    if (!changed) {
        cmd[0][0] = '!';
        return;
    }
    cmd++;
    n--;
    void (*operation)(uint32_t, uint8_t, char**, int) = &makeBreak; //the function to be called

    uint8_t flag = 0;
    uint32_t address = 0;
    //if(strlen(command) == 1){
    //Cannot happen, that would mean cmd[0] != b
    //}
    char target;
    char ope;

    if (command[2] == '0') {
        if (n <= 0) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid break command.\n");
                monprintf(monbuf);
            }
            free(command);
            return;
        }

        for (int i = 0; cmd[0][i]; i++) {
            cmd[0][i] = (char)tolower(cmd[0][i]);
        }
        const char* replaced = replaceAlias(cmd[0], breakAliasTable);
        if (replaced == cmd[0]) {
            target = '*';
        } else {
            target = replaced[0];
            if ((target == 'c') || (target == 'd') || (target == 'l') || (target == 'm')) {
                command[4] = target;
                target = '*';
            }
            cmd++;
            n--;
        }
        command[2] = target;
    }

    if (command[4] == '0') {
        if (n <= 0) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid break command.\n");
                monprintf(monbuf);
            }
            free(command);
            return;
        }

        for (int i = 0; cmd[0][i]; i++) {
            cmd[0][i] = (char)tolower(cmd[0][i]);
        }
        ope = replaceAlias(cmd[0], breakAliasTable)[0];
        if ((ope == 'c') || (ope == 'd') || (ope == 'l') || (ope == 'm')) {
            command[4] = ope;
            cmd++;
            n--;
        } else {
            command[4] = 'm';
        }
    }

    switch (command[4]) {
    case 'l':
        operation = &listBreaks;
        break;
    case 'c':
        operation = &clearBreaks;
        break;
    case 'd':
        operation = &deleteBreak;
        break;

    case 'm':
    default:
        operation = &makeBreak;
    };

    switch (command[2]) {
    case 'g':
        switch (command[4]) {
        case 'l':
            debuggerBreakRegisterList((n > 0) && (tolower(cmd[0][0]) == 'v'));
            return;
        case 'c':
            debuggerBreakRegisterClear(n, cmd);
            return;
        case 'd':
            debuggerBreakRegisterDelete(n, cmd);
            return;

        case 'm':
            debuggerBreakRegister(n, cmd);
        default:
            return;
        };
        return;
    case '*':
        flag = 0xf;
        break;
    case 't':
        flag = 0x8;
        break;
    case 'a':
        flag = 0x4;
        break;
    case 'x':
        flag = 0xC;
        break;
    case 'r':
        flag = 0x2;
        break;
    case 'w':
        flag = 0x1;
        break;
    case 'i':
        flag = 0x3;
        break;
    default:
        free(command);
        return;
    };

    free(command);
    bool hasAddress = false;
    if ((n >= 1) && (operation != clearBreaks)) {
        if (!dexp_eval(cmd[0], &address)) {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid expression for address format.\n");
                monprintf(monbuf);
            }
            return;
        }
        hasAddress = true;
    }
    if (operation == listBreaks) {
        operation(address, flag, NULL, hasAddress);
        return;
    } else if (operation == clearBreaks) {
        if (!hasAddress && (n >= 1)) {
            if ((cmd[0][0] == '|' && cmd[0][1] == '|') || ((cmd[0][0] == 'O' || cmd[0][0] == 'o') && (cmd[0][1] == 'R' || cmd[0][1] == 'r'))) {
                operation(address, flag, NULL, 2);
            } else {
                operation(address, flag, NULL, 0);
            }
        } else {
            operation(address, flag, NULL, 0);
        }
    } else if (!hasAddress && (operation == deleteBreak)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Delete breakpoint operation requires at least one address;\n");
            monprintf(monbuf);
        }
        {
            snprintf(monbuf, sizeof(monbuf), "Usage: break [type] delete [address] no.[number] --> Deletes breakpoint [number] of [address].\n");
            monprintf(monbuf);
        }
        //{ snprintf(monbuf, sizeof(monbuf), "Usage: [delete Operand] [address] End [address] --> Deletes range between [address] and [end]\n"); monprintf(monbuf); }
        {
            snprintf(monbuf, sizeof(monbuf), "Usage: break [type] delete [address]\n --> Deletes all breakpoints of [type] on [address].");
            monprintf(monbuf);
        }
        return;
    } else if (!hasAddress && (operation == makeBreak)) {
        {
            snprintf(monbuf, sizeof(monbuf), "Can only create breakpoints if an address is provided");
            monprintf(monbuf);
        }
        //print usage here
        return;
    } else {
        operation(address, flag, cmd + 1, n - 1);
        return;
    }
//brkcmd_special_register:
    switch (command[4]) {
    case 'l':
        debuggerBreakRegisterList((n > 0) && (tolower(cmd[0][0]) == 'v'));
        return;
    case 'c':
        debuggerBreakRegisterClear(n, cmd);
        return;
    case 'd':
        debuggerBreakRegisterDelete(n, cmd);
        return;

    case 'm':
        debuggerBreakRegister(n, cmd);
    default:
        return;
    };
    return;
}

void debuggerDisable(int n, char** args)
{
    if (n >= 3) {
        debuggerUsage("disable");
        return;
    }
    while (n > 1) {
        int i = 0;
        while (args[3 - n][i]) {
            args[3 - n][i] = (char)tolower(args[2 - n][i]);
            i++;
        }
        if (strcmp(args[3 - n], "breg")) {
            enableRegBreak = false;
            {
                snprintf(monbuf, sizeof(monbuf), "Break on register disabled.\n");
                monprintf(monbuf);
            }
        } else if (strcmp(args[3 - n], "tbl")) {
            canUseTbl = false;
            useWordSymbol = false;
            {
                snprintf(monbuf, sizeof(monbuf), "Symbol table disabled.\n");
                monprintf(monbuf);
            }
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid command. Only tbl and breg are accepted as commands\n");
                monprintf(monbuf);
            }
            return;
        }
        n--;
    }
}

void debuggerEnable(int n, char** args)
{
    if (n >= 3) {
        debuggerUsage("enable");
        return;
    }
    while (n > 1) {
        int i = 0;
        while (args[3 - n][i]) {
            args[3 - n][i] = (char)tolower(args[2 - n][i]);
            i++;
        }
        if (strcmp(args[3 - n], "breg")) {
            enableRegBreak = true;
            {
                snprintf(monbuf, sizeof(monbuf), "Break on register enabled.\n");
                monprintf(monbuf);
            }
        } else if (strcmp(args[3 - n], "tbl")) {
            canUseTbl = true;
            useWordSymbol = thereIsATable;
            {
                snprintf(monbuf, sizeof(monbuf), "Symbol table enabled.\n");
                monprintf(monbuf);
            }
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "Invalid command. Only tbl and breg are accepted as commands\n");
                monprintf(monbuf);
            }
            return;
        }
        n--;
    }
}

DebuggerCommand debuggerCommands[] = {
    //simple commands
    { "?", debuggerHelp, "Shows this help information. Type ? <command> for command help. Alias 'help', 'h'.", "[<command>]" },
    //	{ "n", debuggerNext, "Executes the next instruction.", "[<count>]" },
    //	{ "c", debuggerContinue, "Continues execution", NULL },
    //	// Hello command, shows Hello on the board

    //{ "br", debuggerBreakRead, "Break on read", "{address} {size}" },
    //{ "bw", debuggerBreakWrite, "Break on write", "{address} {size}" },
    //{ "bt", debuggerBreakWrite, "Break on write", "{address} {size}" },

    //{ "ba", debuggerBreakArm, "Adds an ARM breakpoint", "{address}" },
    //{ "bd", debuggerBreakDelete, "Deletes a breakpoint", "<number>" },
    //{ "bl", debuggerBreakList, "Lists breakpoints" },
    //{ "bpr", debuggerBreakRead, "Break on read", "{address} {size}" },
    //{ "bprc", debuggerBreakReadClear, "Clear break on read", NULL },
    //{ "bpw", debuggerBreakWrite, "Break on write", "{address} {size}" },
    //{ "bpwc", debuggerBreakWriteClear, "Clear break on write", NULL },
    { "breg", debuggerBreakRegister, "Breaks on a register specified value", "<register_number> {flag} {value}" },
    { "bregc", debuggerBreakRegisterClear, "Clears all break on register", "<register_number> {flag} {value}" },
    //{ "bt", debuggerBreakThumb, "Adds a THUMB breakpoint", "{address}" }

    //	//diassemble commands
    //	{ "d", debuggerDisassemble, "Disassembles instructions", "[<address> [<number>]]" },
    //	{ "da", debuggerDisassembleArm, "Disassembles ARM instructions", "[{address} [{number}]]" },
    //	{ "dt", debuggerDisassembleThumb, "Disassembles Thumb instructions", "[{address} [{number}]]" },

    { "db", debuggerDontBreak, "Don't break at the following address.", "[{address} [{number}]]" },
    { "dbc", debuggerDontBreakClear, "Clear the Don't Break list.", NULL },
    { "dload", debuggerDumpLoad, "Load raw data dump from file", "<file> {address}" },
    { "dsave", debuggerDumpSave, "Dump raw data to file", "<file> {address} {size}" },
    //	{ "dn", debuggerDisassembleNear, "Disassembles instructions near PC", "[{number}]" },

    { "disable", debuggerDisable, "Disables operations.", "tbl|breg" },
    { "enable", debuggerEnable, "Enables operations.", "tbl|breg" },

    { "eb", debuggerEditByte, "Modify memory location (byte)", "{address} {value}*" },
    { "eh", debuggerEditHalfWord, "Modify memory location (half-word)", "{address} {value}*" },
    { "ew", debuggerEditWord, "Modify memory location (word)", "{address} {value}*" },
    { "er", debuggerEditRegister, "Modify register", "<register number> {value}" },

    { "eval", debuggerEval, "Evaluate expression", "{expression}" },

    { "fillb", debuggerFillByte, "Fills memory location (byte)", "{address} {value} {number of times}" },
    { "fillh", debuggerFillHalfWord, "Fills memory location (half-word)", "{address} {value} {number of times}" },
    { "fillw", debuggerFillWord, "Fills memory location (word)", "{address} {value} {number of times}" },

    { "copyb", debuggerCopyByte, "Copies memory content (byte)", "{address} {second address} {size} optional{repeat}" },
    { "copyh", debuggerCopyHalfWord, "Copies memory content (half-word)", "{address} {second address} {size} optional{repeat}" },
    { "copyw", debuggerCopyWord, "Copies memory content (word)", "{address} {second address} {size} optional{repeat}" },

    { "ft", debuggerFindText, "Search memory for ASCII-string.", "<start> [<max-result>] <string>" },
    { "fh", debuggerFindHex, "Search memory for hex-string.", "<start> [<max-result>] <hex-string>" },
    { "fr", debuggerFindResume, "Resume current search.", "[<max-result>]" },

    { "io", debuggerIo, "Show I/O registers status", "[video|video2|dma|timer|misc]" },
    //	{ "load", debuggerReadState, "Loads a Fx type savegame", "<number>" },

    { "mb", debuggerMemoryByte, "Shows memory contents (bytes)", "{address}" },
    { "mh", debuggerMemoryHalfWord, "Shows memory contents (half-words)", "{address}" },
    { "mw", debuggerMemoryWord, "Shows memory contents (words)", "{address}" },
    { "ms", debuggerStringRead, "Shows memory contents (table string)", "{address}" },

    { "r", debuggerRegisters, "Shows ARM registers", NULL },
    //	{ "rt", debuggerRunTo, "Run to address", "{address}" },
    //	{ "rta", debuggerRunToArm, "Run to address (ARM)", "{address}" },
    //	{ "rtt", debuggerRunToThumb, "Run to address (Thumb)", "{address}" },

    //	{ "reset", debuggerResetSystem, "Resets the system", NULL },
    //	{ "reload", debuggerReloadRom, "Reloads the ROM", "optional {rom path}" },
    { "execute", debuggerExecuteCommands, "Executes commands from a text file", "{file path}" },

    //	{ "save", debuggerWriteState, "Creates a Fx type savegame", "<number>" },
    //	{ "sbreak", debuggerBreak, "Adds a breakpoint on the given function", "<function>|<line>|<file:line>" },
    { "sradix", debuggerSetRadix, "Sets the print radix", "<radix>" },
    //	{ "sprint", debuggerPrint, "Print the value of a expression (if known)", "[/x|/o|/d] <expression>" },
    { "ssymbols", debuggerSymbols, "List symbols", "[<symbol>]" },
    //#ifndef FINAL_VERSION
    //	{ "strace", debuggerDebug, "Sets the trace level", "<value>" },
    //#endif
    //#ifdef DEV_VERSION
    //	{ "sverbose", debuggerVerbose, "Change verbose setting", "<value>" },
    //#endif
    { "swhere", debuggerWhere, "Shows call chain", NULL },

    { "tbl", debuggerReadCharTable, "Loads a character table", "<file>" },

    //	{ "trace", debuggerTrace, "Control tracer", "start|stop|file <file>" },
    { "var", debuggerVar, "Define variables", "<name> {variable}" },
    { NULL, NULL, NULL, NULL } // end marker
};

void printFlagHelp()
{
    monprintf("Flags are combinations of six distinct characters:\n");
    monprintf("\t\te --> Equal to;\n");
    monprintf("\t\tg --> Greater than;\n");
    monprintf("\t\tl --> Less than;\n");
    monprintf("\t\ts --> signed;\n");
    monprintf("\t\tu --> unsigned (assumed by ommision);\n");
    monprintf("\t\tn --> not;\n");
    monprintf("Ex: ge -> greater or equal; ne -> not equal; lg --> less or greater (same as not equal);\n");
    monprintf("s and u parts cannot be used in the same line, and are not negated by n;\n");
    monprintf("Special flags: always(all true), never(all false).\n");
}

void debuggerUsage(const char* cmd)
{
    if (!strcmp(cmd, "break")) {
        monprintf("Break command, composed of three parts:\n");
        monprintf("Break (b, bp or break): Indicates a break command;\n");
        monprintf("Type of break: Indicates the type of break the command applies to;\n");
        monprintf("Command: Indicates the type of command to be applied.\n");
        monprintf("Type Flags:\n\tt (thumb): The Thumb execution mode.\n");
        monprintf("\ta (ARM): The ARM execution mode.\n");
        monprintf("\tx (execution, exe, exec, e): Any execution mode.\n");
        monprintf("\tr (read): When a read occurs.\n");
        monprintf("\tw (write): When a write occurs.\n");
        monprintf("\ti (io, access,acc): When memory access (read or write) occurs.\n");
        monprintf("\tg (register, reg): Special On Register value change break.\n");
        monprintf("\t* (any): On any occasion (except register change).Omission value.\n");
        monprintf("Cmd Flags:\n\tm (make): Create a breakpoint.Default omission value.\n");
        monprintf("\tl (list,lst): Lists all existing breakpoints of the specified type.\n");
        monprintf("\td (delete,del): Deletes a specific breakpoint of the specified type.\n");
        monprintf("\tc (clear, clean, cls): Erases all breakpoints of the specified type.\n");
        monprintf("\n");
        monprintf("All those flags can be combined in order to access the several break functions\n");
        monprintf("EX: btc clears all breaks; bx, bxm creates a breakpoint on any type of execution.\n");
        monprintf("All commands can be built by using [b|bp][TypeFlag][CommandFlag];\n");
        monprintf("All commands can be built by using [b|bp|break] [TypeFlag|alias] [CommandFlag|alias];\n");
        monprintf("Each command has separate arguments from each other.\nFor more details, use help b[reg|m|d|c|l]\n");
        return;
    }
    if (!strcmp(cmd, "breg")) {
        monprintf("Break on register command, special case of the break command.\n");
        monprintf("It allows the user to break when a certain value is inside a register.\n");
        monprintf("All register breaks are conditional.\n");
        monprintf("Usage: breg [regName] [condition] [Expression].\n");
        monprintf("regName is between r0 and r15 (PC, LR and SP included);\n");
        monprintf("expression is an evaluatable expression whose value determines when to break;\n");
        monprintf("condition is the condition to be evaluated in typeFlags.\n");
        printFlagHelp();
        monprintf("---------!!!WARNING!!!---------\n");
        monprintf("Register checking and breaking is extremely expensive for the computer.\n");
        monprintf("On one of the test machines, a maximum value of 600% for speedup collapsed\n");
        monprintf("to 350% just from having them enabled.\n");
        monprintf("If (or while) not needed, you can have a speedup by disabling them, using\n");
        monprintf("disable breg.\n");
        monprintf("Breg is disabled by default. Re-enable them using enable breg.\n");
        monprintf("Use example: breg r0 ne 0x0 --> Breaks as soon as r0 is not 0.\n");
        return;
    }
    if (!strcmp(cmd, "bm")) {
        monprintf("Create breakpoint command. Used to place a breakpoint on a given address.\n");
        monprintf("It allows for breaks on execution(any processor mode) and on access(r/w).\n");
        monprintf("Breaks can be Conditional or Inconditional.\n\n");
        monprintf("Inconditional breaks:\nUsage: [breakTag] [address]\n");
        monprintf("Simplest of the two, the old type of breaks. Creates a breakpoint that, when\n");
        monprintf("the given type flag occurs (like a read, or a run when in thumb mode), halts;\n\n");
        monprintf("Conditional breaks:\n");
        monprintf("Usage:\n\t[breakTag] [address] if {'<type> [expr] [cond] '<type> [expr] <&&,||>}\n");
        monprintf("Where <> elements are optional, {} are repeateable;\n");
        monprintf("[expression] are evaluatable expressions, in the usual VBA format\n(that is, eval acceptable);\n");
        monprintf("type is the type of that expression. Uses C-like names. Omission means integer.\n");
        monprintf("cond is the condition to be evaluated.\n");
        monprintf("If && or || are not present, the chain of evaluation stops.\n");
        monprintf("&& states the next condition must happen with the previous one, or the break\nfails.\n");
        monprintf("|| states the next condition is independent from the last one, and break\nseparately.\n\n");
        monprintf("Type can be:\n");
        monprintf("   [uint8_t, b, byte],[uint16_t, h, hword, halfword],[uint32_t,w, word]\n");
        monprintf("   [int8_t, sb, sbyte],[int16_t, sh, shword, short, shalfword],[int32_t, int, sw, word]\n");
        monprintf("Types have to be preceded by a ' ex: 'int, 'uint8_t\n\n");
        monprintf("Conditions may be:\n");
        monprintf("C-like:\t\t[<], [<=], [>], [>=] , [==], [!= or <>]\n");
        monprintf("ASM-like:\t[lt], [le], [gt], [ge] , [eq], [ne]\n\n");
        monprintf("EX:	bw 0x03005008 if old_value == 'uint32_t [0x03005008]\n");
        monprintf("Breaks on write from 0x03005008, when the old_value variable, that is assigned\n");
        monprintf("as the previous memory value when a write is performed, is equal to the new\ncontents of 0x03005008.\n\n");
        monprintf("EX:	bx 0x08000500 if r0 == 1 || r0 > 1 && r2 == 0 || 'uint8_t [r7] == 5\n");
        monprintf("Breaks in either thumb or arm execution of 0x08000500, if r0's contents are 1,\n");
        monprintf("or if r0's contents are bigger than 1 and r2 is equal to 0, or the content of\nthe address at r7(as byte) is equal to 5.\n");
        monprintf("It will not break if r0 > 1 and r2 != 0.\n");
        return;
    }
    if (!strcmp(cmd, "bl")) {
        monprintf("List breakpoints command. Used to view breakpoints.\n");
        monprintf("Usage: [breakTag] <address> <v>\n");
        monprintf("It will list all breaks on the specified type (read, write..).\n");
        monprintf("If (optional) address is included, it will try and list all breaks of that type\n");
        monprintf("for that address.\n");
        monprintf("The numbers shown on that list (No.) are the ones needed to delete it directly.\n");
        monprintf("v option lists all requested values, even if empty.\n");
        return;
    }
    if (!strcmp(cmd, "bc")) {
        monprintf("Clear breakpoints command. Clears all specified breakpoints.\n");
        monprintf("Usage: [breakTag] <or,||>\n");
        monprintf("It will delete all breaks on all addresses for the specified type.\n");
        monprintf("If (optional) or is included, it will try and delete all breaks associated with\n");
        monprintf("the flags. EX: bic or --> Deletes all breaks on read and all on write.\n");
        return;
    }
    if (!strcmp(cmd, "bd")) {
        monprintf("Delete breakpoint command. Clears the specified breakpoint.\n");
        monprintf("Usage: [breakTag] [address] <only> [number]\n");
        monprintf("It will delete the numbered break on that addresses for the specified type.\n");
        monprintf("If only is included, it will delete only breaks with the specified flag.\n");
        monprintf("EX: bxd 0x8000000 only -->Deletes all breaks on 0x08000000 that break on both\n");
        monprintf("arm and thumb modes. Thumb only or ARM only are unnafected.\n");
        monprintf("EX: btd 0x8000000 5 -->Deletes the thumb break from the 5th break on 0x8000000.\n");
        monprintf("---------!!!WARNING!!!---------\n");
        monprintf("Break numbers are volatile, and may change at any time. before deleting any one\n");
        monprintf("breakpoint, list them to see if the number hasn't changed. The numbers may\n");
        monprintf("change only when you add or delete a breakpoint to that address. Numbers are \n");
        monprintf("internal to each address.\n");
        return;
    }

    for (int i = 0;; i++) {
        if (debuggerCommands[i].name) {
            if (!strcmp(debuggerCommands[i].name, cmd)) {
                snprintf(monbuf, sizeof(monbuf), "%s %s\t%s\n",
                    debuggerCommands[i].name,
                    debuggerCommands[i].syntax ? debuggerCommands[i].syntax : "",
                    debuggerCommands[i].help);
                monprintf(monbuf);
                break;
            }
        } else {
            {
                snprintf(monbuf, sizeof(monbuf), "Unrecognized command '%s'.", cmd);
                monprintf(monbuf);
            }
            break;
        }
    }
}

void debuggerHelp(int n, char** args)
{
    if (n == 2) {
        debuggerUsage(args[1]);
    } else {
        for (int i = 0;; i++) {
            if (debuggerCommands[i].name) {
                {
                    snprintf(monbuf, sizeof(monbuf), "%-10s%s\n", debuggerCommands[i].name, debuggerCommands[i].help);
                    monprintf(monbuf);
                }
            } else
                break;
        }
        {
            snprintf(monbuf, sizeof(monbuf), "%-10s%s\n", "break", "Breakpoint commands");
            monprintf(monbuf);
        }
    }
}

char* strqtok(char* string, const char* ctrl)
{
    static char* nexttoken = NULL;
    char* str;

    if (string != NULL)
        str = string;
    else {
        if (nexttoken == NULL)
            return NULL;
        str = nexttoken;
    };

    char deli[32];
    memset(deli, 0, 32 * sizeof(char));
    while (*ctrl) {
        deli[*ctrl >> 3] |= (1 << (*ctrl & 7));
        ctrl++;
    };
    // can't allow to be set
    deli['"' >> 3] &= ~(1 << ('"' & 7));

    // jump over leading delimiters
    while ((deli[*str >> 3] & (1 << (*str & 7))) && *str)
        str++;

    if (*str == '"') {
        string = ++str;

        // only break if another quote or end of string is found
        while ((*str != '"') && *str)
            str++;
    } else {
        string = str;

        // break on delimiter
        while (!(deli[*str >> 3] & (1 << (*str & 7))) && *str)
            str++;
    };

    if (string == str) {
        nexttoken = NULL;
        return NULL;
    } else {
        if (*str) {
            *str = 0;
            nexttoken = str + 1;
        } else
            nexttoken = NULL;

        return string;
    };
};

void dbgExecute(char* toRun)
{
    char* commands[40];
    int commandCount = 0;
    commands[0] = strqtok(toRun, " \t\n");
    if (commands[0] == NULL)
        return;
    commandCount++;
    while ((commands[commandCount] = strqtok(NULL, " \t\n"))) {
        commandCount++;
        if (commandCount == 40)
            break;
    }

    //from here on, new algorithm.
    // due to the division of functions, some steps have to be made

    //first, convert the command name to a standart lowercase form
    //if more lowercasing needed, do it on the caller.
    for (int i = 0; commands[0][i]; i++) {
        commands[0][i] = (char)tolower(commands[0][i]);
    }

    // checks if it is a quit command, if so quits.
    //if (isQuitCommand(commands[0])){
    //	if (quitConfirm()){
    //		debugger = false;
    //		emulating = false;
    //	}
    //	return;
    //}

    commands[0] = (char*)replaceAlias(commands[0], cmdAliasTable);

    if (commands[0][0] == 'b') {
        executeBreakCommands(commandCount, commands);
        if (commands[0][0] == '!')
            commands[0][0] = 'b';
        else
            return;
    }

    //although it mights seem weird, the old step is the last one to be executed.
    for (int j = 0;; j++) {
        if (debuggerCommands[j].name == NULL) {
            {
                snprintf(monbuf, sizeof(monbuf), "Unrecognized command %s. Type h for help.\n", commands[0]);
                monprintf(monbuf);
            }
            return;
        }
        if (!strcmp(commands[0], debuggerCommands[j].name)) {
            debuggerCommands[j].function(commandCount, commands);
            return;
        }
    }
}

void dbgExecute(std::string& cmd)
{
    char* dbgCmd = new char[cmd.length() + 1];
#if __STDC_WANT_SECURE_LIB__
    strcpy_s(dbgCmd, cmd.length() + 1, cmd.c_str());
#else
    strcpy(dbgCmd, cmd.c_str());
#endif
    dbgExecute(dbgCmd);
    delete[] dbgCmd;
}

int remoteTcpSend(char* data, int len)
{
    return send(remoteSocket, data, len, 0);
}

int remoteTcpRecv(char* data, int len)
{
    return recv(remoteSocket, data, len, 0);
}

bool remoteTcpInit()
{
    if (remoteSocket == -1) {
#ifdef _WIN32
        WSADATA wsaData;
#ifdef _DEBUG
        int error = WSAStartup(MAKEWORD(1, 1), &wsaData);
        fprintf(stderr, "WSAStartup: %d\n", error);
#else
        WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif
#endif // _WIN32
        SOCKET s = socket(PF_INET, SOCK_STREAM, 0);

        remoteListenSocket = s;

        if (s < 0) {
            fprintf(stderr, "Error opening socket\n");
            exit(-1);
        }
        int tmp = 1;
        setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, sizeof(tmp));

        //    char hostname[256];
        //    gethostname(hostname, 256);

        //    hostent *ent = gethostbyname(hostname);
        //    unsigned long a = *((unsigned long *)ent->h_addr);

        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons((unsigned short)remotePort);
        addr.sin_addr.s_addr = htonl(0);
        int count = 0;
        while (count < 3) {
            if (bind(s, (sockaddr*)&addr, sizeof(addr))) {
                addr.sin_port = htons(ntohs(addr.sin_port) + 1);
            } else
                break;
        }
        if (count == 3) {
            fprintf(stderr, "Error binding \n");
            exit(-1);
        }

        fprintf(stderr, "Listening for a connection at port %d\n",
            ntohs(addr.sin_port));

        if (listen(s, 1)) {
            fprintf(stderr, "Error listening\n");
            exit(-1);
        }
        socklen_t len = sizeof(addr);

#ifdef _WIN32
        int flag = 0;
        ioctlsocket(s, FIONBIO, (unsigned long*)&flag);
#endif // _WIN32
        SOCKET s2 = accept(s, (sockaddr*)&addr, &len);
        if (s2 > 0) {
            fprintf(stderr, "Got a connection from %s %d\n",
                inet_ntoa((in_addr)addr.sin_addr),
                ntohs(addr.sin_port));
        } else {
#ifdef _WIN32
#ifdef _DEBUG
            int _error = WSAGetLastError();
            fprintf(stderr, "WSA Error: %d\n", _error);
#endif
#endif // _WIN32
        }
        //char dummy;
        //recv(s2, &dummy, 1, 0);
        //if(dummy != '+') {
        //  fprintf(stderr, "ACK not received\n");
        //  exit(-1);
        //}
        remoteSocket = s2;
        //    close(s);
    }
    return true;
}

void remoteTcpCleanUp()
{
    if (remoteSocket > 0) {
        fprintf(stderr, "Closing remote socket\n");
        close(remoteSocket);
        remoteSocket = (SOCKET)(-1);
    }
    if (remoteListenSocket > 0) {
        fprintf(stderr, "Closing listen socket\n");
        close(remoteListenSocket);
        remoteListenSocket = (SOCKET)(-1);
    }
}

int remotePipeSend(char* data, int len)
{
    int res = write(1, data, len);
    return res;
}

int remotePipeRecv(char* data, int len)
{
    int res = read(0, data, len);
    return res;
}

bool remotePipeInit()
{
    //  char dummy;
    //  if (read(0, &dummy, 1) == 1)
    //  {
    //    if(dummy != '+') {
    //      fprintf(stderr, "ACK not received\n");
    //      exit(-1);
    //    }
    //  }

    return true;
}

void remotePipeCleanUp()
{
}

void remoteSetPort(int port)
{
    remotePort = port;
}

void remoteSetProtocol(int p)
{
    if (p == 0) {
        remoteSendFnc = remoteTcpSend;
        remoteRecvFnc = remoteTcpRecv;
        remoteInitFnc = remoteTcpInit;
        remoteCleanUpFnc = remoteTcpCleanUp;
    } else {
        remoteSendFnc = remotePipeSend;
        remoteRecvFnc = remotePipeRecv;
        remoteInitFnc = remotePipeInit;
        remoteCleanUpFnc = remotePipeCleanUp;
    }
}

void remoteInit()
{
    if (remoteInitFnc)
        remoteInitFnc();
}

void remotePutPacket(const char* packet)
{
    const char* hex = "0123456789abcdef";

    size_t count = strlen(packet);
    char* buffer = new char[count + 5];

    unsigned char csum = 0;

    char* p = buffer;
    *p++ = '$';

    for (size_t i = 0; i < count; i++) {
        csum += packet[i];
        *p++ = packet[i];
    }
    *p++ = '#';
    *p++ = hex[csum >> 4];
    *p++ = hex[csum & 15];
    *p++ = 0;
    //log("send: %s\n", buffer);

    char c = 0;
    while (c != '+') {
        remoteSendFnc(buffer, (int)count + 4);

        if (remoteRecvFnc(&c, 1) < 0) {
            delete[] buffer;
            return;
        }
        //    fprintf(stderr,"sent:%s recieved:%c\n",buffer,c);
    }

    delete[] buffer;
}

void remoteOutput(const char* s, uint32_t addr)
{
    char buffer[16384];

    char* d = buffer;
    *d++ = 'O';

    if (s) {
        char c = *s++;
        while (c) {
            snprintf(d, sizeof(buffer), "%02x", c);
            d += 2;
            c = *s++;
        }
    } else {
        char c = debuggerReadByte(addr);
        addr++;
        while (c) {
            snprintf(d, sizeof(buffer), "%02x", c);
            d += 2;
            c = debuggerReadByte(addr);
            addr++;
        }
    }
    remotePutPacket(buffer);
    //  fprintf(stderr, "Output sent %s\n", buffer);
}

void remoteSendSignal()
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "S%02x", remoteSignal);
    remotePutPacket(buffer);
}

void remoteSendStatus()
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "T%02x", remoteSignal);
    char* s = buffer;
    s += 3;
    for (int i = 0; i < 15; i++) {
        uint32_t v = reg[i].I;
        snprintf(s, sizeof(buffer), "%02x:%02x%02x%02x%02x;", i,
            (v & 255),
            (v >> 8) & 255,
            (v >> 16) & 255,
            (v >> 24) & 255);
        s += 12;
    }
    uint32_t v = armNextPC;
    snprintf(s, sizeof(buffer), "0f:%02x%02x%02x%02x;", (v & 255),
        (v >> 8) & 255,
        (v >> 16) & 255,
        (v >> 24) & 255);
    s += 12;
    CPUUpdateCPSR();
    v = reg[16].I;
    snprintf(s, sizeof(buffer), "19:%02x%02x%02x%02x;", (v & 255),
        (v >> 8) & 255,
        (v >> 16) & 255,
        (v >> 24) & 255);
    s += 12;
    *s = 0;
    //log("Sending %s\n", buffer);
    remotePutPacket(buffer);
}

void remoteBinaryWrite(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, "%x,%x:", &address, &count);
    //  monprintf("Binary write for %08x %d\n", address, count);

    p = strchr(p, ':');
    p++;
    for (int i = 0; i < count; i++) {
        uint8_t b = *p++;
        switch (b) {
        case 0x7d:
            b = *p++;
            debuggerWriteByte(address, (b ^ 0x20));
            address++;
            break;
        default:
            debuggerWriteByte(address, b);
            address++;
            break;
        }
    }
    //  monprintf("ROM is %08x\n", debuggerReadMemory(0x8000254));
    remotePutPacket("OK");
}

void remoteMemoryWrite(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, "%x,%x:", &address, &count);
    //  monprintf("Memory write for %08x %d\n", address, count);

    p = strchr(p, ':');
    p++;
    for (int i = 0; i < count; i++) {
        uint8_t v = 0;
        char c = *p++;
        if (c <= '9')
            v = (c - '0') << 4;
        else
            v = (c + 10 - 'a') << 4;
        c = *p++;
        if (c <= '9')
            v += (c - '0');
        else
            v += (c + 10 - 'a');
        debuggerWriteByte(address, v);
        address++;
    }
    //  monprintf("ROM is %08x\n", debuggerReadMemory(0x8000254));
    remotePutPacket("OK");
}

void remoteMemoryRead(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, "%x,%x:", &address, &count);
    //  monprintf("Memory read for %08x %d\n", address, count);

    char* buffer = new char[(count*2)+1];

    char* s = buffer;
    for (int i = 0; i < count; i++) {
        uint8_t b = debuggerReadByte(address);
        snprintf(s, (count*2), "%02x", b);
        address++;
        s += 2;
    }
    *s = 0;
    remotePutPacket(buffer);

    delete[] buffer;
}

void remoteQuery(char* p)
{
    if (!strncmp(p, "fThreadInfo", 11)) {
        remotePutPacket("m1");
    } else if (!strncmp(p, "sThreadInfo", 11)) {
        remotePutPacket("l");
    } else if (!strncmp(p, "Supported", 9)) {
        remotePutPacket("PacketSize=1000");
    } else if (!strncmp(p, "HostInfo", 8)) {
        remotePutPacket("cputype:12;cpusubtype:5;ostype:unknown;vendor:nintendo;endian:little;ptrsize:4;");
    } else if (!strncmp(p, "C", 1)) {
        remotePutPacket("QC1");
    } else if (!strncmp(p, "Attached", 8)) {
        remotePutPacket("1");
    } else if (!strncmp(p, "Symbol", 6)) {
        remotePutPacket("OK");
    } else if (!strncmp(p, "Rcmd,", 5)) {
        p += 5;
        std::string cmd = HexToString(p);
        dbgExecute(cmd);
        remotePutPacket("OK");
    } else {
        fprintf(stderr, "Unknown packet %s\n", --p);
        remotePutPacket("");
    }
}

void remoteStepOverRange(char* p)
{
    uint32_t address;
    uint32_t final;
    sscanf(p, "%x,%x", &address, & final);

    remotePutPacket("OK");

    remoteResumed = true;
    do {
        CPULoop(1);
        if (debugger)
            break;
    } while (armNextPC >= address && armNextPC < final);

    remoteResumed = false;

    remoteSendStatus();
}

void remoteSetBreakPoint(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n += 4)
        addConditionalBreak(address + n, armState ? 0x04 : 0x08);

    // Out of bounds memory checks
    //if (address < 0x2000000 || address > 0x3007fff) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //if (address > 0x203ffff && address < 0x3000000) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //uint32_t final = address + count;

    //if (address < 0x2040000 && final > 0x2040000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    //else if (address < 0x3008000 && final > 0x3008000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    remotePutPacket("OK");
}

void remoteClearBreakPoint(char* p)
{
    int result = 0;
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n += 4)
        result = removeConditionalWithAddressAndFlag(address + n, armState ? 0x04 : 0x08, true);

    if (result != -2)
        remotePutPacket("OK");
    else
        remotePutPacket("");
}

void remoteSetMemoryReadBreakPoint(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n++)
        addConditionalBreak(address + n, 0x02);

    // Out of bounds memory checks
    //if (address < 0x2000000 || address > 0x3007fff) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //if (address > 0x203ffff && address < 0x3000000) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //uint32_t final = address + count;

    //if (address < 0x2040000 && final > 0x2040000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    //else if (address < 0x3008000 && final > 0x3008000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    remotePutPacket("OK");
}

void remoteClearMemoryReadBreakPoint(char* p)
{
    bool error = false;
    int result;
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n++) {
        result = removeConditionalWithAddressAndFlag(address + n, 0x02, true);
        if (result == -2)
            error = true;
    }

    if (!error)
        remotePutPacket("OK");
    else
        remotePutPacket("");
}

void remoteSetMemoryAccessBreakPoint(char* p)
{
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n++)
        addConditionalBreak(address + n, 0x03);

    // Out of bounds memory checks
    //if (address < 0x2000000 || address > 0x3007fff) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //if (address > 0x203ffff && address < 0x3000000) {
    //	remotePutPacket("E01");
    //	return;
    //}

    //uint32_t final = address + count;

    //if (address < 0x2040000 && final > 0x2040000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    //else if (address < 0x3008000 && final > 0x3008000) {
    //	remotePutPacket("E01");
    //	return;
    //}
    remotePutPacket("OK");
}

void remoteClearMemoryAccessBreakPoint(char* p)
{
    bool error = false;
    int result;
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    for (int n = 0; n < count; n++) {
        result = removeConditionalWithAddressAndFlag(address + n, 0x03, true);
        if (result == -2)
            error = true;
    }

    if (!error)
        remotePutPacket("OK");
    else
        remotePutPacket("");
}

void remoteWriteWatch(char* p, bool active)
{
    uint32_t address;
    int count;
    sscanf(p, ",%x,%x#", &address, &count);

    if (active) {
        for (int n = 0; n < count; n++)
            addConditionalBreak(address + n, 0x01);
    } else {
        for (int n = 0; n < count; n++)
            removeConditionalWithAddressAndFlag(address + n, 0x01, true);
    }

    // Out of bounds memory check
    //fprintf(stderr, "Write watch for %08x %d\n", address, count);

    //if(address < 0x2000000 || address > 0x3007fff) {
    //  remotePutPacket("E01");
    //  return;
    //}

    //if(address > 0x203ffff && address < 0x3000000) {
    //  remotePutPacket("E01");
    //  return;
    //}

    // uint32_t final = address + count;

//if(address < 0x2040000 && final > 0x2040000) {
//  remotePutPacket("E01");
//  return;
//} else if(address < 0x3008000 && final > 0x3008000) {
//  remotePutPacket("E01");
//  return;
//}

#ifdef VBAM_ENABLE_DEBUGGER
    for (int i = 0; i < count; i++) {
        if ((address >> 24) == 2)
            freezeWorkRAM[address & 0x3ffff] = active;
        else
            freezeInternalRAM[address & 0x7fff] = active;
        address++;
    }
#endif

    remotePutPacket("OK");
}

void remoteReadRegister(char* p)
{
    int r;
    sscanf(p, "%x", &r);
    if(r < 0 || r > 15)
    {
        remotePutPacket("E 00");
        return;
    }
    char buffer[1024];
    char* s = buffer;
    uint32_t v = reg[r].I;
    snprintf(s, sizeof(buffer), "%02x%02x%02x%02x", v & 255, (v >> 8) & 255,
        (v >> 16) & 255, (v >> 24) & 255);
    remotePutPacket(buffer);
}

void remoteReadRegisters(char* p)
{
    (void)p; // unused params
    char buffer[1024];

    char* s = buffer;
    int i;
    // regular registers
    for (i = 0; i < 15; i++) {
        uint32_t v = reg[i].I;
        snprintf(s, sizeof(buffer), "%02x%02x%02x%02x", v & 255, (v >> 8) & 255,
            (v >> 16) & 255, (v >> 24) & 255);
        s += 8;
    }
    // PC
    uint32_t pc = armNextPC;
    snprintf(s, sizeof(buffer) - 8, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255,
        (pc >> 16) & 255, (pc >> 24) & 255);
    s += 8;

    // floating point registers (24-bit)
    for (i = 0; i < 8; i++) {
        snprintf(s, sizeof(buffer) - 16, "000000000000000000000000");
        s += 24;
    }

    // FP status register
    snprintf(s, sizeof(buffer) - 40, "00000000");
    s += 8;
    // CPSR
    CPUUpdateCPSR();
    uint32_t v = reg[16].I;
    snprintf(s, sizeof(buffer) - 48, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255,
        (v >> 16) & 255, (v >> 24) & 255);
    s += 8;
    *s = 0;
    remotePutPacket(buffer);
}

void remoteWriteRegister(char* p)
{
    int r;

    sscanf(p, "%x=", &r);

    if(r < 0 || r > 15)
    {
        remotePutPacket("E 00");
        return;
    }

    p = strchr(p, '=');
    p++;

    char c = *p++;

    uint32_t v = 0;

    uint8_t data[4] = { 0, 0, 0, 0 };

    int i = 0;

    while (i < 4) {
        uint8_t b = 0;
        if (c <= '9')
            b = (c - '0') << 4;
        else
            b = (c + 10 - 'a') << 4;
        c = *p++;
        if (c <= '9')
            b += (c - '0');
        else
            b += (c + 10 - 'a');
        data[i++] = b;
        c = *p++;
    }

    v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);

    //  monprintf("Write register %d=%08x\n", r, v);
    reg[r].I = v;
    if (r == 15) {
        armNextPC = v;
        if (armState)
            reg[15].I = v + 4;
        else
            reg[15].I = v + 2;
    }
    remotePutPacket("OK");
}

void remoteStubMain()
{
    if (!debugger)
        return;

    if (remoteResumed) {
        remoteSendStatus();
        remoteResumed = false;
    }

    const char* hex = "0123456789abcdef";
    while (1) {
        char ack;
        char buffer[1024];
        int res = remoteRecvFnc(buffer, 1024);

        if (res == -1) {
            fprintf(stderr, "GDB connection lost\n");
            debugger = false;
            break;
        } else if (res == -2)
            break;
        if (res < 1024) {
            buffer[res] = 0;
        } else {
            fprintf(stderr, "res=%d\n", res);
        }

        //    fprintf(stderr, "res=%d Received %s\n",res, buffer);
        char c = buffer[0];
        char* p = &buffer[0];
        int i = 0;
        unsigned char csum = 0;
        while (i < res) {
            if (buffer[i] == '$') {
                i++;
                csum = 0;
                c = buffer[i];
                p = &buffer[i + 1];
                while ((i < res) && (buffer[i] != '#')) {
                    csum += buffer[i];
                    i++;
                }
            } else if (buffer[i] == '#') {
                buffer[i] = 0;
                if ((i + 2) < res) {
                    if ((buffer[i + 1] == hex[csum >> 4]) && (buffer[i + 2] == hex[csum & 0xf])) {
                        ack = '+';
                        remoteSendFnc(&ack, 1);
                        //fprintf(stderr, "SentACK c=%c\n",c);
                        //process message...
                        char type;
                        switch (c) {
                        case '?':
                            remoteSendSignal();
                            break;
                        case 'D':
                            remotePutPacket("OK");
                            remoteResumed = true;
                            debugger = false;
                            return;
                        case 'e':
                            remoteStepOverRange(p);
                            break;
                        case 'k':
                            remotePutPacket("OK");
                            debugger = false;
                            emulating = false;
                            return;
                        case 'C':
                            remoteResumed = true;
                            debugger = false;
                            return;
                        case 'c':
                            remoteResumed = true;
                            debugger = false;
                            return;
                        case 's':
                            remoteResumed = true;
                            remoteSignal = 5;
                            CPULoop(1);
                            if (remoteResumed) {
                                remoteResumed = false;
                                remoteSendStatus();
                            }
                            break;
                        case 'g':
                            remoteReadRegisters(p);
                            break;
                        case 'p':
                            remoteReadRegister(p);
                            break;
                        case 'P':
                            remoteWriteRegister(p);
                            break;
                        case 'M':
                            remoteMemoryWrite(p);
                            break;
                        case 'm':
                            remoteMemoryRead(p);
                            break;
                        case 'X':
                            remoteBinaryWrite(p);
                            break;
                        case 'H':
                            remotePutPacket("OK");
                            break;
                        case 'q':
                            remoteQuery(p);
                            break;
                        case 'Z':
                            type = *p++;
                            if (type == '0') {
                                remoteSetBreakPoint(p);
                            } else if (type == '1') {
                                remoteSetBreakPoint(p);
                            } else if (type == '2') {
                                remoteWriteWatch(p, true);
                            } else if (type == '3') {
                                remoteSetMemoryReadBreakPoint(p);
                            } else if (type == '4') {
                                remoteSetMemoryAccessBreakPoint(p);
                            } else {
                                remotePutPacket("");
                            }
                            break;
                        case 'z':
                            type = *p++;
                            if (type == '0') {
                                remoteClearBreakPoint(p);
                            } else if (type == '1') {
                                remoteClearBreakPoint(p);
                            } else if (type == '2') {
                                remoteWriteWatch(p, false);
                            } else if (type == '3') {
                                remoteClearMemoryReadBreakPoint(p);
                            } else if (type == '4') {
                                remoteClearMemoryAccessBreakPoint(p);
                            } else {
                                remotePutPacket("");
                            }
                            break;
                        default: {
                            fprintf(stderr, "Unknown packet %s\n", --p);
                            remotePutPacket("");
                        } break;
                        }
                    } else {
                        fprintf(stderr, "bad chksum csum=%x msg=%c%c\n", csum, buffer[i + 1], buffer[i + 2]);
                        ack = '-';
                        remoteSendFnc(&ack, 1);
                        fprintf(stderr, "SentNACK\n");
                    } //if
                    i += 3;
                } else {
                    fprintf(stderr, "didn't receive chksum i=%d res=%d\n", i, res);
                    i++;
                } //if
            } else {
                if (buffer[i] != '+') { //ingnore ACKs
                    fprintf(stderr, "not sure what to do with:%c i=%d res=%d\n", buffer[i], i, res);
                }
                i++;
            } //if
        } //while
    }
}

void remoteStubSignal(int sig, int number)
{
    (void)number; // unused params
    remoteSignal = sig;
    remoteResumed = false;
    remoteSendStatus();
    debugger = true;
}

void remoteCleanUp()
{
    if (remoteCleanUpFnc)
        remoteCleanUpFnc();
}

std::string HexToString(char* p)
{
    std::string hex(p);
    std::string cmd;
    std::stringstream ss;
    uint32_t offset = 0;
    while (offset < hex.length()) {
        unsigned int buffer = 0;
        ss.clear();
        ss << std::hex << hex.substr(offset, 2);
        ss >> std::hex >> buffer;
        cmd.push_back(static_cast<unsigned char>(buffer));
        offset += 2;
    }
    return cmd;
}

std::string StringToHex(std::string& cmd)
{
    std::stringstream ss;
    ss << std::hex;
    for (uint32_t i = 0; i < cmd.length(); ++i)
        ss << std::setw(2) << std::setfill('0') << (int)cmd.c_str()[i];
    return ss.str();
}

void monprintf(std::string line)
{
    std::string output = "O";
    line = StringToHex(line);
    output += line;

    if (output.length() <= 1000) {
        char dbgReply[1000];
#if __STDC_WANT_SECURE_LIB__
        strcpy_s(dbgReply, sizeof(dbgReply), output.c_str());
#else
        strcpy(dbgReply, output.c_str());
#endif
        remotePutPacket(dbgReply);
    }
}
