/*
	SC1Z0232.C
	
	CM71xx/Z1 and CM72xx/Z2

	Demonstration of serial port z0 of the Z180 as a diagnostic port.

	PC terminal or communication program should be set for 9600 baud, 
	8 data bit, 1 stop, no parity. If the terminal program in windows is
	used, select terminal font under terminal preference option.
*/

#define TBUFSIZE 384         // size of transmit buffer
#define RBUFSIZE 384         // size of receive buffer

#define BeepScale 0.04     // BeepScale*millisecond is the actual ontime
									// when up_beep(millisecond) is called.
									// This number has to match the periodicity of
									// lc_beepscan(); 

int adjust_rtc(int data, int cursor);
void showmenu();
void clrscrn();
void box(int tlrow, int tlcol, int brrow, int brcol);
void locxy(int row, int col);
void set_timer0(unsigned int);

main()
{
	char  data;
	int   cursor;
	int   ledonoff;
	int   beeponoff;
	int   changebaud;
	struct tm thistime;
	int   oldsec;
	char tbuf[TBUFSIZE];     // transmit buffer
	char rbuf[RBUFSIZE];     // receive buffer
	char buf[RBUFSIZE + 1];  // dummy buffer for receiving a complete command
	char stime[25];    
	char nextcursor[] = {1,3,3,4,6,6,7,9,9,11,11,12,14,14,15,17,17,18,1};

//  use reload_vec( ..) instead of #INT_VEC SER0_VEC if port0 is also
//  the Dynamic C programming Port

#if ROM==0
	reload_vec(14, Dz0_circ_int);
#endif
	Dinit_z0(rbuf, tbuf, RBUFSIZE, TBUFSIZE, 4, 9600 / 1200, 0, 0);
	z0binaryset();    // interrupt routine will receive all characters

	hitwd();
	set_timer0(11520 - 1);     // timer0 interrupt service routine
										// to service the beeper every 25 ms.
										// 11520x20/9216000 = 25ms. 
	cursor = 1;
	ledonoff = 1;
	beeponoff = 0;
	changebaud = 0;
			
	outport(CS1, ledonoff);
	outport(CS1+1, ledonoff);
	outport(CS1+2, ledonoff);
	outport(CS1+3, ledonoff);
	outport(CS1+4, beeponoff);
	
	tm_rd(&thistime);       // read the real time clock
	// reload the real time clock if not already running or out of range
	if( (thistime.tm_year < 95) || (thistime.tm_year >99) )
	{
		thistime.tm_hour = 8;
		thistime.tm_min = 0;
		thistime.tm_sec = 0;
		thistime.tm_mon = 6;
		thistime.tm_mday = 1;
		thistime.tm_year = 95;
		thistime.tm_wday = 4;
		tm_wr(&thistime);
	}  
		
 
	showmenu();
	locxy(13, 48+cursor);
	while(!Dwrite_z01ch('^'));
	for (;;)    // endless loop constantly monitoring for new character
	{
		runwatch();
		hitwd();
		tm_rd(&thistime);
		if(thistime.tm_sec != oldsec)
		{
			sprintf(stime,"%02d-%02d-%02d-%d %02d:%02d:%02d",
						thistime.tm_mon, thistime.tm_mday, thistime.tm_year, 
						thistime.tm_wday,
						thistime.tm_hour, thistime.tm_min, thistime.tm_sec);
			locxy(12, 48);
			Dwrite_z0(stime, 20);
			oldsec = thistime.tm_sec;
			locxy(14,35);
		}
		if( Dread_z01ch(&data))
		{
			up_beep(100);              // beep for 100 ms if a key is
												// received.
			switch(data)
			{
				case 'a':
				case 'A':
								if(ledonoff) ledonoff = 0;
								else ledonoff = 1;
								outport(CS1, ledonoff);
								outport(CS1+1, ledonoff);
								outport(CS1+2, ledonoff);
								outport(CS1+3, ledonoff);
								break;
				case 'b':
				case 'B':
								up_beep(1000);    // beep for 1 second
								break;
				case 'f':   // move the '*' for RTC adjustment
				case 'F':
								locxy(13, 48+cursor);
								Dwrite_z01ch(' ');
								cursor = nextcursor[cursor];
								locxy(13, 48+cursor);
								Dwrite_z01ch('^');
								break;
				case 'u':
				case 'U':
				case 'd':
				case 'D':      // adjust the time keys
								oldsec = adjust_rtc(data, cursor);
								break;                                          
				default:
								up_beep(250);        // beep for 250 ms if
															// non functional key is pressed.
								showmenu();
								locxy(13, 48+cursor);
								while(!Dwrite_z01ch('^'));
								break;                  
			}
		}
	 }
}



