//
typedef unsigned char  u8;
typedef unsigned short u16;
typedef unsigned int   u32;

typedef signed char  s8;
typedef signed short s16;
typedef signed int   s32;
 
#include "lpc111x.h"
#define NULL ((void *)0)

#include "rom.h"

#define PCLK 50000000
#define _delay_us(n) delay_loop((n*(PCLK/1000)-6000)/4000)
#define _delay_ms(n) delay_loop((n*(PCLK/1000)-6)/4)

/* Already in SD loader ROM
// Active delay.
// When executing from Flash the alignement is Very important
// 4 clocks per iteration -2 clocks of loop exit + 6 clocks for call and return
void delay_loop(unsigned int) __attribute__ (( naked, aligned(8) ));
void delay_loop(unsigned int d)
{
	asm volatile ("Ldelay_loop: sub r0,#1");
	asm volatile ("  bne Ldelay_loop");
	asm volatile ("  bx lr");
}

// UART basic I/O

void U0putch(int d)
{
	while(!(U0LSR&0x20));
	// send CR,LF instead of LF only
	if (d=='\n') U0THR='\r';
	U0THR=d;
}
*/

#define putchar(c) U0putch(c)
#include "printf.c"

// Raw UART RX
int U0getch()
{
	while(!(U0LSR&1));
	return U0RBR;
}

// RX with CR/LF filtering
// Auto detect CR,LF and
// return LF only

int _getch()
{
	static u8 d,od;

	do {
		while(!(U0LSR&1));
		od=d; d=U0RBR;
	} while (d=='\n' && od=='\r');

	return (d=='\r')?'\n':d;
}

// String reading with basic editing
int _gets( unsigned char *buf)
{
	int i,d;
	i=0;
	while(1) {
		d=_getch();
		switch(d) {
			case '\n': buf[i]=0; U0putch(d); return i;
			case '\b':
			case 127:
				if (i) {
					i--;
					_printf("\b \b");
				}
				break;
			default:
				buf[i++]=d;
				U0putch(d);
				break;
		}
	}
}

// Other components of the code

u8 sdok;	// Flag for valid SD card present

//#include "sdspi.c"

//#define rdsector(a,b) SD_read_Block(a,b)
//#include "fat32.c"
#include "fnav.c"

#define CLK_L()	{GPIO0MASK[1<<11]=-1;} 
#define CLK_H()	{GPIO0MASK[1<<11]=0;}
#define OE_L()	{GPIO3MASK[1<<4]=0;} 
#define OE_H()	{GPIO3MASK[1<<4]=-1;}
#define WE_L()	{GPIO3MASK[1<<5]=0;} 
#define WE_H()	{GPIO3MASK[1<<5]=-1;}

#define D_OUT() {GPIO2DIR=0xfff; GPIO3DIR=0x3f;}
#define D_IN() {GPIO2DIR=0; GPIO3DIR=0x30;}

#define GTDATA(d) { GPIO2MASK[0xfff]=d; GPIO3MASK[0xf]=d>>12;}
#define GTDATAIN() (GPIO2MASK[0xfff]|(GPIO3MASK[0xf]<<12))

#include "dissa.c"

//////////////////////////////////////////////////////////////////////////////////////////
//	Helper functions for Gigatron
//////////////////////////////////////////////////////////////////////////////////////////
// Read PC
unsigned int GTaddr()
{
	unsigned int j,i;
	j=GPIO1MASK[0b111100111111];
	i=(j&0b111111)<<6;
	i|=(j>>8)<<12;
	i|=GPIO0MASK[0b11111100]>>2;
	return i;
}
// Execute op-code
void GTexec(u32 d)
{
	GTDATA((d)); 
	asm volatile (" b .+2"); 
	CLK_H(); 
	asm volatile (" b .+2"); 
	CLK_L();
}

// Set the PC value
void set_pc(unsigned int ad)
{
	OE_H();
	D_OUT();

	GTexec((ad&0xFF00)   |0x0014);	// LD $MSB,Y
	GTexec(((ad&0xff)<<8)|0x00E0);	// JMP Y,$LSB
	GTexec(0x0002);					// NOP (LD AC,AC) 
}

// Run an indefinite number of cycles at full speed
void GTrun()
{
	D_IN();
	OE_L();

	// Start clock
	CLK_H();
	TMR32B0MCR=2;		// reset on MR0
	IOCON_R_PIO0_11=3|(1<<7);	// P0.11 as CT32B0_MAT3
	TMR32B0TCR=1;		// Start timer
	_printf("press any key to stop");
	_getch();

	TMR32B0MCR=2+4;		// stop and reset on MR0
	_delay_us(1);
	TMR32B0TCR=3;		// Timer reset
	IOCON_R_PIO0_11=1|(1<<7);	// P0.11 as GPIO
	CLK_L();
	OE_H();
}

////////////////////////////////////////////////////////
// GT operations
////////////////////////////////////////////////////////
// Gigatron State 
struct {
	u16 PC;
	u16	IR;
	u8	AC;
	u8	X;
	u8	Y;
	u8	IN;
	u8	ZP[256];
} GTstate,ost;	// ost used for change remark

u16 codepc[18];	// A few op-codes after [PC] for dissassembly

// Saves the Gigatron state in the GTstate struct
// with the exception of IR register.
void GTsave_state()
{
	u32 i;
	GTstate.PC=GTaddr();
	// read a few more opcodes at PC+i
	for (i=0;i<18;i++) {
		D_IN();	OE_L();
		codepc[i]=GTDATAIN();
		OE_H();	D_OUT();
		GTexec(0x0002);	//NOP
	}
	GTexec(0x00E0);	// JMP Y,$0
	GTexec(0x0016);	// LD AC,Y 
	GTstate.Y=GTaddr()>>8;
	GTexec(0x00E0);	// JMP Y,$0
	GTexec(0x0017);	// LD IN,Y 
	GTstate.AC=GTaddr()>>8;
	GTexec(0x00E0);	// JMP Y,$0
	GTexec(0x0015);	// LD [$00],Y
	GTstate.IN=GTaddr()>>8;
	// save Zero Page
	for (i=0;i<256;i++) {
		GTexec(0x00E0);	// JMP Y,$0
		GTexec(((i+1)<<8)|0x0015);	// LD [$nn],Y
		GTstate.ZP[i]=GTaddr()>>8;
	}
	// Clear Zero Page
	GTexec(0x0000);	// LD 0,AC
	for (i=0;i<256;i++) GTexec((i<<8)|0x00C2);	//ST AC,[nn]
	// Detect X
	GTexec(0xFF00);	// LD $ff,AC
	GTexec(0x00C6);	// ST AC,[X]
	GTexec(0x0015);	// LD [$00],Y
	for (i=0;i<256;i++) {
		GTexec(0x00E0);	// JMP Y,$0
		GTexec(((i+1)<<8)|0x0015);	// LD [$nn],Y
		if ((GTaddr()>>8)==0xFF) break;
	}
	GTstate.X=i;
	GTexec(0x0002);	// NOP
}

// Execute one step and save the state, including the IR reg.
void GTstep_n_save_state()
{
	u32 i;
	D_IN();
	OE_L();
	GTstate.IR=GTDATAIN();
	OE_H();
	D_OUT();
	GTexec(0x0002);	//NOP to execute current IR contents
	GTsave_state();
}

// Load the registers and RAM with their GTstate values
void GTrestore_state()
{
	u32 i;
	OE_H();
	D_OUT();
	// restore Zero Page
	for (i=0;i<256;i++) {
		GTexec(GTstate.ZP[i]<<8);	// LD $xx,AC
		GTexec((i<<8)|0x00C2);	//ST AC,[nn]
	}
	// restore AC
	GTexec(GTstate.AC<<8);	// LD $xx,AC
	// restore PC
	i=(GTstate.PC-1)&0xFFFF;
	GTexec((i&0xFF00)|0x0014);	// LD $xx,Y
	GTexec((i<<8)|0x00E0);		// JMP Y,$0
	// restore Y
	GTexec((GTstate.Y<<8)|0x0014);	// LD $xx,Y	
	// restore IR
	GTexec(GTstate.IR);
}

// Display the GTstate struct contents in a convenient form, including 
// some dissassembly and hexadecimal dump of the Zero Page
// This function relies on ANSI escape sequences.
// Changed values are shown in red.
void GTshow_state()
{
	u32 i,j,k,da;
	u8 *p;
#define TABCOL 28
	_printf("\033[H");	// Clear screen & cursor Home
	for(i=0;i<TABCOL+4;i++) putchar(' ');
	_printf(" PC  AC  X   Y   IN\n");
	// 2nd line
	for(i=0;i<TABCOL+4;i++) putchar(' ');
	_printf("\033[01;34m%04x\033[m ",GTstate.PC);
	_printf(GTstate.AC!=ost.AC?    "\033[01;31m%02x\033[m  ":"%02x  ",GTstate.AC);
	_printf(GTstate.X!=ost.X?      "\033[01;31m%02x\033[m  ":"%02x  ",GTstate.X);
	_printf(GTstate.Y!=ost.Y?      "\033[01;31m%02x\033[m  ":"%02x  ",GTstate.Y);
	_printf(GTstate.IN!=ost.IN?    "\033[01;31m%02x\033[m\n":"%02x\n",GTstate.IN);
	// 3rd line
	p=dissassemble(GTstate.IR);
	_printf("\033[01;31m IR: %04x %s\033[m        \n",GTstate.IR,p);
	// 4th line and more
	_printf("\033[01;34m"); 
	da=GTstate.PC;
	for (j=0;j<18;j++) {
		p=dissassemble(codepc[j]);
		_printf("%04x %04x %s",da,codepc[j],p);
		//for(i=strlen(p)+10;i<TABCOL;i++) putchar(' ');
		i=0; while(p[i]) i++;
		for(i+=10;i<TABCOL;i++) putchar(' ');
		da++;
		if (j==0) {
			_printf("\033[m   ");
			for (k=0;k<16;k++) _printf(" %2x",k);
		} else if (j>1) {
			// Page 0 dump
			_printf("%02x ",(j-2)*16);	// address
			for (k=0;k<16;k++) {
				_printf(GTstate.ZP[(j-2)*16+k]!=ost.ZP[(j-2)*16+k]?
					" \033[01;31m%02x\033[m" : " %02x",GTstate.ZP[(j-2)*16+k]);
			}
		}
		putchar('\n');
	}
	ost=GTstate;		// Actual state is now the old one
}

// Routine to generate "n" clock pulses at 6.25MHz
// (8 ARM cycles per pulse)
// Alignment is critial if running from flash
void __attribute__((naked)) ncyloop(u32 n)
{
asm volatile(
"		ldr	r3,=GPIO0MASK+(4<<11)		\n"
"		mov	r2,#0						\n"
"		mvn	r1,r2						\n"
"		.align 4						\n"
"1:		str	r2,[r3]						\n"
"		sub	r0,#1						\n"
"		str	r1,[r3]						\n"
"		bne	1b							\n"
"		bx	lr							\n"
"		.ltorg							\n"
);
}

void GTNcy(u32 n)
{
	D_IN();
	OE_L();
	ncyloop(n);
	OE_H();
}

