
#define COMPILEME /*
x="${0/.c/}.exe"
if
gcc \
	-Wall \
	-Werror \
	-O0 \
	-g \
	-std=c99 \
	-o "$x" "$0"
then
	exec "$x" "$@"
fi
exit $?
*/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#define FLASH_SIZE 2097152
#define MEMORY_SIZE 8388608
#define REVERSE_FLASH_ENDIAN 1
#define BROADCOM 3349
#define BROADCOM_CHIP_REV 0xa0
#define LUKE_SPECIAL 0
//#define DO_HCS
// NOTE: CRC mode does not work :(
//#define DO_CRC
//#define DO_DELZMA
//#define FLASH_FILE "ram.sto.decompressed"

#ifndef FLASH_FILE
#	define FLASH_FILE "flash"
#endif
#define INTFLASH_SIZE (FLASH_SIZE / 4)

#define F_dbg  stderr
#define F_info stderr
#define F_uart stdout

typedef unsigned char  uint8;
typedef   signed char  sint8;
typedef unsigned short uint16;
typedef   signed short sint16;
typedef unsigned int   uint32;
typedef   signed int   sint32;
typedef unsigned long  uint64;
typedef   signed long  sint64;

typedef uint8 byte;
typedef uint32 reg;
typedef sint32 sreg;
typedef uint32 ins_t;
typedef uint32 vaddr_t;
typedef sint16 imm_t;
typedef uint16 uimm_t;
typedef sint64 offset_t;
typedef uint8 opcode_t;

#ifdef BROADCOM
#	include "bcm_map_part.h"
#endif

byte flash[FLASH_SIZE];
uint32 *intflash = (uint32*)(&flash);
byte memory[MEMORY_SIZE];
reg PC, nPC;

typedef enum regid {
	zero= 0,
	at =  1,
	v0 =  2,
	v1 =  3,
	a0 =  4,
	a1 =  5,
	a2 =  6,
	a3 =  7,
	t0 =  8,
	t1 =  9,
	t2 = 10,
	t3 = 11,
	t4 = 12,
	t5 = 13,
	t6 = 14,
	t7 = 15,
	s0 = 16,
	s1 = 17,
	s2 = 18,
	s3 = 19,
	s4 = 20,
	s5 = 21,
	s6 = 22,
	s7 = 23,
	t8 = 24,
	t9 = 25,
	k0 = 26,
	k1 = 27,
	gp = 28,
	sp = 29,
	s8 = 30,
	fp = 30,
	ra = 31,
	LO,
	HI,
	REG_MAX,
} regid;

typedef enum bool {
	False = 0,
	True = -1,
} bool;

reg R[REG_MAX];
sreg *sR = (sreg*)&R;

#ifdef BROADCOM
PerfControl BcmPerf;
Uart BcmUart;
byte BcmRAM[0x70000]; // at 0x81f80000 for some reason
byte Bcm2000[0x1000] = {
	0x1e,0,0,0xc,
};
#endif

#ifdef DO_CRC
byte have_CRC[4];
byte CRC_table[256 * 4];
#endif

void advance_pc_abs(vaddr_t dest) {
	PC = nPC;
	nPC = dest;
}

void advance_pc(uint32 offset) {
	PC = nPC;
	nPC += offset;
}

uint8 getChar(reg vaddr) {
	reg raddr = vaddr;
	if ((vaddr & 0xfffffffc) == 0xfffffffc)
		return 0xff;
#ifdef DO_DELZMA
	if ((vaddr & 0xfff80000) == 0x81f80000)
		return flash[vaddr & 0x7ffff];
#endif
#ifdef BROADCOM
	if ((vaddr & 0xffff0000) == PERF_BASE)
	{
		//vaddr &= 0xffff;
		// Broadcom extensions
		// https://dev.openwrt.org/browser/trunk/target/linux/brcm63xx-2.6/files/arch/mips/bcm963xx/include/6338_map_part.h?rev=6848
		fprintf(F_dbg, "bcm %x\n", vaddr);
		// fffe0000 -- uint32 RevID: 0xffff0000 = chipid; 0xffff = chiprev
#	if BROADCOM > 1
		if (vaddr == 0xfffe0000)
			return ((BROADCOM / 1000) << 4) | ((BROADCOM / 100) % 10);
		if (vaddr == 0xfffe0001)
			return (((BROADCOM / 10) % 10) << 4) | (BROADCOM % 10);
		if (vaddr == 0xfffe0003)
			return BROADCOM_CHIP_REV;
#	endif
		if (vaddr >= PERF_BASE && vaddr <= PERF_BASE + sizeof(PerfControl))
			return ((char*)&BcmPerf)[vaddr & 0x1ff];
		if (vaddr == 1 + (reg)&UART->intStatus)
			// FIXME: THIS ASSUMES HOST ENDIAN != TARGET ENDIAN (or ==?)
			// TXFIFOEMT is always set here
			return ((char*)&BcmUart)[vaddr & 0x1ff] | 0x20;
		if (vaddr >= UART_BASE && vaddr <= UART_BASE + sizeof(Uart))
			return ((char*)&BcmUart)[vaddr & 0x1ff];
		if (vaddr == 0xfffe2007)
			return 0x11;
		if ((vaddr & ~0x3) == 0xfffe2000)
			return Bcm2000[vaddr & 0xfff];
		if ((vaddr & ~0xfff) == 0xfffe2000)
			return 0;
	}
	//if ((vaddr & 0xdfffffff) >= 0x81f9d5b0 && (vaddr & 0xdfffffff) < 0x81f9d600)
	if ((vaddr & 0xdff80000) == 0x81f80000)
		return BcmRAM[vaddr & 0x7ffff];
#endif
#ifdef DO_CRC
	if ((vaddr & ~3) == 0x81f8d1dc)
		return have_CRC[vaddr & 3];
	if (vaddr >= 0x81f8de08 && vaddr < 0x81f8e208)
		return CRC_table[vaddr - 0x81f8de08];
	if ((vaddr & 0xffff0000) == 0xb1f80000)
		vaddr += 0xdc80000;
#endif
	if ((vaddr & 0xdfe00000) == 0x9fc00000)
		return flash[vaddr & 0x1fffff];
	// NOTE: memory must follow flash :)
	if ((vaddr & 0xc0000000) == 0x80000000)
		return memory[vaddr & 0x1fffffff];
	fprintf(F_dbg, "ACCESS TO 0x%x\n", raddr);
	exit(1);
}

void setChar(reg vaddr, uint8 nv) {
	reg raddr = vaddr;
	fprintf(F_dbg, "set 0x%x = 0x%02x\n", raddr, nv);
#ifdef DO_DELZMA
	if ((vaddr & 0xfff80000) == 0x81f80000)
	{
		flash[vaddr & 0x7ffff] = nv;
		return;
	}
#endif
#ifdef BROADCOM
	if ((vaddr & 0xffff0000) == PERF_BASE)
	{
		fprintf(F_dbg, "bcm %x\n", vaddr);
		if (vaddr >= PERF_BASE && vaddr <= PERF_BASE + sizeof(PerfControl))
		{
			((char*)&BcmPerf)[vaddr & 0x1ff] = nv;
			return;
		}
		if ((vaddr & ~1) == (reg)&UART->Data)
		{
			fprintf(F_dbg, "UART TX: %02x (%c)\n", nv, isprint(nv) ? nv : '.');
			fputc(nv, F_uart);
			fflush(F_uart);
			return;
		}
		if (vaddr >= UART_BASE && vaddr <= UART_BASE + sizeof(Uart))
		{
			((char*)&BcmUart)[vaddr & 0x1ff] = nv;
			return;
		}
		if ((vaddr & ~0xfff) == 0xfffe2000)
			// Data gets copied here at boot, no clue why.
			Bcm2000[vaddr & 0xfff] = nv;
			return;
		if ((vaddr & ~0xff) == 0xfffe0400)
			// Data gets copied here at boot, no clue why.
			return;
	}
	//if ((vaddr & 0xdfffffff) >= 0x81f9d5b0 && (vaddr & 0xdfffffff) < 0x81f9d600)
	if ((vaddr & 0xdff80000) == 0x81f80000)
	{
		BcmRAM[vaddr & 0x7ffff] = nv;
		return;
	}
#endif
#ifdef DO_CRC
	if ((vaddr & ~3) == 0x81f8d1dc)
	{
		have_CRC[vaddr & 3] = nv;
		return;
	}
	if (vaddr >= 0x81f8de08 && vaddr < 0x81f8e208)
	{
		CRC_table[vaddr - 0x81f8de08] = nv;
		return;
	}
#endif
	if ((vaddr & 0xdfe00000) == 0x9fc00000)
	{
		flash[vaddr & 0x1fffff] = nv;
		return;
	}
	// NOTE: memory must follow flash :)
	if ((vaddr & 0xc0000000) == 0x80000000)
	{
		memory[vaddr & 0x1fffffff] = nv;
		return;
	}
	fprintf(F_dbg, "WRITE TO 0x%x\n", raddr);
	exit(1);
}

uint16 getHalfWord(reg addr) {
	return 0
		| getChar(addr    ) << 8
		| getChar(addr + 1)
	;
}

void setHalfWord(reg addr, uint16 nv) {
	setChar(addr    , (nv >>  8)       );
	setChar(addr + 1, (nv      ) & 0xff);
}

uint32 getWord(reg addr) {
	return 0
		| getChar(addr    ) << 24
		| getChar(addr + 1) << 16
		| getChar(addr + 2) << 8
		| getChar(addr + 3)
	;
}

void setWord(reg addr, uint32 nv) {
	setChar(addr    , (nv >> 24)       );
	setChar(addr + 1, (nv >> 16) & 0xff);
	setChar(addr + 2, (nv >>  8) & 0xff);
	setChar(addr + 3, (nv      ) & 0xff);
}

void doins(ins_t ins) {
	opcode_t opcode, funct;
	regid d, s, t;
	regid h; // not a regid, but has the same size... O.o
	imm_t imm;
	offset_t boff;
	ins_t target;
	bool skipAdv = False;
	reg oldPC = PC;
	
	R[zero] = 0;
	
	opcode = ins >> 26;
	s = (ins >> 21) & 31;
	t = (ins >> 16) & 31;
	d = (ins >> 11) & 31;
	h = (ins >>  6) & 31;
	funct = ins & 63;
	imm = (ins & 0xffff);
	boff = imm << 2;
	target = (nPC & 0xf0000000) | ((ins & 0x03ffffff) << 2);
	if (ins == 0)
		;
	else
	if ((ins & 0xfc0007fe) == 0x20) {
		// add[u] $d, $s, $t
		if (ins & 1)
		R[d] = R[s] + R[t];
		else
			sR[d] = sR[s] + sR[t];
	}
	else
	if ((ins & 0xfc000000) == 0x20000000) {
		// addi $t, $s, imm
		// TODO: overflow check?
		R[t] = R[s] + imm;
	}
	else
	if ((ins & 0xfc000000) == 0x24000000) {
		// addiu $t, $s, imm
		R[t] = R[s] + imm;
	}
	else
	if ((ins & 0xfc0007ff) == 0x24) {
		// and $d, $s, $t
		R[d] = R[s] & R[t];
	}
	else
	if ((ins & 0xfc000000) == 0x30000000) {
		// andi $t, $s, imm
		R[t] = R[s] & imm;
	}
	else
	if ((ins & 0xfc000000) == 0x10000000) {
		// beq $s, $t, offset
		if (R[s] == R[t])
			advance_pc(boff);
	}
	else
	if (opcode == 0x14)
	{
		// beq[z]l
		if (R[s] == R[t])
			advance_pc(boff);
		else
		{
			// Weird hack?
			advance_pc(4);
			advance_pc(4);
		}
	}
	else
	if ((ins & 0xfc1f0000) == 0x10000000)
	{
		// NOTE: this mask and match is a GUESS
		// beqz $s, offset
		if (R[s] == 0)
			advance_pc(boff);
	}
	else
	if ((ins & 0xfc1f0000) == 0x04010000) {
		// bgez $s, offset
		if (sR[s] >= 0)
			advance_pc(boff);
	}
	else
	if ((ins & 0xfc1f0000) == 0x04110000) {
		// bgezal $s, offset
		if (sR[s] >= 0) {
			R[31] = PC + 8;
			advance_pc(boff);
		}
	}
	else
	if ((ins & 0xfc1f0000) == 0x1c000000)
	{
		// bgtz $s, offset
		if (sR[s] > 0)
			advance_pc(boff);
	}
	else
	if ((ins & 0xfc1f0000) == 0x18000000)
	{
		// blez $s, offset
		if (sR[s] <= 0)
			advance_pc(boff);
	}
	else
	if (opcode == 0x16 && t == 0)
	{
		// blezl $s, offset
		if (R[s] <= 0)
			advance_pc(boff);
		else
		{
			// Weird hack?
			advance_pc(4);
			advance_pc(4);
		}
	}
	else
	if ((ins & 0xfc1f0000) == 0x04000000)
	{
		// bltz $s, offset
		if (sR[s] < 0)
			advance_pc(boff);
	}
	else
	if ((ins & 0xfc000000) == 0x14000000)
	{
		// bne $s, $t, offset
		if (R[s] != R[t])
			advance_pc(boff);
	}
	else
	if (opcode == 0x15)
	{
		// bne[z]l
		if (R[s] != R[t])
			advance_pc(boff);
		else
		{
			// Weird hack?
			advance_pc(4);
			advance_pc(4);
		}
	}
	else
	if ((ins & 0xfc00fffe) == 0x1a)
	{
		if (ins & 1)
		{
		// divu $s, $t
		R[LO] = R[s] / R[t];
		R[HI] = R[s] % R[t];
		}
		else
		{
			// div $s, $t
			R[LO] = ((sreg*)R)[s] / ((sreg*)R)[t];
			R[HI] = ((sreg*)R)[s] % ((sreg*)R)[t];
		}
	}
	else
	if ((ins & 0xf8000000) == 0x08000000)
	{
		// j[al] target
		if (ins & 0x04000000)
			R[31] = PC + 8;
		advance_pc_abs(target);
	}
	else
	if ((ins & 0xfc1f07ff) == 9)
	{
		R[d] = PC + 8;
		advance_pc_abs(R[s]);
	}
	else
	if ((ins & 0xfc1fffff) == 8)
		// jr $s
		advance_pc_abs(R[s]);
	else
	if ((ins & 0xfc000000) == 0x80000000)
	{
		// lb
		byte b = getChar(R[s] + imm);
		sR[t] = *((sint8*)&b);
	}
	else
	if ((ins & 0xfc000000) == 0x90000000)
		// lbu
		R[t] = getChar(R[s] + imm);
	else
	if ((ins & 0xfc000000) == 0x84000000)
	{
		// lh
		R[t] = getHalfWord(R[s] + imm);
		if (R[t] & 0x8000)
			R[t] |= 0xffff0000;
	}
	else
	if ((ins & 0xfc000000) == 0x94000000) {
		// lhu
		R[t] = getHalfWord(R[s] + imm);
	}
	else
	if ((ins & 0xfc000000) == 0x3c000000) {
		// lui
		R[t] = imm << 16;
	}
	else
	if ((ins & 0xfc000000) == 0x8c000000)
		// lw $t, offset($s)
		R[t] = getWord(R[s] + imm);
	else
	if ((ins & 0xf0000000) == 0xc0000000)
	{
		// LWCz rt, offset(base)
		uint8 co = (ins >> 26) & 3;
		fprintf(F_dbg, "> co%d.$%d = 0x%x\n", co, t, R[s] + imm);
	}
	else
	if ((ins & 0xffe007ff) == 0x40000000)
	{
		// mfc0 $t, $d
		fprintf(stderr, "> $%d = co0.$%d\n", t, d);
#if BROADCOM == 3349
		if (t == 12)
			// Company Options = 0x03 = Unknown
			// Company ID      = 0x34 = Broadcom
			//     CPU ID      = 0x91 = MIPS 4KEmp (MIPS32R2 compliant)
			// Silicon Revision= 0x7f = Unknown/Arbitrary
			R[t] = 0x0334917f;
		else
#endif
		R[t] = 0;
	}
	else
	if ((ins & 0xffff07ff) == 0x10)
		// mfhi $d
		R[d] = R[HI];
	else
	if ((ins & 0xffff07ff) == 0x12)
		// mflo $d
		R[d] = R[LO];
	else
	if (!opcode && funct == 11)
	{
		// movn
		if (R[t])
			R[d] = R[s];
	}
	else
	if (!opcode && funct == 10)
	{
		// movz $d, $s, $t
		if (R[t] == 0)
			R[d] = R[s];
	}
	else
	if ((ins & 0xffe007ff) == 0x40800000) {
		// mtc0 $t, $d
		fprintf(F_dbg, "> co0.$%d = 0x%x\n", d, R[t]);
	}
	else
	if (opcode == 0x1c && funct == 2 && h == 0)
		sR[d] = sR[s] * sR[t];
	else
	if ((ins & 0xfc00ffff) == 0x18)
		// mult $s, $t
		// TODO: do we need to populate HI too?
		R[LO] = sR[s] * sR[t];
	else
	if (!opcode && funct == 0x19)
		// multu $s, $t
		R[LO] = R[s] * R[t];
	else
	if ((ins & 0xfc0007ff) == 0x27)
		// nor $d, $s, $t
		// FIXME: GUESSWORD
		// 0000 0000 0000 1011 0001 0000 0010 0111
		// <opcod><arg2><arg3> <arg1><----><funct>
		R[d] = ~(R[s] | R[t]);
	else
	if ((ins & 0xfc0007ff) == 0x25)
		// or $d, $s, $t
		R[d] = R[s] | R[t];
	else
	if ((ins & 0xfc000000) == 0x34000000)
		// ori $t, $s, imm
		R[t] = R[s] | imm;
	else
	if ((ins & 0xfc000000) == 0xa0000000)
		// sb $t, offset($s)
		setChar(R[s] + imm, R[t]);
	else
	if ((ins & 0xfc000000) == 0xa4000000)
		// sh $t, offset($s)
		setHalfWord(R[s] + imm, R[t]);
	else
	if ((ins & 0xfc00003f) == 0)
		// sll $d, $t, h
		R[d] = R[t] << h;
	else
	if ((ins & 0xfc00003f) == 4)
		// sllv $d, $t, $s
		R[d] = R[t] << R[s];
	else
	if (!opcode && funct == 0x2a)
		// slt $d, $s, $t;
		R[d] = (sR[s] < sR[t]) ? 1 : 0;
	else
	if ((ins & 0xfc000000) == 0x28000000)
		// slti
		R[t] = (R[s] < imm) ? 1 : 0;
	else
	if ((ins & 0xfc000000) == 0x2c000000)
		// sltiu
		R[t] = (R[s] < *((uimm_t*)&imm)) ? 1 : 0;
	else
	if ((ins & 0xfc0007ff) == 0x2b)
		// sltu $d, $s, $t
		R[d] = (R[s] < R[t]) ? 1 : 0;
	else
	if (!opcode && funct == 3)
		// sra $d, $t, h
		sR[d] = sR[t] >> h;
	else
	if (!opcode && funct == 7)
		// srav $d, $t, $s
		sR[d] = sR[t] >> sR[s];
	else
	if ((ins & 0xfc00003f) == 2)
		// srl $d, $t, h
		R[d] = R[t] >> h;
	else
	if ((ins & 0xfc0007ff) == 6)
		// srlv $d, $t, $s
		R[d] = R[t] >> R[s];
	else
	if ((ins & 0xfc0007ff) == 0x22)
		// sub $d, $s, $t
		// neg $d, $t (if $s == 0)
		R[d] = R[s] - R[t];
	else
	if ((ins & 0xfc0007ff) == 0x23)
		// subu
		R[d] = R[s] - R[t];
	else
	if ((ins & 0xfc000000) == 0xac000000)
		// sw $t, offset($s)
		setWord(R[s] + imm, R[t]);
	else
	if (!opcode && funct == 0x26)
		// xor $d, $s, $t
		R[d] = R[s] ^ R[t];
	else
	if ((ins & 0xfc000000) == 0x38000000)
		// xori $t, $s, imm
		R[t] = R[s] ^ imm;
	else
	if ((ins & 0xfc000000) == 0xbc000000)
		// cache imm-t, offset($s)
		// 1011 1101 0000 1000 0000 0000 0000 0000
		//        < reg><oper><      offset      >
		// SeeMIPSRun pg 93 / pdf 114
		fprintf(F_dbg, "TODO: cache 0x%x,%d($%d)\n", t, imm, d);
	else
#ifdef BROADCOM
	if (ins == 0x40088001)
		fprintf(F_dbg, "UNKNOWN BROADCOM EXTENSION: %08x\n", ins);
	else
	if ((ins & 0xfc1f0000) == 0x70050000)
		// UNKNOWN BROADCOM EXTENSION ( @ b58: 0x71455002 )
		// 0111 0001 0100 0101 0101 0000 0000 0010
		//              <fixd>
		// <fixed><s:t2><t:a1> <d:t2><h:0 ><fixed>
		//                     < ...or immediate >
		fprintf(F_dbg, "UNKNOWN BROADCOM EXTENSION: %08x\n", ins);
	else
#endif
#ifdef LUKE_SPECIAL
	if (ins == 0xffffffff)
	{
		if (R[v0])
			fprintf(F_info, "Warning: v0 is not null\n");
		
		uint32 size = getWord(0x80100000);
		fprintf(F_info, "Dumping %d bytes...\n", size);
		for (uint32 i = 0; i < size; ++i)
			fputc(memory[i], F_uart);
		exit(0);
	}
	else
#endif
#ifdef DO_HCS
	if (ins == 0xffffffff)
	{
		if (R[v0])
			fprintf(F_info, "Warning: v0 is not null\n");
		
		uint16 HCS = getHalfWord(0x80000002);
		fprintf(F_info, "HCS: %04x\n", HCS);
		fputc(HCS >> 8  , F_uart);
		fputc(HCS & 0xff, F_uart);
		exit(0);
	}
	else
#endif
#ifdef DO_CRC
	if (ins == 0xffffffff)
	{
		if (R[v0])
			fprintf(F_info, "Warning: v0 is not null\n");
		
		fprintf(F_info, "CRC: %08x\n", v0);
		fputc((v0 >> 24)       , F_uart);
		fputc((v0 >> 16) & 0xff, F_uart);
		fputc((v0 >>  8) & 0xff, F_uart);
		fputc( v0        & 0xff, F_uart);
		exit(0);
	}
	else
#endif
#ifdef DO_DELZMA
	if (ins == 0xffffffff)
	{
		if (R[v0])
			fprintf(F_info, "Warning: v0 is not null\n");
		
		uint32 size = getWord(R[sp]);
		fprintf(F_info, "Dumping %d bytes...\n", size);
		for (uint32 i = 0; i < size; ++i)
			fputc(memory[i], F_uart);
		exit(0);
	}
	else
#endif
	{
		fprintf(F_dbg, "Unknown Instruction: 0x%08x\n", ins);
		fprintf(F_dbg, "\topcode=0x%x (%d)\n\tfunct=0x%x (%d)\n\ts=%d t=%d d=%d h=%d\n\timm=0x%x (%d)\n\tboff=0x%lx (%ld)\n\ttarget=0x%x\n", opcode, opcode, funct, funct, s, t, d, h, imm, imm, boff, boff, target);
		exit(1);
	}
	
	if (oldPC == PC && !skipAdv)
		advance_pc(4);
}

void readflash() {
	FILE *f = fopen(FLASH_FILE, "r");
	fread(flash, FLASH_SIZE, 1, f);
#if REVERSE_FLASH_ENDIAN
	for (int i = 0; i < INTFLASH_SIZE; ++i)
	{
		intflash[i] = 0
			| (intflash[i] & 0x000000ff) << 24
			| (intflash[i] & 0x0000ff00) << 8
			| (intflash[i] & 0x00ff0000) >> 8
			| (intflash[i] & 0xff000000) >> 24
		;
	}
#endif
	fclose(f);
}

reg oR[REG_MAX];

int main(int argc, char**argv) {
	fprintf(F_dbg, "Emulating a %x :)\n", getHalfWord(0xfffe0000));
	
	readflash();
	PC = 0xbfc00000;
	
#ifdef LUKE_SPECIAL
	PC = 0xbfc012c0;
#if LUKE_SPECIAL == 0 // Bootloader
	R[a0] = 0x9fc01648;
	R[a1] = 0x68a4;
#endif
#if LUKE_SPECIAL == 1 // Image 1
	R[a0] = 0x9fc10000;
	R[a1] = 0xb8019;
#endif
	R[a2] = 0x80000000;
	R[a3] = 0x80100000;
#endif
#ifdef DO_HCS
	PC = 0xbfc04ef8;
	R[sp] = 0x8000fff0;
	R[a0] = 0x80001000;
	fread(memory + 0x1000, 84, 1, stdin);
	R[a1] = 0;
	R[a2] = 84;
	R[a3] = 0x80000000;
	R[t0] = 0;
#endif
#ifdef DO_CRC
	PC = 0xbfc04cd4;
	R[sp] = 0x80800000;
	R[a0] = 0x80000000;
	R[a1] = 
	fread(memory, 1, 0x800000, stdin);
	if (R[a1] > 0x800000)
		fprintf(F_dbg, "WARNING: Filled CRC data buffer!\n");
	else
	if (R[a1] > 0x7ff000)
		fprintf(F_dbg, "WARNING: Nearly filled CRC data buffer!\n");
#endif
#ifdef DO_DELZMA
	PC = 0x81f898e8;
	R[t0] =
	R[sp] = 0x807ffff0;
	R[a0] = 0x80800000;
	R[a1] =
	fread(memory, 1, 0x800000, stdin);
	R[a2] = 0x80000000;
	R[a3] = 0x1700000;
	
	flash[0x101b0] = 0xff;
	flash[0x101b1] = 0xfe;
	flash[0x101b2] = 3;
	flash[0x101b3] = 0;
#endif
	R[ra] = 0xfffffffc;
	
	nPC = PC + 4;
	
	while(1) {
		fprintf(F_dbg, "PC=0x%x nPC=0x%x ins=0x%08x\n", PC, nPC, getWord(PC));
		if (PC == 0xbfc00e94)
			R[t2] = 0x80000; // HACK HACK HACK to avoid TLB exception
		doins(getWord(PC));
		for (int i = 0; i < REG_MAX; ++i)
		{
			if (R[i] != oR[i])
			{
#ifdef LUKE_SPECIAL
				if (i == t2)
					fprintf(F_info, "\tPosIN = %d\n", R[t2]);
				else
				if (i == t6)
					fprintf(F_info, "\tOutLen = %d\n", R[t6]);
				else
#endif
				fprintf(F_dbg, "\t$%d = 0x%x\n", i, R[i]);
			}
			oR[i] = R[i];
		}
	}
	
	return 0;
}



