scummvm/backends/platform/gp32/debug-gdbstub-usb.cpp
2006-07-06 21:44:48 +00:00

1928 lines
46 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001/2004 The ScummVM project
* Copyright (C) 2002 Ph0x - GP32 Backend
* Copyright (C) 2003/2004 DJWillis - GP32 Backend
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/*
*
* Basic GP32 USB GDB Debug Stub - Use with Multi-firmware that supports GDB
* Mithris kindly gave me the basic stub code.
*
*/
#include <sys/types.h>
#include <gpcomm.h>
#include <gpos_def.h>
#include <gpdef.h>
#include <gpmain.h>
#include <gpos_def.h>
#include <gpstdio.h>
#include <gpgraphic.h>
#include <gpfont.h>
#include <gpstdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
//#define USE_PRINTF // If there is a Printf(const char *pFormat,...) implemented
///////////////////////////////////////////////////////////////
// Externs
extern int __text_start;
extern int __data_start;
extern int __bss_start;
#ifdef USE_PRINTF
extern void Printf(char *pFormat, ...);
#endif
///////////////////////////////////////////////////////////////
extern "C" {
int OpenUSB();
void InstallISR();
}
struct g_Namedregisters {
unsigned int r0;
unsigned int r1;
unsigned int r2;
unsigned int r3;
unsigned int r4;
unsigned int r5;
unsigned int r6;
unsigned int r7;
unsigned int r8;
unsigned int r9;
unsigned int r10;
unsigned int r11;
unsigned int r12;
unsigned int sp;
unsigned int lr;
unsigned int pc;
unsigned int fps;
unsigned int fcpsr;
};
///////////////////////////////////////////////////////////////
// Defines
#define _REG_R1 0
#define _REG_R2 2
#define _REG_R3 3
#define _REG_R4 4
#define _REG_R5 5
#define _REG_R6 6
#define _REG_R7 7
#define _REG_R8 8
#define _REG_R9 9
#define _REG_R10 10
#define _REG_R11 11
#define _REG_R12 12
#define _REG_FP 11
#define _REG_IP 12
#define _REG_SL 10
#define _REG_SP 13
#define _REG_LR 14
#define _REG_PC 15
#define PACKET_SIZE 0x100
#define MODE_USER 0
#define MODE_FIQ 1
#define MODE_IRQ 2
#define MODE_SUPERVISOR 3
#define MODE_ABORT 4
#define MODE_UNDEF 5
#define MODE_SYSTEM 6
#define MODE_DUNNO -1
///////////////////////////////////////////////////////////////
// Global stuff
// Register array.
int g_Registers[50] = {1, 2, 3, 4 ,5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19,
21, 22, 23, 24, 25, 26, 27, 28, 29,
31, 32, 33, 34, 35, 36, 37, 38, 39,
41, 42, 43, 44, 45, 46, 47, 48, 49};
// Register name strings, not used right now.
static char * arm_register_name_strings[] =
{"r0", "r1", "r2", "r3", /* 0 1 2 3 */
"r4", "r5", "r6", "r7", /* 4 5 6 7 */
"r8", "r9", "r10", "r11", /* 8 9 10 11 */
"r12", "sp", "lr", "pc", /* 12 13 14 15 */
"f0", "f1", "f2", "f3", /* 16 17 18 19 */
"f4", "f5", "f6", "f7", /* 20 21 22 23 */
"fps", "cpsr" }; /* 24 25 */
// Some USB stuff
GPN_DESC g_CommDesc;
GPN_COMM g_Comm;
const char HexDigits[17] = "0123456789abcdef";
char g_SendBuffer[256];
char g_TempBuffer[256];
char g_ReadBuffer[0x100];
unsigned int g_CurrentCSPR = 0;
unsigned int g_SavedStepInstruction = 0;
unsigned int g_StepAddress = 0;
bool g_LastWasStep = false;
int g_iTrap = 0;
bool g_GDBConnected = false;
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Prototypes
void BreakPoint();
int GetException(unsigned int CSPR);
unsigned int *GetNextInstruction(unsigned int *pAddr);
bool CondWillExecute(unsigned int uiCond, unsigned int CSPR);
unsigned int DecodeInstruction(unsigned int uiInstruction, unsigned int PC);
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Code Begins here
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Open usb comms stuff
int OpenUSB()
{
int iResult;
g_CommDesc.port_kind = COMM_USB_D;
g_CommDesc.tr_mode = 1;
g_CommDesc.tr_buf_size = PACKET_SIZE;
g_CommDesc.sz_pkt = PACKET_SIZE;
g_CommDesc.isr_comm_ram = 0;
iResult = GpCommCreate(&g_CommDesc, &g_Comm);
return iResult;
}
///////////////////////////////////////////////////////////////
// No need for explanation
void CloseUSB()
{
GpCommDelete(&g_CommDesc);
}
///////////////////////////////////////////////////////////////
// No need for explanation
int SendUSB(const void *pData, unsigned int uiSize)
{
return g_Comm.comm_send((unsigned char *)pData, uiSize);
}
///////////////////////////////////////////////////////////////
// No need for explanation
int RecvUSB(char *pBuffer, unsigned int uiSize)
{
return g_Comm.comm_recv((unsigned char *)pBuffer, uiSize);
}
///////////////////////////////////////////////////////////////
// Waits for an acknowledge from the server
void WaitACK()
{
bool bBreak = false;
int iResult;
while(!bBreak) {
iResult = g_Comm.comm_recv((unsigned char *)g_SendBuffer, 0x100);
if (iResult > 0) {
bBreak = true;
}
}
}
///////////////////////////////////////////////////////////////
// Formats and sends a command to the server,
// it also calculates checksum
void SendCommand(unsigned char *pCommand)
{
int iOffset;
unsigned int iCheckSum;
iOffset = 4;
g_SendBuffer[iOffset++] = '$';
iCheckSum = 0;
while(*pCommand != 0) {
g_SendBuffer[iOffset++] = *pCommand;
iCheckSum += *pCommand++;
}
g_SendBuffer[iOffset++] = '#';
iCheckSum = iCheckSum & 0xff;
g_SendBuffer[iOffset++] = HexDigits[(iCheckSum & 0xf0) >> 4];
g_SendBuffer[iOffset++] = HexDigits[(iCheckSum & 0x0f)];
g_SendBuffer[iOffset] = 0;
(*(int *)&g_SendBuffer[0]) = (iOffset - 4);
SendUSB(g_SendBuffer, 0x100);
WaitACK();
}
///////////////////////////////////////////////////////////////
// This function creates and sends a package which tells gdb that
// the last command was unsupported
void UnSupportedCommand()
{
(*(int *)&g_SendBuffer[0]) = 4;
g_SendBuffer[4] = '$';
g_SendBuffer[5] = '#';
g_SendBuffer[6] = '0';
g_SendBuffer[7] = '0';
g_SendBuffer[8] = '0';
SendUSB(g_SendBuffer, 0x100);
WaitACK();
}
///////////////////////////////////////////////////////////////
// This function is quite similar to the SendCommand above
// But it allows the user to set the package length.
// If the length of a package exceeds 256bytes including
// the protocol stuff and the package length you can split
// the packages by sending the package size * -1.
// The server will then merge all packages until a package
// with a length >0 is recieved.
void SendCommandSize(unsigned char *pCommand, int iSize)
{
int iOffset;
unsigned int iCheckSum;
iOffset = 4;
g_SendBuffer[iOffset++] = '$';
iCheckSum = 0;
while(*pCommand != 0) {
g_SendBuffer[iOffset++] = *pCommand;
iCheckSum += *pCommand++;
}
g_SendBuffer[iOffset++] = '#';
iCheckSum = iCheckSum & 0xff;
g_SendBuffer[iOffset++] = HexDigits[(iCheckSum & 0xf0) >> 4];
g_SendBuffer[iOffset++] = HexDigits[(iCheckSum & 0x0f)];
g_SendBuffer[iOffset] = 0;
(*(int *)&g_SendBuffer[0]) = iSize;
SendUSB(g_SendBuffer, 0x100);
WaitACK();
}
///////////////////////////////////////////////////////////////
// Writes a 32bit hexadeciman number in ascii to the string.
void HexToString(char *pString, int iNumber)
{
pString[0] = HexDigits[(iNumber >> 28) & 0x0f];
pString[1] = HexDigits[(iNumber >> 24) & 0x0f];
pString[2] = HexDigits[(iNumber >> 20) & 0x0f];
pString[3] = HexDigits[(iNumber >> 16) & 0x0f];
pString[4] = HexDigits[(iNumber >> 12) & 0x0f];
pString[5] = HexDigits[(iNumber >> 8) & 0x0f];
pString[6] = HexDigits[(iNumber >> 4) & 0x0f];
pString[7] = HexDigits[(iNumber) & 0x0f];
}
///////////////////////////////////////////////////////////////
// This does the same, but in a differend endian.
void HexToStringBW(char *pString, int iNumber)
{
pString[6] = HexDigits[(iNumber >> 28) & 0x0f];
pString[7] = HexDigits[(iNumber >> 24) & 0x0f];
pString[4] = HexDigits[(iNumber >> 20) & 0x0f];
pString[5] = HexDigits[(iNumber >> 16) & 0x0f];
pString[2] = HexDigits[(iNumber >> 12) & 0x0f];
pString[3] = HexDigits[(iNumber >> 8) & 0x0f];
pString[0] = HexDigits[(iNumber >> 4) & 0x0f];
pString[1] = HexDigits[(iNumber) & 0x0f];
pString[8] = 0;
}
///////////////////////////////////////////////////////////////
// equiv to strcat i imagine
void PrintToString(char *pString, char *pOtherString)
{
while(*pOtherString != 0) {
*pString = *pOtherString;
*pString++;
*pOtherString++;
}
}
///////////////////////////////////////////////////////////////
// converts "ff" -> 0xff
unsigned char StringToByte(char *pString)
{
unsigned char ucValue = 0;
for (int i = 0; i < 2; i++) {
ucValue = ucValue << 4;
switch(pString[i]) {
case '0':
break;
case '1':
ucValue |= (0x01);
break;
case '2':
ucValue |= (0x02);
break;
case '3':
ucValue |= (0x03);
break;
case '4':
ucValue |= (0x04);
break;
case '5':
ucValue |= (0x05);
break;
case '6':
ucValue |= (0x06);
break;
case '7':
ucValue |= (0x07);
break;
case '8':
ucValue |= (0x08);
break;
case '9':
ucValue |= (0x09);
break;
case 'a':
ucValue |= (0x0a);
break;
case 'b':
ucValue |= (0x0b);
break;
case 'c':
ucValue |= (0x0c);
break;
case 'd':
ucValue |= (0x0d);
break;
case 'e':
ucValue |= (0x0e);
break;
case 'f':
ucValue |= (0x0f);
break;
}
}
return ucValue;
}
///////////////////////////////////////////////////////////////
// Sends a package with the program offsets to GDB
// Of some reason all offsets should be NULL.
void SendOffsets()
{
char String[255];
int a = 0;//(int)&__text_start;
int b = 0;//(int)&__data_start;
int c = 0;//(int)&__bss_start;
PrintToString(String, "Text=");
HexToString(&String[5], a);
PrintToString(&String[5+8], ";Data=");
HexToString(&String[5+8+6], b);
PrintToString(&String[5+8+6+8], ";Bss=");
HexToString(&String[5+8+6+8+5], c);
String[5+8+6+8+5+8] = 0;
SendCommand((unsigned char *)String);
}
///////////////////////////////////////////////////////////////
// This function dumps all registers to GDB
// It utilizes the SendCommandSize function to split the package.
void DumpRegisters()
{
char Buffer[0x100];
// Ugly hack so far.
for (int i = 0; i < 21; i++) {
//g_Registers[i] = i;
HexToStringBW(&Buffer[i * 8], g_Registers[i]);
}
Buffer[21*8] = 0;
SendCommandSize((unsigned char *)Buffer, -1 * (21 * 8 + 4));
for (int i = 0; i < 21; i++) {
//g_Registers[20 + i] = i;
HexToStringBW(&Buffer[i * 8], g_Registers[21 + i]);
}
Buffer[21*8] = 0;
SendCommandSize((unsigned char *)Buffer, (21 * 8) + 4);
}
///////////////////////////////////////////////////////////////
// This function extracts an address from a string.
void *GetAddr(char *pBuffer)
{
int iAddr;
int i = 0;
iAddr = 0;
while (pBuffer[i] != ',') {
iAddr = iAddr << 4;
switch(pBuffer[i]) {
case '0':
//iAddr |=
break;
case '1':
iAddr |= (0x01);
break;
case '2':
iAddr |= (0x02);
break;
case '3':
iAddr |= (0x03);
break;
case '4':
iAddr |= (0x04);
break;
case '5':
iAddr |= (0x05);
break;
case '6':
iAddr |= (0x06);
break;
case '7':
iAddr |= (0x07);
break;
case '8':
iAddr |= (0x08);
break;
case '9':
iAddr |= (0x09);
break;
case 'a':
iAddr |= (0x0a);
break;
case 'b':
iAddr |= (0x0b);
break;
case 'c':
iAddr |= (0x0c);
break;
case 'd':
iAddr |= (0x0d);
break;
case 'e':
iAddr |= (0x0e);
break;
case 'f':
iAddr |= (0x0f);
break;
}
i++;
}
return (void *)iAddr;
}
///////////////////////////////////////////////////////////////
// This function does pretty much the same, but returns an int
// I know, some redundant code.
int GetBytes(char *pBuffer)
{
int iBytes = 0;
int i = 0;
while ((pBuffer[i] != '#') && (pBuffer[i] != ':')) {
iBytes = iBytes << 4;
switch(pBuffer[i]) {
case '0':
//iAddr |=
break;
case '1':
iBytes |= 0x01;
break;
case '2':
iBytes |= 0x02;
break;
case '3':
iBytes |= 0x03;
break;
case '4':
iBytes |= 0x04;
break;
case '5':
iBytes |= 0x05;
break;
case '6':
iBytes |= 0x06;
break;
case '7':
iBytes |= 0x07;
break;
case '8':
iBytes |= 0x08;
break;
case '9':
iBytes |= 0x09;
break;
case 'a':
iBytes |= 0x0a;
break;
case 'b':
iBytes |= 0x0b;
break;
case 'c':
iBytes |= 0x0c;
break;
case 'd':
iBytes |= 0x0d;
break;
case 'e':
iBytes |= 0x0e;
break;
case 'f':
iBytes |= 0x0f;
break;
}
i++;
}
return iBytes;
}
///////////////////////////////////////////////////////////////
// This function reads memory and sends it back to GDB.
void SendMemory(void *pAddr, int iBytes)
{
unsigned char *pData;
unsigned char iData;
int iBufferPos = 0;
int iBytesToSend;
char Byte;
int i;
pData = (unsigned char *)pAddr;
do {
if (iBytes > 100) {
iBytesToSend = 100;
iBytes -= 100;
} else {
iBytesToSend = iBytes;
iBytes = 0;
}
iBufferPos = 0;
for (i = 0; i < iBytesToSend; i+=1) {
iData = *pData++;
g_TempBuffer[iBufferPos++] = HexDigits[(iData & 0xf0) >> 4];
g_TempBuffer[iBufferPos++] = HexDigits[(iData & 0x0f)];
}
if (iBytes > 0) {
// This mean that we have not yet sent our last command.
g_TempBuffer[iBufferPos] = 0;
SendCommandSize((unsigned char *)g_TempBuffer, -1 * (i + 1 + 4));
}
} while (iBytes > 0 );
g_TempBuffer[iBufferPos] = 0;
SendCommand((unsigned char *)g_TempBuffer);
}
///////////////////////////////////////////////////////////////
// Does pretty much what it says.
void WriteMemory(void *pAddr, unsigned int uiBytes, char *pHexString)
{
unsigned char *pData = ((unsigned char *)pAddr);
unsigned int uiOffset = 0;
unsigned char ucByte;
//Printf("0x%x 0x%x", pAddr, uiBytes);
for (unsigned int i = 0; i < uiBytes ; i++) {
ucByte = StringToByte(&pHexString[uiOffset]);
//Printf("0x%x", ucByte);
*pData++ = ucByte;
uiOffset += 2;
}
/*
while(1);
unsigned int *piData = (unsigned int *)pAddr;
*piData = 0xe7ffdefe;//0xfedeffe7;
*/
}
///////////////////////////////////////////////////////////////
// Sends the required information about a trap
// TODO: correct numbers need to be placed there.
void SendBreakPoint()
{
int iOffset = 0;
g_TempBuffer[iOffset++] = 'T';
g_TempBuffer[iOffset++] = '0';
g_TempBuffer[iOffset++] = '5';
g_TempBuffer[iOffset++] = '0';
g_TempBuffer[iOffset++] = 'd';
g_TempBuffer[iOffset++] = ':';
HexToStringBW(&g_TempBuffer[iOffset], g_Registers[_REG_SP]);
iOffset += 8;
g_TempBuffer[iOffset++] = ';';
g_TempBuffer[iOffset++] = '0';
g_TempBuffer[iOffset++] = 'b';
g_TempBuffer[iOffset++] = ':';
HexToStringBW(&g_TempBuffer[iOffset], g_Registers[_REG_FP]);
iOffset += 8;
g_TempBuffer[iOffset++] = ';';
g_TempBuffer[iOffset++] = '0';
g_TempBuffer[iOffset++] = 'f';
g_TempBuffer[iOffset++] = ':';
HexToStringBW(&g_TempBuffer[iOffset], g_Registers[_REG_PC]);
iOffset += 8;
g_TempBuffer[iOffset++] = ';';
g_TempBuffer[iOffset] = 0;
SendCommand((unsigned char *)g_TempBuffer);
}
///////////////////////////////////////////////////////////////
// Finds a character in a string and returns the offset.
int FindChar(char *pBuffer, char sign)
{
int iRetVal = 0;
while(*pBuffer != sign) {
iRetVal++;
*pBuffer++;
}
return iRetVal;
}
// Attibute naked.
///////////////////////////////////////////////////////////////
// This is the ISR(Interrupt Service Routine) which handles
// All traps, it's basically a context switcher.
void ISR() __attribute__ ((naked));
void ISR()
{
// Lets snatch the registers!
asm volatile(" \n"
" str r4, [%0, #0x10] \n"
" str r5, [%0, #0x14] \n"
" str r6, [%0, #0x18] \n"
" str r7, [%0, #0x1C] \n"
" str r8, [%0, #0x20] \n"
" str r9, [%0, #0x24] \n"
" str r10, [%0, #0x28] \n"
" str r11, [%0, #0x2C] \n"
" str r12, [%0, #0x30] \n"
" str r14, [%0, #0x3C] \n"
" @// skip 8 * 12byte(96bit) = 0x60 \n"
" mov r4, %0 \n"
" ldmia sp!, {r0, r1, r2, r3} \n"
" str r0, [r4, #0x00] \n"
" str r1, [r4, #0x04] \n"
" str r2, [r4, #0x08] \n"
" str r3, [r4,#0x0C] \n"
" mrs r1, SPSR \n"
" str r1, [r4, #0x48] \n"
" str r1, [r4,#0xA0] \n"
" str r1, [r4,#0xA4] \n"
" mrs r1, CPSR \n"
" mov r2, r1 \n"
" @// Let us set the mode to supervisor so we can get r13 and r14 \n"
" bic r1, r1, #0x1f \n"
" orr r1, r1, #0x13 \n"
" msr CPSR_c, r1 \n"
" @// Just remember that we're in the supervisor stack aswell now \n"
" str r13, [r4,#0x34] \n"
" str r14, [r4,#0x38] \n"
" @// Lets move back to whatever mode we was in. \n"
" @// Make sure that IRQ's are turned on \n"
" bic r2, r2, #0x80 \n"
" msr CPSR_fsxc, r2 \n"
" \n"
:
: "r" (g_Registers)
: "%0", "r1", "r2", "r4");
// Get Current CSPR and save LR
asm volatile(" \n"
" mrs r0, CPSR \n"
" str r0, [%0] \n"
" str r14, [%1, #0x40] \n"
" str r0, [%1, #0x44] \n"
" str r13, [%1, #0x4C] \n"
" \n"
:
: "r" (&g_CurrentCSPR), "r" (g_Registers)
: "r0");
switch(g_CurrentCSPR & 0x1f) {
case 0x10: // USER
g_iTrap = 0;
break;
case 0x11: // FIQ
g_iTrap = 1;
break;
case 0x12: // IRQ
g_iTrap = 2;
break;
case 0x13: // Supervisor
g_iTrap = 3;
break;
case 0x17: // Abort
g_iTrap = 4;
break;
case 0x1B: // Undefined/Breakpoint
// We cannot continue like this!
g_Registers[15] -= 4;
g_Registers[16] -= 4;
g_iTrap = 5;
break;
case 0x1F: // System
g_iTrap = 6;
break;
default:
g_iTrap = -1;
}
#ifdef USE_PRINTF
Printf("Trap@0x%x:%d", g_Registers[15], g_iTrap);
#endif
/*
switch (g_iTrap) {
case MODE_USER:
break;
case MODE_FIQ:
break;
case MODE_IRQ:
break;
case MODE_SUPERVISOR:
break;
case MODE_ABORT:
break;
case MODE_UNDEF:
break;
case MODE_SYSTEM:
break;
case MODE_DUNNO:
default:
while(1);
}
*/
SendBreakPoint();
BreakPoint();
//Printf("0x%x 0x%x", g_Registers[15], g_Registers[16]);
// Okay, it's time to continue.
switch (g_iTrap) {
case MODE_USER:
//Printf("Dunno!!\n");
break;
case MODE_FIQ:
//Printf("Dunno!!\n");
break;
case MODE_IRQ:
//Printf("Dunno!!\n");
break;
case MODE_SUPERVISOR:
//Printf("Dunno!!\n");
break;
case MODE_ABORT:
asm volatile(" \n"
" mov r10, #0 \n"
" @Invalidate I&D cache \n"
" mcr p15, 0, r10, c7, c7 \n"
" @ restore the registers \n"
" ldr r1,[%0, #0x04] \n"
" ldr r2,[%0, #0x08] \n"
" ldr r4,[%0, #0x10] \n"
" ldr r5,[%0, #0x14] \n"
" ldr r6,[%0, #0x18] \n"
" ldr r7,[%0, #0x1C] \n"
" ldr r8,[%0, #0x20] \n"
" ldr r9,[%0, #0x24] \n"
" ldr r10,[%0, #0x28] \n"
" ldr r11,[%0, #0x2C] \n"
" ldr r12,[%0, #0x30] \n"
" ldr r14,[%0, #0x40] \n"
" ldr r0, [%0, #0x44] \n"
" msr CPSR_fsxc, r0 \n"
" ldr r0, [%0, #0x48] \n"
" msr SPSR_fsxc, r0 \n"
" ldr r0,[%0, #0x00] \n"
" ldr r3,[%0, #0x0C] \n"
" subs pc, lr, #0x04 \n"
" \n"
:
:"r" (g_Registers)
:"r0");
break;
case MODE_UNDEF:
// This will be the breakpoint i'm gonna test with.
asm volatile(" \n"
" mov r10, #0 \n"
" @Invalidate I&D cache \n"
" mcr p15, 0, r10, c7, c7 \n"
" @ restore the registers \n"
" ldr r1,[%0, #0x04] \n"
" ldr r2,[%0, #0x08] \n"
" ldr r4,[%0, #0x10] \n"
" ldr r5,[%0, #0x14] \n"
" ldr r6,[%0, #0x18] \n"
" ldr r7,[%0, #0x1C] \n"
" ldr r8,[%0, #0x20] \n"
" ldr r9,[%0, #0x24] \n"
" ldr r10,[%0, #0x28] \n"
" ldr r11,[%0, #0x2C] \n"
" ldr r12,[%0, #0x30] \n"
" ldr r14,[%0, #0x40] \n"
" ldr r0, [%0, #0x44] \n"
" msr CPSR_fsxc, r0 \n"
" ldr r0, [%0, #0x48] \n"
" msr SPSR_fsxc, r0 \n"
" ldr r0,[%0, #0x00] \n"
" ldr r3,[%0, #0x0C] \n"
" @ The subbing has already been done! \n"
" @subs pc, lr, #0x04 \n"
" movs pc, lr \n"
" \n"
:
:"r" (g_Registers)
:"r0");
break;
case MODE_SYSTEM:
//Printf("Dunno!!\n");
break;
case MODE_DUNNO:
default:
//Printf("Dunno!!\n");
while(1);
}
}
///////////////////////////////////////////////////////////////
// Returns which exception occured based on CSPR
int GetException(unsigned int CSPR)
{
switch(CSPR & 0x1f) {
case 0x10: // USER
return 0;
case 0x11: // FIQ
return 1;
case 0x12: // IRQ
return 2;
case 0x13: // Supervisor
return 3;
case 0x17: // Abort
return 4;
case 0x1B: // Undefined/Breakpoint
return 5;
case 0x1F: // System
return 6;
default:
return -1;
}
}
///////////////////////////////////////////////////////////////
// Installs the ISR address into the system RAM
void InstallISR()
{
int *pPointer = (int *)(0x0c7ac040);
int *pPointer2 = (int *)(0x0c7ac018);
void (*pISR)();
pISR = ISR;
*pPointer = (int)pISR;
*pPointer2 = (int)pISR;
}
void DEBUG_Print(char *pFormat, ...)
{
char Temp[0x100];
va_list VaList;
int iLength;
int iOffset;
unsigned char MyChar;
if (!g_GDBConnected) return;
va_start(VaList , pFormat);
vsnprintf(Temp, 0x100, pFormat , VaList);
va_end(VaList);
iLength = strlen(Temp);
if (iLength > 100) iLength = 100;
g_TempBuffer[0] = 'O';
iOffset = 1;
for (int i = 0; i < iLength; i++) {
MyChar = (unsigned char)Temp[i];
g_TempBuffer[iOffset++] = HexDigits[(MyChar & 0xf0) >> 4];
g_TempBuffer[iOffset++] = HexDigits[(MyChar & 0x0f)];
}
g_TempBuffer[iOffset] = 0;
SendCommand((unsigned char *)g_TempBuffer);
}
///////////////////////////////////////////////////////////////
// The main thread when the GDB thread has control
void BreakPoint()
{
unsigned int *pNextInstruction;
bool bBreakLoop = false;
int iResult;
int iMessageLength;
int iOffsetAdd;
int iNullVal = 0;
void *pAddr;
int iOffset;
int iBytes;
// Find out if we got here through a STEP command
if (g_LastWasStep) {
#ifdef USE_PRINTF
Printf("I:0x%x 0x%x", *((unsigned int *)(g_Registers[15] + 4)), *((unsigned int *)(g_Registers[15])));
Printf("S: 0x%x", g_StepAddress);
#endif
if ((unsigned int)g_Registers[15] == g_StepAddress) {
// Yes it was, Lets restore the instruction.
*((unsigned int *)g_StepAddress) = g_SavedStepInstruction;
#ifdef USE_PRINTF
Printf("Restore: 0x%x", g_SavedStepInstruction);
#endif
} else {
while(1);
}
g_LastWasStep = false;
}
while(!bBreakLoop) {
iResult = RecvUSB(g_ReadBuffer, 0x100);
//Printf("%d\n", iResult);
if (iResult > 0) {
// If we recieve a package we can assume that GDB is connected.. or smth..:D
g_GDBConnected = true;
// Well, we have recieved a package, lets print the contents.
iMessageLength = *(int *)&g_ReadBuffer[0];
g_ReadBuffer[4 + iMessageLength] = 0;
//Printf("%s\n %d", &g_ReadBuffer[4], iMessageLength);
// Let us also send an ACK '+'
(*(int *)&g_SendBuffer[0]) = 1;
g_SendBuffer[4] = '+';
SendUSB((const void *)g_SendBuffer, 0x100);
WaitACK();
// I can see that i get a bunch of '+' and '-' in the messages.. lets remove them.
iOffsetAdd = 4;
while((g_ReadBuffer[iOffsetAdd] == '+') || (g_ReadBuffer[iOffsetAdd] == '-')) iOffsetAdd++;
// Check whether it's legimit command
if (g_ReadBuffer[iOffsetAdd] == '$') {
// Well it is!
switch(g_ReadBuffer[iOffsetAdd + 1]) {
case 'H': // Set thread, we're not having any threads.. so.. just return OK
SendCommand((unsigned char *)"OK");
break;
case 'q': // Query, there are some queries, but GDB first asks for Offsets
switch(g_ReadBuffer[iOffsetAdd + 2]) {
case 'O': // Offsets
SendOffsets();
break;
case 'C':
SendCommand((unsigned char *)"QC0000");
//SendBreakPoint();
break;
}
break;
case '?':
// This will have to be modified later to send the correct signal.
SendBreakPoint();
break;
case 'g':
DumpRegisters();
break;
case 'm':
pAddr = GetAddr(&g_ReadBuffer[iOffsetAdd + 2]);
iOffset = FindChar(&g_ReadBuffer[iOffsetAdd + 2], ',');
iBytes = GetBytes(&g_ReadBuffer[iOffsetAdd + 2 + iOffset]);
SendMemory(pAddr, iBytes);
break;
case 'X': //Write memory binary, which we DON't support.. ofcourse.
UnSupportedCommand();
break;
case 'P': // Write register
{
SendCommand((unsigned char *)"OK");
}
break;
case 'M': // Write memory not binary
{
pAddr = GetAddr(&g_ReadBuffer[iOffsetAdd + 2]);
iOffset = FindChar(&g_ReadBuffer[iOffsetAdd + 2], ',');
iBytes = GetBytes(&g_ReadBuffer[iOffsetAdd + 2 + iOffset]);
iOffset = FindChar(&g_ReadBuffer[iOffsetAdd + 2], ':');
WriteMemory(pAddr, iBytes, &g_ReadBuffer[iOffsetAdd + 2 + iOffset + 1]);
SendCommand((unsigned char *)"OK");
}
break;
case 'c': // continue
{
return;
}
break;
case 's': // Stepping.
{
// Get the address of the next instruction.
pNextInstruction = GetNextInstruction((unsigned int *)g_Registers[15]);
// Read whatsever there.
g_SavedStepInstruction = *pNextInstruction;
g_StepAddress = (unsigned int)pNextInstruction;
g_LastWasStep = true;
//Printf("Curr: 0x%x", g_Registers[15]);
#ifdef USE_PRINTF
Printf("Next: 0x%x->0x%x", g_Registers[15], pNextInstruction);
#endif
//Printf("Trap: 0x%x", GetException((unsigned int)g_Registers[40]));
// Write a breakpoint instruction to the address.
*pNextInstruction = 0xe7ffdefe;
return;
}
break;
case 'Z': // BreakPoint.
{
switch(g_ReadBuffer[iOffsetAdd + 2]) {
case '0':
// Software breakpoint, i think it's up to me to add, it, lets send OK for now.
UnSupportedCommand();
break;
default:
// We only support software breakpoints for now, lets return unsupported.
// Actually we don't even support SW breakpoints now
UnSupportedCommand();
break;
}
}
break;
default:
UnSupportedCommand();
break;
}
}
}
}
}
///////////////////////////////////////////////////////////////
// Tries to find the next instruction to be executed after a break
unsigned int *GetNextInstruction(unsigned int *pAddr)
{
unsigned int uiInstruction = *pAddr;
unsigned int uiAndVal = 0;
unsigned int iNewPC = 0;
int iNewAddr = 0;
int iRegsbeforePC = 0;
unsigned int uiBaseRegister = 0;
unsigned int uiRegVal = 0;
unsigned int uiData = 0;
unsigned int uiCondMask = 0;
int iPCOffset = 0;
unsigned int uiNewPC = DecodeInstruction(uiInstruction, (unsigned int)pAddr);
return (unsigned int *)uiNewPC;
// Set new PC to pAddr + 4, because we really hope that is the case...:D
iNewPC = (unsigned int)pAddr;
iNewPC += 4; // Next instruction (atleast in ARM mode, we don't support thumb yet)
// Now it's a good point to find out if the instruction would be executed anyway.
uiCondMask = (uiInstruction & 0xf0000000) >> 28;
if (CondWillExecute(uiCondMask, (unsigned int)g_Registers[18])) {
//Printf("Condition will execute");
// Find out if it's a B or BL instruction. (This is the easy one)
if ((uiInstruction & 0xE000000 ) == 0xA000000) {
#ifdef USE_PRINTF
Printf("0x%x", uiInstruction);
#endif
// Okay, it's a branch instruction, lets get the address it's for
iNewAddr = uiInstruction & 0xffffff;
// We might need to sign extend this instruction.
if ((iNewAddr & 0x00800000) != 0) {
#ifdef USE_PRINTF
printf("Sign extending");
#endif
//iNewAddr *= -1;
iNewAddr |= 0xff000000;
}
#ifdef USE_PRINTF
Printf("0x%x", iNewAddr);
#endif
iNewAddr *= 4; // Instruction size.
iNewPC = ((int)pAddr + iNewAddr + 8);
}
// Well, it might be a ldm(ea)?
if ((uiInstruction & 0xE000000) == 0x8000000) {
#ifdef USE_PRINTF
Printf("LDM");
#endif
// this is a LDM/STM alright.
if ((uiInstruction & 0x100000) != 0) {
// This is a LDM instruction
// Lets see if the PC is ever loaded.
#ifdef USE_PRINTF
Printf("includes PC");
#endif
if ((uiInstruction & 0x8000) != 0) {
// Well (damn the PC is loaded)
for (int i = 0; i < 15; i++) {
uiAndVal = 1 << i;
if ((uiInstruction & uiAndVal) != 0) iRegsbeforePC++;
}
#ifdef USE_PRINTF
Printf("%d regs", iRegsbeforePC);
#endif
/*
<mr_spiv> da = fa
<mr_spiv> ia = fd
<mr_spiv> db = ea
<mr_spiv> ib = ed
*/
// Lets find out which register is used as base for this operation.
uiBaseRegister = (uiInstruction & 0xF0000) >> 16;
uiRegVal = ((unsigned int *)g_Registers)[uiBaseRegister];
// First, have a look at the U bit.
if ((uiInstruction & (1 << 23)) != 0) {
// Transfer is made descending
// Which also means that the PC is closest to the base register i just found out.
// Lets check the P bit (If i'm supposed to increment before or after.
iPCOffset = iRegsbeforePC * 4;
if (((uiInstruction) & (1 << 24)) != 0) iPCOffset += 4;
} else {
// Transfer is done ascending
// Lets check the P bit (If i'm supposed to decrement before or after.
if (((uiInstruction) & (1 << 24)) != 0) iPCOffset = -4;
}
iNewPC = *(unsigned int *)((((int)uiRegVal) + iPCOffset) & ~0x03);
}
}
}
// Check if it's a mov pc, Rn
}
return (unsigned int *)iNewPC;
}
///////////////////////////////////////////////////////////////
// Determines if uiCond will be true with this CSPR
bool CondWillExecute(unsigned int uiCond, unsigned int CSPR)
{
switch(uiCond) {
case 0: // EQ
// This is true if Z is set in CSPR
if ((CSPR & (1 << 30)) != 0) return true;
else return false;
case 1: // NE
// This should be true if Z is not set.
if ((CSPR & (1 << 30)) == 0) return true;
else return false;
case 2: // CS/HS
// this one should be true if C is set.
if ((CSPR & (1 << 29)) != 0) return true;
else return false;
case 3: // CC/LO
// this one should be true if C is clear.
if ((CSPR & (1 << 29)) == 0) return true;
else return false;
case 4: // MI
// this one should be true if N is set
if ((CSPR & (1 << 31)) != 0) return true;
else return false;
case 5: // PL
// this one should be true if N is clear.
if ((CSPR & (1 << 31)) == 0) return true;
else return false;
case 6: // VS
// this one should be true if V is set
if ((CSPR & (1 << 28)) != 0) return true;
else return false;
case 7: // VC
// this one should be true if V is clear.
if ((CSPR & (1 << 28)) == 0) return true;
else return false;
case 8: // HI
// This is true if C and Z is clear
if (((CSPR & (1 << 30)) == 0) && ((CSPR & (1 << 29)) == 0)) return true;
else return false;
case 9: // LS
// C clear OR Z set
if (((CSPR & (1 << 29)) == 0) || ((CSPR & (1 << 30)) != 0)) return true;
else return false;
case 10: // GE
// N set AND V set || N clear and V clear
if ((CSPR & (1 << 31)) == (CSPR & (1 << 28))) return true;
else return false;
case 11: // LT
// N != V
if ((CSPR & (1 << 31)) != (CSPR & (1 << 28))) return true;
else return false;
case 12: // GT
// Z == 0, N == V
if (((CSPR & (1 << 30)) == 0) && ((CSPR & (1 << 31)) == (CSPR & (1 << 28)))) return true;
else return false;
case 13: // LE
if (((CSPR & (1 << 30)) == 1) && ((CSPR & (1 << 31)) != (CSPR & (1 << 28)))) return true;
else return false;
case 14: // AL
return true;
default:
break;
}
}
// I got the idea for this layout from the singlestep.c (found in eCos)
// But i thought the code was a bit tricky to port, and i wanna learn more about this anyway so, i'll just do smth similar to that
typedef struct
{
unsigned Rm : 4; // Rm
unsigned resv2 : 1; // Reserved 2 (0)
unsigned shift : 2; // hmm.. dunno actually but probably (LSL, LSR, ASR, ROR )
unsigned amount : 5; // Shift amount 0-31
unsigned Rd : 4; // Rd
unsigned Rn : 4; // Rn
unsigned s : 1; // S-flag
unsigned opcode : 4; // Opcode (Mov etc)
unsigned resv1 : 3; // Reserved 1 (000)
unsigned cond : 4; // Condition
} dpisr; // Data Processing Immediate Register Shift
#define DPISR_R1 0
#define DPISR_R2 0
// Example <opcode> Rd, Rm, <shift> amount
typedef struct
{
unsigned Rm : 4; // Rm
unsigned resv3 : 1; // Reserved 3 (1)
unsigned shift : 2; // (LSL, LSR, ASR, ROR )
unsigned resv2 : 1; // Reserved 2 (0)
unsigned Rs : 4; // Rs
unsigned Rd : 4; // Rd
unsigned Rn : 4; // Rn
unsigned s : 1; // S-flag
unsigned opcode : 4; // Opcode
unsigned resv1 : 3; // Reserved 1 (000)
unsigned cond : 4; // Condition
} dprrs; // Data Processing Register Register Shift
#define DPRRS_R1 0
#define DPRRS_R2 0
#define DPRRS_R3 1
// Example <opcode> Rd, Rn, Rm <shift> Rs
// This intruction is unpredictable if R15 is one of the used registers anyway.
typedef struct
{
unsigned immed : 8; // Immediate value
unsigned rotate : 4; // rotate
unsigned Rd : 4; // Rd
unsigned Rn : 4; // Rn
unsigned s : 1; // S-flag
unsigned opcode : 4; // Opcode
unsigned resv1 : 3; // Reserved 1 (001)
unsigned cond : 4; // Condition
} dpi; // Data processing immediate
// example add r0, r1, (ror <immed>, <rotate * 2>)
#define DPI_R1 1
typedef struct
{
unsigned immed : 12; // Immediate
unsigned Rd : 4; // Rd
unsigned Rn : 4; // Rn
unsigned L : 1; // L-bit (Load/Store)?
unsigned W : 1; // W-bit
unsigned B : 1; // B-bit
unsigned U : 1; // U-bit
unsigned p : 1; // P-bit
unsigned resv1 : 3; // Reserved 1 (010)
unsigned cond : 4; // Condition
} lsio; // Load/store immediate offset
// Example ldr Rd, [Rn, #<immed>]
#define LSIO_R1 2
typedef struct
{
unsigned Rm : 4; // Rm
unsigned resv2 : 1; // Reserved 2 (0)
unsigned shift : 2; // Shit type (LSL, LSR, ASR, ROR )
unsigned amount : 5; // Shift amount (0-31)
unsigned Rd : 4; // Rd
unsigned Rn : 4; // Rn
unsigned L : 1; // L-bit (Load/Store)?
unsigned W : 1; // W-bit
unsigned B : 1; // B-bit
unsigned U : 1; // U-bit
unsigned p : 1; // P-bit
unsigned resv1 : 3; // Reserved 1 (011)
unsigned cond : 4; // Condition
} lsro; // Load/Store register offset
// Example ldr Rd, [Rn + Rm lsl 5]
#define LSRO_R1 3
#define LSRO_R2 0
typedef struct
{
unsigned regs : 16; // Register mask
unsigned Rn : 4; // Rn
unsigned L : 1; // L-bit (Load/Store)?
unsigned W : 1; // W-bit
unsigned S : 1; // B-bit
unsigned U : 1; // U-bit
unsigned p : 1; // P-bit
unsigned resv1 : 3; // Reserved 1 (100)
unsigned cond : 4; // Condition
} lsm; // Load store multiple
// Example: ldm r0, {r1, r2, r3}
#define LSM_R1 4
typedef struct
{
unsigned offset : 24; // Branch offset
unsigned link : 1; // Link flag
unsigned resv1 : 3; // Reserved 1 (101)
unsigned cond : 4; // Condition
} bl; // Branch with link(optional)
#define BL_R1 5
typedef union {
dpisr DPISR;
dprrs DPRRS;
dpi DPI;
lsio LSIO;
lsro LSRO;
lsm LSM;
bl BL;
unsigned int uiInstruction;
} Instruction;
/*
#define DPISR_R1 0
#define DPISR_R2 0
#define DPRRS_R1 0
#define DPRRS_R2 0
#define DPRRS_R3 1
#define DPI_R1 1
#define LSIO_R1 2
#define LSRO_R1 3
#define LSRO_R2 0
#define LSM_R1 4
#define BL_R1 5
*/
/*
* Data Processiong Opcode field values
*/
#define OPCODE_MOV 0xD
#define OPCODE_MVN 0xF
#define OPCODE_ADD 0x4
#define OPCODE_ADC 0x5
#define OPCODE_SUB 0x2
#define OPCODE_SBC 0x6
#define OPCODE_RSB 0x3
#define OPCODE_RSC 0x7
#define OPCODE_AND 0x0
#define OPCODE_EOR 0x1
#define OPCODE_ORR 0xC
#define OPCODE_BIC 0xE
#define OPCODE_CMP 0xA
#define OPCODE_CMN 0xB
#define OPCODE_TST 0x8
#define OPCODE_TEQ 0x9
/*
* Shift field values
*/
#define SHIFT_LSL 0x0
#define SHIFT_LSR 0x1
#define SHIFT_ASR 0x2
#define SHIFT_ROR 0x3
#define SHIFT_RRX 0x3 /* Special case: ROR(0) implies RRX */
unsigned int DecodeDPISR(dpisr Instr, unsigned int PC);
unsigned int DecodeDPRRS(dprrs Instr, unsigned int PC);
unsigned int DecodeDPI(dpi Instr, unsigned int PC);
unsigned int DecodeLSIO(lsio Instr, unsigned int PC);
unsigned int DecodeLSRO(lsro Instr, unsigned int PC);
unsigned int DecodeLSM(lsm Instr, unsigned int PC);
unsigned int DecodeBL(bl Instr, unsigned int PC);
///////////////////////////////////////////////////////////////
//
unsigned int DecodeInstruction(unsigned int uiInstruction, unsigned int PC)
{
Instruction myInstruction;
unsigned int uiCondMask;
uiCondMask = (uiInstruction & 0xf0000000) >> 28;
// This instruction can do whatever it wants, but if it doesn't execute we don't give a shit.
if (!CondWillExecute(uiCondMask, (unsigned int)g_Registers[18])) return PC + 4;
//Printf("CondWillExec");
myInstruction.uiInstruction = uiInstruction;
// Start decoding.. phuu
if ((myInstruction.DPISR.resv1 == DPISR_R1) && (myInstruction.DPISR.resv2 == DPISR_R2)) return DecodeDPISR(myInstruction.DPISR, PC);
else if ((myInstruction.DPRRS.resv1 == DPRRS_R1) &&
(myInstruction.DPRRS.resv2 == DPRRS_R2) &&
(myInstruction.DPRRS.resv3 == DPRRS_R3)) return DecodeDPRRS(myInstruction.DPRRS, PC);
else if ((myInstruction.DPI.resv1 == DPI_R1)) return DecodeDPI(myInstruction.DPI, PC);
else if ((myInstruction.LSIO.resv1 == LSIO_R1)) return DecodeLSIO(myInstruction.LSIO, PC);
else if ((myInstruction.LSRO.resv1 == LSRO_R1) &&
(myInstruction.LSRO.resv2 == LSRO_R2)) return DecodeLSRO(myInstruction.LSRO, PC);
else if (myInstruction.LSM.resv1 == LSM_R1) return DecodeLSM(myInstruction.LSM, PC);
else if (myInstruction.BL.resv1 == BL_R1) return DecodeBL(myInstruction.BL, PC);
return 0;
}
///////////////////////////////////////////////////////////////
//
unsigned int LSL(unsigned int uiValue, unsigned int uiSteps)
{
return uiValue << uiSteps;
}
///////////////////////////////////////////////////////////////
//
unsigned int LSR(unsigned int uiValue, unsigned int uiSteps)
{
return uiValue >> uiSteps;
}
///////////////////////////////////////////////////////////////
//
// This one could be trickier since, i'm nor sure if a signed shift really is a signed shift.
unsigned int ASR(unsigned int uiValue, unsigned int uiSteps)
{
unsigned int uiSignMask = 0;
// Check if it's a negative number
if (uiValue & 0x80000000) {
// Yes, damn
uiSignMask = ((~0) << (32 - uiSteps));
}
return ((uiValue >> uiSteps) | uiSignMask);
}
///////////////////////////////////////////////////////////////
//
unsigned int ROR(unsigned int uiValue, unsigned int uiSteps)
{
unsigned int uiRetval;
while(uiSteps-- > 0) {
if (uiValue & 0x01) {
uiValue = (uiValue >> 1) | 0x80000000;
} else {
uiValue = uiValue >> 1;
}
}
return uiValue;
}
///////////////////////////////////////////////////////////////
//
unsigned int Shift_Operand(unsigned int Rm, unsigned int amount, unsigned int shift)
{
unsigned int uiRegisterValue;
uiRegisterValue = g_Registers[Rm];
if (Rm == 0x0f) {
// Rm is PC, and PC is offseted by 8.
uiRegisterValue += 8;
}
// Determine the shift mode.
//(LSL, LSR, ASR, ROR )
switch (shift) {
case 0: // LSL
return LSL(uiRegisterValue, amount);
case 1: // LSR
return LSR(uiRegisterValue, amount);
case 2: // ASR
return ASR(uiRegisterValue, amount);
case 3: // ROR
return ROR(uiRegisterValue, amount);
default:
break;
}
return 0;
}
///////////////////////////////////////////////////////////////
//
// Example <opcode> Rd, Rm, <shift> amount
unsigned int DecodeDPISR(dpisr Instr, unsigned int uiPC)
{
unsigned int uiOperand = Shift_Operand(Instr.Rm, Instr.amount, Instr.shift);
unsigned int uiRnVal = g_Registers[Instr.Rn];
// Only do this i Pc is Rd
if (Instr.Rd != 0x0f) return uiPC + 4;
// The actual value that PC contains when executing this instruction is the instruction address+8
if (Instr.Rn == 0x0f) uiRnVal += 8;
// Check what opcode it is!
switch (Instr.opcode) {
case OPCODE_MOV:
return uiOperand;
case OPCODE_MVN:
return ~uiOperand;
case OPCODE_ADD:
return uiRnVal + uiOperand;
case OPCODE_ADC:
return uiRnVal + uiOperand + (((g_Registers[18] & (1 << 29))) == 0?0:1);
case OPCODE_SUB:
return uiRnVal - uiOperand;
case OPCODE_SBC:
return uiRnVal - uiOperand - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_RSB:
return uiOperand - uiRnVal;
case OPCODE_RSC:
return uiOperand - uiRnVal - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_AND:
return (uiRnVal & uiOperand);
case OPCODE_EOR:
return (uiRnVal^uiOperand);
case OPCODE_ORR:
return (uiRnVal | uiOperand);
case OPCODE_BIC:
return (uiRnVal & ~uiOperand);
default:
return 0;
}
}
///////////////////////////////////////////////////////////////
//
//dprrs; // Data Processing Register Register Shift
// Example <opcode> Rd, Rn, Rm <shift> Rs
unsigned int DecodeDPRRS(dprrs Instr, unsigned int uiPC)
{
unsigned int uiRmValue = g_Registers[Instr.Rm];
unsigned int uiRsValue = g_Registers[Instr.Rs];
unsigned int uiRnVal = g_Registers[Instr.Rn];
if ((Instr.Rm = 0x0f)) uiRmValue += 8;
unsigned int uiOperand = Shift_Operand(uiRmValue, uiRsValue, Instr.shift);
// Check if destination is PC
if (Instr.Rd != 0x0f) return uiPC + 4;
if ((Instr.Rn = 0x0f)) uiRnVal += 8;
// Check what opcode it is!
switch (Instr.opcode) {
case OPCODE_MOV:
return uiOperand;
case OPCODE_MVN:
return ~uiOperand;
case OPCODE_ADD:
return uiRnVal + uiOperand;
case OPCODE_ADC:
return uiRnVal + uiOperand + (((g_Registers[18] & (1 << 29))) == 0?0:1);
case OPCODE_SUB:
return uiRnVal - uiOperand;
case OPCODE_SBC:
return uiRnVal - uiOperand - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_RSB:
return uiOperand - uiRnVal;
case OPCODE_RSC:
return uiOperand - uiRnVal - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_AND:
return (uiRnVal & uiOperand);
case OPCODE_EOR:
return (uiRnVal^uiOperand);
case OPCODE_ORR:
return (uiRnVal | uiOperand);
case OPCODE_BIC:
return (uiRnVal & ~uiOperand);
default:
return 0;
}
}
///////////////////////////////////////////////////////////////
//
// dpi; // Data processing immediate
// example add r0, r1, (ror <immed>, <rotate * 2>)
unsigned int DecodeDPI(dpi Instr, unsigned int uiPC)
{
unsigned int uiOperand = (ROR(Instr.immed, Instr.rotate << 1));
unsigned int uiRnVal = g_Registers[Instr.Rn];
// Check if PC is destination
if (Instr.Rd != 0x0f) return uiPC + 4; // Next instruction
if ((Instr.Rn = 0x0f)) uiRnVal += 8;
// Check what opcode it is!
switch ((Instr.opcode)) {
case OPCODE_MOV:
return uiOperand;
case OPCODE_MVN:
return ~uiOperand;
case OPCODE_ADD:
return uiRnVal + uiOperand;
case OPCODE_ADC:
return uiRnVal + uiOperand + (((g_Registers[18] & (1 << 29))) == 0?0:1);
case OPCODE_SUB:
return uiRnVal - uiOperand;
case OPCODE_SBC:
return uiRnVal - uiOperand - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_RSB:
return uiOperand - uiRnVal;
case OPCODE_RSC:
return uiOperand - uiRnVal - (((g_Registers[18] & (1 << 29))) == 0?1:0);
case OPCODE_AND:
return (uiRnVal & uiOperand);
case OPCODE_EOR:
return (uiRnVal^uiOperand);
case OPCODE_ORR:
return (uiRnVal | uiOperand);
case OPCODE_BIC:
return (uiRnVal & ~uiOperand);
default:
return 0;
}
}
///////////////////////////////////////////////////////////////
//
// lsio; // Load/store immediate offset
// Example ldr Rd, [Rn, #<immed>]
unsigned int DecodeLSIO(lsio Instr, unsigned int uiPC)
{
unsigned int uiRnValue = g_Registers[Instr.Rn];
unsigned int uiMemValue;
// Check if destination is PC
if (Instr.Rd != 0x0f) return uiPC + 4;
// Check if it's a LDR instruction
if (Instr.L != 1) return uiPC + 4;
if (Instr.Rn == 0x0f) uiRnValue += 8;
// Check if it's pre-indexed
if (Instr.p == 1){
if (Instr.U == 1) {
// Add offset
uiRnValue += Instr.immed;
} else {
// Sub offset
uiRnValue -= Instr.immed;
}
}
uiMemValue = *(unsigned int *)(uiRnValue);
return uiMemValue;
}
///////////////////////////////////////////////////////////////
//
// lsro; // Load/Store register offset
// Example ldr Rd, [Rn + Rm lsl 5]
unsigned int DecodeLSRO(lsro Instr, unsigned int uiPC)
{
unsigned int uiRnValue = g_Registers[Instr.Rn];
unsigned int uiRmValue = g_Registers[Instr.Rm];
unsigned int uiIndex;
unsigned int uiMemValue;
if (Instr.Rm == 0x0f) uiRmValue += 8;
if (Instr.Rn == 0x0f) uiRnValue += 8;
// Check if destination is PC and that it's LDR instruction
if ((Instr.Rd != 0x0f) || (Instr.L != 1)) return uiPC + 4;
uiIndex = Shift_Operand(Instr.Rm, Instr.amount, Instr.shift);
if (Instr.p == 1){
if (Instr.U == 1) {
// Add offset
uiRnValue += uiIndex;
} else {
// Sub offset
uiRnValue -= uiIndex;
}
}
uiMemValue = *(unsigned int *)(uiRnValue);
return uiMemValue;
}
///////////////////////////////////////////////////////////////
//
// lsm; // Load store multiple
// Example: ldm r0, {r1, r2, r3}
unsigned int DecodeLSM(lsm Instr, unsigned int uiPC)
{
unsigned int uiRnValue = g_Registers[Instr.Rn];
unsigned int uiOffsetToPC = 0;
unsigned int uiMemValue;
// Make sure PC is destination and it's Load instruction
if (((Instr.regs & (1 << 15)) == 0) || (Instr.L != 1)) return uiPC + 4;
// Check if U bit it set
if (Instr.U == 0) {
// This means that it's ascending
// Also means that the PC is closest to Rn
if (Instr.p == 1) {
// Pre decrement.
uiOffsetToPC -= 4;
} else {
uiOffsetToPC = 0;
}
} else {
// The stack is descending, that means that the PC is as far away as possible.
// Lets find out how many registers before it.
for (int i = 0; i < 15; i++) {
if ((Instr.regs & (1 << i)) != 0) uiOffsetToPC += 4;
}
// If the P bit is set, it uses pre increment
if (Instr.p == 1) uiOffsetToPC += 4;
}
// read from out calculated address.
uiMemValue = *(unsigned int *)((uiRnValue + uiOffsetToPC) & ~0x03);
return uiMemValue;
}
///////////////////////////////////////////////////////////////
//
// bl; // Branch with link(optional)
unsigned int DecodeBL(bl Instr, unsigned int uiPC)
{
//Printf("Decode BL");
unsigned int uiAddress;
uiAddress = Instr.offset;
if (uiAddress & 0x00800000) {
uiAddress |= 0xff000000;
}
uiAddress <<= 2;
uiAddress += 8;
return uiPC + uiAddress;
}