/*
 *                               5963DEMO.CPP
 *
 *
 *  A program to exercise Rohm BU5963AS OSD chip through PC printer port.
 *
 *  Copyright (c) 1992 by Michael A. Hardwick.  All rights reserved.
 *  Completed version 1.00 July 31, 1992.  In Turbo C++ 3.0.
 *
 *
 *  Connect BU5963AS to printer port pins as follows:
 *
 *      port pin:  1  -> OSD Chip Select...pin 29
 *      port pin:  2  -> OSD Data Input....pin 31
 *      port pin: 14  -> OSD Clock Input...pin 30
 *
 *  Printer port pins 18~25 should be grounded to BU5963AS test/demo
 *  fixture.  If the cable is long, line terminations should be added
 *  at the BU5963AS pins.  330/680 (330 pullup/680 pulldown) resistive
 *  terminations were adequate in my experiments, using an average
 *  quality 3 meter cable with two different (486, 286) computers.
 *
 *
 *
 *
 */

#include <stdio.h>
#include <dos.h>
#include <conio.h>

#define true    1
#define false   0
                                //the following structure types are
 typedef struct {               //all 16 bits total size...
        unsigned start : 6;     //display starting position
        unsigned size1 : 2;     //char size for top line
        unsigned size2 : 2;     //char size for all other lines
        unsigned lovr  : 6;     //leftover bits
        } hvreg;                //this struct type used for H & V OSD regs

 typedef struct {
        unsigned bkcol : 3;     //main background color
        unsigned cscol : 3;     //char background color
        unsigned olcol : 3;     //char outline color
        unsigned lovr  : 7;     //leftover bits
        } colreg;               //for OSD color register

 typedef struct {
        unsigned omode : 3;     //output mode, 1=norm(?), table in OSD data
        unsigned bfreq : 1;     //blink frequency, 0=fast, 1=slow
        unsigned bduty : 2;     //blink duty-- off, 25%, 50%, 75%
        unsigned bi    : 1;     //if blink off, 0 sets reverse/blank chars
        unsigned brb   : 1;     //blink mode, 1=blank, 0=reverse video
        unsigned oline : 1;     //char outline control, 1=on
        unsigned csize : 1;     //char background control, 1=on
        unsigned port1 : 1;     //digital output ports, 1=high...
        unsigned port2 : 1;     //
        unsigned port3 : 1;     //
        unsigned lovr  : 3;     //leftover bits
        } miscreg;              //for OSD misc. register

 typedef struct {
            char ochar : 8;     //ascii char for OSD chip goes here
        unsigned blnk  : 1;     //1=blink/rev on, 0=blink/rev off
        unsigned color : 3;     //color for this char
        unsigned spc   : 1;     //1=superimpose (normal), 0=RGB out
        unsigned lovr  : 3;     //leftover bits
        } charword;             //for sending displayable chars to OSD

        hvreg         hreg, vreg;     //declare some globals
        colreg        creg;
        miscreg       mreg;
        charword      cvar, image[240];
        unsigned char curpos, oldcurpos, ncolor = 7;
        char          nblink = false;
        int           pport, cport;


void ship(int *pword) {         //ship() shifts 16 bits out the parallel port
                                //serially on D0, with clocking on CTL bit 1
        char i;                 //note: D1~7 are driven with garbage!
        int sdat;

sdat = *pword;                  //get the word to be shipped
for(i = 0; i < 16; i++) {
        outport(pport, (sdat >> i));    //put data on parallel port
        outport(cport, 0x01);           //raise the clock line to latch bit
        outport(cport, 0x03);           //then lower it again
        }                               //(CS\ & SCLK are inverted in hdwe)
}


void setadd(unsigned char addr) //setadd() loads the OSD address register
{
        int adword = 0x2000;    //address mode bit is preset high

adword |= addr;                 //or in the address byte
ship(&adword);                  //call the serial output function
}

                                //regup() maintains OSD control registers,
