Source file: /~heha/ewa/Kram/Solarpaneel.zip/C-Programm/vt100.c

/*
Projekt: Photovoltaik-Präsentation "solar" mit MSP430
Detail: Zweit-Anzeige des Displays und der Tableaus via HyperTerminal
061106	erstellt
*/
#include "solar.h"
#include <stdio.h>
#include <string.h>	// strlen

char AnsiGetKey(void) {
//Tastenabfrage mit Konvertierung von Kursortasten u.ä., blockierend
 char c;
rep1:
 c=(char)getchar();		// blockierendes getchar()!
 switch (c) {
  case 0: goto rep1;		// HyperTerminal-Müll
  case 033: {
rep2:
   c=getchar();
   switch (c) {
    case '[':
    case 'O': goto rep2; 
    case 033: c=0x03; break;	// 2x ESC -> ^C
    case 'H': c=0x01; break;	// Pos1   -> ^A
    case 'K': c=0x05; break;	// Ende   -> ^E
    case 'A': c=0x10; break;	// hoch   -> ^P
    case 'B': c=0x0E; break;	// runter -> ^N
    case 'F': c=0x06; break;	// rechts -> ^F
    case 'D': c=0x02; break;	// links  -> ^B
    case 'P':
    case 'Q':
    case 'R':
    case 'S': c-='P'-0x1C;	// F1..F4 -> ^]..^_
   }
  }
 }
 return c;
}

char Visible(char c) {
 if ((unsigned char)c<' ' || c==0x7F) c='_';
 return c;
}

void AnsiGotoXY(INT x, INT y) {
 printf(CSI "%d;%dH",y+1,x+1);
}

static BYTE CurrentColor;

void AnsiSetColor(BYTE NewColor) {
 bool bSendSemi=false;		// Flag zum Aussenden eines Semikolons
 
 void SendSemi(void) {	// lokale Hilfsprozedur
  if (bSendSemi) putchar(';');
  bSendSemi=true;
 }
// Setze Farbe für folgende Ausgaben, kürzestmögliche Sequenz mit Cache
//	Bit3:0 = Vordergrund (Bit 3 = Intensitätsbit)
//	Bit6:4 = Hintergrund
//	Bit7 = Blinken
// Farb-Bitzuordnung anders als in DOS!! Bit 0=rot, Bit 1=grün, Bit2=blau
// Unterstreichung wird hier nicht gehandhabt.
 if (NewColor!=CurrentColor) {
  putchar(0x1B);
  putchar('[');
//Das "Rücksetzen von Attributen" ist nur notwendig, wenn ein Intensitäts-
//oder ein Blinkbit verschwindet - oder kurzerhand wenn grau-auf-weiß gewünscht
  if (NewColor==0x07 || ~NewColor&CurrentColor&0x88) {
   putchar('0');
   bSendSemi=true;
   CurrentColor=0x07;
  }
  if ((NewColor^CurrentColor)&0x80) {	// neues Blink-Bit
   SendSemi();
   putchar('5');
  }
  if ((NewColor^CurrentColor)&0x70) {	// neue Hintergrundfarbe
   SendSemi();
   putchar('4');
   putchar(((NewColor>>4)&7)+'0');
  }
  if ((NewColor^CurrentColor)&0x08) {	// neues Intensitätsbit
   SendSemi();
   putchar('1');
  }
  if ((NewColor^CurrentColor)&0x07) {	// neue Textfarbe
   SendSemi();
   putchar('3');
   putchar((NewColor&7)+'0');
  }
  putchar('m');
  CurrentColor=NewColor;
 }
}

// vor der Ausgabe von Strings in Fenstern aufrufen!
void AnsiSaveCursor(void) {
 printf(CSI"s");
}

// Cursor in Ausgabefenster zurückstellen
void AnsiRestoreCursor(void) {
 printf(CSI"u");
 AnsiSetColor(0x07);
}

// hier als Funktionen statt als ANSI-Inlines, zwecks Anpassung an
// (möglicherweise) andere Terminal-Sequenzen
void AnsiUnderlineOn(void) {
 printf(CSI"4m");
}

void AnsiUnderlineOff(void) {
 printf(CSI"24m");
}

// <count> gleiche Zeichen ausgeben
void AnsiDrawChars(char c, INT count) {
 if (count>0) do putchar(c); while(--count);
}

// zur vertikalen Zeichenausgabe in AnsiDrawBox
static void vert(void) {
// if (aktuelle Spalte!=79)
 putchar(0x08);	// nach links
 putchar(0x0A);	// nach unten
}

