Exemplo n.º 1
0
void cpu_instr_callback()
{
    static char buff[100];
    static unsigned int pc;

    if (verbose)
    {
        pc = m68k_get_reg(NULL, M68K_REG_PC);
        m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000);
        printf("E %03x: %s\n", pc, buff);
#if 0 /* Dump all regs */
        printf("    D0       D1       D2       D3       D4       D5       D6       D7\n");
        printf("    %08x %08x %08x %08x %08x %08x %08x %08x\n"
            , m68k_get_reg(0, M68K_REG_D0)
            , m68k_get_reg(0, M68K_REG_D1)
            , m68k_get_reg(0, M68K_REG_D2)
            , m68k_get_reg(0, M68K_REG_D3)
            , m68k_get_reg(0, M68K_REG_D4)
            , m68k_get_reg(0, M68K_REG_D5)
            , m68k_get_reg(0, M68K_REG_D6)
            , m68k_get_reg(0, M68K_REG_D7));
        printf("    A0       A1       A2       A3       A4       A5       A6       A7\n");
        printf("    %08x %08x %08x %08x %08x %08x %08x %08x\n"
            , m68k_get_reg(0, M68K_REG_A0)
            , m68k_get_reg(0, M68K_REG_A1)
            , m68k_get_reg(0, M68K_REG_A2)
            , m68k_get_reg(0, M68K_REG_A3)
            , m68k_get_reg(0, M68K_REG_A4)
            , m68k_get_reg(0, M68K_REG_A5)
            , m68k_get_reg(0, M68K_REG_A6)
            , m68k_get_reg(0, M68K_REG_A7));
#endif
        fflush(stdout);
    }
}
Exemplo n.º 2
0
static int CreateDisassFile(TCHAR* filename, unsigned int start, unsigned int end, int nCPU)
{
	unsigned int size;
	unsigned int pc = start;
	FILE* fp = _tfopen(filename, _T("wt"));

	if (fp == NULL) {
		return 1;
	}

	// Print the disassembly
	while (pc <= end) {
		char buf[100] = "";
		unsigned int j;

		if (pc > 0x00FFFFFE) {
			break;
		}

		// Get the mnemonic
		size = m68k_disassemble(buf, pc, nCPU);

		// Print the address
		_ftprintf(fp, _T("%06x "), pc);

		// Print the word values
		if (nCPU == M68K_CPU_TYPE_68000) {
			for (j = 10; j > size; j -= 2) {
				_ftprintf(fp, _T("     "));
			}
			for (j =  0; j < size; j += 2) {
				_ftprintf(fp, _T("%04x "), SekFetchWord(pc + j));
			}
		} else {
			if (size > 20) {
				for (j =  0; j < 18; j += 2) {
					_ftprintf(fp, _T("%04x "), SekFetchWord(pc + j));
				}
				_ftprintf(fp, _T("...  "));
			} else {
				for (j = 20; j > size; j -= 2) {
					_ftprintf(fp, _T("     "));
				}
				for (j =  0; j < size; j += 2) {
					_ftprintf(fp, _T("%04x "), SekFetchWord(pc + j));
				}
			}
		}

		// Print the instruction
		_ftprintf(fp, _T("%hs\n"), buf);

		pc += size;
	}

	fclose(fp);

	return 0;
}
Exemplo n.º 3
0
void gentrace(void)
{
    return;
    if (TRACE_COUNT > 0 || VDP.vcounter() == 33)
    {
        uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC);
        char buf[2048];
        int oplen = m68k_disassemble(buf, pc, M68K_CPU_TYPE_68000);
        fprintf(stdout, "%06x\t%s\t\t[HC=%x]\n", pc, buf, VDP.hcounter());

        --TRACE_COUNT;
    }
}
bool soakTestOpWordDecoding(void)
{
    const char* previousDescription = 0;
    uint opWord;

    printf("Attempting to decode lengths for all opwords...\n");

    for (opWord = 0; opWord < 0x10000; ++opWord)
    {
        InstructionLength instructionLength;
        bool success;
        uint16_t instructionBuffer[16] = { 0 };
        uint musashiInstructionLength;
        char musashiDisassembledInstruction[256];

        instructionBuffer[0] = opWord;

        success = decodeInstructionLengthFromInstructionWords(instructionBuffer, sizeof instructionBuffer / sizeof instructionBuffer[0], &instructionLength);

        if (!success)
        {
            printf("Error: decoding failed for instruction %04x\n", opWord);
            return false;
        }

        writeInstructionBufferToMusashiMemory(instructionBuffer, sizeof instructionBuffer / sizeof instructionBuffer[0]);
        musashiInstructionLength = m68k_disassemble(musashiDisassembledInstruction, 0, M68K_CPU_TYPE_68040);

        if (diffAgainstMusashi(instructionLength.totalWords, musashiInstructionLength, opWord))
        {
            printf("Error: instruction length decoding mismatch for opword %04x -\n", opWord);
            printf("M68060InstructionLengthDecoder claims instruction %s, %d bytes\n", instructionLength.description, instructionLength.totalWords * 2);
            printf("Musashi disassembler claims instruction %s, %d bytes\n",musashiDisassembledInstruction, musashiInstructionLength);
            return false;
        }

        if (previousDescription != instructionLength.description)
        {
            printf("%04x: %s\n", opWord, instructionLength.description);
            previousDescription = instructionLength.description;
        }
    }

    return true;
}
Exemplo n.º 5
0
static unsigned int TraceBack(unsigned int nStartAddress, unsigned int nTotalInstructions, int nCPU)
{
	unsigned int pc = nStartAddress;
	unsigned int trace = nTotalInstructions;

	if (nStartAddress == 0 || nTotalInstructions == 0) {
		return 0;
	}

	// If there's no valid instruction at the start adress, increase it
	while (m68k_is_valid_instruction(SekFetchWord(pc), nCPU) == 0 && (pc - nStartAddress) < ((nCPU == M68K_CPU_TYPE_68000) ? 10 : 30) - 2) {
		pc += 2;
	}

	do {
		trace_size = 0;
		trace_pc = pc;
		// Trace back a few extra instructions to ensure we'll return a valid code path
		trace_max = trace + 4;

		TraceBackDo(pc, 0, nCPU);

		pc -= 2;

	} while (trace_size == 0 && pc >= nStartAddress);

	pc = trace_pc;

	// If we've traced back more instructions than needed, skip the extra ones
	while (trace_size > trace) {
		char buf[100] = "";
		pc += m68k_disassemble(buf, pc, nCPU);
		trace++;
	}

	// If we can't trace back at all, simply decrease the address
	if (trace_size == 0) {
		return nTotalInstructions * 2 < nStartAddress ? nStartAddress - nTotalInstructions * 2 : 0;
	}

	return pc;
}
Exemplo n.º 6
0
static void TraceBackDo(unsigned int start_pc, unsigned int trace_level, int cpu_type)
{
	unsigned int pc;
	unsigned int trace;

	// Go over all possible instruction sizes (the longest 68020 instruction is 30 bytes)
	for (unsigned int size = (cpu_type == M68K_CPU_TYPE_68000) ? 10 : 30; size != 0 ; size -= 2) {
		pc = start_pc;
		trace = trace_level;

		// Make sure we don't go past the start of memory
		if (size > pc) {
			break;
		}

		// Check if there is a valid instruction at the current address
		if (m68k_is_valid_instruction(SekFetchWord(pc - size),  cpu_type) != 0) {
			char buf[100] = "";

			// Check if the instruction at the current address has the right size
			if (m68k_disassemble(buf, pc - size,  cpu_type) == size) {
				pc -= size;
				trace++;

				// Update best result
				if (trace_size < trace) {
					trace_size = trace;
					trace_pc = pc;
				}

				// Recurse until we've traced back enough instructions
				if (trace < trace_max) {
					TraceBackDo(pc, trace,  cpu_type);
				}
			}
		}
	}
}
Exemplo n.º 7
0
void Dasm(uint32_t offset, uint32_t qt)
{
#ifdef CPU_DEBUG
// back up a few instructions...
//offset -= 100;
	static char buffer[2048];//, mem[64];
	int pc = offset, oldpc;
	uint32_t i;

	for(i=0; i<qt; i++)
	{
/*		oldpc = pc;
		for(int j=0; j<64; j++)
			mem[j^0x01] = jaguar_byte_read(pc + j);

		pc += Dasm68000((char *)mem, buffer, 0);
		WriteLog("%08X: %s\n", oldpc, buffer);//*/
		oldpc = pc;
		pc += m68k_disassemble(buffer, pc, 0);//M68K_CPU_TYPE_68000);
//		WriteLog("%08X: %s\n", oldpc, buffer);//*/
		printf("%08X: %s\n", oldpc, buffer);//*/
	}
#endif
}
void M68KDasmBrowserWindow::RefreshContents(void)
{
	char string[1024];//, buf[64];
	QString s;

	char buffer[2048];
	int pc = memBase, oldpc;

	for(uint32_t i=0; i<32; i++)
	{
		oldpc = pc;
		pc += m68k_disassemble(buffer, pc, 0);
//		WriteLog("%06X: %s\n", oldpc, buffer);
		sprintf(string, "%06X: %s<br>", oldpc, buffer);

		buffer[0] = 0;	// Clear string
		char singleCharString[2] = { 0, 0 };

		for(int j=0; j<strlen(string); j++)
		{
			if (string[j] == 32)
				strcat(buffer, "&nbsp;");
			else
			{
				singleCharString[0] = string[j];
				strcat(buffer, singleCharString);
			}
		}

//		s += QString(string);
		s += QString(buffer);
	}

	text->clear();
	text->setText(s);
}
Exemplo n.º 9
0
int m68k_disasm_pc(char* outputBuffer, int outputBufferSize, int pc, int* instCount)
{
	int i, end;

	// we make sure we have some head room as we sprintf directly into the buffer outputbuffer
	const char* outputBufferEnd = outputBuffer + outputBufferSize - 32;

	if (outputBuffer > outputBufferEnd)
	{
		printf("M68KDebugger:: outputBuffer needs to be at least 32 bytes and bigger\n");
		return 0;
	}

	for (i = 0, end = *instCount; i < end; ++i)
	{
		int textSize = 0;
		int inst_size;
		char instructionText[256];

        memset(instructionText, 0, sizeof(instructionText));
		inst_size = m68k_disassemble(instructionText, pc, M68K_CPU_TYPE_68040);

		textSize = sprintf(outputBuffer, "%08x %s\n", pc, instructionText);
		outputBuffer += textSize;

		if (outputBuffer >= outputBufferEnd)
		{
			*instCount = i;
			return 1;
		}

		pc += inst_size;
	}

	return 0;
}
Exemplo n.º 10
0
// -----------------------------------------------------------------------------------
//  �����Τᤤ��롼��
// -----------------------------------------------------------------------------------
void WinX68k_Exec(void)
{
	if(!(Memory_ReadD(0xed0008)==ram_size)){
		Memory_WriteB(0xe8e00d, 0x31);             // SRAM write permission
		Memory_WriteD(0xed0008, ram_size);         // Define RAM amount
	}

	//char *test = NULL;
	int clk_total, clkdiv, usedclk, hsync, clk_next, clk_count, clk_line=0;
	int KeyIntCnt = 0, MouseIntCnt = 0;
	DWORD t_start = timeGetTime(), t_end;

	if ( Config.FrameRate == 0)
		Config.FrameRate = 7;

	if ( Config.FrameRate != 7 ) {
		DispFrame = (DispFrame+1)%Config.FrameRate;
	} else {				// Auto Frame Skip
		if ( FrameSkipQueue ) {
			if ( FrameSkipCount>15 ) {
				FrameSkipCount = 0;
				FrameSkipQueue++;
				DispFrame = 0;
			} else {
				FrameSkipCount++;
				FrameSkipQueue--;
				DispFrame = 1;
			}
		} else {
			FrameSkipCount = 0;
			DispFrame = 0;
		}
	}

	vline = 0;
	clk_count = -ICount;
	clk_total = (CRTC_Regs[0x29] & 0x10) ? VSYNC_HIGH : VSYNC_NORM;

	clk_total = (clk_total*clockmhz)/10;
	clkdiv = clockmhz;

	/*if (Config.XVIMode == 1) {
		clk_total = (clk_total*16)/10;
		clkdiv = 16;
	} else if (Config.XVIMode == 2) {
		clk_total = (clk_total*24)/10;
		clkdiv = 24;
	} else {
		clkdiv = 10;
	}*/

	if(clkdiv != old_clkdiv || ram_size != old_ram_size){
		printf("CPU Clock: %d%s\n",clkdiv,"MHz");
		printf("RAM Size: %ld%s\n",ram_size/1000000,"MB");
		old_clkdiv = clkdiv;
		old_ram_size = ram_size;	
	}


	ICount += clk_total;
	clk_next = (clk_total/VLINE_TOTAL);
	hsync = 1;

	do {
		int m, n = (ICount>CLOCK_SLICE)?CLOCK_SLICE:ICount;
		//C68K.ICount = m68000_ICountBk = 0;			// ������ȯ������Ϳ���Ƥ����ʤ��ȥ����CARAT��

		if ( hsync ) {
			hsync = 0;
			clk_line = 0;
			MFP_Int(0);
			if ( (vline>=CRTC_VSTART)&&(vline<CRTC_VEND) )
				VLINE = ((vline-CRTC_VSTART)*CRTC_VStep)/2;
			else
				VLINE = (DWORD)-1;
			if ( (!(MFP[MFP_AER]&0x40))&&(vline==CRTC_IntLine) )
				MFP_Int(1);
			if ( MFP[MFP_AER]&0x10 ) {
				if ( vline==CRTC_VSTART )
					MFP_Int(9);
			} else {
				if ( CRTC_VEND>=VLINE_TOTAL ) {
					if ( (long)vline==(CRTC_VEND-VLINE_TOTAL) ) MFP_Int(9);		// ���������ƥ��󥰥���Ȥ���TOTAL<VEND��
				} else {
					if ( (long)vline==(VLINE_TOTAL-1) ) MFP_Int(9);			// ���쥤�������饤�ޡ��ϥ���Ǥʤ��ȥ��ᡩ
				}
			}
		}

#ifdef WIN68DEBUG
		if (traceflag/*&&fdctrace*/)
		{
			FILE *fp;
			static DWORD oldpc;
			int i;
			char buf[200];
			fp=fopen("_trace68.txt", "a");
			for (i=0; i<HSYNC_CLK; i++)
			{
				m68k_disassemble(buf, C68k_Get_Reg(&C68K, C68K_PC));
//				if (MEM[0xa84c0]) /**test=1; */tracing=1000;
//				if (regs.pc==0x9d2a) tracing=5000;
//				if ((regs.pc>=0x2000)&&((regs.pc<=0x8e0e0))) tracing=50000;
//				if (regs.pc<0x10000) tracing=1;
//				if ( (regs.pc&1) )
//				fp=fopen("_trace68.txt", "a");
//				if ( (regs.pc==0x7176) /*&& (Memory_ReadW(oldpc)==0xff1a)*/ ) tracing=100;
//				if ( (/*((regs.pc>=0x27000) && (regs.pc<=0x29000))||*/((regs.pc>=0x27000) && (regs.pc<=0x29000))) && (oldpc!=regs.pc))
				if (/*fdctrace&&*/(oldpc != C68k_Get_Reg(&C68K, C68K_PC)))
				{
//					//tracing--;
				  fprintf(fp, "D0:%08X D1:%08X D2:%08X D3:%08X D4:%08X D5:%08X D6:%08X D7:%08X CR:%04X\n", C68K.D[0], C68K.D[1], C68K.D[2], C68K.D[3], C68K.D[4], C68K.D[5], C68K.D[6], C68K.D[7], 0/* xxx �Ȥꤢ����0 C68K.ccr */);
				  fprintf(fp, "A0:%08X A1:%08X A2:%08X A3:%08X A4:%08X A5:%08X A6:%08X A7:%08X SR:%04X\n", C68K.A[0], C68K.A[1], C68K.A[2], C68K.A[3], C68K.A[4], C68K.A[5], C68K.A[6], C68K.A[7], C68k_Get_Reg(&C68K, C68K_SR) >> 8/* regs.sr_high*/);
					fprintf(fp, "<%04X> (%08X ->) %08X : %s\n", Memory_ReadW(C68k_Get_Reg(&C68K, C68K_PC)), oldpc, C68k_Get_Reg(&C68K, C68K_PC), buf);
				}
				#ifdef CYCLONE
				oldpc = m68000_get_reg(M68K_PC);
				// * C68KICount = 1;
				m68000_execute(1);
				#else
				oldpc = C68k_Get_Reg(&C68K, C68K_PC);
				//C68K.ICount = 1;
				//C68k_Exec(&C68K, C68K.ICount);
				C68k_Exec(&C68K, 1);
				#endif
			}
			fclose(fp);
			usedclk = clk_line = HSYNC_CLK;
			clk_count = clk_next;
		}
		else
#endif
		{
			//C68K.ICount = n;
			#ifdef CYCLONE
			m68000_execute(n);
			#else
			C68k_Exec(&C68K, n);
			#endif
			m = (n-m68000_ICountBk);
			//m = (n-C68K.ICount-m68000_ICountBk);			// clockspeed progress
			ClkUsed += m*10;
			usedclk = ClkUsed/clkdiv;
			clk_line += usedclk;
			ClkUsed -= usedclk*clkdiv;
			ICount -= m;
			clk_count += m;
			//C68K.ICount = m68000_ICountBk = 0;
		}

		MFP_Timer(usedclk);
		RTC_Timer(usedclk);
		DMA_Exec(0);
		DMA_Exec(1);
		DMA_Exec(2);

		if ( clk_count>=clk_next ) {
			//OPM_RomeoOut(Config.BufferSize*5);
			//MIDI_DelayOut((Config.MIDIAutoDelay)?(Config.BufferSize*5):Config.MIDIDelay);
			MFP_TimerA();
			if ( (MFP[MFP_AER]&0x40)&&(vline==CRTC_IntLine) )
				MFP_Int(1);
			if ( (!DispFrame)&&(vline>=CRTC_VSTART)&&(vline<CRTC_VEND) ) {
				if ( CRTC_VStep==1 ) {				// HighReso 256dot��2���ɤߡ�
					if ( vline%2 )
						WinDraw_DrawLine();
				} else if ( CRTC_VStep==4 ) {		// LowReso 512dot
					WinDraw_DrawLine();				// 1��������2�������ʥ��󥿡��졼����
					VLINE++;
					WinDraw_DrawLine();
				} else {							// High 512dot / Low 256dot
					WinDraw_DrawLine();
				}
			}

			ADPCM_PreUpdate(clk_line);
			OPM_Timer(clk_line);
			MIDI_Timer(clk_line);
#ifndef	NO_MERCURY
			Mcry_PreUpdate(clk_line);
#endif

			KeyIntCnt++;
			if ( KeyIntCnt>(VLINE_TOTAL/4) ) {
				KeyIntCnt = 0;
				Keyboard_Int();
			}
			MouseIntCnt++;
			if ( MouseIntCnt>(VLINE_TOTAL/8) ) {
				MouseIntCnt = 0;
				SCC_IntCheck();
			}
			DSound_Send0(clk_line);

			vline++;
			clk_next  = (clk_total*(vline+1))/VLINE_TOTAL;
			hsync = 1;
		}
	} while ( vline<VLINE_TOTAL );
Exemplo n.º 11
0
static void CreateDisass(HWND hWindow, unsigned int nAddress, int nCPU)
{
	int nPageSize = bLargeWindow ? 16 : 12;
	TCHAR szBuffer[16384] = _T("");
	TCHAR* pszBuf = szBuffer;
	unsigned int pc, nSize;

	pc = nAddress;

	// RTF header and initial settings
	pszBuf += _stprintf(pszBuf, _T("{\\rtf1\\ansi\\ansicpg1252\\deff0{\\fonttbl(\\f0\\fmodern\\charset0\\fprq0 Courier New;)}{\\colortbl\\red0\\green0\\blue0;\\red96\\green96\\blue96;\\red255\\green0\\blue0;\\red255\\green96\\blue96;\\red224\\green224\\blue224;}\\deflang1033\\horzdoc\\pard\\plain\\f0"));
	if (bLargeWindow) {
		if (nCPU == M68K_CPU_TYPE_68000) {
			// Tabstops							5555    4444    3333    2222    1111    Mnemonic
			pszBuf += _stprintf(pszBuf, _T("\\tx1050\\tx1513\\tx1976\\tx2429\\tx2892\\tx3450"));
		} else {
			// Tabstops							6666    5555    4444    3333    2222    1111    Mnemonic
			pszBuf += _stprintf(pszBuf, _T("\\tx1050\\tx1435\\tx1820\\tx2205\\tx2590\\tx2975\\tx3450"));
		}
		// Indentation
		pszBuf += _stprintf(pszBuf, _T("\\fi-4665\\li4665 "));
	} else {
		// Tabstops
		pszBuf += _stprintf(pszBuf, _T("\\tx735"));
	}

	// Print the disassembly
	for (int i = 0; i < nPageSize; i++) {
		char buf[100] = "";
		unsigned int j;
		unsigned int nCol;

		if (pc > 0x00FFFFFE) {

			do {
				nDisassAddress[i++] = 0x00FFFFFE;
			} while (i < nPageSize);

			break;
		}

		nDisassAddress[i] = pc;

		// Get the mnemonic
		nSize = m68k_disassemble(buf, pc, nCPU);
		// Display the instruction at the current PC in red
		nCol = (pc == SekDbgGetRegister(SEK_REG_PC)) ? 2 : 0;

		// Print the address
		pszBuf += _stprintf(pszBuf, _T("\\fs%i\\cf%i %06x\t"), bLargeWindow ? 24 : 16, nCol, pc);

		if (bLargeWindow) {
			// Print the word values
			if (nCPU == M68K_CPU_TYPE_68000) {
				// Print a filler
				pszBuf += _stprintf(pszBuf, _T("\\fs16\\cf4 "));
				for (j = 10; j > nSize; j -= 2) {
					pszBuf += _stprintf(pszBuf, _T(" -- \t"));
				}
				// Print the words that make up the instruction
				pszBuf += _stprintf(pszBuf, _T("\\cf%i "), nCol + 1);
				for (j =  0; j < nSize; j += 2) {
					pszBuf += _stprintf(pszBuf, _T("%04x\t"), SekFetchWord(pc + j));
				}
			} else {
				if (nSize > 12) {
					// There's not enough room for all words, so print the first 5 words that make up the instruction
					pszBuf += _stprintf(pszBuf, _T("\\fs13\\cf%i "), nCol + 1);
					for (j =  0; j < 10; j += 2) {
						pszBuf += _stprintf(pszBuf, _T("%04x\t"), SekFetchWord(pc + j));
					}
					// Print an ellipsis
					pszBuf += _stprintf(pszBuf, _T("... \t"));
				} else {
					// Print a filler
					pszBuf += _stprintf(pszBuf, _T("\\fs13\\cf4 "));
					for (j = 12; j > nSize; j -= 2) {
						pszBuf += _stprintf(pszBuf, _T(" -- \t"));
					}
					// Print the words that make up the instruction
					pszBuf += _stprintf(pszBuf, _T("\\cf%i "), nCol + 1);
					for (j =  0; j < nSize; j += 2) {
						pszBuf += _stprintf(pszBuf, _T("%04x\t"), SekFetchWord(pc + j));
					}
				}
			}
		}

		// Colour Invalid instructions gray
		if (nCol == 0) {
			if (m68k_is_valid_instruction(SekFetchWord(pc), nCPU) == 0) {
				nCol++;
			}
		}
		// Print the instruction
		pszBuf += _stprintf(pszBuf, _T("\\fs%i\\cf%i %hs\\par"), bLargeWindow ? 24 : 16, nCol, buf);

		pc += nSize;
	}
	pszBuf += _stprintf(pszBuf, _T("}\0"));

	// Send the new data to the RichEdit control
	SendMessage(hWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)szBuffer);

#if 0
	FILE* fp = _tfopen(_T("disass.rtf"), _T("wb"));
	if (fp) {
		fwrite(szBuffer, 1, _tcslen(szBuffer), fp);
		fclose(fp);
	}
#endif
}
Exemplo n.º 12
0
int main(int argc, const char *argv[])
{
    ez::ezOptionParser opt;

    opt.overview = "GenEmu -- Sega Genesis Emulator";
    opt.syntax = "genemu [OPTIONS] rom";
    opt.add("",0,0,0,"Display usage instructions.", "-h", "--help");
    opt.add("",0,-1,',',"Apply Game Genie codes [format=ABCD-EFGH]", "--gamegenie", "--gg");
    opt.add("",0,1,0,"Force console type [accepted values: PAL or NTSC]", "--mode", "--type");
    opt.add("",0,-1,',',"Make screenshots on the specified frames and exit", "--screenshots");
    opt.add("",0,1,0,"Load from saved state", "--load");

    opt.parse(argc, argv);
    if (opt.isSet("-h"))
    {
        std::string usage;
        opt.getUsage(usage, 120);
        std::cout << usage;
        return 0;
    }
    std::vector<std::string*> args;
    if (opt.firstArgs.size() >= 2)
    {
        strcpy(romname, opt.firstArgs[1]->c_str());
    }
    else if (opt.lastArgs.size() >= 1)
        strcpy(romname, opt.lastArgs[0]->c_str());
    else
    {
        std::cerr << "ERROR: no ROM specified\n";
        return 2;
    }

    int romsize;
    if (strstr(romname, ".smd"))
        romsize = load_smd(romname);
    else
        romsize = load_bin(romname);
    mem_init(romsize);

#if 0
    uint16_t checksum = 0;
    for (int i=0;i<(romsize-512)/2;i++)
        checksum += m68k_read_memory_16(512+i*2);
    assert(checksum == m68k_read_memory_16(0x18e));
#endif

#if 1
    char buf[256];
    int pc = 0;
    strcpy(buf, argv[1]);
    strcat(buf, ".asm");
    FILE *f = fopen(buf, "w");
    while (pc < romsize) {
        int oplen = m68k_disassemble(buf, pc, M68K_CPU_TYPE_68000);
        fprintf(f, "%06x\t%s\n", pc, buf);
        pc += oplen;
    }
    fclose(f);
#endif

    if (opt.isSet("--mode"))
    {
        std::string mode;
        opt.get("--mode")->getString(mode);
        if (mode == "PAL")
        {
            VERSION_PAL = 1;
            std::cerr << "Forced mode: PAL\n";
        }
        else if (mode == "NTSC")
        {
            VERSION_PAL = 0;
            std::cerr << "Forced mode: NTSC\n";
        }
        else
        {
            std::cerr << "ERROR: invalid mode: " << mode << std::endl;
            return 2;
        }
    }

    std::vector<int> ss_frames;
    int ss_idx = 0;

    hw_init(YM2612_FREQ, VERSION_PAL ? 50 : 60);

    if (!opt.isSet("--screenshots"))
    {
        hw_enable_video(true);
        hw_enable_audio(true);
        gfx_enable(true);
    }
    else
    {
        opt.get("--screenshots")->getInts(ss_frames);
    }

    CPU_M68K.init();
    CPU_Z80.init();

    MASTER_CLOCK = 0;
    CPU_M68K.reset();
    VDP.reset();

    if (opt.isSet("--load"))
    {
        std::string sn;
        opt.get("--load")->getString(sn);
        loadstate(sn.c_str());
    }

    while (hw_poll())
    {
        if (ss_idx < ss_frames.size() && framecounter == ss_frames[ss_idx])
        {
            gfx_enable(true);
        }

        int numscanlines = VDP.num_scanlines();

        uint8_t *screen;
        int pitch;
        hw_beginframe(&screen, &pitch);

        int16_t *audio; int nsamples;
        hw_beginaudio(&audio, &nsamples);
        int audio_index = 0;
        int audio_step = (nsamples << 16) / numscanlines;

        for (int sl=0;sl<numscanlines;++sl)
        {
            CPU_M68K.run(MASTER_CLOCK + VDP_CYCLES_PER_LINE);
            CPU_Z80 .run(MASTER_CLOCK + VDP_CYCLES_PER_LINE);

            vdp_scanline(screen);
            screen += pitch;

            int prev_index = audio_index;
            audio_index += audio_step;
            YM2612Update(audio + ((prev_index+0x8000)>>16)*2, (audio_index-prev_index+0x8000)>>16);

            MASTER_CLOCK += VDP_CYCLES_PER_LINE;
        }

        hw_endaudio();
        hw_endframe();

        if (framecounter == 100 && opt.isSet("--gamegenie"))
        {
            std::vector<std::string> codes;
            opt.get("--gamegenie")->getStrings(codes);
            for (int i=0;i<codes.size();++i)
                mem_apply_gamegenie(codes[i].c_str());
        }

        if (ss_idx < ss_frames.size() && framecounter == ss_frames[ss_idx])
        {
            static char ssname[2048];
            sprintf(ssname, "%s.%d.%s.bmp", romname, framecounter, (VERSION_PAL ? "PAL" : "NTSC"));
            std::cerr << "Saving screenshot " << ssname << std::endl;
            hw_save_screenshot(ssname);

            sprintf(ssname, "%s.%d.%s.gs", romname, framecounter, (VERSION_PAL ? "PAL" : "NTSC"));
            savestate(ssname);

            ++ss_idx;
            if (ss_idx == ss_frames.size())
                break;
        }

        ++framecounter;
        state_poll();
    }

#if 0
    checksum = 0;
    for (int i=0;i<(romsize-512)/2;i++)
        checksum += m68k_read_memory_16(512+i*2);
    assert(checksum == m68k_read_memory_16(0x18e));
#endif

#if 0
    {
    char buf[256];
    int pc = 0xffffee00;
    FILE *f = fopen(buf, "w");
    while (pc <= 0xffffef00) {
        int oplen = m68k_disassemble(buf, pc, M68K_CPU_TYPE_68000);
        fprintf(stdout, "%06x\t%s\n", pc, buf);
        pc += oplen;
    }
    fclose(f);
    }
#endif

    return 0;
}