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

/*
Projekt: Photovoltaik-Präsentation "solar" mit MSP430
Detail: Steuerung des vollgrafischen Displays (DispXxx),
	GDI-ähnliche Grafikfunktionen fürs Display (GrafXxx),
	Tasten- und Schalter-Abfrage (Inp_Xxx)
060704	erstellt
0610xx	Polygone implementiert
061220	auf DOS zurückgestutzt (für diejenigen, die sich an BGI erinnern:-)
*/
#include "solar.h"
#include <stdio.h>	//vsprintf
#include <string.h>	//memchr

/* Hardware-Anschluss:
 P3.2 = L wenn Nockenschalter RECHTS
 P3.3 = L wenn Nockenschalter LINKS
 P4.7 = Tastenabfrageleitung (Pullup wurde nachgerüstet 060601)
 P4.6 = Command (High) / Data (Low)
 P4.5 = /Reset (Low-aktiv) fürs Display
 P4.4 = /WR
 P4.3 = Fontauswahl 8x8 (Low), 6x8 (High)
 P4.2 = /RD
 P4.1 = Textmode 40 Spalten (Low), 32 Spalten (High)
 P4.0 = CE/ (steigende(!) Flanke)
 P5 = bidirektionales Datenport, auch zur Tastenabfrage
 */
// Bitmasken für die (wichtigen) Steuerleitungen:
#define CE 		0x01	// -------X
#define RD 		0x04	// -----X--
#define WR 		0x10	// ---X----
#define CD 		0x40	// -X------
#define RST		0x20	// --X-----
// Alle übrigen Bits werden hier per "Ruhe-Byte" erledigt:
#if (FONT_X==6)
# define P4DEF		0xFD	// Ruhezustand Z1111101
#elif (FONT_X==8)
# define P4DEF		0xF5	// Ruhezustand Z1110101
#else
# error Falsche vorgewählte Fontbreite (6 oder 8)!
#endif
 
// Mit den "select"-Byte-basierten Ein/Ausgaberoutinen werden äußerst kurze
// und schnelle Routinen ohne Fallunterscheidung möglich.
// Der Quelltext bleibt dennoch gut lesbar sowie flexibel.

#define WAIT_NORMAL	0x03	// ------XX (X = Bits werden bewertet)
#define WAIT_AUTO_IN	0x04	// -----X--
#define WAIT_AUTO_OUT	0x08	// ----X---
#define DISPLAY_FAILED	0x80	// X------- Code für Display-Versagen

#define WAIT_MAX 1	// max. 1..2 ms warten

// Diese globale Variable ist vertretbar, da die Display-Ansteuerung
// ohnehin nicht reentrant ist. Im wesentlichen eine WAIT-Bitmaske!
BYTE DispFlags;

#define BI_STATUS	(P4DEF^CE^RD)	// Port P4 für Status-Transfer
#define BI_DATA		(P4DEF^CE^RD^CD)// Port P4 für Daten-Transfer
/************************************************************************
 * Byte-Eingabe vom Display
 * select==BI_STATUS: Status-Lesen (mit C/D = high)
 * select==BI_DATA: Daten-Lesen (mit C/D = low)
 ************************************************************************/
static BYTE ByteIn(BYTE select){
 P5DIR=0x00;	// alles Eingabe
 P4OUT=select;
 select^=CE;	// steigende Flanke bei CE/ erzeugen
 P4OUT=select;
 nop(); nop();	// 150 ns Zugriffszeit sicherstellen (1x NOP = 125 ns @ 8 MHz)
 return P5IN;
}

// DispFlags gibt die Bit-Positionen an, die 1 sein müssen.
static void WaitReady(void){
 unsigned tic;
 if (DispFailed()) return;
 tic=GetTickCount();
 while (~ByteIn(BI_STATUS)&DispFlags) {
  if (GetTickCount()-tic>WAIT_MAX) {
   DispFlags|=DISPLAY_FAILED;
   return;
  }
 }
}

// Wie ByteIn(), jedoch mit zusätzlichem Warten vor Ausführung des IN-Transfers
static BYTE ByteInW(BYTE select){
 WaitReady();
 return ByteIn(select);
}

#define BO_COMMAND	(P4DEF^CE^WR)	// Port P4 für Kommando-Transfer
#define BO_DATA		(P4DEF^CE^WR^CD)// Port P4 für Daten-Transfer
/************************************************************************
 * Byte-Ausgabe zum Display
 * select==BO_COMMAND: Kommando-Ausgabe (mit C/D = high)
 * select==BO_DATA: Daten-Ausgabe (mit C/D = low)
 ************************************************************************/
static void ByteOut(BYTE select, BYTE data){
 P4OUT=select;
 P5DIR=0xFF;	// alles Ausgabe
 P5OUT=data;
 select^=CE;	// steigende Flanke bei CE/ erzeugen
 P4OUT=select;
}

static void ByteOutW(BYTE select, BYTE data){
 WaitReady();
 ByteOut(select,data);
}

/************************************************************************
 * Nächstes Level: Präfigierung von 1-2 Daten-Zyklen
 ************************************************************************/
static void ZeroDataCommand(BYTE cmd){
 ByteOutW(BO_COMMAND,cmd);
}

static void OneDataCommand(BYTE data, BYTE cmd){
 ByteOutW(BO_DATA,data);
 ZeroDataCommand(cmd);
}

static void TwoDataCommand(BYTE data1, BYTE data2, BYTE cmd){
 ByteOutW(BO_DATA,data1);
 OneDataCommand(data2,cmd);
}	//Compiler erweist sich zu doof für "gemeinsame Enden"

// das "W" steht hier für "Word", nicht "Wait"!
static void TwoDataCommandW(WORD data, BYTE cmd){
 TwoDataCommand((BYTE)data,(BYTE)(data>>8),cmd);
}

static BYTE CommandRead(BYTE cmd){
 ByteOutW(BO_COMMAND,cmd);
 return ByteInW(BI_DATA);
}

/************************************************************************
 * Blockweise Datenübertragung (auch: Display löschen)
 ************************************************************************/
// --- Weil einige strukturell verschiedene Routinen zum Daten-Schreiben er-
// --- forderlich sind, zunächst Hilfsroutinen zum Aufbau von Schreibroutinen.
// --- [Alternative ist eine Routine mit Funktionszeiger, aber umständlich]
// Vorbereitung des Daten-Schreibens
#define PreDataWrite() {						\
 ZeroDataCommand(0xB0);			/* "Data Auto Write Set" */	\
 DispFlags|=WAIT_AUTO_OUT;						\
}

#define PreDataWriteA(addr){						\
 TwoDataCommandW(addr,0x24);		/* "Address Pointer Set" */	\
 PreDataWrite();							\
}

// Der "Mittelteil" des Daten-Schreibens
#define DoDataWrite(b){							\
 ByteOutW(BO_DATA,b);			/* Byte ausgeben */		\
 DispFlags=WAIT_AUTO_OUT;						\
}

#define PostDataWrite(){						\
 ZeroDataCommand(0xB2);	/* "Auto Mode Reset" (mit modifizierter DispFlags) */\
 DispFlags=WAIT_NORMAL;							\
}

// Schreiben von Datenblöcken (svw. "memcpy")
void DispDataWrite(WORD addr, const BYTE*data, unsigned len){
 if (!len) return;		// Nichts tun bei Länge Null
 PreDataWriteA(addr);
 do{
  DoDataWrite(*data++);		// Byte ausgeben
 }while(--len);
 PostDataWrite();
}

// svw. "memset" im Display-Speicher
void DispDataFill(WORD addr, BYTE fill, unsigned len){
 if (!len) return;		// Nichts tun bei Länge Null
 PreDataWriteA(addr);
 do{
  DoDataWrite(fill);		// Byte ausgeben
 }while(--len);
 PostDataWrite();
}

static const char gDisplaySpecial[]={
// ROM-Sonderzeichen (statt '¤' eigentlich 'Pt' - Peseta)
 'Ç','ü','é','â','ä','à','å','ç','ê','ë','è','ï','î','ì','Ä','Å',
 'É','æ','Æ','ô','ö','ò','û','ù','ÿ','Ö','Ü','¢','£','¥','¤','ƒ',
// Selbst definierte Sonderzeichen (RAM)
 '°','g','ß','±','µ',129,
};

