/* Native-dependent code for PowerPC's running MorphOS, for GDB.

   Copyright (C) 2013-2019 Free Software Foundation, Inc.

   This file is part of GDB.

   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 3 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, see <http://www.gnu.org/licenses/>.  */

#define SYSTEM_PRIVATE
#define USE_INLINE_STDARG
#define __IXEMUL_EXEC_MIXING_ALLOWED
#include <proto/exec.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <dos/dostags.h>
#include <libraries/objdata.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/sysdebug.h>
#include <clib/debug_protos.h>

#include "morphos-nat.h"
#include <stdio.h>
#include <string.h>
#include <signal.h>

#undef RETURN_ERROR
#include "defs.h"

#include "gdbcore.h"
#include "inferior.h"
#include "infrun.h"
#include "regcache.h"
#include "inf-child.h"
#include "regcache.h"
#include "objfiles.h"
#include "gdbcmd.h"


#include <sys/types.h>
//#include <machine/frame.h>
#include <machine/pcb.h>
#include <machine/reg.h>

struct DosLibrary *DOSBase;
struct Library *SysDebugBase;


//#include "gregset.h"
#include "ppc-tdep.h"
#include "morphos-tdep.h"

#define LOCPRINT kprintf("%s(%d)\n", __func__, __LINE__);

#define EHDEBUG 1
#define ESIGDEBUG 0
#define RESUMEDEBUG 0

#if EHDEBUG
#define EHDEBUGPRINT(x...) kprintf(x)
#else
#define EHDEBUGPRINT(x...) do { } while(0)
#endif
#if ESIGDEBUG
#define ESIGPRINT(x...) kprintf(x)
#else
#define ESIGPRINT(x...) do { } while(0)
#endif

static int autofreeze = 0;
static int taskmonitor = 0;

static const char follow_addtask_mode_child[] = "child";
static const char follow_addtask_mode_parent[] = "parent";

static const char *const follow_addtask_mode_kind_names[] =
{
	follow_addtask_mode_child,
	follow_addtask_mode_parent,
	NULL
};

static const char *follow_addtask_mode_string = follow_addtask_mode_parent;
static void
show_follow_addtask_mode_string (struct ui_file *file, int from_tty,
			      struct cmd_list_element *c, const char *value)
{
	fprintf_filtered (file,
		    _("Debugger response to a program "
		      "adding a new MorphOS exec task is \"%s\".\n"),
		    value);
}


class morphos_nat_target final : public inf_child_target
{
public:

	morphos_nat_target ();
	~morphos_nat_target () override;

	void close() override;
	
	ptid_t wait(ptid_t, struct target_waitstatus *, int) override;

	void fetch_registers(struct regcache *, int) override;
	void store_registers(struct regcache *, int) override;
	
	enum target_xfer_status xfer_partial (enum target_object object,
                                          const char *annex,
                                          gdb_byte *readbuf,
                                          const gdb_byte *writebuf,
                                          ULONGEST offset, ULONGEST len,
                                          ULONGEST *xfered_len) override;

	void attach(const char *, int) override;
	
	bool attach_no_wait () override
	{ return true; }

	void post_attach(int) override;
	
	void detach(inferior *inf, int from_tty) override;

	void create_inferior(const char *, const std::string &,
	                     char **, int) override;

	void mourn_inferior() override;
	
	void prepare_to_store (regcache *regs) override;

	void resume (ptid_t, int , enum gdb_signal) override;
	
	void kill () override;
	
	const char *pid_to_str (ptid_t ptid) override;
	
	char *pid_to_exec_file (int pid) override;
	
	bool info_proc (const char *args, enum info_proc_what what) override;
};

void morphos_relocate_sections(ptid_t);

static void FreezeTask(struct Task *task, UBYTE*);

static morphos_nat_target the_morphos_nat_target;

struct DebugInfo di;

BOOL Ux2DOS(char *uxpath, char *dospath, LONG dospathlength)
{
	BOOL doears = FALSE;

	if(dospathlength < 4) return FALSE; //don't bother me, stupid

	/* handle device name at start */
	dospathlength--; //for termination at end!

	if(*uxpath == '"')
	{
		*dospath++ = '"';
		dospathlength--;
		doears = TRUE;
		uxpath++;
	}

	if(*uxpath == '/')
	{
		uxpath ++;

		while(dospathlength)
		{
			UBYTE uxchar = *uxpath++;

			if(uxchar == '/')
			{
				*dospath++ = ':';
				dospathlength--;
				break;
			}
			else if(uxchar == 0)
			{
				*dospath++ = ':';
				dospathlength--;
				goto end;
			}
			else
			{
				*dospath++ = uxchar;
				dospathlength--;
			}
		}
	}

	while(*uxpath && dospathlength)
	{
		UBYTE uxchar = *uxpath++;

		switch(uxchar)
		{
		case '.':
			if(uxpath[0] == '.' && uxpath[1] == '/')
			{
				*dospath++ = '/';
				dospathlength--;
			}
			else if(uxpath[0] == '/')
			{
				uxpath++;
			}
			else
			{
				*dospath++ = uxchar;
				dospathlength--;
			}
			break;

		case '\\':
			break;

		default:
			*dospath++ = uxchar;
			dospathlength--;
			break;
		}
	}

end:
	if(doears) *dospath++ = '"';
	*dospath = 0;

	return (*uxpath && !dospathlength) ? FALSE : TRUE;
};

BOOL FileExists(char *filename) 
{ 
	BPTR lock = 0;
	if (filename && *filename) 
	{ 
		int omask = sigsetmask(~0);
		struct Process *me = (struct Process *)FindTask(NULL);
		APTR oldwindowptr = me->pr_WindowPtr;
		me->pr_WindowPtr = (APTR) -1;
		if ((lock = Lock(filename,ACCESS_READ))) UnLock(lock);
		me->pr_WindowPtr = oldwindowptr;
		sigsetmask(omask);
	} 
	return lock ? TRUE : FALSE;
}

static void exechookfunc(const struct Hook *hook, void *obj, const struct ExecNotifyMessage *msg)
{
	struct DebugInfo *di = (struct DebugInfo*)hook->h_Data;
	
	int follow_child = (follow_addtask_mode_string == follow_addtask_mode_child);

	if(msg->Type == EXECLIST_TASK)
	{
		struct Task *task = (struct Task *) obj;
		kprintf("EXECHOOK:: task=0x%p, flags=0x%08x\n", task, msg->Flags);
		struct Task *parent = FindTask(NULL);
		
		if(task == di->childtask && di->attaching == 2)
		{
			if((msg->Flags & (EXECNOTIFYF_REMOVE|EXECNOTIFYF_POST)) == (EXECNOTIFYF_REMOVE|EXECNOTIFYF_POST)) 
			{
				//kprintf("EXECHOOK: attached task 0x%p just terminated\n", task);

				LOCPRINT
				di->programended = 1;
				Signal(di->debuggertask, di->debuggersigmask);
			}
		}
		else
		if((msg->Flags & (EXECNOTIFYF_REMOVE|EXECNOTIFYF_POST)) == (EXECNOTIFYF_REMOVE|EXECNOTIFYF_POST))
		{
			kprintf("task=0x%p, di->childtask=0x%p, orgchildtask=0x%p, parent=0x%p\n", task, di->childtask, di->orgchildtask, task->tc_ETask->Parent);
			
			if(task->tc_ETask->Parent == di->childtask) //|| (di->childtask != di->orgchildtask &&  task == di->childtask))
			{
				//kprintf("EXECHOOK: child task 0x%p was removed\n", task);
				struct DebugEvent *de = (DebugEvent *)AllocMem(sizeof(*de), MEMF_CLEAR);
				if(de)
				{
					de->Type = TASKEVENT_REMCHILD;
					de->ChildOfDebugtask = task;

					PutMsg(di->debuggerport, &de->Message);
				}
			}
		}
		else
		if (parent == di->childtask && (msg->Flags & EXECNOTIFYF_REMOVE) == 0 && (msg->Flags & EXECNOTIFYF_POST) != 0)
		{
			//kprintf("EXECHOOK: task 0x%p (%s) was added, childtask = 0x%p, parent=0x%p\n", task, ((struct Node*)task)->ln_Name, di->childtask, parent);
			
			struct DebugEvent *de = (DebugEvent *)AllocMem(sizeof(*de), MEMF_CLEAR);
			if(de)
			{
				de->Type = TASKEVENT_ADDCHILD;
				de->ChildOfDebugtask = task;
				
				if(autofreeze || follow_child)
				{
					Forbid();
					FreezeTask(de->ChildOfDebugtask, &di->oldchildtaskstate);
					//FreezeTask(di->childtask, &di->oldtaskstate);
					Permit();
				}

				PutMsg(di->debuggerport, &de->Message);
			}
		}
	}
	else if(msg->Type == EXECNOTIFY_RUNCOMMAND)
	{
		BPTR seglist = (BPTR) obj;
		struct Task *task = FindTask(NULL); // the process calling RunCommand()

		if(msg->Flags & EXECNOTIFYF_POST)
		{
			if(task == di->childtask && di->attaching == 2)
			{			
				kprintf("EXECHOOK: runcommand task %p seglist 0x%lx just terminated\n", task, seglist);
				
				di->programended = 1;
				Signal(di->debuggertask, di->debuggersigmask);
			}
		}
	}
}

#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
static void ReallyLaunchProgram(struct DebugInfo *di, const char *args)
{
	unsigned int *ptr;
	unsigned int (*funcptr)(void *, unsigned int, void *);

	ptr = (unsigned int*)BADDR(di->childsegs + 1);

	//dprintf("%08x %08x\n", ptr[0], ptr[1]);
	//dprintf("args=%s\n", args);

	Signal(di->debuggertask, di->debuggersigmask);
	Wait(SIGF_SINGLE);

	if((*ptr & 0xff000000) == 0xff000000)
	{
		//kprintf("Child program is a PPC program, let's do some magic\n");

		ptr = (unsigned int*)BADDR(di->childsegs);
		if(ptr)
			ptr = (unsigned int*)BADDR(*ptr);

		if(ptr)
		{
			int alen;

			if(args == NULL)
				alen = 0;
			else
				alen = strlen(args);

			di->arguments = (char*)AllocVec(alen + 2, MEMF_ANY);

			sprintf(di->arguments, "%s\n", alen == 0 ? "" : args);

			SetProgramName(di->namebuf);

			const int minalloc = 208; /* don't ask, it's magic */
			UBYTE *fakebuf;
			int argsize = strlen(di->arguments);

			fakebuf = (UBYTE*)AllocVec(MIN(minalloc, argsize), MEMF_PUBLIC);
			if(fakebuf)
			{
				BPTR infh, tempfh = 0;
				struct FileHandle *input;
				LONG in_buf;
				LONG in_pos;
				LONG in_end;

				infh = Input();
				if(!infh)
				{
					tempfh = Open("NIL:", MODE_NEWFILE);
					SelectInput(tempfh);
					infh = tempfh;
				}

				input = (FileHandle*)BADDR(infh);
				if(input)
				{
					in_buf = input->fh_Buf;
					in_pos = input->fh_Pos;
					in_end = input->fh_End;

					CopyMem(di->arguments, fakebuf, argsize);
					input->fh_Buf = MKBADDR(fakebuf);
					input->fh_Pos = 0;
					input->fh_End = argsize;
				}

				/* do stuff here */
				/* ... */

				funcptr = (unsigned int (*)(void*, unsigned int, void*))(ptr + 1);
				funcptr(di->arguments, argsize, 0);

				/* restore original values if buffer ptr hasn't changed */
				if(input && input->fh_Buf == MKBADDR(fakebuf))
				{
					input->fh_Buf = in_buf;
					input->fh_Pos = in_pos;
					if(input->fh_End)
						input->fh_End = in_end;
				}

				FreeVec(fakebuf);
				if(tempfh)
				{
					SelectInput(0);
					Close(tempfh);
				}
			}
		}
		else
		{
			MyEmulHandle->EmulCallDirect68k(ptr);
		}
	}

	Forbid();

	LOCPRINT
	di->programended = 1;
	Signal(di->debuggertask, di->debuggersigmask);

}

static void LaunchProgram(struct DebugInfo *di, const char *programname, const char *args)
{
	BPTR segs;
	ULONG seglisttype;

	di->namebuf = (char*)AllocVec(strlen(programname) + 1, MEMF_ANY);
	strcpy(di->namebuf, programname);
	di->argsbuf = (char*)AllocVec(strlen(args) + 1, MEMF_ANY);
	strcpy(di->argsbuf, args);

	char *programname2 = di->namebuf;
	char *arguments = di->argsbuf;

	int omask = sigsetmask(~0);

	if(programname2 && arguments)
	{
		segs = NewLoadSegTags(programname2, TAG_DONE);

		if(GetSegListAttr(segs, SEGLISTTAG_SEGLISTTYPE, &seglisttype, sizeof(seglisttype)))
		{
			di->childsegs = segs;
			di->orgchildsegs = segs;
			
			char *homedir = strdup(programname2);
			if (!homedir)
			{
				sigsetmask(omask);
				return;
			}
			char *filepart = FilePart(homedir);
			*filepart = '\0';

			BPTR pdlock = Lock((CONST_STRPTR) homedir, ACCESS_READ); 
			free(homedir);
			if(!pdlock)
			{
				sigsetmask(omask);
				kprintf("Failed to get a lock on the program directory\n");
				return;
			}
			
			Forbid();
			
			di->childtask = (struct Task *)CreateNewProcTags(
			                    NP_CodeType, CODETYPE_PPC,
			                    NP_Entry, (ULONG)ReallyLaunchProgram,
			                    NP_PPC_Arg1, (ULONG)di,
			                    NP_PPC_Arg2, (ULONG)arguments,
			                    NP_Name, (ULONG)programname2,
			                    NP_Input, Input(),
			                    NP_CloseInput, FALSE,
			                    NP_Output, Output(),
			                    NP_CloseOutput, FALSE,
			                    NP_Cli, TRUE,
			                    NP_HomeDir, (ULONG)pdlock,
			                    TAG_DONE);
								
			if(di->childtask)
			{
				di->orgchildtask = di->childtask;
				NewSetTaskAttrs(di->childtask, &di->exceptionhandlerport, sizeof(di->exceptionhandlerport), TASKINFOTYPE_TRAPMSGPORT, TAG_DONE);
			}
			else
			{
				kprintf("Failed to create child process\n");
				UnLock(pdlock);
			}

			Permit();

		}
		//free(programname2);
	}
	sigsetmask(omask);
}

