/**********************************************/
/* Example Program For ET-BASE Z51F6412 V1.0  */
/* MCU      : Zilog MCS51 / Z51F6412          */
/*          : Run Internal RC (8.00 MHz)      */
/* Compiler : Keil C51 (V9.50a)               */
/* Editor   : uVision V4.53.0.6               */
/* Write By : Eakachai Makarn(ETT CO.,LTD.)   */
/* Function : Demo Calculator on UART0        */
/* Update   : 18 August 2012                  */
/**********************************************/
// ET-BASE Z51F6412 Hardware Statdard I/O
// SW1    = P8[0] GPIO Input
// LED    = P8[1] GPIO Output
// BUZZER = P5[0] Buzzer Drive
// UART0  = P0[2]:TX0,P0[3]:RX0
// UART1  = P3[2]:TX1,P3[3]:RX1
//
#include <z51f6412.h>																							// Z8051 : Z51F6412

/******************************************/
/* ET-BASE Z51F6412 Hardware System Clock */
/******************************************/
#define FREQ_32KHZ    			(32768ul)															// 32.768 KHz
#define FREQ_125KHZ					(125000ul)														// 125 KHz
#define FREQ_1MHZ						(1000000ul)														// 1.00 MHZ
#define FREQ_2MHZ						(2000000ul)														// 2.00 MHz
#define FREQ_4MHZ						(4000000ul)   												// 4.00 MHz
#define FREQ_8MHZ						(8000000ul)   												// 8.00 MHz
#define FREQ_10MHZ					(10000000ul)   												// 10.00 MHz
#define FREQ_12MHZ					(12000000ul)  												// 12.00 MHz
#define FREQ_16MHZ					(16000000ul)  												// 16.00 MHz
#define FREQ_PLL	    			(14745600ul)  												// 14.7456MHz
#define MY_FREQ							FREQ_8MHZ

/*******************************************/
/* ET-BASE Z51F6412 Hardware UART Baudrate */
/*******************************************/
#define BAUD_1200     			(1200ul)															// 1200
#define BAUD_2400     			(2400ul)															// 2400
#define BAUD_4800     			(4800ul)															// 4800
#define BAUD_9600     			(9600ul)															// 9600
#define BAUD_19200    			(19200ul)															// 19200
#define BAUD_38400    			(38400ul)															// 38400
#define BAUD_57600    			(57600ul)															// 57600
#define BAUD_115200   			(115200ul)														// 115200
#define MY_BAUD							BAUD_9600					
	
/************************************/
/* ET-BASE Z51F6412 Hardware SW Pin */
/************************************/
#define SW_PIN							(1 << 0)															// P8[0] = SW
#define SW_PORT_DIR					P8IO																	// Port Direction
#define SW_PORT_DATA				P8																		// Port Data
#define SW_PORT_PULLUP			P8PU																	// Port Pull-Up
#define SW_PORT_DEBOUNCE		P8DB																	// Port Debounce
#define SW_PORT_INIT() 			SW_PORT_DIR      	&= ~(SW_PIN)				// SW = Input 
#define SW_LOGIC_INIT()     SW_PORT_DATA     	|=  (SW_PIN)				// SW Defualt Logic = "1"
#define SW_PULLUP_INIT()		SW_PORT_PULLUP   	|=  (SW_PIN)				// Enable Pull-Up SW Pin
#define SW_DEBOUNCE_INIT()  SW_PORT_DEBOUNCE 	|=  (SW_PIN)				// Enable Debounce SW Pin

/*************************************/
/* ET-BASE Z51F6412 Hardware LED Pin */
/*************************************/
#define LED_PIN							(1 << 1)															// P8[1] = LED
#define LED_PORT_DIR				P8IO
#define LED_PORT_DATA 			P8
#define LED_PORT_INIT()    	LED_PORT_DIR 			|=  (LED_PIN)				// LED = Output
#define LED_OFF()						LED_PORT_DATA			&= ~(LED_PIN) 			// LED Pin = 0
#define LED_ON()  					LED_PORT_DATA			|=  (LED_PIN) 			// LED Pin = 1
#define LED_TOGGLE()  			LED_PORT_DATA			^=  (LED_PIN) 			// LED Pin = Toggle

/*************************************/
/* ET-BASE Z51F6412 Hardware SPI Pin */
/*************************************/
#define SPI0_CS_PIN	  			(1 << 4)															// P3[4] = CS0#
#define SPI0_PORT_DIR				P3IO
#define SPI0_PORT_DATA			P3
#define SPI0_CS_INIT()  		SPI0_PORT_DIR 		|=  (SPI0_CS_PIN)
#define SPI0_CS_LOW()				SPI0_PORT_DATA  	&= ~(SPI0_CS_PIN)
#define SPI0_CS_HIGH()  		SPI0_PORT_DATA  	|=  (SPI0_CS_PIN)

#define SPI1_CS_PIN	  			(1 << 4)															// P4[4] = CS1#
#define SPI1_PORT_DIR				P4IO
#define SPI1_PORT_DATA			P4
#define SPI1_CS_INIT()  		SPI1_PORT_DIR 		|=  (SPI1_CS_PIN)
#define SPI1_CS_LOW()				SPI1_PORT_DATA  	&= ~(SPI1_CS_PIN)
#define SPI1_CS_HIGH()  		SPI1_PORT_DATA  	|=  (SPI1_CS_PIN)

/*******************
 * Global Variable *
 *******************/
// Integer Calculator Unit
#define CAL_DIV_START 0x01
#define CAL_DIV_DONE 	0x02
#define CAL_DIV_BY_0 	0x04
static long long_buf;
static short short_buf;

/**********************
 * Function Prototype *
 **********************/
void init_uart0(unsigned long freq, unsigned long baud);
void my_putchar0(char ch);
void print_uart0(char *uart_buf);
void print_hex_byte(unsigned char hexval);
void print_hex_short(unsigned short hexval);
void print_hex_long(unsigned long hexval);
long mul_calculator(short a,short b);
void div_calculator(long a,short b,long* result,short* remainder);
//short div_calculator(long a,short b);

/********************
 * Function: main() *
 ********************/
void main(void)
{	  
	/* Config System Clock = Internal RC 8.00 MHz */
	//PLLCR = 0,0,0,00,00,0
	// 0xxxxxxx : PLL Output Status
	// x0xxxxxx : PLL Output Bypass
	// xx0xxxxx : Power PLL = Default
	// xxx00xxx : FBDiv = Default
	// xxxxx00x : PLL M = Default
	// xxxxxxx0 : PLL Disable
	PLLCR = 0x00;																										// Disable PLL
	
	// SCCR = 0,01,0,0,1,00
	// 0xxxxxxx : Stop Mode = Mode 2
	// x01xxxxx : INTRC Clock Divide = INTRC(16MHz)/2 = 8MHz
	// xxx0xxxx : Clock Change By Hardware
	// xxxx0xxx : RC Oscillator Enable
	// xxxxx1xx : XTAL Oscillator Disable
	// xxxxxx00 : System Clock Source = INTRC(16MHz)
	SCCR = 0x24;  																									// INT-RC 8MHz	
	/* Now System Clock = 8.00MHz */
  
	// Initial UART
	init_uart0(MY_FREQ, MY_BAUD);
  
	print_uart0("\n\rET-BASE Z51F6412 V1.0 Run Internal RC at 8MHZ");
	print_uart0("\n\rDemo Integer Calculator");
	
	print_uart0("\n\rCalculator Multiply 0x1234 x 0x5678 = ");
	long_buf = mul_calculator(0x1234,0x5678);												// 0x06260060
	print_hex_long(long_buf);																				// Multiply Result
	
	print_uart0("\n\rCalculator Division 0x12345678 / 0x1234 = ");	
	div_calculator(0x12345678,0x1234,&long_buf ,&short_buf);				// 0x00010004 -> 0x0DA8
	print_hex_long(long_buf);																				// Division Result
	my_putchar0(',');
	print_hex_short(short_buf);																			// Devision Remainder
	
	//Main Loop
	while(1)
	{		
	} 
}

/*
 * Function    : init_uart0()
 * Description : initialize UART0 resources
 *             : baudrates, parirty, stop bit
 */
void init_uart0(unsigned long freq, unsigned long baud)
{
	// UCTRL01 = 00,00,011,0
  // 00xxxxxx : UART Mode
  // xx00xxxx : No-Parity 
  // xxxx011x : 8 Bit Data
	// xxxxxxx0 : Don't Care in UART Mode
	UCTRL01 = 0x06;

	// UCTRL02 = 0,0,0,0,1,1,1,1
  // 0xxxxxxx : Disable Data Empty Interrupt
  // x0xxxxxx : Disable TX Complete Interrupt
	// xx0xxxxx : Disable RX Complete Interrupt
	// xxx0xxxx : Disable Wakeup Interrupt
	// xxxx1xxx : Enable TX UART
	// xxxxx1xx : Enable RX UART
	// xxxxxx1x : Enable UART Module Function
	// xxxxxxx1 : Enable Double Baudrate
	UCTRL02 = 0x0F;
	
	// UCTRL03 = 0,0,0,0,0,0,0,0
	// 0xxxxxxx : Don't Care in UART Mode
	// x0xxxxxx : Normal Mode
	// xx0xxxxx : Don't Care in UART Mode
	// xxx0xxxx : Don't Care in UARt Mode
	// xxxx0xxx : Reserve Bit
	// xxxxx0xx : 1 Stop Bit
	// xxxxxx0x : Don't Care TX8 in 8 Bit Mode
	// xxxxxxx0 : Don't Care RX8 in 8 Bit Mode	
	UCTRL03 = 0x00;
	
	// Config UBARDx = Double Speed Mode Baudrate 
	// baudrate = (Fx / (8 * (UBARDx + 1)))
	// UBAUDx   = (Fx / (baudrate * 8))-1
	// Example Fx:8MHz,Baudrate:9600
	// UBAUDx   = (8MHz/(9600*8))-1
	//          = (8MHz/76800)-1
	//          = 104.166-1
  //          = 103.166
  //          = 103 = 0x67  
	UBAUD0 = (freq  / ((unsigned long)baud * 8ul)) - 1ul;
	
	// xxxxxxx0 :	Disable UART0 RX Interrupt									
	IE1 &= ~0x01;	
}

/*
 * Function    : my_putchar0
 * Description : Output a character to uart0
 */
void my_putchar0(char ch)
{
  while(!(USTAT0 & 0x80));																				// Wait until Trammit Buffer is Empty
	UDATA0 = ch;
}

/*
 * Function    : print_uart0
 * Description : print a string to uart0
 */
void print_uart0(char *uart_buf)
{
	while(*uart_buf != '\0')
	{	
	  my_putchar0(*uart_buf++);	
	}	
}

/*
 * Function    : print_hex_byte()
 * Description : print a character in hexadecimal format
 */
void print_hex_byte(unsigned char hexval)
{
  unsigned int temp;
	char i;

	for (i = 1; i>= 0; i--)
	{
		temp = hexval;
		temp >>= 4*i;
		temp &= 0x0F;

		if (temp >= 10) 
		{
			temp -= 10;
			temp += 'A';
		}
		else 
		{	
			temp +='0';
		}	
		my_putchar0(temp);
	}
}

/*
 * Function    : print_hex_short()
 * Description : print a character in hexadecimal format
 */
void print_hex_short(unsigned short hexval)
{
  print_uart0("0x");
	print_hex_byte((hexval >> 8) & 0xFF);
	print_hex_byte((hexval >> 0) & 0xFF);
}

/*
 * Function    : print_hex_long()
 * Description : print a character in hexadecimal format
 */
void print_hex_long(unsigned long hexval)
{
	print_uart0("0x");
	print_hex_byte((hexval >> 24)& 0xFF);
	print_hex_byte((hexval >> 16)& 0xFF);
	print_hex_byte((hexval >> 8) & 0xFF);
	print_hex_byte((hexval >> 0) & 0xFF);
}

//=============================================
//                       MA[15..8]:MA[7..0]
//                                         X
//                       MB[15..8]:MB[7..0]
//---------------------------------------------
// MO[31..24]:MO[23..16]:MO[15..8]:MO[7..0]
//=============================================
// Integer Calculator Multiply Buffer = 8 Byte
// Calculator Buffer 0x00:0x01 = MA[15..8]:MA[7..0]
// Calculator Buffer 0x02:0x03 = MB[15..8]:MB[7..0]
// Calculator Buffer 0x04:0x07 = MO[31..24]:MO[23..16]:MO[15..8]:MO[7..0]
long mul_calculator(short a,short b)
{
  long mul_o;
	
  mul_o = 0;
  CAL_ADDR = 0; 																									// currently point to MA[15:8]
  CAL_DATA = a >> 8; 																							// MA[15:08]<-a[15:08], ADDR<-ADDR+1
  CAL_DATA = a; 																									// MA[07:00]<-a[07:00], ADDR<-ADDR+1
  CAL_DATA = b >> 8; 																							// MB[15:08]<-b[15:08], ADDR<-ADDR+1
  CAL_DATA = b; 																									// MB[07:00]<-b[07:00], ADDR<-ADDR+1
	
  // Now ADDR Points to MO[31:24],so just read it
  mul_o  = (unsigned long)CAL_DATA << 24; 												// MO[31:24], ADDR<-ADDR+1
  mul_o |= (unsigned long)CAL_DATA << 16; 												// MO[23:16], ADDR<-ADDR+1
  mul_o |= (unsigned long)CAL_DATA << 8; 													// MO[15:08], ADDR<-ADDR+1
  mul_o |= (unsigned long)CAL_DATA; 															// MO[07:00], ADDR<-ADDR+1
	
  return mul_o;	
}

// Integer Calculator Division Buffer = 12 Byte(0x08..0x13]
// Calculator Buffer 0x08:0x0B = DA[31..24]:DA[23..16]:DA[15..8]:DA[7..0]
// Calculator Buffer 0x0C:0x0D =                       DB[15..8]:DB[7..0]
// Calculator Buffer 0x0E:0x11 = DQ[31..24]:DQ[23..16]:DQ[15..8]:DQ[7..0]
// Calculator Buffer 0x12:0x03 =                       DR[15..8]:DR[7..0]
void div_calculator(long a, short b, long* result, short* remainder)
{
	long div_result;																								// Result
	short div_remainder;																						// Remainder	
	div_result = 0;
	div_remainder = 0;
	
	CAL_ADDR = 0x08; 																								// Currently Point to DA[31:24]
	
	//Division-A Number
  CAL_DATA = a >> 24; 																						// DA[31:24]<-a[31:24], ADDR<-ADDR+1
  CAL_DATA = a >> 16; 																						// DA[23:16]<-a[23:16], ADDR<-ADDR+1
  CAL_DATA = a >> 8; 																							// DA[15:08]<-a[15:08], ADDR<-ADDR+1
  CAL_DATA = a; 																									// DA[07:00]<-a[07:00], ADDR<-ADDR+1
	
	//Division-B Number
  CAL_DATA = b >> 8; 																							// DB[15:08]<-b[15:08], ADDR<-ADDR+1
  CAL_DATA = b; 																									// DB[07:00]<-b[07:00], ADDR<-ADDR+1
	
	//Start Calculator A/B
	CAL_CNTR = CAL_DIV_START;																				// Start Calculator
	
	//Wait until division is done (need 32clock cycles)
  while((CAL_CNTR & CAL_DIV_DONE) == 0);
	
  // Now ADDR Points to DQ[31:24],so just read it
	//Get Result
  div_result  = (unsigned long)CAL_DATA << 24; 										// DQ[31:24], ADDR<-ADDR+1
  div_result |= (unsigned long)CAL_DATA << 16; 										// DQ[23:16], ADDR<-ADDR+1
  div_result |= (unsigned long)CAL_DATA << 8; 										// DQ[15:08], ADDR<-ADDR+1
  div_result |= (unsigned long)CAL_DATA; 													// DQ[07:00], ADDR<-ADDR+1
	
	//Get Remainder
  div_remainder  = (unsigned long)CAL_DATA << 8;									// DR[15:08], ADDR<-ADDR+1
  div_remainder |= (unsigned long)CAL_DATA; 											// DR[07:00], ADDR<-ADDR+1

  *result = div_result;																						// Result
  *remainder = div_remainder;																			// Remainder
}