// svw. "puts" in Display-Speicher
void DataPuts(WORD addr, const char*str, size_t len){
 char c;
 if (!str || !len) return;	// nichts tun bei NULL oder Länge Null
 PreDataWriteA(addr);
 do{
  c=*str++;
  const char *p=memchr(gDisplaySpecial,c,sizeof(gDisplaySpecial));
  if (p) c=p-gDisplaySpecial+0x60;
  else c-=0x20;
  DoDataWrite((BYTE)c);		// Byte ausgeben
 }while (--len);
 PostDataWrite();
}

// Festlegung der Speicheraufteilung "auf dem Rücken des Displays"
// (keine Scroll-Tricks)
//#define GRAFSTART 0
//#define TEXTSTART (GRAFSTART+TEXT_X*GRAF_Y)
//#define CGSTART (((TEXTSTART+TEXT_X*TEXT_Y)+0x7FF)&0xF800)
// untere 11 Bits von CGSTART (Zeichengenerator-Start) müssen 0 sein
#define CGSTART 0		// Platz für (stets!!!) 256 Zeichen: 2KB
#define TEXTSTART CGSTART+256*8	// Platz für Text: 640B (bis 1 KB)
#define GRAFSTART (3*1024)	// Platz für Grafik: 5KB
#define PAGEADD (8*1024)	// Länge - bis zur nächsten Grafikseite
// Bei Speicherausbau 32K ergeben sich so handhabbare 4 Zeichengenerator-Seiten,
// 4 Textseiten (scrollbar zur Not) und 4 Grafikseiten

static const BYTE gUserChars[]={	// Zeichenbildtabelle fehlende Zeichen
 0x0C,0x12,0x12,0x0C,0x00,0x00,0x00,0x00,	// °
 0x00,0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E,	// g hübscher
 0x0C,0x12,0x12,0x16,0x11,0x11,0x16,0x00,	// ß
 0x00,0x04,0x04,0x1F,0x04,0x04,0x1F,0x00,	// ±
 0x00,0x00,0x12,0x12,0x12,0x12,0x1D,0x10,	// µ
 0x00,0x0E,0x11,0x11,0x11,0x0A,0x1B,0x00,	// Omega, hier \x81
};

// Text-Ausgabe auf Display UND Terminal-Echo
// Zeichenkette muss vollständig auf Zeile passen; wird ggf. abgeschnitten
// Kursor muss (per AnsiSaveCursor()) außerhalb des Rollbereichs sein!
void DispPrintf(int x, int y, const char*template,...){
 va_list p;
 size_t len;
 char buffer[TEXT_X],*s;
 va_start(p,template);
 len=vsnprintf(buffer,TEXT_X-x,template,p);
 va_end(p);
 if (!len) return;
 DataPuts(TEXTSTART+y*TEXT_X+x,buffer,len);
// Echo auf Terminal
 AnsiGotoXY(x+39,y+1);
 AnsiSetColor(0x4F);	// weiß auf blau (einzige Farbe)
 s=buffer;
 do{
  putchar(*s);
  s++;
 }while(--len);
}

// Lesen von Datenblöcken (svw. "memcpy" rückwärts)
void DispDataRead(WORD addr, BYTE*data, unsigned len){
 if (!len) return;			// Nichts tun bei Länge Null
 TwoDataCommandW(addr,0x24);		// "Address Pointer Set"
 ZeroDataCommand(0xB1);			// "Data Auto Read Set"
 // Beim ersten Datenbyte müssen alle 3 Bits High sein
 DispFlags|=WAIT_AUTO_IN;
 do{
  *data++=ByteInW(BI_DATA);	// (Byte einlesen)
  DispFlags=WAIT_AUTO_IN; // Bei allen folgenden nur das eine Bit abwarten
 }while(--len);
 PostDataWrite();	// geht auch für's Lesen
}

// Öfters auftauchende Adresskonvertierung: x->Byte-Spalte, bitno->Bitnummer
#if (FONT_X==6)		// 6x8-Font
# define X2ADDR(x,bitno) {bitno=5-(x%6); x/=6;}	//Sieht der Compiler "div+mod"?
#else
# define X2ADDR(x,bitno) {bitno=~x&7; x>>=3;}
#endif

void GrafSetPixel(int x, int y, int color){
 BYTE bitno;
 if ((unsigned)x>=GRAF_X) return;	// "unsigned"-Cast deckt x<0 ab!
 if ((unsigned)y>=GRAF_Y) return;	// Clipping!
 X2ADDR(x,bitno);
 TwoDataCommandW(GrafDC.ba+y*TEXT_X+x,0x24);
 if (color) bitno|=0x08;	// setzen, sonst rücksetzen
 bitno|=0xF0;
 ZeroDataCommand(bitno);	// Bit setzen/rücksetzen
}

void GrafInternalSetPixel(int x, int y, int unused){
 unsigned t=GrafDC.linestyle;
 if (t&1) GrafSetPixel(x,y,GrafDC.color);
 t=(t<<1)|(t>>15);
 GrafDC.linestyle=t;
}

#define swap(x,y) {typeof(x) t=(x); (x)=(y); (y)=t;}

// Bresenham-Algorithmus...
void GrafLineDDA(int xa,int ya,int xe,int ye,TGrafLineDDAProc proc, int param){
 int *lead,*trail;		// Zeiger auf Führungs- und Folgegröße
 int xs,ys;			// ±1 (Schritt)
 unsigned ramp,akku;
 xe-=xa; xs=1;			// xe = vzb. X-Differenz
 if (xe<0) {xe=-xe; xs=-1;}	// xe = vzl. X-Länge
 ye-=ya; ys=1;			// ye = vzb. Y-Differenz
 if (ye<0) {ye=-ye; ys=-1;}	// ye = vzl. Y-Länge
 lead=&xa; trail=&ya;		// "flache" Linie (X Führungsgröße) annehmen
 if (ye<=xe){
  if (xe==0) return;		// beide Größen Null: nichts zeichnen!
 }else{				// "steile" Linie (Y Führungsgröße)
  lead=&ya; trail=&xa;
  swap(xe,ye);			// xe jetzt Länge in Pixel, ye<xe
  swap(xs,ys);			// xs = Richtung der Führungsgröße
 }
 ramp=(ye<<8)/xe;		// Steilheit mit "8 Nachkommastellen"
 akku=ramp>>1;			// Akku mit Hälfte vorladen
 do{
  proc(xa,ya,param);
  *lead+=xs;
  akku+=ramp;
  if (akku>=0x0100){	// besser wäre C-Flag auswerten?
   akku-=0x0100;
   *trail+=ys;
  }
 }while(--xe);
}

// Linie mit Linien-Stil
void GrafInternalLine(int xa, int ya, int xe, int ye){
 GrafLineDDA(xa,ya,xe,ye,GrafInternalSetPixel,0);
}

TGrafDC GrafDC;

void GrafPolyline(const POINT *pt, int numpt){
 const POINT *prev;
 if (numpt<2) return;
 numpt--;
 do{
  prev=pt++;
  GrafInternalLine(prev->x,prev->y,pt->x,pt->y);
 }while(--numpt);
}

static const BYTE Bit2Mask[8]={0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF};
// So wie Windows es tut, die Operationen in den Kode zu stecken
// und "life" auf dem Stack zu compilieren weiß ich (noch) nicht, wie das geht.

static BYTE DoRop3(BYTE D, BYTE P, BYTE S, BYTE rop3){
 switch (rop3){	// TODO: müsste für alle 256 Fälle ausgearbeitet werden!
  case BLACKNESS:	D=0;	break;	// 00  0	R2_BLACK
  case 0x05:		D=~(D|P);break;	// 05 DPon	R2_NOTMERGEPEN
  case 0x0A:		D&=~P;	break;	// 0A DPna	R2_MASKNOTPEN
  case 0x0F:		D=~P;	break;	// 0F Pn	R2_NOTCOPYPEN
  case NOTSRCERASE:	D=~(D|S);break;	// 11 DSon
  case NOTSRCCOPY:	D=~S;	break;	// 33 Sn
  case SRCERASE:	D=~D&S; break;	// 44 SDna
  case 0x50:		D=~D&P;	break;	// 50 PDna	R2_MASKPENNOT
  case DSTINVERT:	D=~D;	break;	// 55 Dn	R2_NOT
  case PATINVERT:	D^=P;	break;	// 5A DPx	R2_XORPEN
  case 0x5F:		D=~(D&P);break;	// 5F DPan	R2_NOTMASKPEN
  case SRCINVERT:	D^=S;	break;	// 66 DSx
  case SRCAND:		D&=S;	break;	// 88 DSa
  case 0xA0:		D&=P;	break;	// A0 DPa	R2_MASKPEN
  case 0xA5:		D^=~P;	break;	// A5 DPxn	R2_NOTXORPEN
  case 0xAA:		/*D=D*/	break;	// AA D		R2_NOP
  case 0xAF:		D|=~P;	break;	// AF DPno	R2_MERGENOTPEN
  case MERGEPAINT:	D|=~S;	break;	// BB DSno
  case MERGECOPY:	D=S&P;	break;	// C0 SPa
  case SRCCOPY:		D=S;	break;	// CC S
  case SRCPAINT:	D|=S;	break;	// EE DSo
  case PATCOPY:		D=P;	break;	// F0 P		R2_COPYPEN
  case 0xF5:		D=~D|P;	break;	// F5 PDno	R2_MERGEPENNOT
  case 0xFA:		D|=P;	break;	// FA DPo	R2_MERGEPEN
  case PATPAINT:	D|=P|~S;break;	// FB DPSnoo
  case WHITENESS:	D=0xFF;	break;	// FF  1	R2_WHITE
 }
 return D;
}