// Adjust the real time clock based on the adjust field position and
// the character data from the dumb terminal.
int adjust_rtc(int data, int cursor)
{
		int monlimit, temp;
		struct tm thistime;
		
		tm_rd(&thistime);
		switch(thistime.tm_mon)
		{
			case 1:
			case 3:
			case 5:
			case 7:
			case 8:
			case 10:
			case 12: monlimit = 31;
						break;
			case 4:
			case 6:
			case 9:
			case 11:
						monlimit = 30;
						break;
			case 2:
						if(((thistime.tm_year+1900) % 4)==0)
							monlimit = 29;
						else
							monlimit = 28;
						break;
			default:
						break;
		}  

		if(data == 'u' || data == 'U')
		{
			switch(cursor)
			{
				case 1:
						thistime.tm_mon = thistime.tm_mon + 1;
						if(thistime.tm_mon > 12) thistime.tm_mon = 1;
						break;
				case 3:
						thistime.tm_mday = thistime.tm_mday + 10;
						if(thistime.tm_mday > monlimit) 
							thistime.tm_mday = 1;
						break;
				case 4:
						thistime.tm_mday = thistime.tm_mday + 1;
						if(thistime.tm_mday > monlimit) 
							thistime.tm_mday = 1;
						break;
				case 6:
						thistime.tm_year = thistime.tm_year + 10;
						if(thistime.tm_year > 99) thistime.tm_year = 0; 
						break;
				case 7:
						thistime.tm_year = thistime.tm_year + 1;
						if(thistime.tm_year > 99) thistime.tm_year = 0;
						break;
				case 9:
						thistime.tm_wday = thistime.tm_wday + 1;
						if(thistime.tm_wday > 6) thistime.tm_wday = 0;
						break;
				case 11:
						thistime.tm_hour = thistime.tm_hour + 10;
						if(thistime.tm_hour > 23) thistime.tm_hour = 0;
						break;
				case 12:
						thistime.tm_hour = thistime.tm_hour + 1;
						if(thistime.tm_hour > 23) thistime.tm_hour = 0;
						break;
				case 14:
						thistime.tm_min = thistime.tm_min + 10;
						if(thistime.tm_min > 59) thistime.tm_min = 0;
						break;
				case 15:
						thistime.tm_min = thistime.tm_min + 1;
						if(thistime.tm_min > 59) thistime.tm_min = 0;
						break;
				case 17:
						thistime.tm_sec = thistime.tm_sec + 10;
						if(thistime.tm_sec > 59) thistime.tm_sec = 0;
						break;
				case 18:
						thistime.tm_sec = thistime.tm_sec + 1;
						if(thistime.tm_sec > 59) thistime.tm_sec = 0;
						break;
			}
		}
		else
		{
			switch(cursor)
			{
				case 1:
						thistime.tm_mon = thistime.tm_mon - 1;
						if(thistime.tm_mon < 1 ) thistime.tm_mon = 12;
						break;
				case 3:
						temp = thistime.tm_mday;
						temp = temp - 10;
						if(temp < 1) temp = monlimit;
						thistime.tm_mday = temp;
						break;
				case 4:
						temp = thistime.tm_mday;
						temp = temp - 1;
						if(temp < 1) temp = monlimit;
						thistime.tm_mday = temp;
						break;
				case 6:
						temp = thistime.tm_year;
						temp = temp - 10;
						if(temp < 0) temp = 99;
						thistime.tm_year = temp;
						break;
				case 7:
						temp = thistime.tm_year;
						temp = temp - 1;
						if(temp < 0) temp = 99;
						thistime.tm_year = temp;
						break;
				case 9:
						temp = thistime.tm_wday;
						temp = temp - 1;
						if(temp < 0) temp = 6;
						thistime.tm_wday = temp;
						break;
				case 11:
						temp = thistime.tm_hour;
						temp = temp - 10;
						if(temp < 0) temp = 23;
						thistime.tm_hour = temp;
						break;
				case 12:
						temp = thistime.tm_hour;
						temp = temp - 1;
						if(temp < 0) temp = 23;
						thistime.tm_hour = temp;
						break;
				case 14:
						temp = thistime.tm_min;
						temp = temp - 10;
						if(temp < 0) temp = 59;
						thistime.tm_min = temp;
						break;
				case 15:
						temp = thistime.tm_min;
						temp = temp - 1;
						if(temp < 0) temp = 59;
						thistime.tm_min = temp;
						break;
				case 17:
						temp = thistime.tm_sec;
						temp = temp - 10;
						if(temp < 0) temp = 59;
						thistime.tm_sec = temp;
						break;
				case 18:
						temp = thistime.tm_sec;
						temp = temp - 1;
						if(temp < 0) temp = 59;
						thistime.tm_sec = temp;
						break;
			}
		}
		tm_wr(&thistime);
		return(thistime.tm_sec - 1); // return sec - 1 of new set time
}