// Rand in aktueller Farbe mit den angegebenen boxdrawing-Zeichen malen
// [0]=waag., [1]=senkr., [2]=l.o., [3]=r.o., [4]l.u., [5]=r.u.
// Kann sein, dass diese Routine am rechten Rand versagt;
// dafür wird ein Minimum an Steuerzeichen verwendet!
// <title> ist im ANSI-, <boxdrawing> im OEM-Zeichensatz
void AnsiDrawBox(INT x,INT y,INT w,INT h,const char* title,const char*boxdrawing) {
 INT i,j;
 BYTE SaveFlags=Ser0Flags;
 AnsiGotoXY(x+1,y);
 i=strlen(title); if (i>w-2) i=w-2;	// String kürzen bei Ausgabe
 j=w-2-i;		// verbleibende waagerechte Striche
 if (i>0) do putchar(*title++); while (--i);
 Ser0Flags&=~(fCRLF|fOutConv);	// 0Ah (LF) und OEM nicht umsetzen!
 AnsiDrawChars(boxdrawing[0],j);
 putchar(boxdrawing[3]);
 i=h-2; if (i>0) do {
  vert();	// rechts runter, ohne Cursorpositionierung
  putchar(boxdrawing[1]);
 }while (--i);
 AnsiGotoXY(x,y);
 putchar(boxdrawing[2]);
 vert();
 i=h-2; if (i>0) do {
  putchar(boxdrawing[1]);
  vert();	// links runter
 }while (--i);
 putchar(boxdrawing[4]);
 AnsiDrawChars(boxdrawing[0],w-2);
 putchar(boxdrawing[5]);
 Ser0Flags=SaveFlags;
}

// Ausgefülltes Rechteck mit aktueller Farbe zeichnen
void AnsiDrawRectangle(INT x, INT y, INT w, INT h, char c) {
 if (h>0) do{
  AnsiGotoXY(x,y);
  AnsiDrawChars(' ',w);
  y++;
 }while (--h);
}

// Vergleicht (Anfang des) Eingabedatenstroms mit der angegebenen Zeichenkette,
// liefert 0 wenn OK.  Es wird <ms> gewartet, bis die Daten kommen.
// Mindestens 1 Idle()-Aufruf erfolgt.
static char AnsiCompareInput(const char*comparer, unsigned ms) {
 unsigned tic=GetTickCount();
// Eine Zeit lang versuchen, Zeichen zu empfangen
 do{
  Idle();
  if (!*comparer) break;
  if (kbhit()) {
   if (getchar()!=*comparer) break;	// Vergleich falsch: raus!
   comparer++;			// nächstes Zeichen
  }
 }while (GetTickCount()-tic<ms);
// Rest der Zeit warten, um den (HyperTerminal-)Müll zu empfangen
// Nicht via Sleep() und ClearInputBuffer(), um XON/XOFF zu vermeiden!
 do{
  Idle();
  if (kbhit()) getchar();
 }while (GetTickCount()-tic<ms);
 return *comparer;
}

/******************************************
 * Ab hier anwendungsspezifische Routinen *
 ******************************************/
static const char boxDouble[6]={0xCD,0xBA,0xC9,0xBB,0xC8,0xBC};

// Garniert die Wert- und Balkenausgabe mit Beschriftung
static void TermDrawUIP(INT y, const char*desc) {
 static const char Formelz[3]={'U','I','P'};
 static const char Einheit[3]={'V','A','W'};
 AnsiGotoXY(1,y);
 AnsiSetColor(0x20);
 puts(desc);
 AnsiSetColor(0x24);
 for (y=0;y<3;y++) printf(CSI "2C%c =" CSI "7C%c\n",Formelz[y],Einheit[y]);
}

typedef struct{
 int val;
 unsigned balken;
 int hex;	// wenn hex=0xFFFF dann alles neu zeichnen
}TDrawValueCache;

// Komprimiert 2 Bits von "bar" zu einem Bit von "balken"
unsigned bar2balken(unsigned long bar) {
 INT i;
 unsigned ret/*blindes gcc:*/=0;
 bar&=0x7FFFFFFEUL;
 for (i=0; i<16; i++) {
  ret>>=1;
  if (bar&3) ret|=0x8000U;
  bar>>=2;
 }
 return ret;
}

int MSB(unsigned x) {	// liefert Nummer des höchsten gesetzten Bits in <x>
 int i;
 if (!x) return -1;
 for (i=15; !(x&0x8000U); x<<=1, i--);
 return i;
}

int LSB(unsigned x) {	// liefert Nummer des niedrigsten gesetzten Bits in <x>
 int i;
 if (!x) return -1;
 for (i=0; !(x&1); x>>=1, i++);
 return i;
}

static TDrawValueCache DrawValueCache[9];

// Macht eine Wert- und Balkenausgabe
void TermDrawValue(INT t, int val, INT nk, unsigned long bar, int hex) {
// Zeilennummern im HyperTerminal
 static const BYTE Term_line[9]={2,3,4,6,7,8,10,11,12};
// Cache, weil serielle Schnittstelle elend langsam
 INT i,j;
 char buf[8];
 unsigned balken,bch;
 TDrawValueCache*pCache=DrawValueCache+t;
 t=Term_line[t];	// ab jetzt Zeilennummer
// Wert ausgeben (ohne Gleitkomma-printf)
 if (val!=pCache->val || pCache->hex==-1) {
  nk++;
  AnsiGotoXY(5,t);
  AnsiSetColor(0x2F);	// weiß auf grün
  i=snprintf(buf,7,"%0*d",nk+(val<0?1:0),val);	// max. 6 Stellen: -32768
  for (j=5;j>i;j--) putchar(' ');
  for (i=0;j;j--,i++) {
   putchar(buf[i]);
   if (j==nk) putchar(',');
  }
  pCache->val=val;
 }
// "bar" ausgeben:
 balken=bar2balken(bar);		// Zu zeichnende Bits
 bch=balken^pCache->balken;		// geänderte Bits
 if (pCache->hex==-1) bch=0xFFFFU;	// "alles geändert" ansetzen
 if (bch) {
  AnsiSetColor(0x09);	// hellrot auf schwarz
  i=LSB(bch);		// "unterstes" geändertes Bit
  j=MSB(bch);		// "oberstes" geändertes Bit
  AnsiGotoXY(15+i,t);	// auf notwendige Position
  for (; i<=j; i++) {	// notwendige Zeichen ausgeben
   putchar(balken&(unsigned)SHLD[i]?'I':' ');
  }
  pCache->balken=balken;
 }
// ADU-Wert (in Hex) ausgeben (nicht bei "Leistung")
 if (hex!=-1) {
  if (hex!=pCache->hex) {
   AnsiGotoXY(33,t);
   AnsiSetColor(0x20);	// schwarz auf grün
   printf("%03X",hex);
   pCache->hex=hex;
  }
 }else pCache->hex=0;	// Cache gültig machen
}

#define DISP_ORG_X (80-TEXT_X-1)
#define DISP_ORG_Y 1

void TermDrawDisplayMirror(INT x, INT y, const char*text) {
 AnsiGotoXY(x+DISP_ORG_X,y+DISP_ORG_Y);
 puts(text);
}

static const char KeyBit2Char[8]="81234967";
// entsprechend der Anordnung auf dem numerischen Tastenblock

// Zeichen in Bit-Nummer (für Taster-Simulation) umwandeln
// -1 wenn kein gültiges Zeichen dafür
int TermChar2KeyBit(char c) {
 char*p=memchr(KeyBit2Char,c,sizeof(KeyBit2Char));
 if (p) return p-KeyBit2Char;
 return -1;
}

BYTE TermTaster;	// Virtueller(!) Status der 8 Tasten
BYTE TermSchalter;	// Virtueller(!) Status Nockenschalter

void TermDrawTaster(INT i) {
		//Bit        0  1  2  3  4  5  6  7
		//Taste      8  1  2  3  4  9  6  7
 static const BYTE PosX[8]={ 6, 1, 6,11, 4,11, 8, 1};
 static const BYTE PosY[8]={14,16,16,16,15,14,15,14};
 if (!TermMode) return;
 AnsiGotoXY(PosX[i],PosY[i]);
 AnsiSetColor(TermTaster&SHL[i] ? 0x1F : 0x6F);
 if (i+'0'==KeyBit2Char[i]){	// Bitnummer gleich NumPad-Ziffer?
  putchar(' ');
  AnsiUnderlineOn();
  putchar(i+'0');
  AnsiUnderlineOff();
  putchar(' ');
 }else{
  putchar(i+'0');		// andere Beschriftung wenn ungleich
  putchar('=');
  AnsiUnderlineOn();
  putchar(KeyBit2Char[i]);
  AnsiUnderlineOff();
 }
}

// TermTaster setzen und Veränderungen im Terminalfenster anzeigen
void TermSetTaster(BYTE NewState) {
 INT i;
 NewState^=TermTaster;	// Änderungen
 TermTaster^=NewState;
 if (NewState) for (i=0; i<8; i++) {
  if (NewState&SHL[i]) TermDrawTaster(i);
 }
}

void TermDrawSchalter(void) {
 INT i;
 if (!TermMode) return;
 AnsiSetColor(0x60);
 for (i=0; i<3; i++) {
  AnsiGotoXY(18,14+i); putchar(TermSchalter==i ? 254 : ' ');
 }
}

void TermSetSchalter(BYTE NewState) {
 if (TermSchalter==NewState) return;
 TermSchalter=NewState;
 TermDrawSchalter();
}