/************************************************************************
 * Kernfunktion für gefüllte Figuren: Füllen einer Pixelzeile
 * xa,xe: Anfang und Ende+1 der horizontalen Linie
 * y: Vertikalposition der Zeile
 * pattern: Füllmuster
 * data: Bilddaten (wenn später ein BitBlt draus wird)
 ************************************************************************/
void OutScanline(int xa, int xe, int y, BYTE pattern, BYTE*data){
 BYTE bita,bite;		// Bitnummer Anfangs- und Endpixel
 BYTE mask,b;			// Bitmaske und "Arbeitsbyte"
 if ((unsigned)y>=GRAF_Y) return;	// "unsigned"-Cast deckt y<0 ab!
 if (xa<0) xa=0;
 if (xe>=GRAF_X) xe=GRAF_X;
 if (xa>=xe) return;
 X2ADDR(xa,bita);
 X2ADDR(xe,bite);
 TwoDataCommandW(GrafDC.ba+y*TEXT_X+xa,0x24);	// "Address Pointer Set"
// Vier Fälle (mindestens?)

 if (xa==xe){ // Anfangs- und Endpixel fallen auf die gleiche Byte-Adresse
  mask=Bit2Mask[bita]&~Bit2Mask[bite];	// Bitmasken-Kombination
  goto partial;
 }else{
  if (bita!=FONT_X-1){		// "krumme" Anfangsadresse?
   mask=Bit2Mask[bita];
   b=CommandRead(0xC5);		// "Data Read" ohne Adresszeiger-Veränderung
   b=(b&~mask)|(DoRop3(b,pattern,0,GrafDC.rop2)&mask);
   OneDataCommand(b,0xC0);	// "Data Write" mit Auto-Inkrement
   xa++;
  }
  
  switch (GrafDC.rop2) {
   case R2_BLACK:
   case R2_NOTCOPYPEN:
   case R2_COPYPEN:
   case R2_WHITE: {		// Lesen des Zielbytes nicht nötig (Zeit sparen)
    b=DoRop3(0,pattern,0,GrafDC.rop2);
    if (xe>=xa+2){		// "memset" lohnt sich erst ab 2 Bytes
     PreDataWrite();
     do{
      DoDataWrite(b);
      xa++;
     }while (xa!=xe);
     PostDataWrite();
    }else while (xa<xe){
     OneDataCommand(b,0xC0);	// "Data Write" mit Auto-Inkrement
     xa++;
    }
   }break;
   
   default: while (xa<xe) {	// alle Modi mit Lesen des Zielbytes
    b=CommandRead(0xC5);	// "Data Read" ohne Adresszeiger-Veränderung
    b=DoRop3(b,pattern,0,GrafDC.rop2);
    OneDataCommand(b,0xC0);	// "Data Write" mit Auto-Inkrement
    xa++;
   }
  }

  if (bite!=FONT_X-1){		// "krumme" Endadresse?
   mask=~Bit2Mask[bite];	// Bitmasken-Kombination
partial:
   b=CommandRead(0xC5);		// "Data Read" ohne Adresszeiger-Veränderung
   b=(b&~mask)|(DoRop3(b,pattern,0,GrafDC.rop2)&mask);
   OneDataCommand(b,0xC0);	// "Data Write" mit Auto-Inkrement
  }
 }
}

/************************************************************************
 * Der folgende Kode für Polygone kann vorläufig nur ALTERNATE
 * und kein vernünftiges Clipping
 ************************************************************************/
#define MAXLISTBYTES 600
	// bestimmt maximale Polygonkomplexität sowie Stack-Speicherverbrauch
typedef struct {
 WORD len;
 BYTE list[MAXLISTBYTES];
}TList;

//=== Platz in Punktliste machen oder entfernen ===
static bool ListMachePlatz(TList*L, BYTE*p, int Platz) {
 unsigned idx=p-L->list;
 unsigned newlen=L->len+Platz;
 unsigned tail=L->len-idx;
 if (newlen>MAXLISTBYTES) return false;
 if (Platz>0) memmove(p+Platz,p,tail);
 else memmove(p,p-Platz,tail+Platz);
 L->len=newlen;
 return true;
}

//=== Punkt in Punktliste hinzufügen oder (wegen ALTERNATE) entfernen ===
static bool ListInsert(TList*L, int x, int y) {
 BYTE *pb=L->list;
 BYTE *pe=pb+L->len;
 BYTE *pxl, xl;	// X-Länge
// Koordinaten einschränken
 if ((unsigned)y>=GRAF_Y) return true;
 if (x<0) x=0; if (x>GRAF_X) x=GRAF_X;
// y-Koordinate suchen
 for (; pb<pe; pb+=2+pb[1]) {
  if (*pb==(BYTE)y) {
// Gleiche Y-Koordinate?
   pxl=++pb;
   pb++;
   xl=*pxl;	// muss >=1 sein!
   do{
    if (*pb==(BYTE)x) {
// Wenn Punkt bereits vorhanden, Punkt löschen
     if (*pxl>1) {
      ListMachePlatz(L,pb,-1);	// nur X weg
      (*pxl)--;			// 1 weniger
     }
     else ListMachePlatz(L,pb-2,-3);	// X und Y weg
     return true;
    }
    if (*pb>(BYTE)x) break;
    pb++;
   }while (--xl);
// Punkt nicht vorhanden, hinzufügen
   if (!ListMachePlatz(L,pb,1)) return false;
   *pb=(BYTE)x;
   (*pxl)++;
   return true;
  }
  if (*pb>(BYTE)y) break;
 }
// neue y-Koordinate erzeugen (pb zeigt auf Folge-y-Koordinate)
 if (!ListMachePlatz(L,pb,3)) return false;
 pb[0]=(BYTE)y;	// neues Y
 pb[1]=1;	// nur 1 X in dieser Zeile
 pb[2]=(BYTE)x;	// neues X
 return true;
}

//=== LineDDA für Polygone mit ALTERNATE-Besonderheiten: ===
// * Abarbeitung stets von oben nach unten
// * genau einem Punkt pro Y-Koordinate
static bool ListLineDda(TList*L, int xa, int ya, int xe, int ye) {
 long xs,xp/*blindes gcc:*/=0;
 ye-=ya; if (ye==0) return true;	// bei waagerechten Linien nichts tun
 xe-=xa;
// nach Y sortieren, ye>=ya (immer nach unten zeichnen)
 if (ye<0) {ya+=ye; xa+=xe; xe=-xe; ye=-ye;}
 xs=((long)xe<<16)/ye;	// Steigung als Hex-Festkommazahl (auch negativ)
 xp=(long)xa<<16;	// Start als Hex-Festkommazahl
// hier ggf. runden! xp|=0x8000;
 do{
  if (!ListInsert(L,xp>>16,ya)) return false;	// Polygon zu komplex
  xp+=xs;		// Festkomma-Steigung auf Festkomma-Position
  ya++;
 }while(--ye);
 return true;
}

//=== ein Polygon in Punktliste einfügen ===
static bool ListPolyDda(TList*L, const POINT*pt, int numpt) {
 const POINT *p0=pt,*pn;
 if (numpt<0) return false;	// darf nicht sein
 if (numpt<=2) return true;	// so tun, als wär's auch OK, oder?
 do{
  pn=p0;
  if (numpt!=1) pn=pt+1;	// nächster Punkt
  if (!ListLineDda(L,pt->x,pt->y,pn->x,pn->y)) return false;
  pt=pn;
 }while(--numpt);
 return true;
}

//=== mehrere Polygone in Punktliste einfügen ===
static bool ListPolyPolyDda(TList*L, const POINT*pt, const int*numpt, int numpoly) {
 if (numpoly<=0) return false;
 do{
  if (!ListPolyDda(L,pt,*numpt)) return false;
  pt+=*numpt++;
 }while(--numpoly);
 return true;
}

//=== Scanzeilen-Liste ausgeben ===
static void ListScanline(const TList*L) {
 const BYTE *py=L->list;
 const BYTE *pe=py+L->len;
 for (;py<pe; py+=2+py[1]) {
  int y=py[0];
  int xn=py[1];	// muss gerade sein!!
  const BYTE *px=py+2;
  if (xn&1) return;	// Notbremse
  do{
   OutScanline(px[0],px[1],y,GrafDC.pattern[y&7],NULL);
   px+=2;
  }while(xn-=2);
 }
}

//=== Begrenzung für mehrere Polygone zeichnen ===
// (ist für Stiftbreiten>1 NICHT dasselbe wie mehrere Aufrufe von OutlinePoly)
void drawpolypoly(int numpoly, const int*numpt, const POINT*pt) {
 if (numpoly<=0) return;
 do{
  drawpoly(*numpt,pt);
  pt+=*numpt++;
 }while(--numpoly);
}

void fillpolypoly(int numpoly, const int*numpt, const POINT*pt) {
 TList L;	// auf dem Stack, nicht statisch (besser Heap?)
 L.len=0;
 if (!ListPolyPolyDda(&L,pt,numpt,numpoly)) return;
 ListScanline(&L/*,brush*/);
}

/************************************************************************
 * PolyPolygon zeichnen
 * (ist nicht dasselbe wie mehrere Aufrufe von Polygon()!)
 ************************************************************************/
bool GrafPolyPolygon(const POINT*pt, const int*numpt, int numpoly) {
 if (GrafDC.brush) fillpolypoly(numpoly,numpt,pt);
 if (GrafDC.pen) drawpolypoly(numpoly,numpt,pt);
 return true;
}

/************************************************************************
 * Polygon zeichnen
 ************************************************************************/
bool GrafPolygon(const POINT*pt, int numpt) {
 return GrafPolyPolygon(pt,&numpt,1);
}

/************************************************************************
 * Rechteck wie Windows mit Füllung und Rand
 ************************************************************************/
void GrafRectangle(int left, int top, int right, int bottom){
 if (GrafDC.brush) bar(left,top,right,bottom);
 if (GrafDC.pen) rectangle(left,top,right,bottom);
}

/************************************************************************
 * Füllmuster (8 Byte) setzen, NULL für keine Füllung
 ************************************************************************/
void GrafSetBrush(const BYTE pattern[8]) {
 if (pattern) {
  memcpy(GrafDC.pattern,pattern,8);
  GrafDC.brush=1;
 }else GrafDC.brush=0;	// HOLLOW_BRUSH
}

/************************************************************************
 * Füllmuster (8 Byte) generieren mit Helligkeitseffekt
 ************************************************************************/
void GrafCreateSolidBrush(BYTE pattern[8], COLORREF color) {
 int i,j;
 unsigned a=0;	// Akku
 BYTE b/*blindes gcc:*/=0;
 for (i=0; i<8; i++) {
  for (j=0; j<8; j++) {
   b<<=1;
   a+=color;
   if (a>=255) {	// bei Überlauf...
    b++;		// Bit 0 setzen
    a-=255;		// Überlauf weg
   }
  }
  if (i&1) b=(b<<1)|(b>>7);	// rotieren für 45°-Effekt
  *pattern++=b;
 }
}

void setvisualpage(int page) {
 TwoDataCommandW(GRAFSTART+page*PAGEADD,0x42);	// Graphic Home
}

// GrafDC initialisieren
void initgraph(void) {
 setvisualpage(0);
 TwoDataCommandW(TEXT_X,0x43);		// Graphic Area - svw. Bytes pro Zeile
 setactivepage(0);
 setviewport(0,0,getmaxx(),getmaxy(),false);
 cleardevice();
 setcolor(1);	// weiße Linie
 GrafDC.pen=1;
 GrafDC.brush=1;
 GrafDC.linestyle=(unsigned)-1;	// durchgezogene Linie
 GrafDC.rop2=R2_COPYPEN;
}

/************************************************************************
 * Flüssigkristall-Anzeige 240x128 Pixel initialisieren:
 * Ports auf Ausgabe schalten, 6x8-Font (40x16 Zeichen) benutzen
 ************************************************************************/