// Show a menu on the dumb terminal.

char *menutext[] = {
						"This sample program shows the usage of the serial port0",
						"as a diagnostic port. The serial driver uses circular  ",
						"buffers for tansmitting and receiving data. ",
						"Key Choices:",
						"'a' Toggle leds.",
						"'b' Beep for 1 second.",
						"'f' Move time adjust field.",
						"'u' Increment time adjust field.",
						"'d' Decrement time adjust field.",
						"Type your choice >> "  
						};

// show menu to the dumb terminal            
void showmenu()
{
	int i;
	clrscrn();

	box(3,9,16,71);
	box(4,10,15,70);       

	for(i=0;i<10;i++)
	{
		locxy(i+5, 12);
		while(!Dwrite_z0(menutext[i], strlen(menutext[i]))) hitwd();
	}
	box(11,47,14,67);
}

// Position cursor on the dumb terminal.
void locxy(int row, int col)
{
	char xx[8];
	char temp;
	xx[0] = '\x1b';
	xx[1] = '[';
	temp  = row/10;
	xx[2] = temp+0x30;
	xx[3] = (row - 10*temp) + 0x30;
	xx[4] = ';';
	temp = col/10;
	xx[5] = temp+ 0x30;
	xx[6] = (col - 10*temp) + 0x30;
	xx[7] = 'H';
	while(!Dwrite_z0(xx,8));
}

// Clear the screen on the dumb terminal.
void clrscrn()
{
	while(!Dwrite_z0("\x1b[2J",4));
}


// Draw a box on the dumb terminal.
void box(int tlrow, int tlcol, int brrow, int brcol)
{
	int i, j;
	char block[80];
	
	j = brcol - tlcol + 1;
	for(i=0;i<j;i++) block[i] = 196;         // horizontal line
	block[0] = 218;                          // corners
	block[j-1] = 191;
	locxy(tlrow, tlcol);
	while(!Dwrite_z0(block, j));
	block[0] = 192;                          // corners
	block[j-1] = 217;
	locxy(brrow,tlcol);
	while(!Dwrite_z0(block, j));
	for(i=1;i<j-1;i++) block[i] = ' ';
	block[0] = 179;                          // vertical lines
	block[j-1] = 179;
	tlrow++;
	do
	{
		locxy(tlrow++,tlcol);
		hitwd();                               // in case of lower baud rate
		while(!Dwrite_z0(block, j));
	} while(tlrow<brrow);
}

// This function set up timer0 to interrupt every
// load_value*20/sysclock() seconds. 
void set_timer0(unsigned int load_value)
{
	DI();
	outport(TCR, inport(TCR) & '\B11101110');    // inhibit TIMER0 interrupt
	outport(TMDR0L, load_value & 0xff);
	outport(TMDR0H, (load_value >> 8) & 0xff);
	outport(RLDR0L, load_value & 0xff);
	outport(RLDR0H, (load_value >> 8) & 0xff);
	outport(TCR, inport(TCR) | '\B00010001');
	EI();
}

#INT_VEC PRT0_VEC timer0

// Timer0 interrupt service routine. Services the beeper every 25 ms
interrupt timer0()
{
	inport(TCR);
	inport(TMDR0L);          // clear TIF
	lc_beepscan();
}