void TermDrawRelais(INT i) {
 if (!TermMode) return;
 AnsiSetColor(0x60);
 AnsiGotoXY(31,14+i); putchar(Rel_GetState(i) ? 'X' : ' ');
}

static void TermDrawSchalterStatic(INT i) {
 static const char* const Text[]={"links ","Mitte ","rechts"};
 AnsiSetColor(0x60);
 AnsiGotoXY(17,14+i);
 printf("( ) ");
 AnsiUnderlineOn();
 putchar(Text[i][0]);
 AnsiUnderlineOff();
 printf(Text[i]+1);
}

static void TermDrawRelaisStatic(INT i) {
 AnsiSetColor(0x60);
 AnsiGotoXY(30,14+i);
 printf("[ ] ");
 putchar(i+'0');
 putchar('=');
 AnsiUnderlineOn();
 putchar(i+'X');	// ergibt X, Y und Z
 AnsiUnderlineOff();
}

// Anzeige für D/A-Wandler (Strom-Einstellung)
static void TermDrawDAU0Static(void) {
 AnsiGotoXY(39,17);
 printf("D/A-Umsetzer:");
}
void TermDrawDAU0(void) {
 AnsiSetColor(0x76);
 AnsiGotoXY(53,17);
 printf("%03X",DAU_Get(0));
}

void TermDrawDrehstrich(void) {
 static const char Drehstrich[4]="\\|/-";
 static INT index;
 AnsiGotoXY(78,0); AnsiSetColor(0x74);	// blau auf grau
 putchar(Drehstrich[index=(index+1)&3]);
}

BYTE TermMode;		// vorgefundener Terminal-Typ

void TermInit(void) {
 INT i;
 TermMode=0;
 CurrentColor=0xFF;
 AnsiSetColor(0x07);	// auf ANSI schalten, grau auf schwarz
 printf(CSI"2J"		// Bild löschen
	CSI"H"		// Cursor links oben
	CSI"6n"		// Cursorposition abfragen
	CSI"19;24r");	// Roll-Bereich (18..23) setzen
 AnsiGotoXY(0,18);		// Cursor in Rollbereich setzen
 if (!AnsiCompareInput(CSI"1;1R",100)) { 	// 0 wenn OK
  printf(CSI"c");
  if (!AnsiCompareInput(CSI"?1;",100)) {	// 0 wenn VT100
   TermMode|=TERMMODE_VT100;
   printf("VT100-");
  }else{
   TermMode|=TERMMODE_ANSI;
   printf("ANSI-");
  }
 }else{
  printf("\rdummes ");	// sichtbaren Schrott mit '\r' überschreiben
 }
 puts("Terminal");
 if (!TermMode) return;
 for(i=0; i<9; i++) DrawValueCache[i].hex=-1;
 AnsiSaveCursor();	// hier: (0,19) mit grau auf schwarz
 AnsiGotoXY(0,0);
// obere "Bildhälfte" grauer Hintergrund, unten schwarz (Meldungen)
 AnsiSetColor(0x70/*schwarz auf grau*/);
 for(i=0; i<18; i++) puts(CSI"K");	// Zeile löschen, nächste Zeile
 AnsiGotoXY(1,0); printf("Tableaus:");
 AnsiGotoXY(39,0); printf("Display:");
 AnsiSetColor(0x76);
 AnsiGotoXY(17,0); printf("Firmware: " __DATE__);
 AnsiGotoXY(59,0); printf("Matthias Olescher");
// Kopie der Tableaus hinterlegen
 AnsiSetColor(0x2F/*weiß auf grün*/);
 AnsiDrawRectangle(1,1,37,12,' ');
 TermDrawUIP(1,"Solarzelle");
 TermDrawUIP(5,"Last");
 TermDrawUIP(9,"Akku");
// Box für Taster, Schalter und Relais
 AnsiSetColor(0x7F);
 AnsiDrawBox(0,13,15,5,"Taster",boxDouble);
 for (i=0; i<8; i++) TermDrawTaster(i);
 AnsiSetColor(0x7F);
 AnsiDrawBox(16,13,12,5,"Schalter",boxDouble);
 for (i=0; i<3; i++) TermDrawSchalterStatic(i);
 TermDrawSchalter();
 AnsiSetColor(0x7F);
 AnsiDrawBox(29,13,9,5,"Relais",boxDouble);
 TermDrawDAU0Static();
 TermDrawDAU0();
 for (i=0; i<3; i++) TermDrawRelaisStatic(i);
 for (i=0; i<3; i++) TermDrawRelais(i);
// Kopie des Grafikdisplays vorbereiten (natürlich nur für Textdarstellung!)
 AnsiSetColor(0x47/*grau auf blau*/);
 AnsiDrawRectangle(DISP_ORG_X,DISP_ORG_Y,TEXT_X,TEXT_Y,' ');
 AnsiRestoreCursor();
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded