///////////////////////////////////////////////////////////////////////////////
//                              TV_MM.c                                      //
//                 For TV Character Generator - 2015                         //
//                                                                           //
// tv_putc(m,x,y,c)  Displays the character string "c" in the position "x,y".//
//                   The maximum string length is 40 characters, 1 full line,//
//                   while "x" can go from 0 to 39, and "y" from 0 to 23.    //
//                   "m" is the image mode, "1" for normal, "2" for inverted.//
//                   Make sure that the string length plus the "x" position  //
//                   never exceeds 39, the rightmost limit of each line.     //
//                   Example how to call this function:                      //
//                    _________________________________                      //
//                   |  char c[41];                    |                     //
//                   |  c="This is my line of text";   |                     //
//                   |  tv_putc(1,5,12,c);             |                     //
//                   |_________________________________|                     //
//                   This code will display: This is my line of text         //
//                   at text line 12 ("y"), starting 5 characters from the   //
//                   left ("x"). Since the string contains 23 characters, and//
//                   "x" is 5, the string reaches position 28, which is OK.  //
//                   In the example, "c" is dimensioned as the maximum string//
//                   length acceptable here, 40 characters plus null.        //
//                                                                           //
// tv_put_ascii(m,x,y,a)                                                     //
//                   Displays the symbol that corresponds to the ASCII code  //
//                   "a"; it also applies for the User Defined Graphics (UDG)//
//                   located in positions 0 to 31 of the characters' memory. //
//                   The position on screen is indicated by "x,y", while the //
//                   mode ("1" - normal, "2" - inverted) is set by "m".      //
//                                                                           //
// tv_white_pixel(x,y)                                                       //
//                   Displays a single white pixel in the screen position set//
//                   by "x,y". In this high resolution mode, "x" can go from //
//                   0 to 319 (so "x" is defined as "int16") and "y" from 0  //
//                   to 191. This command may be used to delete a black pixel//
//                   in a white background.                                  //
//                                                                           //
// tv_black_pixel(x,y)                                                       //
//                   Displays a single black pixel in the screen position set//
//                   by "x,y". In this high resolution mode, "x" can go from //
//                   0 to 319 (so "x" is defined as "int16") and "y" from 0  //
//                   to 191. This command may be used to delete a white pixel//
//                   in a black background.                                  //
//                                                                           //
// tv_white_line(x1,y1,x2,y2)                                                //
//                   Draws a white line from point "x1,y1" to point "x2,y2". //
//                   Since "x" ranges from 0 to 319 it is defined as "int16".//
//                   In case of "y" it may go from 0 to 191. This command may//
//                   be used to delete a black line in a white background.   //
//                                                                           //
// tv_black_line(x1,y1,x2,y2)                                                //
//                   Draws a black line from point "x1,y1" to point "x2,y2". //
//                   Since "x" ranges from 0 to 319 it is defined as "int16".//
//                   In case of "y" it may go from 0 to 191. This command may//
//                   be used to delete a white line in a black background.   //
//                                                                           // 
// tv_white_box(x1,y1,x2,y2,f)                                               //
//                   Draws a white box based on 2 opposite corners, "x1,y1"  //
//                   to "x2,y2". Since "x" ranges from 0 to 319 it is defined//
//                   as "int16". In case of "y" it may go from 0 to 191. This//
//                   command may be used to delete a black box in a white    //
//                   background. The box may be empty (f=0) or filled (f=1). //
//                                                                           // 
// tv_black_box(x1,y1,x2,y2,f)                                               //
//                   Draws a black box based on 2 opposite corners, "x1,y1"  //
//                   to "x2,y2". Since "x" ranges from 0 to 319 it is defined//
//                   as "int16". In case of "y" it may go from 0 to 191. This//
//                   command may be used to delete a white box in a black    //
//                   background. The box may be empty (f=0) or filled (f=1). //
//                                                                           //
// tv_white_circle(x,y,r,f)                                                  //
//                   Draws a white circle with center "x,y" and radius "r".  //
//                   Since "x" ranges from 0 to 319 it is defined as "int16".//
//                   In case of "y" it may go from 0 to 191, and "r" from 0  //
//                   to 95. This command may be used to delete a black circle//
//                   in a white background. The circle may be empty (f=0) or //
//                   filled (f=1).                                           //
//                                                                           // 
// tv_black_circle(x,y,r,f)                                                  //
//                   Draws a black circle with center "x,y" and radius "r".  //
//                   Since "x" ranges from 0 to 319 it is defined as "int16".//
//                   In case of "y" it may go from 0 to 191, and "r" from 0  //
//                   to 95. This command may be used to delete a white circle//
//                   in a black background. The circle may be empty (f=0) or //
//                   filled (f=1).                                           //
//                                                                           // 
// tv_white_ellipse(x,y,a,b,f)                                               //
//                   Draws a white ellipse with center "x,y", horizontal     //
//                   radius "a" and vertical radius "b". Since "x" ranges    //
//                   from 0 to 319 it is defined as "int16". In case of "y"  //
//                   it may go from 0 to 191, "a" from 0 to 159 and "b" from //
//                   0 to 95. This command may be used to delete a black     //
//                   ellipse in a white background. The ellipse may be empty //
//                   (f=0) or filled (f=1).                                  //
//                                                                           // 
// tv_black_ellipse(x,y,a,b,f)                                               //
//                   Draws a black ellipse with center "x,y", horizontal     //
//                   radius "a" and vertical radius "b". Since "x" ranges    //
//                   from 0 to 319 it is defined as "int16". In case of "y"  //
//                   it may go from 0 to 191, "a" from 0 to 159 and "b" from //
//                   0 to 95. This command may be used to delete a white     //
//                   ellipse in a black background. The ellipse may be empty //
//                   (f=0) or filled (f=1).                                  //
//                                                                           // 
// tv_clear_black()  Clears the entire screen, leaving a black background.   //
//                                                                           //
// tv_clear_white()  Clears the entire screen, leaving a white background.   //
//                                                                           //
// tv_bright_high()  Sets the entire screen brightness to high level. Useful //
//                   for viewing in bright environments (e.g. daylight).     //
//                                                                           //
// tv_bright_low()   Sets the entire screen brightness to low level. Useful  //
//                   for viewing in dark environments (e.g. nighttime).      //
//                                                                           //
// tv_vertical(v)    Moves the visible screen vertically, according to the   //
//                   contents of "v" which may go from 0 (up) to 20 (down).  //
//                   The default value is 10, which puts the visible screen  //
//                   in the middle of a properly adjusted analog TV set.     //
//                                                                           //
// tv_horizontal(h)  Moves the visible screen horizontally, according to the //
//                   contents of "h" which may go from 0 (left) to 4 (right).//
//                   The default value is 2, which puts the visible screen   //
//                   in the middle of a properly adjusted analog TV set.     //
//                                                                           //
// tv_udg(p,g0,g1,g2,g3,g4,g5,g6,g7)                                         //
//                   Creates an User Defined Graphic (UDG) in the memory     //
//                   position indicated by "p", which may go from 0 to 31.   //
//                   The following values, g0 to g7, represent each one of   //
//                   the 8 bytes that compose an UDG (8x8 square). This UDG  //
//                   can be retrieved later by using the location "p" as the //
//                   ASCII code.                                             //
//                                                                           //
// NOTE: the PIC must be connected to the TV Character Generator by 1 serial //
// line properly set with a valid baud rate and configuration, e.g.:         //
// #use rs232(baud=9600,parity=N,xmit=PIN_A0,rcv=PIN_A1,bits=8,stream=PORT1) //
// Only the xmit pin needs to be connected for basic setup (plus VCC & GND). //
///////////////////////////////////////////////////////////////////////////////
#include <string.h>

void tv_putc(int tv_mode, int tv_xpos, int tv_ypos, char tv_text[]){
   int i;
   for(i=0;i<strlen(tv_text);++i){
      putc(tv_mode);
      if((i+tv_xpos)>=40)putc(39);
      else putc(i+tv_xpos);
      if(tv_ypos>=24)putc(23);
      else putc(tv_ypos);
      putc(tv_text[i]);
   }
}

void tv_put_ascii(int tv_mode, int tv_xpos, int tv_ypos, int ascii){
   putc(tv_mode);
   putc(tv_xpos);
   putc(tv_ypos);
   putc(ascii);
}

void tv_white_pixel(int16 tv_xlpos, int tv_ypos){
   int overflow=tv_xlpos/256;
   putc(128+overflow);
   putc(tv_xlpos);
   putc(tv_ypos);
}

void tv_black_pixel(int16 tv_xlpos, int tv_ypos){
   int overflow=tv_xlpos/256;
   putc(192+overflow);
   putc(tv_xlpos);
   putc(tv_ypos);
}

void line(int tv_mode, signed int16 tv_xlpos1, signed int16 tv_ylpos1, signed int16 tv_xlpos2, signed int16 tv_ylpos2){
   signed int16 tv_dx=abs(tv_xlpos2-tv_xlpos1), tv_sx=tv_xlpos1<tv_xlpos2 ? 1 : -1;
   signed int16 tv_dy=-abs(tv_ylpos2-tv_ylpos1), tv_sy=tv_ylpos1<tv_ylpos2 ? 1 : -1; 
   signed int16 tv_err=tv_dx+tv_dy, tv_e2;
 
   for(;;){
      if(tv_mode==128)tv_white_pixel(tv_xlpos1,tv_ylpos1);
      else tv_black_pixel(tv_xlpos1,tv_ylpos1);
      if(tv_xlpos1==tv_xlpos2 && tv_ylpos1==tv_ylpos2)break;
      tv_e2=2*tv_err;
      if(tv_e2 >= tv_dy){tv_err+=tv_dy; tv_xlpos1+=tv_sx;}
      if(tv_e2 <= tv_dx){tv_err+=tv_dx; tv_ylpos1+=tv_sy;}
   }
}

void tv_white_line(int16 tv_xlpos1, int tv_ypos1, int16 tv_xlpos2, int tv_ypos2){
   line(128,tv_xlpos1,tv_ypos1,tv_xlpos2,tv_ypos2);
}

void tv_black_line(int16 tv_xlpos1, int tv_ypos1, int16 tv_xlpos2, int tv_ypos2){
   line(192,tv_xlpos1,tv_ypos1,tv_xlpos2,tv_ypos2);
}

void tv_white_box (int16 tv_xlpos1, int tv_ypos1, int16 tv_xlpos2, int tv_ypos2, int1 tv_f){
   if(tv_f==0){
      tv_white_line(tv_xlpos1, tv_ypos1, tv_xlpos2, tv_ypos1);
      tv_white_line(tv_xlpos2, tv_ypos1, tv_xlpos2, tv_ypos2);
      tv_white_line(tv_xlpos2, tv_ypos2, tv_xlpos1, tv_ypos2);
      tv_white_line(tv_xlpos1, tv_ypos2, tv_xlpos1, tv_ypos1);
   }
   else{
      int tv_y;
      if(tv_ypos2>=tv_ypos1)
         for(tv_y=tv_ypos1;tv_y<=tv_ypos2;++tv_y)
            tv_white_line(tv_xlpos1, tv_y, tv_xlpos2, tv_y);
      else
         for(tv_y=tv_ypos2;tv_y<=tv_ypos1;++tv_y)
            tv_white_line(tv_xlpos1, tv_y, tv_xlpos2, tv_y);  
   }
}

void tv_black_box (int16 tv_xlpos1, int tv_ypos1, int16 tv_xlpos2, int tv_ypos2, int1 tv_f){
   if(tv_f==0){
      tv_black_line(tv_xlpos1, tv_ypos1, tv_xlpos2, tv_ypos1);
      tv_black_line(tv_xlpos2, tv_ypos1, tv_xlpos2, tv_ypos2);
      tv_black_line(tv_xlpos2, tv_ypos2, tv_xlpos1, tv_ypos2);
      tv_black_line(tv_xlpos1, tv_ypos2, tv_xlpos1, tv_ypos1);
   }   
   else{
      int tv_y;
      if(tv_ypos2>=tv_ypos1)
         for(tv_y=tv_ypos1;tv_y<=tv_ypos2;++tv_y)
            tv_black_line(tv_xlpos1, tv_y, tv_xlpos2, tv_y);
      else
         for(tv_y=tv_ypos2;tv_y<=tv_ypos1;++tv_y)
            tv_black_line(tv_xlpos1, tv_y, tv_xlpos2, tv_y);  
   }
}

void tv_circle(int tv_mode, signed int16 tv_xm, signed int16 tv_ym, signed int16 tv_r, int1 tv_f){
   signed int16 tv_x = -tv_r, tv_y = 0, tv_err = 2-2*tv_r; 
   do {
      if(tv_mode==128){
         if(tv_f==0){
            tv_white_pixel(tv_xm-tv_x, tv_ym+tv_y);
            tv_white_pixel(tv_xm-tv_y, tv_ym-tv_x);
            tv_white_pixel(tv_xm+tv_x, tv_ym-tv_y);
            tv_white_pixel(tv_xm+tv_y, tv_ym+tv_x);
         }
         else{
            tv_white_line(tv_xm-tv_x,tv_ym-tv_y,tv_xm+tv_x,tv_ym-tv_y);
            tv_white_line(tv_xm-tv_x,tv_ym+tv_y,tv_xm+tv_x,tv_ym+tv_y);
         }
      }
      else{
         if(tv_f==0){
            tv_black_pixel(tv_xm-tv_x, tv_ym+tv_y);
            tv_black_pixel(tv_xm-tv_y, tv_ym-tv_x);
            tv_black_pixel(tv_xm+tv_x, tv_ym-tv_y);
            tv_black_pixel(tv_xm+tv_y, tv_ym+tv_x);
         }
         else{
            tv_black_line(tv_xm-tv_x,tv_ym-tv_y,tv_xm+tv_x,tv_ym-tv_y);
            tv_black_line(tv_xm-tv_x,tv_ym+tv_y,tv_xm+tv_x,tv_ym+tv_y);
         }
      }
      tv_r = tv_err;
      if(tv_r<=tv_y)tv_err+=++tv_y*2+1;      
      if(tv_r>tv_x || tv_err>tv_y)tv_err+=++tv_x*2+1;
   } while(tv_x<0);
}

void tv_white_circle(int16 tv_xm, int tv_ym, int tv_r, int1 tv_f){
   tv_circle (128,tv_xm,tv_ym,tv_r,tv_f);
}

void tv_black_circle(int16 tv_xm, int tv_ym, int tv_r, int1 tv_f){
   tv_circle (192,tv_xm,tv_ym,tv_r,tv_f);
}

void tv_ellipse(int tv_mode, signed int32 tv_xm, signed int32 tv_ym, signed int32 tv_a, signed int32 tv_b, int1 tv_f)
{
   signed int32 tv_x = -tv_a, tv_y = 0;           /* II. quadrant from bottom left to top right */
   signed int32 tv_e2 = tv_b*tv_b, tv_err = tv_x*(2*tv_e2+tv_x)+tv_e2;         /* error of 1.step */
                                                          
   do {  
      if(tv_mode==128){
         if(tv_f==0){
            tv_white_pixel(tv_xm-tv_x, tv_ym+tv_y);     
            tv_white_pixel(tv_xm+tv_x, tv_ym+tv_y);               
            tv_white_pixel(tv_xm+tv_x, tv_ym-tv_y);                        
            tv_white_pixel(tv_xm-tv_x, tv_ym-tv_y);      
         }
         else{
            tv_white_line(tv_xm-tv_x,tv_ym-tv_y,tv_xm+tv_x,tv_ym-tv_y);
            tv_white_line(tv_xm-tv_x,tv_ym+tv_y,tv_xm+tv_x,tv_ym+tv_y);
         }
      }
      else{
         if(tv_f==0){
            tv_black_pixel(tv_xm-tv_x, tv_ym+tv_y);     
            tv_black_pixel(tv_xm+tv_x, tv_ym+tv_y);               
            tv_black_pixel(tv_xm+tv_x, tv_ym-tv_y);                        
            tv_black_pixel(tv_xm-tv_x, tv_ym-tv_y);      
         }
         else{
            tv_black_line(tv_xm-tv_x,tv_ym-tv_y,tv_xm+tv_x,tv_ym-tv_y);
            tv_black_line(tv_xm-tv_x,tv_ym+tv_y,tv_xm+tv_x,tv_ym+tv_y);
         }
      }
       tv_e2 = 2*tv_err;                                        
       if (tv_e2 >= (tv_x*2+1)*tv_b*tv_b)         
          tv_err += (++tv_x*2+1)*tv_b*tv_b;                     
       if (tv_e2 <= (tv_y*2+1)*tv_a*tv_a)                  
          tv_err += (++tv_y*2+1)*tv_a*tv_a;
   } while (tv_x <= 0);

   while (tv_y++ < tv_b) {               
       if(tv_mode==128){
         tv_white_pixel(tv_xm, tv_ym+tv_y);                      
         tv_white_pixel(tv_xm, tv_ym-tv_y);
       }
       else{
         tv_black_pixel(tv_xm, tv_ym+tv_y);                     
         tv_black_pixel(tv_xm, tv_ym-tv_y);
       }
   }
}

void tv_white_ellipse(int16 tv_xm, int tv_ym, int tv_a, int tv_b, int1 tv_f){
   tv_ellipse (128,tv_xm,tv_ym,tv_a,tv_b,tv_f);
}

void tv_black_ellipse(int16 tv_xm, int tv_ym, int tv_a, int tv_b, int1 tv_f){
   tv_ellipse (192,tv_xm,tv_ym,tv_a,tv_b,tv_f);
}

void tv_clear_black(){
   putc(3);
}

void tv_clear_white(){
   putc(4);
}

void tv_bright_high(){
   putc(5);
}

void tv_bright_low(){
   putc(6);
}

void tv_vertical(int ver){
   putc(64+ver);
} 
 
void tv_horizontal(int hor){
   putc(96+hor);
}  

void tv_udg(int pos, int g0, int g1, int g2, int g3, int g4, int g5, int g6, int g7){
   putc(32+pos);
   putc(g0);
   putc(g1);
   putc(g2);
   putc(g3);
   putc(g4);
   putc(g5);
   putc(g6);
   putc(g7);
}