// Routine to run until the PC reaches a given value.
// Parametres are the values we wait at ports, derived from the PC value
// This function is slow, so it is not suited for video generation.
// Average frequency about 50MHz/15 (3.3 MHz)
void __attribute__((naked)) topcloop(u32 pio0, u32 pio1)
{
asm volatile(
"		push {r4-r7,lr}					\n"
"		ldr	r3,=GPIO0MASK+(0xFC<<2)		\n"
"		ldr	r4,=GPIO1MASK+(0xF3F<<2)	\n"
"		ldr	r7,=GPIO0MASK+(4<<11)		\n"
"		mov	r5,#0						\n"
"		mvn	r6,r5						\n"
"		b	2f							\n"
"		.align 4						\n"
"1:		str	r5,[r7]						\n"	// rising clock
"		b	.+2							\n"	// 3 cycles
"		ldr	r2,[r3]						\n"	// 2 cycles
"		str	r6,[r7]						\n"	// falling clock
"2:		ldr	r2,[r3]						\n"
"		cmp	r0,r2						\n"
"		bne	1b							\n"
"		ldr	r2,[r4]						\n"
"		cmp	r1,r2						\n"
"		bne	1b							\n"
"topcloopend: 							\n"
"		pop {r4-r7,pc}					\n"
"		.ltorg							\n"
);
}
// wrapper for the above asm code.
// UART interrupts are enabled and they can stop the asm loop.
void GTrun_until_PC(u32 pc)
{
	u32 l,h;
	_printf("press any key to stop");
	D_IN();
	OE_L();
	l=(pc<<2)&0xFC;
	h=((pc>>6)&0x3f) | ((pc>>4)&0xF00);
	ICPR=1<<21;
	ISER=1<<21;
	topcloop(l,h);
	ICER=1<<21;
}

////////////////////////////////////////////////////////////////
/////////////////////////// BREAKPOINTS ////////////////////////
////////////////////////////////////////////////////////////////
#define MAXBRK	4	// max number of breackpoints, including temporary
struct {
	u16	nbrk;		// actual number of brks
	u16 pc[MAXBRK];	// Address
	u16 ir[MAXBRK];	// Original op-code
} brkps;
volatile u8 brkint;		// Flag for brkloop interrupted

// Routine to run until a BRK op-code is found
// 6.25MHz generated by a PWM output from TIMER 32B0
// The BRK opcode is executed before stopping, but it is a NOP
void __attribute__((naked)) brkloop()
{
asm volatile(
"		push {r4,lr}					\n"
"		ldr	r3,=TMR32B0BAS+4			\n"	//TMR32B0TCR
"		ldr	r4,=GPIO2MASK+(0xff<<2)		\n"
"		mov	r0,#0						\n"
"		mov	r1,#1						\n"
"		.align 4						\n"
"		str	r1,[r3]						\n" // start timer
"		ldr	r2,[r4]						\n"
"1:		ldr	r2,[r4]						\n"
"		ldr	r2,[r4]						\n"
"		cmp	r2,#0x4E					\n"	// BRK opcode?
"		bne	1b							\n"
"brkloopend:							\n"
"		str	r0,[r3]						\n" // stop timer
"		pop {r4,pc}						\n"
"		.ltorg							\n"
);
}

// Function to run the Gigatron with breakpoints enabled.
// It is way more than a simple wrapper to the above asm code.
void GTrunBRK()
{
	u32 i,ir,pc;

	// Set the breakpoints in program memory
	for (i=0;i<brkps.nbrk;i++) {
		set_pc(brkps.pc[i]);
		D_IN();					// first read the original op-code
		OE_L();
		brkps.ir[i]=GTDATAIN();
		OE_H();					// then place a BRK in its address
		D_OUT();
		GTDATA(0x004E);
		WE_L(); WE_H();
		_printf("brk: %04x (%04x: %s)\n",brkps.pc[i],brkps.ir[i],dissassemble(brkps.ir[i]));
	}
	
	// Prepare to run
	GTrestore_state();
	D_IN();
	OE_L();
	// Set UART IRQ to stop execution
	_printf("press any key to stop");
	ICPR=1<<21;
	ISER=1<<21;
	brkint=0;
	// Start clock
	CLK_H();
	TMR32B0MCR=2;		// reset on MR0
	IOCON_R_PIO0_11=3|(1<<7);	// P0.11 as CT32B0_MAT3
	// Enter timing critical code
	brkloop();
	// 
	ICER=1<<21;			// No more IRQs
	// Stop timer completely
	CLK_L();
	IOCON_R_PIO0_11=1|(1<<7);	// P0.11 as GPIO
	TMR32B0TCR=3;

	// Save PC 
	pc=GTaddr();
	pc=(pc-1)&0xffff;
	// check for exit via interrupt
	if (brkint) {		// Interrupted. Not a BRK
		D_IN();
		OE_L();
		GTstate.IR=GTDATAIN();
		OE_H();
		D_OUT();
		GTexec(0x0002);	//NOP to execute IR contents
	}
	// Save the rest of the state
	GTsave_state();

	// restore program memory
	OE_H();
	D_OUT();
	for (i=0;i<brkps.nbrk;i++) {
		set_pc(brkps.pc[i]);
		GTDATA(brkps.ir[i]);
		WE_L(); WE_H();
		// restore also opcodes for dissasembly
		if ((brkps.pc[i]-pc-1)<18) {
			codepc[brkps.pc[i]-pc-1]=brkps.ir[i];
		}
	}
	// BRK: copy the original op-code to GTstate.IR
	if (!brkint) {
		set_pc(pc);
		D_IN();
		OE_L();
		GTstate.IR=GTDATAIN();
		OE_H();
		D_OUT();
	}
}

// Load a ROM image from an open file in the SD card
void load_ROM()
{
	u16 *ps;
	u32 i,j;
	set_pc(0);
	ps=FATbuf;
	do {
		i=rdfile(ps);
		for (j=0;j<i/2;j++) {
			GTDATA(ps[j]); 
			WE_L(); WE_H();
			GTDATA(0x0002); CLK_H(); CLK_L();
		}
	} while (i==512);
}

// Load a ROM image via XMODEM
void load_ROMx()
{
	u16 *ps;
	u32 i,j;
	set_pc(0);
	ps=FATbuf;
	j=0;
	putchar(21);	// NACK to start
	while(1) {
		i=rx_xmodem128(ps); 
		if (!i) break;
		if (i!=128) continue;
		for (i=0;i<64;i++) {
			GTDATA(ps[i]); 
			WE_L(); WE_H();
			GTDATA(0x0002); CLK_H(); CLK_L();
		}
		j+=128;
		putchar(6);	// ACK
	}
}

/////////////////////////////////////////////////////////////
// Helper functions for memory fransfers
/////////////////////////////////////////////////////////////
// write to data RAM
void write_GT_RAM(u32 addr, u8 *psrc, u32 nbytes)
{
	u32 y;
	OE_H();
	D_OUT();
	y=addr&0xFF00;
	GTexec(y|0x0014);					// LD $nn,Y
	for (;nbytes;nbytes--){
		GTexec(((*psrc++)<<8)|0x0000);	// LD $nn,AC
		GTexec((addr<<8)|0x00CA);		// ST AC,[Y,$nn]
		addr++;
		if ((addr&0xff00)!=y) {
			y=addr&0xff00;
			GTexec(y|0x0014);			// LD $nn,Y
		}
	}
	GTexec(0x0002);						// NOP
}

// read from data RAM
void read_GT_RAM(u32 addr, u8 *pdst, u32 nbytes)
{
	u32 y;
	OE_H();
	D_OUT();
	GTexec((addr&0xff00)|0x0014);		// LD $nn,Y
	for (;nbytes;nbytes--){
		GTexec((addr<<8)|0x0009);		// LD [Y,$nn],AC
		GTexec(0x0016);					// LD AC,Y
		GTexec(0x00E0);					// JMP Y,0
		addr++;
		GTexec((addr&0xff00)|0x0014);	// LD $nn,Y
		*pdst++=GTaddr()>>8;
	}
}

// read from program ROM
void read_GT_ROM(u32 addr,u16 *pdst, u32 nwords)
{
	set_pc(addr);
	for(;nwords;nwords--){
		D_IN();
		OE_L();
		*pdst++=GTDATAIN();
		OE_H();
		D_OUT();
		GTexec(0x0002);	//NOP: next addr.
	}
}

///////////////////////////////////////////////////////////////////
// GT1 file loading
///////////////////////////////////////////////////////////////////
// ch: 0 - From SD card
//     1 - From XMODEM
void load_GT1(int ch)
{
	u32 addr,n,j;
	s32 i,d,e;
	u8 buf[256];
	static const u8 * const gt1ext[]={"GT1",NULL};

	if (!ch && !sdok) return;

	GTrestore_state();
	// wait until video line...
	GTrun_until_PC(0x210);
	GTstep_n_save_state();
	
	if(ch) {
		xbopen();
		_printf("\033[2J\033[HInput Xmodem GT1 file\n");
		putchar(21);	// NACK to start
	} else {
		fbopen();
		file_select(gt1ext,"Select GT1 file");
		//file_select(NULL,"Select GT1 file");
	}
	j=0;
	while(1) {
		if((i=muxgetch(ch))<0) break; 
		if (!i && j) {	// End of GT1
			if((d=muxgetch(ch))<0) break;
			if((e=muxgetch(ch))<0) break;
			addr=(d<<8)|e;
			break;
		} else {
			j++;
			if((d=muxgetch(ch))<0) break;
			if((e=muxgetch(ch))<0) break;
			addr=(i<<8)|d;
			n=e;
		}
		//_printf("%04x %d bytes\n",addr,n);
		for (i=0;i<n;i++) {
			if((d=muxgetch(ch))<0) goto ldgend;
			buf[i]=d;
		}
		if (addr<0x100) memcpy(&GTstate.ZP[addr],buf,n);
		else write_GT_RAM(addr,buf,n);
	} 
ldgend:
	if(ch) xbclose();	// for xmodem

	GTstate.ZP[0x1A]=GTstate.ZP[0x16]=(addr-2)&0xff;	// vPC, vLR low
	GTstate.ZP[0x1B]=GTstate.ZP[0x17]=addr>>8;			// vPC, vLR High
	GTstate.ZP[0x1C]=0x00;		// vSP
}

//////////////////////////////////////////////////////////////////
// Hexadecimal DUMP
void hexdump(u8 *buf,u32 addr, u32 nbytes)
{
	u32 i,j,n,d;

	for (i=0;i<nbytes;i+=16){
		n=nbytes-i; if (n>16) n=16;
		_printf("%04X  ",addr+i);
		for (j=0;j<n;j++) {
			if (j==8) putchar(' ');
			_printf("%02X ",buf[i+j]);
		}
		putchar(' ');
		for (j=0;j<n;j++) {
			if (j==8) putchar(' ');
			d=buf[i+j];
			if (d<32 || d>126) d='.';
			putchar(d);
		}
		putchar('\n');
	}
}

//////////////////////////////////////////////////////////////////
// UART INTERRUPT
//////////////////////////////////////////////////////////////////
// Can change its return address in the stack, terminating 
// the loops of clock generation.

// asm entry code
void __attribute__((naked)) UART_interrupt(void)
{
asm volatile(
"	mov	r0,sp	\n"
"	b	UART_ISR	\n"
);
}
// return addresses for the CLK generation loops
extern volatile u16 topcloopend,brkloopend;

// Actual C code. The argument is the value of the stack pointer
void UART_ISR(u32 *sp)
{
	u32 i;
	i=U0RBR;
	i=sp[6];		// PC value when interrupted
	// Execute to PC loop
	if (i>(u32)topcloop && i<(u32)&topcloopend) {
		sp[6]=(u32)&topcloopend;
		CLK_L();
	}
	// Execute with BRK loop
	if (i>(u32)brkloop && i<(u32)&brkloopend) {
		sp[6]=(u32)&brkloopend;
		TMR32B0MCR=2+4;		// stop and reset on MR0
		_delay_us(1);
		TMR32B0TCR=3;		// Timer reset
		brkint=1;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//// 				MAIN
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
u8 mbuf[256];
u16 prgbuf[20];

int __attribute__ ((noreturn)) main()
{
	int i,j;
	u32 dadd=0,padd=0;		// Data mem address and Program mem address.
	u16 *ps;
	static const u8 * const romext[]={"ROM","BIN",NULL};

/*
	//////////////////////////////////////////////////////////////////////
	// SYSTEM INIT
	//////////////////////////////////////////////////////////////////////
    // Turn on clock for IOCON
    SYSAHBCLKCTRL|= BIT16;
	// Release all peripheral resets
	PRESETCTRL=0x0F;	

	// UART. See "Enabling sequence for UART clock" on LPC111x User Manual
	// first assign pin functions for UART	
	IOCON_PIO1_6=1; // RXD
	IOCON_PIO1_7=1; // TXD
	// then enable the UART clock
	UARTCLKDIV=1;	// then enable the UART clock
	SYSAHBCLKCTRL= 0x7FFFF;	// Clock for all (also for UART)
	// data baud & format
    U0LCR=0x83;
	U0DLM=0;
	U0DLL=((PCLK/115200)+8)/16;		// 115741 bps @ 50 MHz (+0.47% error)
	U0LCR=0x3;		// 8-bit 
	U0FCR=7;		// FIFOS on, RX IRX trigger at 1 byte received.
	U0IER=1;		// interrupt on RX

	// PINs	
	//IOCON_PIO0_1=0;
	IOCON_PIO0_8=1;		// MISO	
	IOCON_PIO0_9=1;		// MOSI
	IOCON_SWCLK_PIO0_10=2;	// SCK
*/
	// pins reserved by default as GPIO :(
	IOCON_R_PIO0_11=1|(1<<7);
	// pins with ADC inputs..
	IOCON_R_PIO1_0=IOCON_R_PIO1_1=IOCON_R_PIO1_2=IOCON_SWDIO_PIO1_3=1|(1<<7);
	IOCON_PIO1_4=IOCON_PIO1_10=IOCON_PIO1_11=1<<7;
    GPIO0DIR = (1<<1)|(1<<11); 	// Output pins
	//SD_CS_H();
	CLK_L();
	GPIO3DIR = (1<<4)|(1<<5);
	OE_H();
	WE_H();
	
/*
	//SPI0
	SSP0CLKDIV=1;		// SPI0 @ 50MHz
	SSP0CR0=7|(62<<8);	// 8 bit, SPI, 400kHz (for SD init)
	SSP0CR1=2;			// SSP enabled
	SSP0CPSR=2;			// 25 MHz
*/

	// Timer 32B0
	TMR32B0TCR=3;		// Enable & reset
	TMR32B0PR=0;		// Prescaler = 1/1 (50MHz)
	TMR32B0MR0=7;		// PWM period (8)
	TMR32B0MR3=4;		// PWM duty (50%)
	TMR32B0MCR=2;		// reset on MR0
	TMR32B0PWMC=1<<3;	// PWM on MAT3

	////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////
	brkps.nbrk=0;

	_printf("SD Init...\n");
	
	for (j=3;j;j--){
		if (!(i=SD_init())) break;
	} 
	if (i) _printf("Error sd_init (%d)\n",i);
	else if (i=initFAT()) {_printf("Error initFAT (%d)\n",i); }
	sdok=!i;

	// wait for Gigatron's reset high, because
	// the ARM starts before the GT when power is applied
	_printf("Waiting for GT reset high...");
	OE_H();
	D_OUT();
	GTDATA(0x0002);	// NOP
	do {			// If PC doesn't count it is in reset
		i=GTaddr();
		CLK_H(); CLK_L();
		j=GTaddr();
	} while (i==j);

	// Load file
	if (sdok) {
		cur_dir_cluster=FATvar.root_cluster;

		i=scandir("ROM.BIN",FATvar.root_cluster);
		if (i) _printf("\nLoading ROM.BIN...\n");
		else _printf("\033[2J\033[HLoading %s...\n",file_select(romext,"Select ROM file"));
		load_ROM();
		// and RUN
		set_pc(0);
		GTrun();
	}

	///////////////////////////////////////////////////////////
	// main loop
	///////////////////////////////////////////////////////////
redo1:
	GTstep_n_save_state();

	while(1) {
		_printf("\033[2J");
		GTshow_state();
		_printf("BRKs: ");

		for (i=0;i<brkps.nbrk;i++) {
			_printf((GTstate.PC==brkps.pc[i]+1)?"%d:\033[01;31m%04x\033[m  ":"%d:%04x  ",
					i,brkps.pc[i]);
		}
		putchar('\n');
redo2:
		_printf("(h:help)>");

		_gets(FATbuf); 
		if (!FATbuf[0]) memcpy(FATbuf,&FATbuf[256],256);
		else memcpy(&FATbuf[256],FATbuf,256);

		switch(FATbuf[0]) {
		case 'h':
			_printf(
			"Gigatron ROM emulator v1.1 (J. Arias, 2019)\n"
			"Commands:\n"
			" s        - Single step\n"
			" c        - Continue execution\n"
			" n cycles - Execute number of clock cycles\n"
			" = nnnn   - Execute until PC equals nnnn\n"
			" b nnnn   - Add a breakpoint at PC=nnnn\n"
			" br n     - Remove breakpoint number n\n"
			" bra      - Remove all breakpoints\n"
			" lr       - Load ROM file from SD card\n"
			" lrx      - Load ROM file from XMODEM\n"
			" lg       - Load GT1 file from SD card\n"
			" lgx      - Load GT1 file from XMODEM\n"
			" r        - Reset Gigatron\n"
			" m nnnn   - Data Memory dump from address nnnn\n"
			" m        - Data Memory dump from last address\n"
			" d nnnn   - Dissassemble program memory from address nnnn\n"
			" d        - Dissassemble program memory from last address\n"
			" <empty>  - Repeat last command\n"
			" <other>  - Redisplay\n"
			);
			goto redo2;
		case 's':	// single step
			GTrestore_state();
			GTstep_n_save_state();
			break;

		case 'c':	// Continue
			GTrunBRK();
			break;

		case 'n':	// N cycles
			i=_atoi(&FATbuf[1]);
			//_printf("%d cycles\n",i);
			if (!i) break;
			GTrestore_state();
			if (i>1) GTNcy(i-1);
			GTstep_n_save_state();
			break;

		case 'b':
			if (FATbuf[1]=='r'){
				if (FATbuf[2]=='a') brkps.nbrk=0;
				else {
					i=_atoi(&FATbuf[2]);
					if(i<brkps.nbrk) {
						brkps.nbrk--;
						for (;i<brkps.nbrk;i++) brkps.pc[i]=brkps.pc[i+1];
					}
				}
			} else{
				i=atohex(&FATbuf[1],2);
				if(brkps.nbrk<MAXBRK-1)	brkps.pc[brkps.nbrk++]=i;
			}
			break;

		case 'l':	// load
			if (FATbuf[1]=='r') {	// Load ROM
				if (FATbuf[2]=='x'){
					_printf("\033[2J\033[HInput Xmodem ROM file\n");
					load_ROMx();
				}else{ 
					if(sdok) {
						file_select(romext,"Select ROM file");
						load_ROM();
					}
				}
				GTstate.PC=0; GTstate.IR=0x0002;
				GTrestore_state();
				goto redo1;
			}
			if (FATbuf[1]=='g') {	// Load GT1
				load_GT1((FATbuf[2]=='x'));
				break;
			}

		case 'r':	// Reset
			GTstate.PC=0; GTstate.IR=0x0002;
			GTrestore_state();
			goto redo1;

		case 'm':	// Dump RAM
			i=0; while(FATbuf[i]) i++;
			if (i>2) dadd=atohex(&FATbuf[1],2);
			if (dadd) read_GT_RAM(dadd,mbuf,256);
			else memcpy(mbuf,GTstate.ZP,256);
			hexdump(mbuf,dadd,256);
			dadd+=256;
			goto redo2;

		case 'd':	// Disassemble
			i=0; while(FATbuf[i]) i++;
			if (i>2) padd=atohex(&FATbuf[1],2);
			read_GT_ROM(padd,prgbuf,20);
			for (i=0;i<20;i++) {
				_printf("%04x %04x %s\n",padd++,prgbuf[i],dissassemble(prgbuf[i]));
			}
			goto redo2;

		case '=':	// Temporary Breakpoint
			i=atohex(&FATbuf[1],2);
			brkps.pc[brkps.nbrk++]=i;
			GTrunBRK(); 
			brkps.nbrk--;
			break;
		default:	break;
		}
	}
}