void DispInit(void){
 int i;
 static const POINT TestPoly[]={	// zum Test des Polygons: Fünfeck
  {0,80},
  {60,80},
  {10,120},
  {30,60},
  {50,120}};
 static const POINT TestPolyPoly[]={	// zum Test PolyPolygon: 2 Dreiecke
  {90,75},
  {120,120},
  {60,120},
  {60,90},
  {120,90},
  {90,135}};
 static const int TPP33[]={3,3};	// ineinander verschachtelt zum Judenstern
 DispFlags=WAIT_NORMAL;
 P4OUT=P4DEF^RST;	// Reset aktiv
 P4DIR=0x7F;	// alles Ausgang, außer Tastenabfrageleitung
 for (i=100;i;i--) nop();
 P4OUT=P4DEF;		// Reset-Zustand entfernen
 ZeroDataCommand(0x81);	// "XOR"+"CGROM" Mode
 if (DispFailed()) return;
 TwoDataCommandW(CGSTART>>11,0x22);	// Anfangsadresse der Zeichenbildtabelle
 TwoDataCommandW(TEXTSTART,0x40);	// Text Home: gleich hinter der Grafik
 TwoDataCommandW(TEXT_X,0x41);		// Text Area - svw. Zeichen pro Zeile
 initgraph();
// for (i=0; i<8; i++) GrafDC.pattern[i] = i&1 ? 0x55 : 0xAA;
 GrafCreateSolidBrush(GrafDC.pattern,128);
// Die ersten 400h liegen brach (wo ist das dokumentiert?)
 DispDataWrite(CGSTART+0x400,gUserChars,sizeof(gUserChars));
 ZeroDataCommand(0x9F);	// "Text"+"Graphic" ON

// Ab hier nur Testkode!
 DataPuts(TEXTSTART,"Hallo!",6);	// müsste links oben erscheinen
 DataPuts(TEXTSTART+15,"ÄÖÜäöüß±gµ\x81",11);
 TwoDataCommand(0x02,0x03,0x21);
 {
  char a[32];
  for (i=0; i<32; i++) a[i]=0x60+i;
  DispDataWrite(TEXTSTART+40,a,32);
 }
 GrafSetPixel(100,100,1);
 moveto(30,40); lineto(50,70); lineto(66,-4);
 GrafRectangle(80,20,120,80);
 GrafPolygon(TestPoly,elemof(TestPoly));
 GrafPolyPolygon(TestPolyPoly,TPP33,elemof(TPP33));
 bar(200,100,220,120);	// Na??
 bar(90,70,220,78);
 GrafDC.rop2=R2_XORPEN;
 bar(90,65,220,69);
}

/************************************************************************
 * Tastenstatus der 8 Tasten erfragen
 * Jede gedrückte Taste entspricht einem gesetzten Bit im Ergebnisbyte
 ************************************************************************/
BYTE Inp_Tasten(void){
 BYTE mask,ret=0;
// P4OUT=P4DEF;	// ggf. CE\ entfernen
 P5DIR=0xFF;	// alles Ausgänge
 for (mask=0x01; mask; mask<<=1){
// In der Schaltung wurde ein Pullup an P4.7 vergessen
// (beim ATmega sind diese im Mikrocontroller:-)
// Statt Schaltungsänderung wird hier das Pin "dynamisch" aufgeladen...
  P5OUT=0xFF;	// keinen Taster abfragen (kein Kurzschluss erzeugen)
  P4DIR=0xFF;	// Pin P4.7 aufladen (P4OUT.7 = 1)
  P4DIR=0x7F;	// Pin P4.7 wieder hochohmig
  P5OUT=~mask;	// Alles high, bis auf die zu prüfende Taste
  nop();	// Zeit lassen
  if (!(P4IN&0x80)) ret|=mask;	// eine gedrückte Taste zieht P4.7 nach Low
 }
 return ret;
}

/************************************************************************
 * Zustand des Nockenschalters erfragen
 ************************************************************************/
BYTE Inp_Schalter(void) {
 BYTE p3=~P3IN;
 if (p3&0x08) return 0;	// Schaltplan: J1 Kontakt links
 if (p3&0x04) return 2;	// Schaltplan: J1 Kontakt rechts
 return 1;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded