#ifdef SUPERMODEL_DEBUGGER

#include "ConsoleDebugger.h"
#include "CPUDebug.h"
#include "CodeAnalyser.h"
#include "Label.h"

#include <string.h>

namespace Debugger
{
	CConsoleDebugger::CConsoleDebugger() : CDebugger(), m_listDism(0), m_listMem(0), m_analyseCode(true), 
		m_addrFmt(HexDollar), m_portFmt(Decimal), m_dataFmt(HexDollar),
		m_showLabels(true), m_labelsOverAddr(true), m_showOpCodes(false), m_memBytesPerRow(12)
	{
		//
	}

	void CConsoleDebugger::WaitCommand(CCPUDebug *cpu)
	{
		m_cpu = cpu;

		UINT32 pc = m_cpu->pc;
		m_listDism = (m_cpu->instrCount > 0 && pc > 10 * m_cpu->minInstrLen ? pc - 10 * m_cpu->minInstrLen : 0);

		char bpChr;
		char addrStr[20];
		char labelStr[13];
		char opCodes[50];
		int codesLen;
		char mnemonic[255];
		char operands[255];
		char cmd[255];
		
		for (;;)
		{
			// Get code analyser and if available analyse code now if required
			if (m_analyseCode)
			{
				CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser();
				if (analyser->NeedsAnalysis())
				{
					printf("Analysing %s...\n", m_cpu->name);
					analyser->AnalyseCode();
				}
			}
		
			// Get details for current PC address
			bool hasLabel;
			if (m_cpu->instrCount > 0)
			{
				m_cpu->FormatAddress(addrStr, pc);
				hasLabel = GetLabelText(labelStr, 12, pc);
				codesLen = m_cpu->Disassemble(pc, mnemonic, operands);
				FormatOpCodes(opCodes, pc, abs(codesLen));
			}
			else
			{
				addrStr[0] = '-';
				addrStr[1] = '\0';
				hasLabel = false;
				labelStr[0] = '\0';
				opCodes[0] = '-';
				opCodes[1] = '\0';
				codesLen = 0;
			}
			CBreakpoint *bp = m_cpu->GetBreakpoint(pc);
			bpChr = (bp != NULL ? bp->symbol : ' ');
		
			// Output command prompt
			printf("%s%c", m_cpu->name, bpChr);
			if (m_showLabels)
			{
				if (m_labelsOverAddr)
					printf("%-12s ", (hasLabel ? labelStr : addrStr));
				else
					printf("%s %-12s ", addrStr, labelStr);
			}
			else
				printf("%s ", addrStr);
			if (m_showOpCodes) 
				printf("[%s] ", opCodes);
			if (codesLen > 0)
				printf("%-*s %s > ", (int)m_cpu->maxMnemLen, mnemonic, operands);
			else
				printf("??? > ");
			fflush(stdout);

			// Wait for command
			gets(cmd);

			if (cmd[0] == '\0')
			{
				m_cpu->SetStepMode(StepInto);
				break;
			}

			char *token = strtok(cmd, " ");
			if (ProcessToken(token, cmd))
				break;

			pc = m_cpu->pc;
		}
	}