static void ExceptionHandler(struct DebugInfo *di)
{
	struct MsgPort *exceptionhandlerport;
	struct TaskTrapMessage *msg;
	struct DebugEvent *de;
	unsigned int srr0;
	unsigned int lr;
	unsigned int ctr;
	unsigned int cr;
	unsigned int xer;
	unsigned int gpr[32];
	ULONG segnum;
	ULONG segoffset;
	ULONG segstart;
	int stopped;
	struct CommandLineInterface *cli;
	BPTR seg;
	unsigned int ptr;
	//unsigned int *p;
	unsigned int ignoreexception;

	exceptionhandlerport = CreateMsgPort();
	di->exceptionhandlerport = exceptionhandlerport;
	if(exceptionhandlerport)
	{
		Signal(di->debuggertask, di->debuggersigmask);

		while(!di->programended && !di->exceptionhandlershouldquit)
		{
			Wait((1 << exceptionhandlerport->mp_SigBit));

			while((msg = (struct TaskTrapMessage *)GetMsg(exceptionhandlerport)))
			{
				NewGetTaskAttrs(di->childtask, &srr0, sizeof(srr0), TASKINFOTYPE_PPC_SRR0, TAG_DONE);
				NewGetTaskAttrs(di->childtask, &lr, sizeof(lr), TASKINFOTYPE_PPC_LR, TAG_DONE);
				NewGetTaskAttrs(di->childtask, &ctr, sizeof(ctr), TASKINFOTYPE_PPC_CTR, TAG_DONE);
				NewGetTaskAttrs(di->childtask, &cr, sizeof(cr), TASKINFOTYPE_PPC_CR, TAG_DONE);
				NewGetTaskAttrs(di->childtask, &xer, sizeof(xer), TASKINFOTYPE_PPC_XER, TAG_DONE);
				NewGetTaskAttrs(di->childtask, gpr, sizeof(gpr), TASKINFOTYPE_PPC_GPR, TASKINFOTAG_REGSTART, 0, TASKINFOTAG_REGCOUNT, 32, TAG_DONE);
				segoffset = 0xffffffff;

				EHDEBUGPRINT("ExceptionHandler: Exception SRR0 0x%08x LR 0x%08x\n", srr0, lr);

				di->lastexceptionsrr0 = srr0;
				di->lastexceptionlr = lr;
				di->lastexceptionctr = ctr;
				di->lastexceptioncr = cr;
				di->lastexceptionr1 = gpr[1];
				
				if(SysDebugFindSeg(srr0, &segnum, &segoffset))
				{
					if(di->childsegs)
					{
						seg = di->childsegs;
					}
					else
					{
						cli = (CommandLineInterface*)BADDR(((struct Process *)di->childtask)->pr_CLI);
						seg = cli->cli_Module;
					}

					segstart = srr0 - segoffset;

					while(seg)
					{
						ptr = (unsigned int)BADDR(seg);
						if(ptr + 4 == segstart)
							break;

						seg = *(unsigned int *)ptr;
					}

					if(seg)
					{
						EHDEBUGPRINT("ExceptionHandler: Crash is inside program\n");
						stopped = 1;
					}
					else
					{
						EHDEBUGPRINT("ExceptionHandler: Crash is outside program\n");
						stopped = 0;
					}
				}
				else
				{
					EHDEBUGPRINT("ExceptionHandler: Crash is outside program\n");
					stopped = 1;
				}

				ignoreexception = 0;

				if(!ignoreexception)
				{
					EHDEBUGPRINT("ExceptionHandler: Sending exception message\n");

					de = (DebugEvent *)AllocMem(sizeof(*de), MEMF_CLEAR);
					if(de)
					{
						de->Type = msg->Type;
						de->DAR = msg->DAR;
						de->DSISR = msg->DSISR;
						de->SRR0 = srr0;
						CopyMem(gpr, de->GPR, sizeof(de->GPR));
						de->Stopped = stopped;
						de->SegOffset = segoffset;
						Forbid();
						di->childtask->tc_State = TS_FROZEN;
						Permit();

						PutMsg(di->debuggerport, &de->Message);
					}
				}

				if(ignoreexception)
				{
					EHDEBUGPRINT("ExceptionHandler: Ignoring exception and restarting child at the current instruction\n");
					//RestartChildProgramAtCurrentInstruction(di);
				}
				else if(!stopped)
				{
					EHDEBUGPRINT("ExceptionHandler: Restarting child at the next instruction\n");
					//RestartChildProgramAtNextInstruction(di);
				}
			}
		}

		DeleteMsgPort(exceptionhandlerport);
	}

	Forbid();
	di->exceptionhandlerport = 0;
	Signal(di->debuggertask, di->debuggersigmask);
}

int LoadDebuggerChild(struct DebugInfo *di, const char *path, const char *args)
{
	int omask;

	LaunchProgram(di, path, args);

	if(di->childtask == NULL)
	{
		// LaunchProgram didn't work
		return 0;
	}

	omask = sigsetmask(~0);
	Wait(di->debuggersigmask);
	sigsetmask(omask);

	//kprintf("Child task ready to start, so let's start it and wait for events\n");

	return 1;
}

int InitDebugger(struct DebugInfo *di)
{
	struct MsgPort *debugport;
	BYTE signum;
	int omask = sigsetmask(~0);

	memset(di, 0, sizeof(*di));

	if(SysDebugBase)
	{
		signum = AllocSignal(-1);
		if(signum != -1)
		{
			debugport = CreateMsgPort();
			if(debugport)
			{
				di->debuggertask = FindTask(0);
				di->debuggersigmask = (1 << signum);
				di->debuggersignum = signum;
				di->debuggerport = debugport;
				di->exceptionhandlerport = 0;

				//kprintf("Starting exception handler\n");

				di->exceptionhandler = (struct Task *)CreateNewProcTags(
				                           NP_Entry, (ULONG)ExceptionHandler,
				                           NP_CodeType, CODETYPE_PPC,
				                           NP_Name, (ULONG)"GDB Exception Handler",
				                           NP_PPC_Arg1, (ULONG)di,
				                           NP_Priority, 50,
				                           TAG_DONE);

				if(di->exceptionhandler)
				{
					Wait(di->debuggersigmask);

					if(di->exceptionhandlerport)
					{
						di->exechook.h_Entry = (HOOKFUNC)&exechookfunc;
						di->exechook.h_Data = di;
						AddExecNotify(&di->exechook);

						//kprintf("Exception handler started: 0x%p\n", di->exceptionhandler);

						sigsetmask(omask);

						return 1;
					}
				}

				DeleteMsgPort(debugport);
			}

			FreeSignal(signum);
		}
	}

	sigsetmask(omask);

	return 0;
}

void ShutdownDebugger(struct DebugInfo *di)
{
	int omask = sigsetmask(~0);

	Forbid();
	di->exceptionhandlershouldquit = 1;

	Signal(di->exceptionhandler, (1 << di->exceptionhandlerport->mp_SigBit));
	Permit();

	while(di->exceptionhandlerport)
	{
		Wait(di->debuggersigmask);
	}

	DeleteMsgPort(di->debuggerport);

	FreeSignal(di->debuggersignum);

	if(di->arguments)
		FreeVec(di->arguments);

	if(di->namebuf)
		FreeVec(di->namebuf);
	
	if(di->argsbuf)
		FreeVec(di->argsbuf);
		
	di->childstarted = 0;
	
	di->attaching = 0;
	
	di->frozen = 0;
	
	di->exceptionhandlershouldquit = 0;
	
	RemExecNotify(&di->exechook);

	sigsetmask(omask);
}


void morphos_nat_target::create_inferior(const char *exec_file,
                                    const std::string &origallargs,
                                    char **in_env, int from_tty)
{
	char mos_file[1024];
	ptid_t ptid;
	pid_t pid;
	struct inferior *inf;
	
	if (!exec_file)
		error("No executable specified\n");

	if(!Ux2DOS((char*)exec_file, mos_file, sizeof(mos_file)))
		error("Failed to convert MorphOS path\n");
		
	if(!FileExists(mos_file))
		error("Executable '%s' does not exist\n", exec_file);

	if(InitDebugger(&di))
	{
		const char *args = origallargs.c_str ();
		
		if(LoadDebuggerChild(&di, mos_file, args))
		{
			pid = (int)di.childtask;

			inferior_ptid = ptid_t(pid);
			inf = current_inferior();
			inferior_appeared(inf, pid);
			
			unpush_target(this);
			
			if(!target_is_pushed(this))
				push_target(this);

			add_thread(inferior_ptid);
			
			clear_proceed_status (0);
			init_wait_for_inferior ();

			morphos_relocate_sections(inferior_ptid);
			
		}
		else
		{
			error("Failed to create the process\n");
		}
	}
	else
		error("Failed to Init the MorphOS Debugger\n");
}

static BPTR GetSegList(struct Process *process)
{
	ULONG *array;
	BPTR segList;

	if (process->pr_Task.tc_Node.ln_Type != NT_PROCESS)
	{
		return 0;
	}
	
	int omask = sigsetmask(~0);
	struct CommandLineInterface *cli = (CommandLineInterface*)BADDR(process->pr_CLI);
	if (cli)
	{
		segList = cli->cli_Module;
		kprintf("CLI seglist=0x%p\n", segList);
	}
	else
	{
		array = (ULONG*)BADDR(process->pr_SegList);
		segList = array[3];
		kprintf("non-CLI seglist=0x%p\n", segList);
	}

	// This kind of depends on dos.library internals:
	// Identify NP_Entry fake seglist (the seglist ptr is right after the array)
	if(segList == process->pr_SegList + 5)
	{
		union
		{
			const struct EmulLibEntry ppc;
			const struct
			{
				UWORD  jmp;
				void (*func)(void) __attribute__((packed));
			} m68k;
		} *segptr;
		segptr = (typeof(segptr)) BADDR(segList + 1);
		void (*entry)(void) = NULL;
		if(segptr->ppc.Trap == TRAP_LIB && segptr->ppc.Extension == 0)
		{
			// PPC
			entry = segptr->ppc.Func;
		}
		else if(segptr->m68k.jmp == 0x4ef9)
		{
			// 68k
			entry = segptr->m68k.func;
		}

		if(entry)
		{
			// Attempt to find the seglist from segtracker.
			APTR foundseglist;

			if(SysDebugFindSeg((ULONG)entry, (ULONG*)&foundseglist, (ULONG*)&foundseglist))
			{
				// We found the pointer is a seglist. Return that.
				segList = MKBADDR(foundseglist);
				kprintf("NP_Entry 0x%p seglist=0x%p\n", entry, segList);
			}
		}
	}

	sigsetmask(omask);

	return segList;
}

static BOOL IsTask(struct Task *mytask)
{
	//Forbid();
	
	struct Node *task;

	for (task = SysBase->TaskWait.lh_Head;
		 task->ln_Succ;
		 task = task->ln_Succ)
	{
		if (task == (Node*)mytask)
		{
			return true;
		}
	}
	
	for (task = SysBase->TaskReady.lh_Head;
		 task->ln_Succ;
		 task = task->ln_Succ)
	{
		if (task == (Node*)mytask)
		{
			return true;
		}
	}
	
	//Permit();
	
	return false;
}

static void FreezeTask(struct Task *task, UBYTE *oldtaskstate)
{
	//Forbid();

	REMOVE(&task->tc_Node);
	*oldtaskstate = task->tc_State;
	task->tc_State = TS_FROZEN;
	ADDTAIL((struct List *) &SysBase->TaskWait, &task->tc_Node);

	//Permit();

	return;
}

static void ActivateTask(struct Task *task, UBYTE oldtaskstate)
{
	int omask = sigsetmask(~0);

	Forbid();

	REMOVE(&task->tc_Node);
	task->tc_State = oldtaskstate;
	if (oldtaskstate == TS_WAIT)
	{
		ADDTAIL((struct List *) &SysBase->TaskWait, &task->tc_Node);
		Signal(task, task->tc_SigRecvd);
	}
	else
	{
		Enqueue((struct List *) &SysBase->TaskReady, &task->tc_Node);
		SysBase->SysFlags |= SF_SAR;
	}

	Permit();

	sigsetmask(omask);
}

struct Process *ProcessByName(const char *name, int *nummatches)
{
	struct Node *process;
	struct Node *foundprocess=NULL;
	int nmatches = 0;

	for(process = SysBase->TaskWait.lh_Head;
		process->ln_Succ && nmatches < 2;
		process = process->ln_Succ)
	{
		if(strcasecmp(process->ln_Name, name) == 0)
		{
			nmatches++;
			foundprocess = process;
		}
	}

	for(process = SysBase->TaskReady.lh_Head;
		process->ln_Succ && nmatches < 2;
		process = process->ln_Succ)
	{
		if(strcasecmp(process->ln_Name, name) == 0)
		{
			nmatches++;
			foundprocess = process;
		}
	}

	*nummatches =nmatches;
	return (struct Process *)foundprocess;
}

morphos_nat_target::morphos_nat_target ()
{
	int omask = sigsetmask(~0);
	DOSBase = (struct DosLibrary*) OpenLibrary("dos.library", 0);
	SysDebugBase = OpenLibrary("sysdebug.library", 52);
	if(!DOSBase || !SysDebugBase)
	{
		CloseLibrary(SysDebugBase);
		CloseLibrary((Library*)DOSBase);
		sigsetmask(omask);
		error("Failed to open sysdebug.library v52 or higher\n");
	}
	else
		sigsetmask(omask);
}

morphos_nat_target::~morphos_nat_target ()
{
	int omask = sigsetmask(~0);
	//RemExecNotify(&di.exechook); 
	CloseLibrary(SysDebugBase);
	CloseLibrary((Library*)DOSBase);
	sigsetmask(omask);
}

void morphos_nat_target::attach(const char *args, int from_tty)
{
	struct Process *process = NULL;
	BPTR segs;
	int omask = sigsetmask(~0);

	Forbid();
	
	if(args[0] == '0' && args[1] == 'x')
	{
		process = (struct Process *)strtol(args, 0, 0);
	}
	else
	{
		int nmatches;
		process = ProcessByName(args, &nmatches);
		if(nmatches == 0)
		{
			Permit();
			sigsetmask(omask);
			error("Could not find a process with the name '%s'\n", args);
		}
		else if(nmatches > 1)
		{
			Permit();
			sigsetmask(omask);
			error("Found several processes with the name '%s', please attach using the address instead\n", args);
		}
	}

	if(!process || !IsTask((Task*)process))
	{
		Permit();
		sigsetmask(omask);
		error("There is no process with the address '%s'\n", args);
	}

	kprintf("Debugging process 0x%p\n", process);

	if(process->pr_Task.tc_Node.ln_Type != NT_PROCESS)
	{
		Permit();
		sigsetmask(omask);
		error("'%s' is a task and not a full process. Not possible to attach\n", args);
	}
/*
	if(process->pr_Task.tc_State == TS_FROZEN)
	{
		Permit();
		sigsetmask(omask);
		error("'%s' is already frozen\n", args);
	}
*/

	/*if(process->pr_Task.tc_ETask->Parent)
	{
		segs = GetSegList((struct Process*)(process->pr_Task.tc_ETask->Parent));
		kprintf("parent seglist=0x%p\n", segs);
	}
	else*/
	{
		segs = GetSegList(process);
		kprintf("child seglist=0x%p\n", segs);
	}

	if(!segs)
	{
		Permit();
		sigsetmask(omask);
		error("Could not find a SegList for this process\n");
	}
	

	if(InitDebugger(&di))
	{
		ULONG seglisttype;
		//const char *args = origallargs.c_str ();

		/* from infcmd.c->run_command_1

		reopen_exec_file ();
		reread_symbols ();*/

		if(GetSegListAttr(segs, SEGLISTTAG_SEGLISTTYPE, &seglisttype, sizeof(seglisttype)) && seglisttype == SEGLISTTYPE_ELF)
		{
			ptid_t ptid;
			pid_t pid;
			struct inferior *inf;
	
			di.childsegs = segs;
			di.childtask = (Task*)process;

			if(di.childtask)
			{
				NewSetTaskAttrs(di.childtask, &di.exceptionhandlerport, sizeof(di.exceptionhandlerport), TASKINFOTYPE_TRAPMSGPORT, TAG_DONE);
			}

			if(process->pr_Task.tc_State != TS_FROZEN)
				FreezeTask(di.childtask, &di.oldtaskstate);
			
			Permit();
			
			pid = (int)di.childtask;

			inferior_ptid = ptid_t(pid);
			inf = current_inferior();
			inferior_appeared(inf, pid);

			unpush_target(this);

			if(!target_is_pushed(this))
				push_target(this);

			add_thread(inferior_ptid);

			clear_proceed_status(0);
			init_wait_for_inferior();
			
			kprintf("child is now attached\n");
			di.attaching = 1;
		}
		else
		{
			kprintf("seglisttype=%d\n", seglisttype);
			ShutdownDebugger(&di);
			Permit();
			sigsetmask(omask);
			error("Failed to get the ELF segment list\n");
		}
	}
	else
	{
		Permit();
		sigsetmask(omask);
		error("Failed to Init the MorphOS Debugger\n");
	}

	sigsetmask(omask);
}

void morphos_nat_target::detach(inferior *inf, int from_tty)
{
	if (di.attaching)
	{
		ptid_t ptid = minus_one_ptid;
		int childstarted = di.childstarted;
		int attaching = di.attaching;
		int frozen=di.frozen = 0;

		ShutdownDebugger(&di);
		
		di.childstarted = childstarted;
		di.attaching = attaching;
		di.frozen = frozen;

		resume(ptid, 0, GDB_SIGNAL_0);
		
		di.childstarted = 0;
		di.attaching = 0;
		di.frozen = 0;

		detach_inferior(inf);
		maybe_unpush_target();
	}
}

void morphos_nat_target::store_registers(struct regcache *regcache, int r)
{
	int regno = r;
	struct gdbarch *gdbarch = regcache->arch();
	struct gdbarch_tdep *tdep = gdbarch_tdep(gdbarch);
	
	struct TagItem tags[] =
	{
		{TASKINFOTAG_REGSTART, 0},
		{TASKINFOTAG_REGCOUNT, 32},
		{TAG_END}
	};

	struct TagItem emptytags[] =
	{
		{TAG_END, 0}
	};

	int i;

	int omask = sigsetmask(~0);

	if (regno == -1)
	{
		ULONG gpr[32];
		double f[32];

		for(i=0;i<32;i++)
		{
			regcache->raw_collect (i, &gpr[i]);
			regcache->raw_collect (PPC_F0_REGNUM+i, &f[i]);
		}

		NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), gpr, sizeof(gpr), TASKINFOTYPE_PPC_GPR, tags);
		NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), f, sizeof(f), TASKINFOTYPE_PPC_FPR, tags);
	}
	else if(regno >= 0 && regno <= 31)
	{
		ULONG gpr;

		regcache->raw_collect (regno, &gpr);

		tags[0].ti_Data = regno;
		tags[1].ti_Data = 1;

		NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &gpr, sizeof(gpr), TASKINFOTYPE_PPC_GPR, tags);
	}
	else if(regno >= PPC_F0_REGNUM && regno <= PPC_F0_REGNUM+31)
	{
		double f;

		regcache->raw_collect (regno, &f);

		tags[0].ti_Data = regno-PPC_F0_REGNUM;
		tags[1].ti_Data = 1;

		NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &f, sizeof(f), TASKINFOTYPE_PPC_FPR, tags);
	}

	if (regno == -1 || regno == tdep->ppc_lr_regnum)
	{
		ULONG lr;

		regcache->raw_collect (tdep->ppc_lr_regnum, &lr);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &lr, sizeof(lr), TASKINFOTYPE_PPC_LR, emptytags) != sizeof(lr))
			perror_with_name ("Couldn't get registers");
	}
	if (regno == -1 || regno == tdep->ppc_cr_regnum)
	{
		ULONG cr;

		regcache->raw_collect (tdep->ppc_cr_regnum, &cr);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &cr, sizeof(cr), TASKINFOTYPE_PPC_CR, emptytags) != sizeof(cr))
			perror_with_name ("Couldn't get registers");
	}
	if (regno == -1 || regno == tdep->ppc_xer_regnum)
	{
		ULONG xer;

		regcache->raw_collect (tdep->ppc_xer_regnum, &xer);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &xer, sizeof(xer), TASKINFOTYPE_PPC_XER, emptytags) != sizeof(xer))
			perror_with_name ("Couldn't get registers");
	}
	if (regno == -1 || regno == tdep->ppc_ctr_regnum)
	{
		ULONG ctr;

		regcache->raw_collect (tdep->ppc_ctr_regnum, &ctr);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &ctr, sizeof(ctr), TASKINFOTYPE_PPC_CTR, emptytags) != sizeof(ctr))
			perror_with_name ("Couldn't get registers");
	}
	if (regno == -1 || regno == PPC_PC_REGNUM)
	{
		ULONG pc;

		regcache->raw_collect (PPC_PC_REGNUM, &pc);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &pc, sizeof(pc), TASKINFOTYPE_PPC_SRR0, emptytags) != sizeof(pc))
			perror_with_name ("Couldn't get registers");
	}
	if (regno == -1 || regno == tdep->ppc_fpscr_regnum)
	{
		double fpscr;

		regcache->raw_collect (tdep->ppc_fpscr_regnum, &fpscr);

		if (NewSetTaskAttrsA((struct Task *)inferior_ptid.pid(), &fpscr, sizeof(fpscr), TASKINFOTYPE_PPC_FPSCR, emptytags) != sizeof(fpscr))
			perror_with_name ("Couldn't get registers");
	}

	sigsetmask(omask);
}

void morphos_nat_target::fetch_registers(struct regcache *regcache, int r)
{
	int regno = r;

	struct TagItem tags[] =
	{
		{TASKINFOTAG_REGSTART, 0},
		{TASKINFOTAG_REGCOUNT, 32},
		{TAG_END}
	};

	struct TagItem emptytags[] =
	{
		{TAG_END, 0}
	};

	int i;
	struct gdbarch *gdbarch = regcache->arch();
	struct gdbarch_tdep *tdep = gdbarch_tdep(gdbarch);
	int omask = sigsetmask(~0);

	if (regno == -1)
	{
		ULONG gpr[32];
		double f[32];

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), gpr, sizeof(gpr), TASKINFOTYPE_PPC_GPR, tags) != sizeof(gpr))
			perror_with_name ("Couldn't get registers");

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), f, sizeof(f), TASKINFOTYPE_PPC_FPR, tags) != sizeof(f))
			perror_with_name ("Couldn't get FP registers");

		for(i=0;i<32;i++)
		{
			regcache->raw_supply(i, &gpr[i]);
			regcache->raw_supply(i+PPC_F0_REGNUM, &f[i]);
		}
	}
	else if (regno >= 0 && regno <= 31)
	{
		ULONG gpr;

		tags[0].ti_Data = regno;
		tags[1].ti_Data = 1;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &gpr, sizeof(gpr), TASKINFOTYPE_PPC_GPR, tags) != sizeof(gpr))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(regno, &gpr);
	}
	else if (regno >= PPC_F0_REGNUM && regno <= PPC_F0_REGNUM+31)
	{
		float f;

		tags[0].ti_Data = regno-PPC_F0_REGNUM;
		tags[1].ti_Data = 1;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &f, sizeof(f), TASKINFOTYPE_PPC_GPR, tags) != sizeof(f))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(regno-PPC_F0_REGNUM, &f);
	}

	if (regno == -1 || regno == tdep->ppc_lr_regnum)
	{
		ULONG lr;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &lr, sizeof(lr), TASKINFOTYPE_PPC_LR, emptytags) != sizeof(lr))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(tdep->ppc_lr_regnum, &lr);
	}
	if (regno == -1 || regno == tdep->ppc_cr_regnum)
	{
		ULONG cr;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &cr, sizeof(cr), TASKINFOTYPE_PPC_CR, emptytags) != sizeof(cr))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(tdep->ppc_cr_regnum, &cr);
	}
	if (regno == -1 || regno == tdep->ppc_xer_regnum)
	{
		ULONG xer;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &xer, sizeof(xer), TASKINFOTYPE_PPC_XER, emptytags) != sizeof(xer))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(tdep->ppc_xer_regnum, &xer);
	}
	if (regno == -1 || regno == tdep->ppc_ctr_regnum)
	{
		ULONG ctr;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &ctr, sizeof(ctr), TASKINFOTYPE_PPC_CTR, emptytags) != sizeof(ctr))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(tdep->ppc_ctr_regnum, &ctr);
	}
	if (regno == -1 || regno == PPC_PC_REGNUM)
	{
		ULONG pc;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &pc, sizeof(pc), TASKINFOTYPE_PPC_SRR0, emptytags) != sizeof(pc))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(PPC_PC_REGNUM, &pc);
	}
	if (regno == -1 || regno == tdep->ppc_fpscr_regnum)
	{
		double fpscr;

		if (NewGetTaskAttrsA((struct Task *)inferior_ptid.pid(), &fpscr, sizeof(fpscr), TASKINFOTYPE_PPC_FPSCR, emptytags) != sizeof(fpscr))
			perror_with_name ("Couldn't get registers");

		regcache->raw_supply(tdep->ppc_fpscr_regnum, &fpscr);
	}

	sigsetmask(omask);
}

int follow_newchild(ptid_t child_ptid)
{
	ptid_t parent_ptid;
	parent_ptid = inferior_ptid;
	
	/* Follow the child.  */
	struct inferior *parent_inf, *child_inf;
	struct program_space *parent_pspace;

	if(print_inferior_events)
	{
		std::string parent_pid = target_pid_to_str(parent_ptid);
		std::string child_pid = target_pid_to_str(child_ptid);

		target_terminal::ours_for_output();
		fprintf_filtered(gdb_stdlog,
		                 _("[Attaching after adding %s to parent process %s]\n"),
		                 child_pid.c_str(),
						 parent_pid.c_str());
	}

	/* Add the new inferior first, so that the target_detach below
	doesn't unpush the target.  */

	child_inf = add_inferior(child_ptid.pid());

	parent_inf = current_inferior();
	child_inf->attach_flag = parent_inf->attach_flag;
	//copy_terminal_info(child_inf, parent_inf);
	child_inf->gdbarch = parent_inf->gdbarch;
	//copy_inferior_target_desc_info(child_inf, parent_inf);

	parent_pspace = parent_inf->pspace;

	/* If we're vforking, we want to hold on to the parent until the
	child exits or execs.  At child exec or exit time we can
	 remove the old breakpoints from the parent and detach or
	 resume debugging it.  Otherwise, detach the parent now; we'll
	 want to reuse it's program/address spaces, but we can't set
	 them to the child before removing breakpoints from the
	 parent, otherwise, the breakpoints module could decide to
	 remove breakpoints from the wrong process (since they'd be
	 assigned to the same address space).  */

	/*
	if(has_vforked)
	{
		gdb_assert(child_inf->vfork_parent == NULL);
		gdb_assert(parent_inf->vfork_child == NULL);
		child_inf->vfork_parent = parent_inf;
		child_inf->pending_detach = 0;
		parent_inf->vfork_child = child_inf;
		parent_inf->pending_detach = detach_fork;
		parent_inf->waiting_for_vfork_done = 0;
	}*/

	/* Note that the detach above makes PARENT_INF dangling.  */

	/* Add the child thread to the appropriate lists, and switch to
	this new thread, */

	inferior_ptid = child_ptid;
	add_thread_silent(inferior_ptid);
	set_current_inferior(child_inf);

	/* On MorphOS the address-space is shared with the parent (and other tasks).  
	We can reuse the parent's program/address spaces.  */

	child_inf->pspace = parent_pspace;
	child_inf->aspace = child_inf->pspace->aspace;
	
	return 1;
}


#include <signal.h>
ptid_t
morphos_nat_target::wait(ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
	struct Task *childtask = (struct Task*)ptid.pid();
	ULONG sigrec;
	int follow_child = (follow_addtask_mode_string == follow_addtask_mode_child);
	
	if (childtask == (struct Task*)minus_one_ptid.pid())
		childtask = di.childtask;

	ourstatus->kind = TARGET_WAITKIND_IGNORE;

	struct DebugEvent *de;
	
	int omask;
	ULONG oldexcept;

	omask = sigsetmask(~0);
	oldexcept = SetExcept(0, ~0);

	sigrec = Wait(SIGBREAKF_CTRL_C|(di.debuggersigmask | (1 << di.debuggerport->mp_SigBit)));

	SetExcept(oldexcept, ~0);
	
	if (sigrec & SIGBREAKF_CTRL_C)
	{
		Forbid();
		FreezeTask(childtask, &di.oldtaskstate);
		Permit();

		ourstatus->kind = TARGET_WAITKIND_STOPPED;
		ourstatus->value.sig = GDB_SIGNAL_TRAP;
		di.frozen = 1;

		ptid_t((int)childtask, 0, 0);
	}	

	while((de = (struct DebugEvent *)GetMsg(di.debuggerport)))
	{
#if 0
		kprintf("Debugger: Got a message DSISR %08lx SRR0 %08lx R1 %08lx R3 %08lx SegOffset %08lx\n", de->DSISR, de->SRR0, de->GPR[1], de->GPR[3], de->SegOffset);
#endif

		if(de->Stopped && de->Type != TASKEVENT_ADDCHILD)
		{
			di.srr0 = de->SRR0;
			di.r1 = de->GPR[1];
			ourstatus->kind = TARGET_WAITKIND_STOPPED;
			ESIGPRINT("Exception, child was stopped\n");

			// Enable objc printobject
			struct Task *me = FindTask(0);
			struct MyETask { uint8_t dummy[378]; void *obcontext; };
			struct MyETask *mee = (struct MyETask *)me->tc_ETask;
			struct MyETask *childe = (struct MyETask *)childtask->tc_ETask;
			mee->obcontext = childe->obcontext;
		}
		else
		{
			ourstatus->kind = TARGET_WAITKIND_IGNORE;
			ESIGPRINT("Exception, child was not stopped\n");
		}

		switch(de->Type)
		{
			case 2:
			{
				ESIGPRINT("Machine check interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_SEGV;
				break;
			}
			case 3:
			{
				ESIGPRINT("Child tried to %s address %08x at %08x\n", (de->DSISR&(1<<(31-6)))?"write to":"read from", de->DAR, de->SRR0);
				ourstatus->value.sig = GDB_SIGNAL_SEGV;
				break;
			}
			case 4:
			{
				ESIGPRINT("Instruction storage interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_SEGV;
				break;
			}
			case 5:
			{
				ESIGPRINT("External interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_INT;
				break;
			}
			case 6:
			{
				ESIGPRINT("Alignment storage interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_BUS;
				break;
			}
			case 7:
			{
				ESIGPRINT("Program interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_ILL;
				break;
			}
			case 8:
			{
				ESIGPRINT("Floating point interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_FPE;
				break;
			}
			case TASKEVENT_ADDCHILD:
			{
				struct Task* ChildOfDebugtask = de->ChildOfDebugtask;
				
				if(taskmonitor || autofreeze || follow_child)
				{
					Forbid();
					if(IsTask(ChildOfDebugtask))
						printf_unfiltered("New Child: '%s' Address: %p Frozen: %s\n", ((struct Node*)ChildOfDebugtask)->ln_Name, ChildOfDebugtask, ChildOfDebugtask->tc_State == TS_FROZEN ? "yes":"no");
					else
						printf_unfiltered("New Child: already removed! Address: %p\n", ChildOfDebugtask);
					
					if(follow_child)
					{
						LOCPRINT
						NewSetTaskAttrs(ChildOfDebugtask, &di.exceptionhandlerport, sizeof(di.exceptionhandlerport), TASKINFOTYPE_TRAPMSGPORT, TAG_DONE);
						NewSetTaskAttrs(di.childtask, NULL, 0, TASKINFOTYPE_TRAPMSGPORT, TAG_DONE);
						//ActivateTask(di.childtask, di.oldtaskstate);
						//di.childsegs = GetSegList((struct Process*)ChildOfDebugtask);
						
						follow_newchild(ptid_t((int)ChildOfDebugtask, 0, 0));
						di.prevchildtask = di.childtask;
						di.childtask = ChildOfDebugtask;
						ActivateTask(ChildOfDebugtask, di.oldchildtaskstate);
					}
					
					Permit();
				}
				break;
			}
			case TASKEVENT_REMCHILD:
			{

				printf_unfiltered("Rem Child: Address: %p\n", de->ChildOfDebugtask);
				di.childtask = di.orgchildtask;
				set_current_inferior(find_inferior_pid((int)di.childtask));
				set_current_program_space(current_inferior ()->pspace);
				NewSetTaskAttrs(di.childtask, &di.exceptionhandlerport, sizeof(di.exceptionhandlerport), TASKINFOTYPE_TRAPMSGPORT, TAG_DONE);

				if(taskmonitor)
				{
					struct Task* ChildOfDebugtask = de->ChildOfDebugtask;
					printf_unfiltered("Rem Child: Address: %p\n", ChildOfDebugtask);
				}
				break;
			}
			default:
			{
				ESIGPRINT("Unknown interrupt\n");
				ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
				break;
			}
		}
		FreeMem(de, sizeof(*de));

		if(ourstatus->kind == TARGET_WAITKIND_STOPPED)
			break;
	}

	sigsetmask(omask);
	
	if(di.programended)
	{
		printf_unfiltered("Debugger: Program ended with return code %ld\n", di.ProgramReturnCode);
		
		ShutdownDebugger(&di);
		
		//This situation isn't good but it might happen
		if(di.childtask != di.orgchildtask)
		{
			ESIGPRINT("GDB: Warning! Parent task seems to have quit before child\n");
			delete_thread (find_thread_ptid (ptid));
			di.childtask = di.orgchildtask;
		}
		
		ourstatus->kind = TARGET_WAITKIND_EXITED;
		ourstatus->value.integer = di.ProgramReturnCode;
		return ptid_t((int)di.orgchildtask, 0, 0);
		
	}
	
	ESIGPRINT("childtask=0x%p, kind=%d\n", childtask, ourstatus->kind);
	return ptid_t((int)childtask, 0, 0);
}

void morphos_nat_target::mourn_inferior()
{
	inf_child_target::mourn_inferior();
	di.childstarted = 0;
}

void morphos_nat_target::close()
{
}

void morphos_nat_target::kill ()
{
	printf_unfiltered("Killing the child task. Warning: this isn't safe\n");
	di.programended = 1;
	ShutdownDebugger(&di);
	int omask = sigsetmask(~0);
	RemTask((struct Task *)inferior_ptid.pid());
	sigsetmask(omask);
	target_mourn_inferior(inferior_ptid);
}


void morphos_nat_target::prepare_to_store (struct regcache *regcache)
{
}

void
morphos_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
{
	kprintf("resume ptid = %08x, step = %d, sig = %d, attaching=%d\n", ptid.pid(), step, sig, di.attaching);
	
	struct Task *childtask = (struct Task*)ptid.pid();
	
	if (childtask == (struct Task*)minus_one_ptid.pid())
		childtask = di.childtask;

	int omask = sigsetmask(~0);

	if(di.frozen == 1)
	{
		//kprintf("activating the frozen task, 0x%p\n", childtask);
		di.frozen = 0;
		ActivateTask(childtask, di.oldtaskstate);
		sigsetmask(omask);
		return;
	}	
	else if(di.attaching == 1)
	{
		//kprintf("activating the attached task, 0x%p\n", childtask);
		di.attaching = 2;
		di.childstarted = 1;
		ActivateTask(childtask, di.oldtaskstate);
		sigsetmask(omask);
		return;
	}
	else if(!di.childstarted)
	{
		di.childstarted = 1;
		Signal(childtask, SIGF_SINGLE);

		sigsetmask(omask);
		return;
	}
	
	if(sig != GDB_SIGNAL_0 && sig != GDB_SIGNAL_TRAP)
	{
		kprintf("Resuming with sig = %d might not be properly handled\n", sig);
		//sigsetmask(omask);
		//return;
	}
	
	if(step)
	{
		kprintf("Resuming in (hw)single step mode - not supported, and should not happen\n");
		sigsetmask(omask);
		return;
	}
	LOCPRINT
	Disable();
	childtask->tc_State = TS_READY;
	REMOVE(&childtask->tc_Node);
	Enqueue(&SysBase->TaskReady, &childtask->tc_Node);
	SysBase->SysFlags |= SF_SAR;
	Enable();
	sigsetmask(omask);
}

void morphos_nat_target::post_attach(int pid)
{
	morphos_relocate_sections(ptid_t(pid));
}

void morphos_relocate_sections(ptid_t ptid)
{
	int omask = sigsetmask(~0);

	if(symfile_objfile) 
	{
		struct section_offsets *new_offsets;
		APTR segstart;
		int numsections = symfile_objfile->num_sections;

		new_offsets = XALLOCAVEC(struct section_offsets, numsections);

/*		kprintf("sumsections=%d: text=%d, data=%d, bss=%d, rodata=%d\n", numsections,
		SECT_OFF_TEXT(symfile_objfile), SECT_OFF_DATA(symfile_objfile),
		SECT_OFF_BSS(symfile_objfile), SECT_OFF_RODATA(symfile_objfile));*/

		struct obj_section *osect;

		ALL_OBJFILE_OSECTIONS(symfile_objfile, osect)
		{
			int osect_idx;

			osect_idx = osect - symfile_objfile->sections;

			#if 0
			CORE_ADDR orig_addr, addr, offset;
			/* Current address of section.  */
			addr = obj_section_addr(osect);
			/* Offset from where this section started.  */
			offset = ANOFFSET(symfile_objfile->section_offsets, osect_idx);
			/* Original address prior to any past relocations.  */
			orig_addr = addr - offset;
			
			kprintf("bfd_name=%s, addr=0x%p, offset=0x%p, orig_addr=0x%p, idx=%d, filepos=0x%x\n", osect->the_bfd_section->name, addr, offset, orig_addr, osect_idx, osect->the_bfd_section->filepos);
			#endif
			
			for(int i=0; ;i++)
			{
				const char *segname;
				
				if(GetSegListAttrTags(di.childsegs, SEGLISTTAG_SEGSTART, &segstart, sizeof(segstart), SEGLISTTAG_ELF_SEGINDEX, i, TAG_DONE))
				{
					if(GetSegListAttrTags(di.childsegs, SEGLISTTAG_ELF_SEGNAME, &segname, sizeof(segname), SEGLISTTAG_ELF_SEGINDEX, i, TAG_DONE))
					{
						if(strcmp(segname, osect->the_bfd_section->name) == 0)
						{
							//kprintf("idx=%d, segname=%s, segstart=%p\n", osect_idx, segname, segstart);
							new_offsets->offsets[osect_idx] = (CORE_ADDR)segstart;
							break;
						}
					}
				}
				else
				{
					//kprintf("failed to get segstart for section '%s', idx=%d\n", osect->the_bfd_section->name, osect_idx);
					new_offsets->offsets[osect_idx] = (CORE_ADDR)0;
					break;
				}
			}
		}
			
		objfile_relocate(symfile_objfile, new_offsets);
	}
	else if(exec_bfd)
	{
		asection *asect;
		APTR segstart;

		for (asect = exec_bfd->sections; asect != NULL; asect = asect->next)
		{
			//ELF and BFD index numbers should be the same
			if(GetSegListAttrTags(di.childsegs, SEGLISTTAG_SEGSTART, &segstart, sizeof(segstart), SEGLISTTAG_ELF_SEGINDEX, asect->index, TAG_DONE))
			{
				//kprintf("Section '%s', idx=%d was relocated to 0x%p\n", asect->name, asect->index, segstart);
				exec_set_section_address(bfd_get_filename (exec_bfd), asect->index, (CORE_ADDR)segstart);
			}
			else
			{
				//kprintf("Failed to get segstart for section '%s', idx=%d\n", asect->name, asect->index);
				exec_set_section_address(bfd_get_filename (exec_bfd), asect->index, (CORE_ADDR)0);
			}
		}
	}

	sigsetmask(omask);
}

enum target_xfer_status
morphos_nat_target::xfer_partial (enum target_object object,
			       const char *annex, gdb_byte *readbuf,
			       const gdb_byte *writebuf, ULONGEST offset,
			       ULONGEST len,
			       ULONGEST *xfered_len)
{
	target_xfer_status ret = TARGET_XFER_UNAVAILABLE;
	LONGEST transferred = 0;
	
	if(inferior_ptid == null_ptid)
		return TARGET_XFER_EOF;
	
	if (object == TARGET_OBJECT_MEMORY)
	{
		if(writebuf)
		{
			unsigned long *dest = (unsigned long*)offset;
			//unsigned char *bt = (unsigned char*)writebuf;
			//kprintf("gdb: write target memory, %s bytes at %s, data=%x,%x,%x,%x\n", pulongest (len), core_addr_to_string(offset), bt[0], bt[1], bt[2], bt[3]);
			
			memcpy(dest, writebuf, len);
			CacheFlushDataInstArea(dest, len);
			transferred = len;
			ret = TARGET_XFER_OK;
		}
		
		if(readbuf)
		{
			unsigned long *src = (unsigned long*)offset;
			//unsigned char *bt = (unsigned char*)src;
			//kprintf("gdb: read target memory, %s bytes at %s, data=%x,%x,%x,%x\n", pulongest (len), core_addr_to_string(offset), bt[0], bt[1], bt[2], bt[3]);
			
			memcpy((void*)readbuf, src, len);
			transferred = len;
			ret = TARGET_XFER_OK;
			
		}
		
	}
	else
	{
		if (beneath () == NULL)
		{
		  /* This can happen when requesting the transfer of unsupported
			 objects before a program has been started (and therefore
			 with the current_target having no target beneath).  */
		  return TARGET_XFER_E_IO;
		}
		
		return beneath ()->xfer_partial (object, annex,
						   readbuf, writebuf, offset, len,
						   xfered_len);
	}

	*xfered_len = (ULONGEST) transferred;
	return ret;
}

const char *morphos_nat_target::pid_to_str (ptid_t ptid)
{
	static char buf[64];

	xsnprintf (buf, sizeof(buf), "Task %p", (void*)ptid.pid());

	return buf;
}

static int getaddrpathseg(APTR addr, char *buf, size_t bufsize, BPTR *seglistptr)
{
	LONG rc = 0;

	if(addr)
	{
		ULONG seglist;
		STRPTR name;
		int omask = sigsetmask(~0);

		SysDebugLock();
		name = SysDebugFindSeg((ULONG)addr, &seglist, &seglist);
		if(buf && name && strchr(name, ':'))
		{
			size_t len = strlen(name);
			if(len < bufsize)
			{
				memcpy(buf, name, len + 1);
				if (seglistptr)
					*seglistptr = MKBADDR(seglist);
				rc = 1;
			}
		}
		else if(name && buf == NULL && seglistptr)
		{
			*seglistptr = MKBADDR(seglist);
			rc = 1;
		}
		SysDebugUnLock();
		sigsetmask(omask);
	}

	return rc;
}

static int getsegpath(BPTR seglist, char *buf, size_t bufsize)
{
	if (seglist)
	{
		return getaddrpathseg(BADDR(seglist + 1), buf, bufsize, NULL);
	}
	return 0;
}

static int getprocpath(struct Process *proc, char *buf, size_t bufsize)
{
	if(proc)
	{
		return getsegpath(GetSegList(proc), buf, bufsize);
	}
	return 0;
}

#if 0
static int getprocpath(struct Process *proc, char *buf, size_t bufsize)
{
	struct CommandLineInterface *cli;
	BPTR olddir, lock;
	struct Process *self;
	APTR oldwinptr;
	int omask;

	if (proc->pr_Task.tc_Node.ln_Type == NT_TASK || proc->pr_HomeDir == 0)
		return 0;

	omask = sigsetmask(~0);

	self = (struct Process *) FindTask(NULL);

	cli = (CommandLineInterface*)BADDR(proc->pr_CLI);
	if (cli)
	{
		const UBYTE *cmd = (const UBYTE*)BADDR(cli->cli_CommandName);
		if (cmd && cmd[0])
		{
			UBYTE tmp[cmd[0] + 1];

			memcpy(tmp, cmd + 1, cmd[0]);
			tmp[cmd[0]] = '\0';

			oldwinptr = self->pr_WindowPtr;
			self->pr_WindowPtr = (APTR) -1;
			olddir = CurrentDir(proc->pr_HomeDir);
			lock = Lock((CONST_STRPTR) tmp, ACCESS_READ);
		}
		else
		{
			sigsetmask(omask);
			return 0;
		}
	}
	else
	{
		struct ETask *et;
		CONST_STRPTR name;

		if (!(proc->pr_Task.tc_Flags & TF_ETASK))
		{
			sigsetmask(omask);
			return 0;
		}

		/* See if it's Ambient launched process */
		et = proc->pr_Task.tc_ETask;
		Forbid();
		if (et->Parent == NULL ||
		    (strncmp(et->Parent->tc_Node.ln_Name, "Ambient Thread ", 15) != 0 &&
		     strcmp(et->Parent->tc_Node.ln_Name, "Workbench") != 0))
		{
			Permit();
			sigsetmask(omask);
			return 0;
		}
		Permit();

		if (strncmp(proc->pr_Task.tc_Node.ln_Name, "Ambient Thread ", 15) == 0)
			name = "MOSSYS:Ambient/Ambient";
		else
			name = proc->pr_Task.tc_Node.ln_Name;

		oldwinptr = self->pr_WindowPtr;
		self->pr_WindowPtr = (APTR) -1;
		olddir = CurrentDir(proc->pr_HomeDir);
		lock = Lock(name, ACCESS_READ);
	}

	CurrentDir(olddir);

	if (lock)
	{
		LONG rc = NameFromLock(lock, buf, bufsize);
		UnLock(lock);
		self->pr_WindowPtr = oldwinptr;
		if (rc)
		{
			sigsetmask(omask);
			return 1;
		}
	}
	else
		self->pr_WindowPtr = oldwinptr;

	sigsetmask(omask);
	return 0;
}
#endif

char *morphos_nat_target::pid_to_exec_file(int pid)
{
	static char path[1024];
	static char parpath[1024];

	struct Process *proc = (struct Process *)pid;
	struct Process *parent = NULL;
	
	if(proc)
		parent = (struct Process*)(proc->pr_Task.tc_ETask->Parent);

	Forbid();
	
	/*if(proc && (struct Process*)(proc->pr_Task.tc_ETask->Parent))
		proc = (struct Process*)(proc->pr_Task.tc_ETask->Parent);*/
		
	kprintf("proc=0x%p, parent=0x%p\n", proc, parent);
	if(getprocpath(parent, parpath, sizeof(parpath)))
	{
		kprintf("parpath=%s\n", parpath);
	}

	if(getprocpath(proc, path, sizeof(path)))
	{
		kprintf("path=%s\n", path);
	}
	else
	{
		path[0] = '\0';
	}

	Permit();
	
	return path;
}

bool morphos_nat_target::info_proc (const char *args, enum info_proc_what what)
{

	return false;
}

static void
unfreeze_command (const char *args, int from_tty)
{
	struct Process *process = NULL;

	if (args == NULL)
		error ("unfreeze requires an argument (either the address or the name of the task)");
	
	if(args[0] == '0' && args[1] == 'x')
	{
		process = (struct Process *)strtol(args, 0, 0);
	}
	else
	{
		int nmatches;
		process = ProcessByName(args, &nmatches);
		if(nmatches == 0)
		{
			Permit();
			error("Could not find a process with the name '%s'\n", args);
		}
		else if(nmatches > 1)
		{
			Permit();
			error("Found several processes with the name '%s', please specify the address instead\n", args);
		}
	}
	
	if(di.oldchildtaskstate == 0)
	{
		printf_unfiltered("warning: assuming that the child task was previously in a ready state\n");
		di.oldchildtaskstate = TS_READY;
	}
	ActivateTask((struct Task*)process, di.oldchildtaskstate);
	di.oldchildtaskstate = 0;
}

void
_initialize_morphos_nat(void)
{
	add_inf_child_target(&the_morphos_nat_target);
	
	add_setshow_boolean_cmd("autofreeze", class_support,
			&autofreeze,
			"Set autofreezing of new MorphOS exec child tasks.",
			"Show autofreezing of new MorphOS exec child tasks.", NULL,
			NULL,
			NULL,
			&setlist, &showlist);
			
	add_setshow_boolean_cmd("taskmonitor", class_support,
			&taskmonitor,
			"Set monitoring of MorphOS exec child tasks.",
			"Show monitoring of MorphOS exec child tasks.", NULL,
			NULL,
			NULL,
			&setlist, &showlist);
			
	add_setshow_enum_cmd ("follow-addtask-mode", class_run,
			follow_addtask_mode_kind_names,
			&follow_addtask_mode_string, 
			"Set debugger response to a program adding a new MorphOS exec child task.",
			"Show debugger response to a program adding a new MorphOS exec child task.", _("\
			When a new MorphOS task or process is added, follow-newtaks-mode can be:\n\
			  parent  - the original task is debugged\n\
			  child   - the new task is debugged\n\
			The unfollowed task will continue to run.\n\
			By default, the debugger will follow the parent task."),
			NULL,
			show_follow_addtask_mode_string,
			&setlist, &showlist);
			
	add_com("unfreeze", class_run, unfreeze_command, 
	"Unfreeze a task that was previously frozen by GDB autofreeze functionality");
}