void regup(char amount) {       //and PC screen.
        unsigned char tempy;
if(amount ==  0) goto Init;     //complete setup.
if(amount == 10) goto OSDsend;  //just writes PC screen,
                                //and ships current data to OSD regs.
if(amount == 11) goto CBonly;   //updates color & blink data on PC screen.
else goto Normal;               //+/-1 in field at PC cursor Y location.
                                //
Init:                           //
        hreg.start = 41;        //set default OSD register values...
        hreg.size1 =  0;        //
        hreg.size2 =  0;        //
        hreg.lovr  =  0;        //all leftover bits to be zeros

        vreg.start = 41;
        vreg.size1 =  0;
        vreg.size2 =  0;
        vreg.lovr  =  0;

        creg.bkcol =  0;
        creg.cscol =  0;
        creg.olcol =  0;
        creg.lovr  =  0;

        mreg.omode =  1;
        mreg.bfreq =  0;
        mreg.bduty =  3;
        mreg.bi    =  0;
        mreg.brb   =  0;
        mreg.oline =  1;
        mreg.csize =  0;
        mreg.port1 =  0;
        mreg.port2 =  0;
        mreg.port3 =  0;
        mreg.lovr  =  0;

OSDsend:

clrscr();
gotoxy(37,1);
printf("Rohm BU5963AS Demo Program");
gotoxy(40,3);
printf("Copyright (c) 1992 by");
gotoxy(40,4);
printf("Decade Video Products");
gotoxy(40,5);
printf(" All rights reserved.");

gotoxy(30,10);
printf("F1: Increment Color    F2: Toggle Blink   ");
gotoxy(30,11);
printf("F3: Field Select Up    F4: Increment Field");
gotoxy(30,12);
printf("F5: Field Select Down  F6: Decrement Field");
gotoxy(30,13);
printf("F7: Color Bars         F8: Clear Screen   ");
gotoxy(30,15);
printf("Esc: Quit Program");

gotoxy(1,1);
printf("  %2d : hreg.start\n", hreg.start); //1
printf("  %2d : hreg.size1\n", hreg.size1); //2
printf("  %2d : hreg.size2\n", hreg.size2);
printf("  %2d : vreg.start\n", vreg.start);
printf("  %2d : vreg.size1\n", vreg.size1);
printf("  %2d : vreg.size2\n", vreg.size2);
printf("  %2d : creg.bkcol\n", creg.bkcol);
printf("  %2d : creg.cscol\n", creg.cscol);
printf("  %2d : creg.olcol\n", creg.olcol);
printf("  %2d : mreg.omode\n", mreg.omode);
printf("  %2d : mreg.bfreq\n", mreg.bfreq);
printf("  %2d : mreg.bduty\n", mreg.bduty);
printf("  %2d : mreg.bi   \n", mreg.bi   );
printf("  %2d : mreg.brb  \n", mreg.brb  );
printf("  %2d : mreg.oline\n", mreg.oline);
printf("  %2d : mreg.csize\n", mreg.csize);
printf("  %2d : mreg.port1\n", mreg.port1);
printf("  %2d : mreg.port2\n", mreg.port2);
printf("  %2d : mreg.port3\n", mreg.port3); //19
printf("  %2d : cvar.color\n", ncolor % 8); //20
if(nblink)
printf( "   1 : cvar.blnk   "            ); //21
        else
printf( "   0 : cvar.blnk   "            ); //21
gotoxy(1,1);
amount = 0;

Normal:

switch(wherey()) {
        case 1: hreg.start += amount;
                printf("  %2d", hreg.start);
                gotoxy(1, wherey());
                break;
        case 2: hreg.size1 += amount;
                printf("  %2d", hreg.size1);
                gotoxy(1, wherey());
                break;
        case 3: hreg.size2 += amount;
                printf("  %2d", hreg.size2);
                gotoxy(1, wherey());
                break;
        case 4: vreg.start += amount;
                printf("  %2d", vreg.start);
                gotoxy(1, wherey());
                break;
        case 5: vreg.size1 += amount;
                printf("  %2d", vreg.size1);
                gotoxy(1, wherey());
                break;
        case 6: vreg.size2 += amount;
                printf("  %2d", vreg.size2);
                gotoxy(1, wherey());
                break;
        case 7: creg.bkcol += amount;
                printf("  %2d", creg.bkcol);
                gotoxy(1, wherey());
                break;
        case 8: creg.cscol += amount;
                printf("  %2d", creg.cscol);
                gotoxy(1, wherey());
                break;
        case 9: creg.olcol += amount;
                printf("  %2d", creg.olcol);
                gotoxy(1, wherey());
                break;
        case 10:mreg.omode += amount;
                printf("  %2d", mreg.omode);
                gotoxy(1, wherey());
                break;
        case 11:mreg.bfreq += amount;
                printf("  %2d", mreg.bfreq);
                gotoxy(1, wherey());
                break;
        case 12:mreg.bduty += amount;
                printf("  %2d", mreg.bduty);
                gotoxy(1, wherey());
                break;
        case 13:mreg.bi    += amount;
                printf("  %2d", mreg.bi   );
                gotoxy(1, wherey());
                break;
        case 14:mreg.brb   += amount;
                printf("  %2d", mreg.brb  );
                gotoxy(1, wherey());
                break;
        case 15:mreg.oline += amount;
                printf("  %2d", mreg.oline);
                gotoxy(1, wherey());
                break;
        case 16:mreg.csize += amount;
                printf("  %2d", mreg.csize);
                gotoxy(1, wherey());
                break;
        case 17:mreg.port1 += amount;
                printf("  %2d", mreg.port1);
                gotoxy(1, wherey());
                break;
        case 18:mreg.port2 += amount;
                printf("  %2d", mreg.port2);
                gotoxy(1, wherey());
                break;
        case 19:mreg.port3 += amount;
                printf("  %2d", mreg.port3);
                gotoxy(1, wherey());
                break;
        default: ;
        }

setadd(240);                    //load base address for control registers
ship((int *) &hreg);            //cast struct pointer to an int pointer
ship((int *) &vreg);            //OSD address autoincrements
ship((int *) &creg);
ship((int *) &mreg);
goto End;

CBonly:

tempy = wherey();
gotoxy(1, 20);
printf("  %2d", ncolor % 8);
gotoxy(1, 21);
if(nblink)
        printf(  "   1");
else
        printf(  "   0");
gotoxy(1,tempy);

End:
}