	// TODO - tidy up this function - it is a mess!
	bool CConsoleDebugger::ProcessToken(const char *token, const char *cmd)
	{
		UINT32 pc = m_cpu->pc;

		UINT32 addr;
		UINT16 portNum;
		char addrStr[50];
		char portNumStr[50];
		if (CheckToken(token, "x", "exit"))							// exit
		{
			puts("Exiting...");
			SetExit();
			return true;
		}
		else if (CheckToken(token, "n", "next"))					// next [COUNT]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				int count;
				if (!ParseInt(token, &count) || count <= 0)
				{
					puts("Enter a valid instruction count.");
					return false;
				}
				
				if (count > 1)
					printf("Running %d instructions.\n", count);
				m_cpu->SetCount(count);
			}
			else
				m_cpu->SetStepMode(StepInto);
			return true;
		}
		else if (CheckToken(token, "s", "stepover"))				// stepover
		{
			m_cpu->SetStepMode(StepOver);
			return true;
		}
		else if (CheckToken(token, "si", "stepinto"))				// stepinto
		{
			m_cpu->SetStepMode(StepInto);
			return true;
		}
		else if (CheckToken(token, "so", "stepout"))				// stepout
		{
			m_cpu->SetStepMode(StepOut);
			return true;
		}
		else if (CheckToken(token, "c", "continue"))				// continue [ADDR]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &addr))
				{
					puts("Enter a valid address.");
					return false;
				}

				m_cpu->FormatAddress(addrStr, addr);
				printf("Continuing until %s.\n", addrStr);
				m_cpu->SetUntil(addr);
			}
			else
				m_cpu->SetContinue();
			return true;
		}
		else if (CheckToken(token, "lc", "listcpus"))				// listcpus
		{
			ListCPUs();
		}
		else if (CheckToken(token, "sc", "switchcpu"))				// switchcpu (NAME|NUM)
		{	
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing CPU name or number.");
				return false;
			}

			CCPUDebug *cpu = NULL;
			int cpuNum;
			if (ParseInt(token, &cpuNum))
			{
				if (cpuNum >= 0 && cpuNum < (int)cpus.size())
					cpu = cpus[cpuNum];
			}
			if (cpu == NULL)
			{
				cpu = GetCPU(token);
				if (cpu == NULL)
				{
					puts("No CPU with that name or number.");
					return false;
				}
			}
			
			m_cpu = cpu;
			pc = cpu->pc;
			m_listDism = (cpu->instrCount > 0 && pc > 10 * cpu->minInstrLen ? pc - 10 * cpu->minInstrLen : 0);
			return false;
		}
		else if (CheckToken(token, "lr", "listregisters"))			// listregisters
		{
			ListRegisters();
		}
		else if (CheckToken(token, "le", "listexceptions"))			// listexceptions
		{
			ListExceptions();
		}
		else if (CheckToken(token, "li", "listinterrupts"))			// listinterrupts
		{
			ListInterrupts();
		}
		else if (CheckToken(token, "lo", "listios"))				// listios
		{
			ListIOs();
		}
		else if (CheckToken(token, "ln", "listregions"))			// listregions
		{
			ListRegions();
		}
		else if (CheckToken(token, "al", "addlabel"))				// addlabel ADDR NAME
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing label address.");
				return false;
			}
			else if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid label address.");
				return false;
			}

			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing label name.");
				return false;
			}
			const char *name = token;
			
			// Add label
			CLabel *label = m_cpu->AddLabel(addr, name);
			m_cpu->FormatAddress(addrStr, label->addr);
			printf("Label %s added at %s.\n", label->name, addrStr);
		}
		else if (CheckToken(token, "ll", "listlabels"))				// listlabels
		{
			// Parse arguments
			token = strtok(NULL, " ");

			bool customLabels;
			bool autoLabels;
			if (token != NULL)
			{
				if (CheckToken(token, "a", "all"))
				{
					customLabels = true;
					autoLabels = true;
				}
				else if (CheckToken(token, "c", "custom"))
				{
					customLabels = true;
					autoLabels = false;
				}
				else if (CheckToken(token, "u", "auto"))
				{
					customLabels = false;
					autoLabels = true;
				}
				else
				{
					puts("Enter a valid mode (a)ll, (c)ustom or a(u)to.");
					return false;
				}
			}
			else
			{
				customLabels = true;
				autoLabels = true;
			}

			ELabelFlags autoLabelFlags = (autoLabels ? (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine) : LFNone);
			ListLabels(customLabels, autoLabelFlags);
		}
		else if (CheckToken(token, "rl", "removelabel"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing mode (a)ll, (n)ame or a(d)dress.");
				return false;
			}

			if (CheckToken(token, "a", "all"))
			{
				m_cpu->RemoveAllLabels();
				puts("All labels removed.");
			}
			else if (CheckToken(token, "n", "name"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing label name.");
					return false;
				}

				// Remove label
				if (m_cpu->RemoveLabel(token))
					printf("Label [%s] removed.\n", token);
				else
					puts("Enter a valid label name.");
			}
			else if (CheckToken(token, "d", "address"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing label address.");
					return false;
				}
				if (!ParseAddress(m_cpu, token, &addr))
				{
					puts("Enter a valid label address.");
					return false;
				}

				// Remove label
				m_cpu->FormatAddress(addrStr, addr);
				if (m_cpu->RemoveLabel(addr))
					printf("Label at address %s removed.\n", addrStr);
				else
					printf("No label at address %s.\n", addrStr);
			}
			else
			{
				puts("Enter a valid mode (a)ll, (n)ame or a(d)dress.");
				return false;
			}
		}
		else if (CheckToken(token, "ac", "addcomment"))				// addcomment ADDR TEXT...
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing comment address.");
				return false;
			}
			else if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid comment address.");
				return false;
			}

			char text[255];
			text[0] = '\0';
			token = strtok(NULL, " ");
			while (token != NULL)
			{
				size_t len = strlen(text);
				if (len + strlen(token) > 253)
					break;
				if (len > 0)
					strcat(text, " ");
				strcat(text, token);
				token = strtok(NULL, " ");
			}
			if (text[0] == '\0')
			{
				puts("Missing comment text.");
				return false;
			}

			// Add comment
			CComment *comment = m_cpu->AddComment(addr, text);
			m_cpu->FormatAddress(addrStr, comment->addr);
			printf("Comment added at %s.\n", addrStr);
		}
		else if (CheckToken(token, "rc", "removecomment"))			// removecomment ADDR
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing comment address.");
				return false;
			}
			else if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid comment address.");
				return false;
			}

			m_cpu->FormatAddress(addrStr, addr);
			if (m_cpu->RemoveComment(addr))
				printf("Comment at address %s removed.\n", addrStr);
			else
				printf("No comment at address %s.\n", addrStr);
		}
		else if (CheckToken(token, "t", "trap") ||					// addtrap (e|i) ID
			CheckToken(token, "at", "addtrap"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing type (e)xception or (i)interrupt");
				return false;
			}
			
			if (CheckToken(token, "e", "exception"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing exception id.");
					return false;
				}
				CException *ex = m_cpu->GetException(token);
				if (ex == NULL)
				{
					puts("Enter a valid exception id.");
					return false;
				}

				if (!ex->trap)
				{
					ex->trap = true;
					printf("Trap added for exceptions of type %s (%s).\n", ex->id, ex->name);
				}
			}
			else if (CheckToken(token, "i", "interrupt"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing interrupt id.");
					return false;
				}
				CInterrupt *in = m_cpu->GetInterrupt(token);
				if (in == NULL)
				{
					puts("Enter a valid interrupt id.");
					return false;
				}

				if (!in->trap)
				{
					in->trap = true;
					printf("Trap added for interrupts of type %s (%s).\n", in->id, in->name);
				}
			}
			else
			{
				puts("Enter valid type (e)xception or (i)interrupt.");
				return false;
			}
		}
		else if (CheckToken(token, "rt", "removetrap"))				// removetrap (e|i) (a|i) ID
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing type (e)xception or (i)interrupt");
				return false;
			}
			
			if (CheckToken(token, "e", "exception"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing mode (a)ll or (i)d.");
					return false;
				}

				if (CheckToken(token, "a", "all"))
				{
					bool removed = false;
					for (vector<CException*>::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++)
					{
						if ((*it)->trap)
							(*it)->trap = false;
						removed = true;
					}
					if (removed)
						puts("All exception traps removed.");
				}
				else if (CheckToken(token, "i", "id"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing exception id.");
						return false;
					}

					CException *ex = m_cpu->GetException(token);
					if (ex != NULL)
					{
						if (ex->trap)
						{
							ex->trap = false;
							printf("Trap for exception %s removed.\n", ex->name);
						}
					}
					else
						puts("Enter a valid exception id.");
				}
				else
				{
					puts("Enter valid mode (a)ll or (i)d.");
					return false;
				}
			}
			else if (CheckToken(token, "i", "interrupt"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing mode (a)ll or (i)d.");
					return false;
				}

				if (CheckToken(token, "a", "all"))
				{
					bool removed = false;
					for (vector<CInterrupt*>::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++)
					{
						if ((*it)->trap)
							(*it)->trap = false;
						removed = true;
					}
					if (removed)
						puts("All interrupt traps removed.");
				}
				else if (CheckToken(token, "i", "id"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing exception id.");
						return false;
					}

					CInterrupt *in = m_cpu->GetInterrupt(token);
					if (in != NULL)
					{
						if (in->trap)
						{
							in->trap = false;
							printf("Trap for interrupt %s removed.\n", in->name);
						}
					}
					else
						puts("Enter a valid interrupt id.");
				}
				else
				{
					puts("Enter valid mode (a)ll or (i)d.");
					return false;
				}
			}
			else
			{
				puts("Enter valid type (e)xception or (i)interrupt.");
				return false;
			}
		}
		else if (CheckToken(token, "w", "memwatch") ||				// addmemwatch ADDR [(n|r|w|rw) [SIZE [(s|c|m|h|f) [ARGS]]]]
			CheckToken(token, "aw", "addmemwatch"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing address.");
				return false;
			}
			if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid address.");
				return false;
			}
			bool read;
			bool write;
			token = strtok(NULL, " ");
			if (token == NULL || CheckToken(token, "n", "none"))
			{
				read = false;
				write = false;
			}
			else if (CheckToken(token, "r", "read"))
			{
				read = true;
				write = false;
			}
			else if (CheckToken(token, "w", "write"))
			{
				read = false;
				write = true;
			}
			else if (CheckToken(token, "rw", "read/write") || CheckToken(token, "wr", "write/read"))
			{
				read = true;
				write = true;
			}
			else
			{
				puts("Enter valid read/write flags (n)one, (r)ead, (w)rite or (rw)ead/write.");
				return false;
			}
			unsigned size;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseDataSize(token, size))
					return false;
			}
			else
				size = 1;

			// Add mem watch
			CWatch *watch;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (CheckToken(token, "s", "simple"))
					watch = m_cpu->AddSimpleMemWatch(addr, size, read, write);
				else if (CheckToken(token, "c", "count"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing watch count.");
						return false;
					}
					int count;
					if (!ParseInt(token, &count) || count <= 0)
					{
						puts("Enter a valid watch count.");
						return false;
					}
					watch = m_cpu->AddCountMemWatch(addr, size, read, write, count);
				}
				else if (CheckToken(token, "m", "match"))
				{
					vector<UINT64> dataSeq;
					while ((token = strtok(NULL, " ")) != NULL)
					{
						UINT64 data;
						if (m_cpu->ParseData(token, size, &data))
							dataSeq.push_back(data);
					}
					if (dataSeq.size() == 0)
					{
						const char *sizeStr = GetSizeString(size);
						printf("Enter a sequence of %s data to match.", sizeStr);
						return false;
					}
					watch = m_cpu->AddMatchMemWatch(addr, size, read, write, dataSeq);
				}
				else if (CheckToken(token, "r", "capture"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing maximum capture length.");
						return false;
					}
					int maxLen;
					if (!ParseInt(token, &maxLen) || maxLen <= 0)
					{
						puts("Enter a valid maximum capture length.");
						return false;
					}
					watch = m_cpu->AddCaptureMemWatch(addr, size, read, write, maxLen);
				}
				else if (CheckToken(token, "p", "print"))
					watch = m_cpu->AddPrintMemWatch(addr, size, read, write);
				else
				{
					puts("Enter a valid watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint.");
					return false;
				}
			}
			else
				watch = m_cpu->AddSimpleMemWatch(addr, size, read, write);	
			m_cpu->FormatAddress(addrStr, watch->addr);
			printf("Memory watch [%s] added at %s.\n", watch->type, addrStr);
		}
		else if (CheckToken(token, "lw", "listmemwatches"))
		{
			ListMemWatches();
		}
		else if (CheckToken(token, "rw", "removememwatch"))			// removememwatch (a|n|d) [NUM|ADDR]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing mode (a)ll, (n)umber or a(d)dress.");
				return false;
			}
			
			if (CheckToken(token, "a", "all"))
			{
				// Remove all memory watches
				vector<CWatch*> watches;
				GetAllMemWatches(watches);
				for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++)
					m_cpu->RemoveWatch(*it);
				puts("All memory watches removed.");
			}
			else if (CheckToken(token, "n", "number"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing watch number.");
					return false;
				}

				// Remove memory watch by number
				vector<CWatch*> watches;
				GetAllMemWatches(watches);
				int number;
				if (ParseInt(token, &number) && number >= 0 && number < watches.size())
				{
					m_cpu->RemoveWatch(watches[number]);
					printf("Memory watch %d removed.\n", number);
				}
				else
					puts("Enter a valid watch number.");
			}
			else if (CheckToken(token, "d", "address"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing address.");
					return false;
				}
				if (!ParseAddress(m_cpu, token, &addr))
				{
					puts("Enter a valid address.");
					return false;
				}

				// Remove watch
				m_cpu->FormatAddress(addrStr, addr);
				if (m_cpu->RemoveMemWatch(addr, 1))
					printf("Memory watch at address %s removed.\n", addrStr);
				else
					printf("No memory watch at address %s.\n", addrStr);
			}
			else
			{
				puts("Enter a valid mode (a)ll, (n)umber or a(d)dress.");
				return false;
			}
		}
		else if (CheckToken(token, "pw", "portwatch") ||			// addportwatch PORT [(i|o|io) [(s|c|m|h|f) [ARGS]]]
			CheckToken(token, "apw", "addportwatch"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing port number.");
				return false;
			}
			if (!m_cpu->ParsePortNum(token, &portNum))
			{
				puts("Enter a valid port number.");
				return false;
			}
			bool input = false;
			bool output = false;
			token = strtok(NULL, " ");
			if (token == NULL || CheckToken(token, "n", "none"))
			{
				input = false;
				output = false;
			}
			else if (CheckToken(token, "i", "input"))
			{
				input = true;
				output = false;
			}
			else if (CheckToken(token, "o", "output"))
			{
				input = false;
				output = true;
			}
			else if (CheckToken(token, "io", "input/output") || CheckToken(token, "oi", "output/input"))
			{
				input = true;
				output = true;
			}
			else
			{
				puts("Enter valid input/output flags (n)one, (i)nput, (o)utput or (io)nput/output.");
				return false;
			}

			// Add watch
			CPortIO *port = m_cpu->GetPortIO(portNum);
			CWatch *watch;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (CheckToken(token, "s", "simple"))
					watch = port->AddSimpleWatch(input, output);
				else if (CheckToken(token, "c", "count"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing watch count.");
						return false;
					}
					int count;
					if (!ParseInt(token, &count) || count <= 0)
					{
						puts("Enter a valid watch count.");
						return false;
					}
					watch = port->AddCountWatch(input, output, count);
				}
				else if (CheckToken(token, "m", "match"))
				{
					vector<UINT64> dataSeq;
					while ((token = strtok(NULL, " ")) != NULL)
					{
						UINT64 data;
						if (m_cpu->ParseData(token, port->dataSize, &data))
							dataSeq.push_back(data);
					}
					if (dataSeq.size() == 0)
					{
						printf("Enter a sequence of %s to match.", GetSizeString(port->dataSize));
						return false;
					}
					watch = port->AddMatchWatch(input, output, dataSeq);
				}
				else if (CheckToken(token, "r", "capture"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing maximum capture length.");
						return false;
					}
					int maxLen;
					if (!ParseInt(token, &maxLen) || maxLen <= 0)
					{
						puts("Enter a valid maximum capture length.");
						return false;
					}
					watch = port->AddCaptureWatch(input, output, maxLen);
				}
				else if (CheckToken(token, "p", "print"))
					watch = port->AddPrintWatch(input, output);
				else
				{
					puts("Enter a valid I/O watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint.");
					return false;
				}
			}
			else
				watch = port->AddSimpleWatch(input, output);	

			m_cpu->FormatPortNum(portNumStr, portNum);
			printf("Port watch (%s) added for port %u.\n", watch->type, portNumStr);
		}
		else if (CheckToken(token, "lpw", "listportwatches"))		// listportwatches
		{
			ListPortWatches();
		}
		else if (CheckToken(token, "rpw", "removeportwatch"))		// removeportwatch (a|n|p) [NUM|PORT]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing mode (a)ll, (n)umber or (p)ort.");
				return false;
			}
			
			if (CheckToken(token, "a", "all"))
			{
				// Remove all port watches
				vector<CWatch*> watches;
				GetAllPortWatches(watches);
				for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++)
					m_cpu->RemoveWatch(*it);
				puts("All port watches removed.");
			}
			else if (CheckToken(token, "n", "number"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing watch number.");
					return false;
				}

				// Remove port watch by number
				vector<CWatch*> watches;
				GetAllPortWatches(watches);
				int number;
				if (ParseInt(token, &number) && number >= 0 && number < watches.size())
				{
					m_cpu->RemoveWatch(watches[number]);
					printf("Port watch %d removed.\n", number);
				}
				else
					puts("Enter a valid port watch number.");
			}
			else if (CheckToken(token, "p", "port"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing port number.");
					return false;
				}
				if (!m_cpu->ParsePortNum(token, &portNum))
				{
					puts("Enter a valid port number.");
					return false;
				}

				// Remove port watch by port number
				CPortIO *port = m_cpu->GetPortIO(portNum);
				m_cpu->FormatPortNum(portNumStr, portNum);
				if (port->RemoveWatch())
					printf("Port watch for port %s removed.\n", portNumStr);
				else
					printf("No port watch for port %s.\n", portNumStr);
			}
			else
			{
				puts("Enter a valid mode (a)ll, (n)umber or (p)ort.");
				return false;
			}
		}
		else if (CheckToken(token, "b", "breakpoint") ||			// addbreakpoint [ADDR [(s|c) [ARGS]]] 
			CheckToken(token, "ab", "addbreakpoint"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			CBreakpoint *bp;
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &addr))
				{
					puts("Enter a valid address.");
					return false;
				}
			
				token = strtok(NULL, " ");
				if (token != NULL)
				{
					if (CheckToken(token, "s", "simple"))
						bp = m_cpu->AddSimpleBreakpoint(addr);
					else if (CheckToken(token, "c", "count"))
					{
						token = strtok(NULL, " ");
						if (token == NULL)
						{
							puts("Missing breakpoint count.");
							return false;
						}
						int count;
						if (!ParseInt(token, &count) || count <= 0)
						{
							puts("Enter a valid breakpoint count.");
							return false;
						}

						bp = m_cpu->AddCountBreakpoint(addr, count);
					}
					else
					{
						puts("Enter a valid breakpoint type (s)imple or (c)ount.");
						return false;
					}
				}
				else
					bp = m_cpu->AddSimpleBreakpoint(addr);
			}
			else
			{
				if (m_cpu->instrCount == 0)
				{
					puts("Cannot set breakpoint at current position as processor not ready.");
					return false;
				}
				bp = m_cpu->AddSimpleBreakpoint(pc);
			}

			m_cpu->FormatAddress(addrStr, bp->addr);
			printf("Breakpoint [%s] added at %s.\n", bp->type, addrStr);
		}
		else if (CheckToken(token, "lb", "listbreakpoints"))		// listbreakpoints
		{
			ListBreakpoints();
		}
		else if (CheckToken(token, "rb", "removebreakpoint"))		// removebreakpoint [(a|n|d) [NUM|ADDR]]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (CheckToken(token, "a", "all"))
				{
					m_cpu->RemoveAllBreakpoints();
					puts("All breakpoints removed.");
				}
				else if (CheckToken(token, "n", "number"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing breakpoint number.");
						return false;
					}

					// Remove breakpoint
					int number;
					if (ParseInt(token, &number) && number >= 0 && number < m_cpu->bps.size())
					{
						m_cpu->RemoveBreakpoint(m_cpu->bps[number]);
						printf("Breakpoint %d removed.\n", number);
					}
					else
						puts("Enter a valid breakpoint number.");
				}
				else if (CheckToken(token, "d", "address"))
				{
					token = strtok(NULL, " ");
					if (token == NULL)
					{
						puts("Missing breakpoint address.");
						return false;
					}
					if (!ParseAddress(m_cpu, token, &addr))
					{
						puts("Enter a valid breakpoint address.");
						return false;
					}

					// Remove breakpoint
					m_cpu->FormatAddress(addrStr, addr);
					if (m_cpu->RemoveBreakpoint(addr))
						printf("Breakpoint at address %s removed.\n", addrStr);
					else
						printf("No breakpoint at address %s.\n", addrStr);
				}
				else
					puts("Enter a valid mode (a)ll, (n)umber or a(d)dress.");
			}
			else
			{
				if (m_cpu->RemoveBreakpoint(pc))
					puts("Current breakpoint removed.");
			}
		}
		else if (CheckToken(token, "m", "monitor") ||				// addmonitor REG
			CheckToken(token, "am", "addmonitor"))
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing register name.");
				return false;
			}
			CRegMonitor *regMon = m_cpu->AddRegMonitor(token);
			if (regMon == NULL)
			{
				puts("Enter a valid register name.");
				return false;
			}
			
			printf("Monitor added to register %s.\n", regMon->reg->name);
		}
		else if (CheckToken(token, "lm", "listmonitors"))			// listmonitors
		{
			ListMonitors();
		}
		else if (CheckToken(token, "rm", "removemonitor"))			// removemonitor (a|n) [REG]
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing mode (a)ll or (n)ame.");
				return false;
			}

			if (CheckToken(token, "a", "all"))
			{
				m_cpu->RemoveAllRegMonitors();
				puts("All register monitors removed.");
			}
			else if (CheckToken(token, "n", "name"))
			{
				token = strtok(NULL, " ");
				if (token == NULL)
				{
					puts("Missing register name.");
					return false;
				}

				CRegister *reg = m_cpu->GetRegister(token);
				if (reg != NULL)
				{
					m_cpu->RemoveRegMonitor(reg->name);
					printf("Monitor for register %s removed.\n", reg->name);
				}
				else
					puts("Enter a valid register name.");
			}
			else
				puts("Enter a valid mode (a)ll or (n)ame.");
		}
		else if (CheckToken(token, "l", "list") ||					// listdisassem [START [END]]
			CheckToken(token, "ld", "listdisassem"))
		{
			// Get start address
			UINT32 start;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &start))
				{
					puts("Enter a valid start address.");
					return false;
				}
			}
			else
			{
				// Default is end of last listing
				start = m_listDism;
			}

			// Get end address
			UINT32 end;
			unsigned numInstrs;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &end))
				{
					puts("Enter a valid end address.");
					return false;
				}
				numInstrs = 0xFFFFFFFF;
			}
			else
			{
				// Default is 20 instructions after start
				end = 0xFFFFFFFF;
				numInstrs = 20;
			}

			// List the disassembled code
			m_listDism = ListDisassembly(start, end, numInstrs);
		}
		else if (CheckToken(token, "ly", "listmemory"))				// listmemory [START [END]]
		{
			// Get start address
			UINT32 start;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &start))
				{
					puts("Enter a valid start address.");
					return false;
				}
			}
			else
			{
				// Default is end of last listing
				start = m_listMem;
			}

			// Get end address
			UINT32 end;
			token = strtok(NULL, " ");
			if (token != NULL)
			{
				if (!ParseAddress(m_cpu, token, &end))
				{
					puts("Enter a valid end address.");
					return false;
				}
			}
			else
				// Default is 8 rows after start
				end = start + 8 * m_memBytesPerRow;

			// List the code
			m_listMem = ListMemory(start, end, m_memBytesPerRow);
		}
		else if (CheckToken(token, "p", "print"))					// print EXPR
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{	
				puts("Missing argument.");
				return false;
			}

			UINT64 data;
			char str[255];
			const char *result;
			if (ParseAddress(m_cpu, token, &addr))
			{
				m_cpu->FormatAddress(str, addr, true, LFAll);
				if (stricmp(token, str) == 0)
					m_cpu->FormatAddress(str, addr, false, LFNone);
				result = str;
			}
			else if (m_cpu->ParseData(token, 8, &data))
			{
				m_cpu->FormatData(str, 8, data);
				result = str;
			}
			else 
				result = token;	
			printf("%s = %s\n", token, result);
		}
		else if (CheckToken(token, "pr", "printregister"))			// printregister REG
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing register name.");
				return false;
			}
			CRegister *reg = m_cpu->GetRegister(token);
			if (reg == NULL)
			{
				printf("No register called '%s'.\n", token);
				return false;
			}

			char valStr[50];
			reg->GetValue(valStr);
			printf("Register %s = %s\n", reg->name, valStr);
		}
		else if (CheckToken(token, "sr", "setregister"))			// setregister REG VALUE
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing register name.");
				return false;
			}
			CRegister *reg = m_cpu->GetRegister(token);
			if (reg == NULL)
			{
				printf("No register called '%s'.\n", token);
				return false;
			}
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing value to set.");
				return false;
			}

			if (!reg->SetValue(token))
			{
				printf("Unable to set value of register %s.\n", reg->name);
				return false;
			}

			char valStr[50];
			reg->GetValue(valStr);
			printf("Set register %s to %s.\n", reg->name, valStr);
		}
		else if (CheckToken(token, "py", "printmemory"))			// printmemory ADDR SIZE
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing address.");
				return false;
			}
			if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid address.");
				return false;
			}

			token = strtok(NULL, " ");
			unsigned dataSize = -1;
			if (token != NULL)
			{
				if (!ParseDataSize(token, dataSize))
					return false;
			}
			else
				dataSize = 1;
			
			const char *sizeStr = GetSizeString(dataSize);
			UINT64 data = m_cpu->ReadMem(addr, dataSize);
			char dataStr[50];
			m_cpu->FormatData(dataStr, dataSize, data);
			m_cpu->FormatAddress(addrStr, addr);
			printf("%s data at %s = %s.\n", sizeStr, addrStr, dataStr);
		}
		else if (CheckToken(token, "sy", "setmemory"))				// setmemory ADDR SIZE VALUE
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing address.");
				return false;
			}
			if (!ParseAddress(m_cpu, token, &addr))
			{
				puts("Enter a valid address.");
				return false;
			}
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing a size (b)yte, (w)ord, (l)ong or (v)erylong or number (1, 2, 4 or 8).");
				return false;
			}
			
			unsigned dataSize;
			if (!ParseDataSize(token, dataSize))
				return false;
			
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing value to set.");
				return false;
			}
			const char *sizeStr = GetSizeString(dataSize);
			UINT64 data;
			if (!m_cpu->ParseData(token, dataSize, &data))
			{
				printf("Enter a valid %s value.\n", sizeStr);
				return false;
			}	
			
			m_cpu->WriteMem(addr, dataSize, data);
			char dataStr[50];
			m_cpu->FormatData(dataStr, dataSize, data);
			m_cpu->FormatAddress(addrStr, addr);
			printf("Set %s data at %s to %s.\n", sizeStr, addrStr, dataStr);
		}
		else if (CheckToken(token, "pp", "printio"))				// printio PORT
		{
			// TODO - read I/O
		}
		else if (CheckToken(token, "sp", "sendio"))					// sendio PORT VALUE
		{
			// TODO - send I/O
		}
		else if (CheckToken(token, "cfg", "configure"))				// configure ...
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				// If no arguments, then print out current configuration
				printf("Configuration:\n");
				printf(" %-20s %-12s %s\n", "Code Analysis",     (m_analyseCode ? "On" : "Off"), "(a)nalysis"); 
				printf(" %-20s %-12s %s\n", "Address Format",    GetFmtConfig(m_addrFmt),        "a(d)dressfmt"); 
				printf(" %-20s %-12s %s\n", "Port Format",       GetFmtConfig(m_portFmt),        "(p)ortfmt"); 
				printf(" %-20s %-12s %s\n", "Data Format",       GetFmtConfig(m_dataFmt),        "da(t)afmt");
				printf(" %-20s %-12s %s\n", "Show Labels",       (m_showLabels  ? "On" : "Off"), "show(l)abels");
				printf(" %-20s %-12s %s\n", "Show Opcodes",      (m_showOpCodes ? "On" : "Off"), "show(o)pcodes");
				printf(" %-20s %-12u %s\n", "Mem Bytes Per Row", m_memBytesPerRow,               "mem(b)ytesrow");
				return false;
			}

			if (CheckToken(token, "a", "analysis"))
			{
				token = strtok(NULL, " ");
				SetBoolConfig(token, m_analyseCode);
			}
			else if (CheckToken(token, "d", "addressfmt"))
			{
				token = strtok(NULL, " ");
				SetFmtConfig(token, m_addrFmt);
			}
			else if (CheckToken(token, "p", "portfmt"))
			{
				token = strtok(NULL, " ");
				SetFmtConfig(token, m_portFmt);
			}
			else if (CheckToken(token, "t", "datafmt"))
			{            
				token = strtok(NULL, " ");
				SetFmtConfig(token, m_dataFmt);
			}
			else if (CheckToken(token, "l", "showlabels"))
			{
				token = strtok(NULL, " ");
				SetBoolConfig(token, m_showLabels);
			}
			else if (CheckToken(token, "o", "showopcodes"))
			{
				token = strtok(NULL, " ");
				SetBoolConfig(token, m_showOpCodes);
			}
			else if (CheckToken(token, "b", "membytesrow"))
			{
				token = strtok(NULL, " ");
				SetNumConfig(token, m_memBytesPerRow);
			}
			else
			{
				puts("Enter a valid option (a)nalysis, a(d)dressfmt, (p)ortfmt, da(t)afmt.");
				return false;
			}
		}
		else if (CheckToken(token, "ls", "loadstate"))				// loadstate FILENAME
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing filename.");
				return false;
			}

			if (LoadState(token))
				printf("Debugger state successfully loaded from <%s>\n", token);
			else
				printf("Unable to load debugger state from <%s>\n", token);
		}
		else if (CheckToken(token, "ss", "savestate"))				// savestate FILENAME
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing filename.");
				return false;
			}

			if (SaveState(token))
				printf("Debugger state successfully saved to <%s>\n", token);
			else
				printf("Unable to save debugger state to <%s>\n", token);
		}
		else if (CheckToken(token, "h", "help"))					// help
		{
			const char *fmt = " %-5s %-15s %s\n";
			printf("Debugger Commands:\n");
			printf(fmt, "x",    "exit",           "");
			printf(fmt, "n",    "next",           "[COUNT]");
			printf(fmt, "s",    "stepover",       "");
			printf(fmt, "si",   "stepinto",       "");
			printf(fmt, "so",   "stepout",        "");
			printf(fmt, "si",   "stepinto",       "");
			printf(fmt, "si",   "continue",       "[ADDR]");
			printf(fmt, "lc",   "listcpus",       "");
			printf(fmt, "sc",   "switchcpu",      "(NAME|NUM)");
			printf(fmt, "lr",   "listregisters",  "");
			printf(fmt, "le",   "listexceptions", "");
			printf(fmt, "li",   "listinterrupts", "");
			printf(fmt, "lo",   "listios",        "");
			printf(fmt, "ln",   "listregions",    "");
			printf(fmt, "al",   "addlabel",       "ADDR NAME");
			printf(fmt, "ll",   "listlabels",     "[(a)ll|(c)ustom|a(u)to]");
			printf(fmt, "rl",   "removelabel",    "((a)ll|(n)ame|a(d)dress) [NAME|ADDR]");
			printf(fmt, "ac",   "addcomment",     "ADDR TEXT...");
			printf(fmt, "rc",   "removecomment",  "ADDR");
			printf(fmt, "t/at", "addtrap",        "(e|i) ID");
			// TODO - finish
		}
		else
			printf("Unknown command '%s'.\n", token);
		return false;
	}

	bool CConsoleDebugger::CheckToken(const char *str, const char *simple, const char *full)
	{
		return (stricmp(str, simple) == 0 || stricmp(str, full) == 0);
	}

	void CConsoleDebugger::Truncate(char *dst, size_t maxLen, const char *src)
	{
		strncpy(dst, src, maxLen);
		if (strlen(src) > maxLen)
			strncpy(&dst[maxLen - 3], "...", 3);
		dst[maxLen] = '\0';
	}

	void CConsoleDebugger::FormatOpCodes(char *str, int addr, int codesLen)
	{
		char *p = str;
		int i = 0;
		while (i < codesLen)
		{
			UINT8 opCode = (UINT8)m_cpu->ReadMem(addr++, 1);
			sprintf(p, "%02X", opCode);
			p += 2;
			i++;
		}
		while (i++ < m_cpu->maxInstrLen)
		{
			*p++ = ' ';
			*p++ = ' ';
		}
		*p = '\0';
	}

	bool CConsoleDebugger::GetLabelText(char *str, int maxLen, UINT32 addr)
	{
		char labelStr[255];
				
		CLabel *label = m_cpu->GetLabel(addr);
		if (label != NULL)
		{
			Truncate(str, maxLen, label->name);
			return true;
		}

		if (m_analyseCode)
		{
			CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser();
			CAutoLabel *autoLabel = analyser->analysis->GetAutoLabel(addr);
			if (autoLabel != NULL && autoLabel->GetLabel(labelStr))
			{
				Truncate(str, maxLen, labelStr);		
				return true;
			}
		}
		
		str[0] = '\0';
		return false;
	}

	bool CConsoleDebugger::ParseAddress(CCPUDebug *cpu, const char *str, UINT32 *addr)
	{
		if (cpu->instrCount > 0 && CheckToken(str, "-", "current"))
		{
			*addr = cpu->pc;
			return true;
		}
		return cpu->ParseAddress(str, addr);
	}

	bool CConsoleDebugger::ParseDataSize(const char *str, unsigned &dataSize)
	{
		int num = -1;
		ParseInt(str, &num);
		if (CheckToken(str, "b", "byte") || num == 1)
		{
			dataSize = 1;
			return true;
		}
		else if (CheckToken(str, "w", "word") || num == 2)
		{
			dataSize = 2;
			return true;
		}
		else if (CheckToken(str, "l", "long") || num == 4)
		{
			dataSize = 4;
			return true;
		}
		else if (CheckToken(str, "v", "verylong") || num == 8)
		{
			dataSize = 8;
			return true;
		}
		else
		{
			puts("Enter a valid size (b)yte, (w)ord, (l)ong or (v)erylong or a number 1, 2, 4 or 8.");
			return false;
		}
	}

	bool CConsoleDebugger::SetBoolConfig(const char *str, bool &cfg)
	{
		if (str == NULL)
		{
			printf("Current setting: %-12s\n", (cfg ? "On" : "Off"));
			puts("Change setting with (o)n, o(f)f.");
			return false;
		}

		if (CheckToken(str, "o", "on"))
		{
			cfg = true;
			printf("Changed setting: On\n");
			ApplyConfig();
			return true;
		}
		else if (CheckToken(str, "f", "off"))
		{
			cfg = false;
			printf("Changed setting: Off\n");
			ApplyConfig();
			return true;
		}
		else
		{
			puts("Enter a valid setting (o)n, o(f)f.");
			return false;
		}
	}

	bool CConsoleDebugger::SetNumConfig(const char *str, unsigned &cfg)
	{
		if (str == NULL)
		{
			printf("Current setting: %-12u\n", cfg);
			return false;
		}

		int number;
		if (!ParseInt(str, &number))
		{
			puts("Enter a valid number.");
			return false;
		}

		cfg = (unsigned)number;
		printf("Changed setting: %-12u\n", cfg);
		ApplyConfig();
		return true;
	}

	const char *CConsoleDebugger::GetFmtConfig(EFormat fmt)
	{
		switch (fmt)
		{
			case Hex0x:     return "Hex (0x00)";
			case HexDollar: return "Hex ($00)";
			case HexPostH:  return "Hex (00h)";
			case Decimal:   return "Decimal";
			case Binary:    return "Binary";
			default:        return "-";
		}
	}

	bool CConsoleDebugger::SetFmtConfig(const char *str, EFormat &cfg)
	{
		if (str == NULL)
		{
			printf("Current setting: %-12s\n", GetFmtConfig(cfg));
			puts("Change setting with (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.");
			return false;
		}

		if      (CheckToken(str, "h", "hex"))       cfg = Hex;
		else if (CheckToken(str, "z", "hexzero"))   cfg = Hex0x;
		else if (CheckToken(str, "l", "hexdollar")) cfg = HexDollar;	
		else if (CheckToken(str, "p", "hexposth"))  cfg = HexPostH;
		else if (CheckToken(str, "d", "decimal"))   cfg = Decimal;
		else if (CheckToken(str, "b", "binary"))    cfg = Binary;
		else
		{
			puts("Enter a valid setting (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.");
			return false;
		}

		printf("Changed setting: %-12s\n", GetFmtConfig(cfg));
		ApplyConfig();
		return true;
	}

	void CConsoleDebugger::ListCPUs()
	{
		puts("CPUs:");
		if (cpus.size() == 0)
		{
			puts(" None");
			return;
		}

		int num = 0;
		for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
			printf(" %d - %s\n", num++, (*it)->name); 
	}

	void CConsoleDebugger::ListRegisters()
	{
		printf("%s Registers:\n", m_cpu->name);
		if (m_cpu->regs.size() == 0)
		{
			puts(" None");
			return;
		}
		
		// Get groups
		vector<const char*> groups;
		vector<vector<CRegister*> > regsByGroup;
		size_t totalRows = 0;
		for (vector<CRegister*>::iterator it = m_cpu->regs.begin(); it != m_cpu->regs.end(); it++)
		{
			const char *group = (*it)->group;
			// TODO - find with stricmp rather than default find
			if (find(groups.begin(), groups.end(), group) == groups.end())
			{
				groups.push_back(group);
				regsByGroup.resize(regsByGroup.size() + 1);
			}
			int index = find(groups.begin(), groups.end(), group) - groups.begin();
			vector<CRegister*> *pRegsInGroup = &regsByGroup[index];
			pRegsInGroup->push_back(*it);
			totalRows = max<size_t>(totalRows, pRegsInGroup->size());
		}

		// Get max label and value widths in each group and print group headers
		size_t numGroups = groups.size();
		vector<size_t> labelWidths(numGroups);
		vector<size_t> valueWidths(numGroups);
		vector<size_t> groupWidths(numGroups);
		char valStr[50];
		for (size_t index = 0; index < numGroups; index++)
		{
			labelWidths[index] = 0;
			valueWidths[index] = 0;

			vector<CRegister*> *pRegsInGroup = &regsByGroup[index];
			for (vector<CRegister*>::iterator it = pRegsInGroup->begin(); it != pRegsInGroup->end(); it++)
			{
				labelWidths[index] = max<size_t>(labelWidths[index], strlen((*it)->name));
				(*it)->GetValue(valStr);
				valueWidths[index] = max<size_t>(valueWidths[index], strlen(valStr));
			}

			const char *group = groups[index];
			groupWidths[index] = max<size_t>(labelWidths[index] + valueWidths[index] + 3, strlen(group) + 1);
			printf("%-*s", (int)groupWidths[index], group);
		}
		puts("");

		// Print rows of register values
		char rowStr[50];
		for (size_t row = 0; row < totalRows; row++)
		{
			for (size_t index = 0; index < numGroups; index++)
			{
				vector<CRegister*> *pRegsInGroup = &regsByGroup[index];
				if (row < pRegsInGroup->size())
				{
					CRegister *reg = (*pRegsInGroup)[row];
					reg->GetValue(valStr);
					bool hasMon = m_cpu->GetRegMonitor(reg->name) != NULL;

					sprintf(rowStr, "%c%-*s %-*s", (hasMon ? '*' : ' '), (int)labelWidths[index], reg->name, (int)valueWidths[index], valStr);
				}
				else
					rowStr[0] = '\0';

				printf("%-*s", (int)groupWidths[index], rowStr);
			}
			puts("");
		}
	}	

	void CConsoleDebugger::ListExceptions()
	{
		printf("%s Exceptions:\n", m_cpu->name);
		if (m_cpu->exceps.size() == 0)
		{
			puts(" None");
			return;
		}

		char addrStr[20];
		UINT32 handlerAddr;
		for (vector<CException*>::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++)
		{
			if (m_cpu->GetHandlerAddr(*it, handlerAddr))
				m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFExcepHandler : LFNone));
			else
				addrStr[0] = '\0';
			printf("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count);
		}
	}

	void CConsoleDebugger::ListInterrupts()
	{
		printf("%s Interrupts:\n", m_cpu->name);
		if (m_cpu->inters.size() == 0)
		{
			puts(" None");
			return;
		}

		char addrStr[20];
		UINT32 handlerAddr;
		for (vector<CInterrupt*>::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++)
		{
			if (m_cpu->GetHandlerAddr(*it, handlerAddr))
				m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFInterHandler : LFNone));
			else
				addrStr[0] = '\0';
			printf("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count);
		}
	}

	void CConsoleDebugger::ListIOs()
	{
		printf("%s I/O Ports:\n", m_cpu->name);
		if (m_cpu->ios.size() == 0)
		{
			puts(" None");
			return;
		}

		const char *group = NULL;
		char locStr[255];
		char dirChar;
		char inStr[50];
		char outStr[50];
		for (vector<CIO*>::iterator it = m_cpu->ios.begin(); it != m_cpu->ios.end(); it++)
		{
			// Print group heading if starting a new group
			if (group == NULL || stricmp((*it)->group, group) != 0)
			{
				group = (*it)->group;
				printf(" %s:\n", group);
			}

			// Get location string (memory address or port number)
			(*it)->GetLocation(locStr);
			
			// See whether port was last read or written
			if ((*it)->inCount + (*it)->outCount > 0)
				dirChar = ((*it)->last == &(*it)->lastIn ? '<' : '>');
			else
				dirChar = ' ';
			// Formatt last input
			if ((*it)->inCount > 0)
				m_cpu->FormatData(inStr, (*it)->dataSize, (*it)->lastIn);
			else
			{
				inStr[0] = '*';
				inStr[1] = '\0';
			}
			size_t inLen = strlen(inStr);
			int inLPad = 5 - (int)inLen / 2;
			int inRPad = 5 - (int)inLen + (int)inLen / 2;
			// Format last output
			if ((*it)->inCount > 0)
				m_cpu->FormatData(outStr, (*it)->dataSize, (*it)->lastOut);
			else
			{
				outStr[0] = '*';
				outStr[1] = '\0';
			}
			size_t outLen = strlen(outStr);
			int outLPad = 5 - (int)outLen / 2;
			int outRPad = 5 - (int)outLen + (int)outLen / 2;
			
			// Print details
			printf(" %c%-12s %-30s %-8s %*s%s%*s%c%*s%s%*s\n", ((*it)->watch != NULL ? '*' : ' '), locStr, (*it)->name, GetSizeString((*it)->dataSize),
				inLPad, "", inStr, inRPad, "", dirChar, outLPad, "", outStr, outRPad, "");
		}
	}

	void CConsoleDebugger::ListRegions()
	{
		printf("%s Regions:\n", m_cpu->name);
		if (m_cpu->regions.size() == 0)
		{
			puts(" None");
			return;
		}

		char startStr[255];
		char endStr[255];
		for (vector<CRegion*>::iterator it = m_cpu->regions.begin(); it != m_cpu->regions.end(); it++)
		{
			// Format start and end address
			m_cpu->FormatAddress(startStr, (*it)->addr);
			m_cpu->FormatAddress(endStr, (*it)->addrEnd);

			// Print details
			printf("%c%s-%s %s\n", ((*it)->isCode ? '*' : ' '), startStr, endStr, (*it)->name);
		}
	}

	void CConsoleDebugger::ListLabels(bool customLabels, ELabelFlags autoLabelFlags)
	{
		printf("%s Labels:\n", m_cpu->name);
		
		unsigned count = 0;

		char addrStr[20];
		if (customLabels && m_cpu->labels.size() > 0)
		{
			puts(" Custom Labels:");
			for (vector<CLabel*>::iterator it = m_cpu->labels.begin(); it != m_cpu->labels.end(); it++)
			{
				m_cpu->FormatAddress(addrStr, (*it)->addr);
				printf("  %s %s\n", addrStr, (*it)->name);
				count++;
			}
		}

		if (m_analyseCode)
		{
			char labelStr[255];
			CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser();
			for (unsigned index = 0; index < CAutoLabel::numLabelFlags; index++)
			{
				ELabelFlags flag = CAutoLabel::GetLabelFlag(index);
				if (!(autoLabelFlags & flag))
					continue;
				vector<CAutoLabel*> withFlag = analyser->analysis->GetAutoLabels(flag);
				if (withFlag.size() == 0)
					continue;
				const char *flagStr = CAutoLabel::GetFlagString(flag);
				printf(" %ss:\n", flagStr);
				for (vector<CAutoLabel*>::iterator it = withFlag.begin(); it != withFlag.end(); it++)
				{
					if (!(*it)->GetLabel(labelStr, flag))
						continue;
					CBreakpoint *bp = m_cpu->GetBreakpoint((*it)->addr);
					char bpChr = (bp != NULL ? bp->symbol : ' ');
					m_cpu->FormatAddress(addrStr, (*it)->addr);
					printf(" %c%s %s\n", bpChr, addrStr, labelStr);
					count++;
				}
			}
		}

		if (count == 0)
		{
			puts(" None");
			return;
		}
	}

	void CConsoleDebugger::GetAllMemWatches(vector<CWatch*> &watches)
	{
		for (vector<CWatch*>::iterator it = m_cpu->memWatches.begin(); it != m_cpu->memWatches.end(); it++)
			watches.push_back(*it);
		for (vector<CWatch*>::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++)
		{
			if (dynamic_cast<CMappedIO*>((*it)->io) != NULL)
				watches.push_back(*it);
		}
	}

	void CConsoleDebugger::ListMemWatches()
	{
		printf("%s Memory Watches:\n", m_cpu->name);

		vector<CWatch*> watches;
		GetAllMemWatches(watches);

		if (watches.size() == 0)
		{
			puts(" None");
			return;
		}

		printf(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Address", "Trig", "Value", "Info");

		char addrStr[20];
		const char *trigStr;
		char valStr[20];
		char infoStr[255];
		unsigned wNum = 0;
		for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++)
		{
			m_cpu->FormatAddress(addrStr, (*it)->addr, true);
			if      ((*it)->trigRead && (*it)->trigWrite) trigStr = "R/W";
			else if ((*it)->trigRead)                     trigStr = "R";
			else if ((*it)->trigWrite)                    trigStr = "W";
			else                                          trigStr = "-";
			m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue());
			if (!(*it)->GetInfo(infoStr))
				infoStr[0] = '\0';
			printf(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, (*it)->type, addrStr, trigStr, valStr, infoStr);
		}
	}

	void CConsoleDebugger::GetAllPortWatches(vector<CWatch*> &watches)
	{
		for (vector<CWatch*>::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++)
		{
			if (dynamic_cast<CPortIO*>((*it)->io) != NULL)
				watches.push_back(*it);
		}
	}

	void CConsoleDebugger::ListPortWatches()
	{
		printf("%s I/O Port Watches:\n", m_cpu->name);

		vector<CWatch*> watches;
		GetAllPortWatches(watches);

		if (watches.size() == 0)
		{
			puts(" None");
			return;
		}

		printf(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Location", "Trig", "Last In/Out", "Info");

		char locStr[255];
		const char *trigStr;
		char valStr[20];
		char infoStr[255];
		unsigned wNum;
		for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++)
		{
			(*it)->io->GetLocation(locStr);
			if      ((*it)->trigRead && (*it)->trigWrite) trigStr = "I/O";
			else if ((*it)->trigRead)                     trigStr = "I";
			else if ((*it)->trigWrite)                    trigStr = "O";
			else                                          trigStr = "-";
			if ((*it)->readCount + (*it)->writeCount == 0)
			{
				valStr[0] = '-';
				valStr[1] = '\0';
			}
			else
				m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue());
			if (!(*it)->GetInfo(infoStr))
				infoStr[0] = '\0';
			printf(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, (*it)->type, locStr, trigStr, valStr, infoStr);
		}
	}

	void CConsoleDebugger::ListBreakpoints()
	{
		printf("%s Breakpoints:\n", m_cpu->name);
		if (m_cpu->bps.size() == 0)
		{
			puts(" None");
			return;
		}

		char addrStr[20];
		char infoStr[255];
		unsigned bpNum = 0;
		for (vector<CBreakpoint*>::iterator it = m_cpu->bps.begin(); it != m_cpu->bps.end(); it++)
		{
			m_cpu->FormatAddress(addrStr, (*it)->addr, true, (m_analyseCode ? LFAll : LFNone));
			if ((*it)->GetInfo(infoStr))
				printf(" %u - %s breakpoint [%s] at %s\n", bpNum++, (*it)->type, infoStr, addrStr);
			else
				printf(" %u - %s breakpoint at %s\n", bpNum++, (*it)->type, addrStr);
		}
	}

	void CConsoleDebugger::ListMonitors()
	{
		printf("%s Register Monitors:\n", m_cpu->name);
		if (m_cpu->regMons.size() == 0)
		{
			puts(" None");
			return;
		}

		char valStr[255];
		int num = 0;
		for (vector<CRegMonitor*>::iterator it = m_cpu->regMons.begin(); it != m_cpu->regMons.end(); it++)
		{
			(*it)->GetBeforeValue(valStr);
			printf(" %d - %s change from: %s\n", num++, (*it)->reg->name, valStr);
		}
	}

	UINT32 CConsoleDebugger::ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs)
	{
		UINT32 addr;
		char startStr[20];
		char endStr[20];
		char opCodes[50];
		char mnemonic[100];
		char operands[155];
		char addrStr[20];
		char labelStr[13];
		unsigned instr;

		UINT32 pc = m_cpu->pc;		
		CCodeAnalyser *analyser = (m_analyseCode ? m_cpu->GetCodeAnalyser() : NULL);

		// Align address to instruction boundary
		start -= start%m_cpu->minInstrLen;

		if (analyser != NULL)
		{
			// Use code analyser to find valid start address
			if (!analyser->analysis->GetNextValidAddr(start))
				return start;
		}
		else
		{
			// In the absence of code analyser, try to align code with current PC address
			if (m_cpu->instrCount > 0 && pc >= start && pc <= end)
			{
				while (start < end)
				{	
					bool okay = false;
					addr = start;
					instr = 0;
					while (addr < end && instr < numInstrs)
					{
						if (addr == pc)
						{
							okay = true;
							break;
						}
						int codesLen = m_cpu->GetOpLength(addr);
						addr += abs(codesLen);
						instr++;
					}
					if (okay)
						break;
					start += m_cpu->minInstrLen;
				}
			}
		}

		// Get true end address
		addr = start;
		instr = 0;
		while (addr < end && instr < numInstrs)
		{
			// Get instruction length
			int codesLen = m_cpu->Disassemble(addr, mnemonic, operands);
			// Move onto next valid instruction address
			addr += abs(codesLen);
			if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr))
				break;
			instr++;
		}
		end = addr;
				
		// Format start and end addresses and output title
		m_cpu->FormatAddress(startStr, start);
		m_cpu->FormatAddress(endStr, end);
		printf("%s Code %s - %s:\n", m_cpu->name, startStr, endStr);

		// Output the disassembly
		addr = start;
		instr = 0;
		while (addr < end)
		{
			// Add markers for current PC address and any breakpoints
			char ind[4];
			if (m_cpu->instrCount > 0 && addr == pc)
			{
				ind[0] = '>';
				ind[1] = '>';
			}
			else
			{
				ind[0] = ' ';
				ind[1] = ' ';
			}
			CBreakpoint *bp = m_cpu->GetBreakpoint(addr);
			ind[2] = (bp != NULL ? bp->symbol : ' ');
			ind[3] = '\0';
			
			// Format current address
			m_cpu->FormatAddress(addrStr, addr);

			// Get labels at address (if any)
			bool hasLabel = GetLabelText(labelStr, 12, addr);

			// Get mnemonic, operands and instruction length and print them
			int codesLen = m_cpu->Disassemble(addr, mnemonic, operands);		
			FormatOpCodes(opCodes, addr, abs(codesLen));
			
			// Get comment at address (if any)
			CComment *comment = m_cpu->GetComment(addr);
			
			// Output line
			printf("%s", ind);
			if (m_showLabels)
			{
				if (m_labelsOverAddr)
					printf("%-12s ", (hasLabel ? labelStr : addrStr));
				else
					printf("%s %-12s ", addrStr, labelStr);
			}
			else
				printf("%s ", addrStr);
			if (m_showOpCodes) 
				printf("[%s] ", opCodes);
			if (codesLen > 0)
				printf("%-*s %-20s", (int)m_cpu->maxMnemLen, mnemonic, operands);
			else
				printf("???");
			if (comment != NULL)
				printf(" ; %s", comment->text);
			printf("\n");

			// Move onto next valid instruction address
			addr += abs(codesLen);
			if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr))
				break;
		}
		return end;
	}

	UINT32 CConsoleDebugger::ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow)
	{
		char startStr[255];
		char endStr[255];
		char addrStr[20];
		char labelStr[13];
		char wChar, dChar;
		UINT8 data;
		
		// Adjust start/end points to be on row boundary
		start -= (start % bytesPerRow);
		end += bytesPerRow - (end % bytesPerRow);

		// Format start and end addresses and output title
		m_cpu->FormatAddress(startStr, start);
		m_cpu->FormatAddress(endStr, end);
		printf("%s Memory %s - %s:\n", m_cpu->name, startStr, endStr);

		UINT32 addr = start;
		while (addr < end)
		{
			// TODO - check address going out of region or out of range

			// Format current address
			m_cpu->FormatAddress(addrStr, addr);
			
			// Get labels at address (if any)
			bool hasLabel = GetLabelText(labelStr, 12, addr);

			// Output line
			if (m_showLabels)
			{
				if (m_labelsOverAddr)
					printf("   %-12s", (hasLabel ? labelStr : addrStr));
				else
					printf("   %s %-12s", addrStr, labelStr);
			}
			else
				printf("   %s%c", addrStr);
			UINT32 lAddr = addr;
			for (unsigned i = 0; i < bytesPerRow; i++)
			{
				CWatch *watch = m_cpu->GetMemWatch(lAddr, 1);
				// TODO - handling of mapped I/O
				//CMappedIO *io = m_cpu->GetMappedIO(lAddr);
				wChar = (watch != NULL ? watch->symbol : ' ');
				//if (io != NULL)
				//	data = (UINT8)io->last;
				//else
					data = (UINT8)m_cpu->ReadMem(lAddr, 1);
				printf("%c%02X", wChar, data);
				lAddr++;
			}
			printf(" ");
			lAddr = addr;
			for (unsigned i = 0; i < bytesPerRow; i++)
			{
				// TODO - handling of mapped I/O
				//CMappedIO *io = m_cpu->GetMappedIO(lAddr);
				//if (io != NULL)
				//	data = io->last;
				//else
					data = (UINT8)m_cpu->ReadMem(lAddr, 1);
				dChar = (data >= 32 && data <= 126 ? (char)data : '.');
				printf("%c", dChar);
			}
			printf("\n");
			addr += bytesPerRow;
		}
		return addr;
	}

	void CConsoleDebugger::Attach()
	{
		CDebugger::Attach();

		ApplyConfig();

		Attached();
	}

	void CConsoleDebugger::Detach()
	{
		Detaching();

		CDebugger::Detach();
	}

	void CConsoleDebugger::ApplyConfig()
	{
		for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++)
		{
			(*it)->addrFmt = m_addrFmt;
			(*it)->portFmt = m_portFmt;
			(*it)->dataFmt = m_dataFmt;
		}
	}

	void CConsoleDebugger::Attached()
	{
		//
	}

	void CConsoleDebugger::Detaching()
	{
		//
	}

	void CConsoleDebugger::AnalysisUpdated(CCodeAnalyser *analyser)
	{
		//
	}

	void CConsoleDebugger::ExceptionTrapped(CException *ex)
	{
		char addrStr[255];
		ex->cpu->FormatAddress(addrStr, ex->cpu->pc, true, (m_analyseCode ? LFExcepHandler : LFNone));
		printf("Exception %s (%s) on %s trapped at %s.\n", ex->id, ex->name, ex->cpu->name, addrStr);
	}

	void CConsoleDebugger::InterruptTrapped(CInterrupt *in)
	{
		char addrStr[255];
		in->cpu->FormatAddress(addrStr, in->cpu->pc, true, (m_analyseCode ? LFInterHandler : LFNone));
		printf("Interrupt %s (%s) on %s trapped at %s.\n", in->id, in->name, in->cpu->name, addrStr);
	}

	void CConsoleDebugger::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead)
	{
		const char *sizeStr = GetSizeString(dataSize);
		char dataStr[20];
		m_cpu->FormatData(dataStr, dataSize, data);
		const char *rwStr = (isRead ? "read" : "write");
		char addrStr[255];
		char infoStr[255];
		watch->cpu->FormatAddress(addrStr, addr, true);
		if (watch->GetInfo(infoStr))
			printf("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type, infoStr);
		else
			printf("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type);
	}

	void CConsoleDebugger::IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput)
	{
		const char *sizeStr = GetSizeString(io->dataSize);
		char dataStr[20];
		m_cpu->FormatData(dataStr, io->dataSize, data);
		const char *ioStr = (isInput ? "input" : "output");
		const char *tfStr = (isInput ? "from" : "to");
		char locStr[255];
		char infoStr[255];
		watch->io->GetLocation(locStr);
		if (watch->GetInfo(infoStr))
			printf("%s I/O %s (%s) by %s %s %s triggered %s watch [%s].\n", sizeStr, ioStr, dataStr, watch->cpu->name, tfStr, locStr, watch->type, infoStr);
		else
			printf("%s I/O %s (%s) by %s %s %s triggered %s watch.\n", sizeStr, ioStr, dataStr, watch->cpu->name, tfStr, locStr, watch->type);
	}

	void CConsoleDebugger::BreakpointReached(CBreakpoint *bp)
	{
		char addrStr[255];
		char infoStr[255];
		bp->cpu->FormatAddress(addrStr, bp->cpu->pc, true, (m_analyseCode ? LFAll : LFNone));
		if (bp->GetInfo(infoStr))
			printf("%s reached %s breakpoint [%s] at %s.\n", bp->cpu->name, bp->type, infoStr, addrStr);
		else
			printf("%s reached %s breakpoint at %s.\n", bp->cpu->name, bp->type, addrStr);
	}

	void CConsoleDebugger::MonitorTriggered(CRegMonitor *regMon)
	{
		char addrStr[255];
		char valStr[255];
		CRegister *reg = regMon->reg;
		reg->cpu->FormatAddress(addrStr, reg->cpu->pc, true, (m_analyseCode ? LFAll : LFNone));
		reg->GetValue(valStr);
		printf("Register %s of %s has changed value at %s to %s.\n", reg->name, reg->cpu->name, addrStr, valStr);
	}

	void CConsoleDebugger::ExecutionHalted(CCPUDebug *cpu, EHaltReason reason)
	{
		if (reason&HaltUser)
		{
			char addrStr[255];
			cpu->FormatAddress(addrStr, cpu->pc, true, (m_analyseCode ? LFAll : LFNone));
			printf("Execution halted on %s at %s.\n", cpu->name, addrStr);
		}
	}

	void CConsoleDebugger::WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl)
	{
		if (cpu != NULL)
			printf("%s: ", cpu->name);
		if (typeStr != NULL)
			printf(" %s - ", typeStr);
		vprintf(fmtStr, vl);
	}
		
	void CConsoleDebugger::FlushOut(CCPUDebug *cpu)
	{
		fflush(stdout);
	}
}

#endif  // SUPERMODEL_DEBUGGER