void colorbar(void) {                   //puts up the color bar pattern

        unsigned char i, j=0, c, t;

cvar.ochar = 0x7f;      //we're going to display colored blocks
cvar.blnk  = 0;         //
setadd(0);              //starting in upper left corner
for(i=0; i<10; i++) {                   //line counter, do every line
        for(c=0; c<8; c++) {            //color bar counter, need 8
                cvar.color = c;         //
                for(t=0; t<3; t++) {    //char counter, 3 blocks to a bar
                        ship((int *) &cvar);
                        image[j++] = cvar;
                        }               //OSD address autoincrements
                }
        }
curpos = 0;
}


void clrOSD(void) {             //clear OSD display RAM & image memory
        unsigned char i;
setadd(0);
cvar.ochar = ' ';               //by filling both with
cvar.color = 7;                 //lovely WHITE blanks!
                                //The reason for this is OSD chip
                                //interprets black chars (even blanks)
                                //as transparent, and they wouldn't
                                //show up when reversed, which would
                                //prevent my cursor from showing!
                                //
cvar.blnk  = 0;                 //
for(i=0; i<240; i++) {          //
        ship((int *) &cvar);    //OSD address autoincrements here too.
        image[i] = cvar;
        }
curpos = 0;                     //reset cursor to upper left corner
}


main(int argc, char *argv[]) {

        unsigned char i;
        char tchar, ublink, blflag;

if(argc < 3) {
        sscanf(argv[1], "%x", &pport);
        if(pport == 0x378) goto Skate;
        if(pport == 0x278) goto Skate;
        }
puts("Required argument for port address: 378 or 278 (hex).");
goto Quit;

Skate:                          //Hey, it's OK to do my thing!
        cport = pport + 2;      //set the control port address

        cvar.ochar = ' ';       //start with a blank space
        cvar.blnk  =   0;       //not blinking
        cvar.color =   7;       //character color = white
        cvar.spc   =   1;       //normal superimposed mode
        cvar.lovr  =   0;       //leftover bits must be zeros
                                //OSD chip initialization...
outport(cport, 0x02);           //set SCLK low
outport(cport, 0x03);           //select OSD chip
clrOSD();                       //wipe OSD display RAM & local image
regup(0);                       //initialize OSD control registers,
                                //and PC screen

             //The Great OSD Screen Editor...

ublink = image[curpos].blnk;    //remember blink state under cursor
image[curpos].blnk = 1;         //cursor is a blinking whatever
setadd(curpos);                 //make the OSD chip show it...
ship((int *) &image[curpos]);   //

while((tchar = getch()) != 0x1B) {      //escape hatch = esc
        switch(tchar) {                 //
        case 0x00 :                     //nul always preceeds special char
                tchar = getch();        //get the special char immediately
                switch(tchar) {         //and act on it...
                case 'P' :      //cursor down
                        if(curpos <= (239 - 24)) curpos += 24;
                        break;
                case 'H' :      //cursor up
                        if(curpos >= 24) curpos -= 24;
                        break;
                case 'K' :      //cursor left
                        if(curpos > 0 && curpos < 24) curpos -= 1;
                        if(curpos > 23 && (curpos % 24) != 0) curpos -= 1;
                        break;
                case 'M' :      //cursor right
                        if(((curpos + 1) % 24) != 0) curpos += 1;
                        break;
                case ';' :      //F1 key, char color changer
                        ncolor += 1;
                        regup(11);
                        break;
                case '<' :      //F2 key, char blink bit changer
                        nblink = !nblink;
                        regup(11);
                        break;
                case '=' :      //F3 key, PC cursor up
                        if(wherey() >1) gotoxy(1,(wherey()-1));
                        break;
                case '?' :      //F5 key, PC cursor down
                        if(wherey() < 19) gotoxy(1,(wherey()+1));
                        break;
                case '>' :      //F4 key, increase reg value at PC cursor
                        regup(1);
                        break;
                case '@' :      //F6 key, decrease reg value at PC cursor
                        regup(-1);
                        break;
                case 'A' :      //F7 key, put up color bars
                        colorbar();
                        break;
                case 'B' :      //F8 key, clear OSD screen
                        clrOSD();
                        break;
                case 'C' :      //F9 key, spare
                        break;
                case 'D' :      //F10 key, spare
                        break;
                default:;
                        break;
                }
                break;
        case 0x08 :                     // <- rubout handler
                if(curpos > 0) {
                        cvar.ochar = ' ';
                        cvar.color = 7;
                        cvar.blnk  = 0;
                        setadd(--curpos);
                        ship((int *) &cvar);
                        image[curpos] = cvar;
                }
                break;

        case 0x0D :                             //CR handler
                curpos -= (curpos % 24);        //subtract space to left
                if(curpos < 216) curpos += 24;  //216 is lower left corner
                break;

        default:                        //normal "printable" char handler
                if(curpos <= 239) {
                        cvar.ochar =  tchar;
                        cvar.blnk  = nblink;
                        cvar.color = ncolor;
                        setadd(curpos);
                        ship((int *) &cvar);
                        image[curpos] = cvar;
                        if(nblink == true)
                                blflag = true;
                        else blflag = false;
                                        //true blflag tells cursor handler
                                        //to let this character blink!
                }
                if(curpos < 239) curpos++;
                break;
        }
                                          //cursor movement handler
        if(blflag == true) ublink = true; //
        blflag = false;                   //
        image[oldcurpos].blnk = ublink;   //retrieve old blink state
        setadd(oldcurpos);                //and restore old OSD char
        ship((int *) &image[oldcurpos]);  //
        ublink = image[curpos].blnk;      //remember blink state
        image[curpos].blnk = 1;           //under new cursor position
        setadd(curpos);                   //then show new cursor
        ship((int *) &image[curpos]);     //
        oldcurpos = curpos;               //remember cursor position

}               //That's the Editor!

outport(cport, 0x02);           //deselect OSD chip
clrscr();                       //clean up the PC screen

Quit:                           //Take my marbles and go home.

return 0;
}

