/******************************************************************************
*
* project name:    TIGCC Tools Suite
* file name:       ttrow.c
* initial date:    15/03/2002
* author:          Peter J. Rowe (mig53@yahoo.com)
*
* description:     Visual RowRead
*
* This is a simple program that I wrote to help me when I am using _rowread
* in my programs.
* The bit numbers across the top and the row numbers down the left side are
* the same as in the documentation.  The hex numbers on the right side are
* the numbers that you would pass into _rowread to read the row corresponding
* to the row number on the left side of the grid. The numbers across the
* bottom are two to the power of the bit number, for easy anding to get a
* specific key.  When you press a key, the appropriate cell of the grid is
* shaded.
*
*
* changes by thomas.nussbaumer@gmx.net:
*
* (1) integration into TIGCC Tools Suite (formatting and naming conventions)
* (2) type of oldfont fixed (should be short)
* (3) missing FontSetSys() at end added
* (4) dependencies from ExtGraph removed and highly optimized by improving
*     previously used ExtGraph function further
*
* $Id: ttrow.c,v 2.2 2002/03/15 12:11:29 tnussb Exp $
*
******************************************************************************/

#define __TI89CHECK__  ((((void*)((*(unsigned long*)0xC8)&0x600000))==(void*)0x400000)==0)

#if defined(USE_TI89) && defined(USE_TI92P)
  #error cannot produce both versions at once
#elif defined(USE_TI89)
  #define CheckHWMatch()    (__TI89CHECK__ ? 1 : 0)
  #define DESIRED_CALCTYPE  "TI89"
  #define C89_92(x,y) (x)
#elif defined(USE_TI92P)
  #define USE_V200
  #define CheckHWMatch()  (__TI89CHECK__ ? 0 : 1)
  #define DESIRED_CALCTYPE  "TI92+"
  #define C89_92(x,y) (y)
#else
  #error Please specify either -DUSE_TI89 or -DUSE_TI92P on commandline
#endif

#define NO_EXIT_SUPPORT      /* doesn't need exit support */
#define OPTIMIZE_ROM_CALLS
#include <tigcclib.h>


//=============================================================================
// draw "pressed key" sprite
//=============================================================================
static inline void DrawPressed(short x,short y) {
    long           addr  = (long)LCD_MEM+(y<<5)-(y<<1)+((x>>3)&0xfffe);
    unsigned short cnt   = 16-(x&15);
    long           val_1 = (long)C89_92(0xAAA8,0xAAAA)<<cnt;
    long           val_2 = (long)C89_92(0x5554,0x5555)<<cnt;

    *(long*)addr|=val_1;addr+=30;*(long*)addr|=val_2;addr+=30;
    *(long*)addr|=val_1;addr+=30;*(long*)addr|=val_2;addr+=30;
    *(long*)addr|=val_1;addr+=30;*(long*)addr|=val_2;addr+=30;
    *(long*)addr|=val_1;
}


//=============================================================================
// clears "pressed key" sprite
//=============================================================================
static inline void DrawBlank(short x,short y) {
    short h    = 7;
    long  addr = (long)LCD_MEM+(y<<5)-(y<<1)+((x>>3)&0xfffe);

    #if defined (USE_TI89)
    long value = ~((long)(0xfffc) <<(16-(x&15)));
    #else
    long value = ~((long)(0xffff)<<(16-(x&15)));
    #endif

    for (;h;h--,addr+=30) *(long*)addr&=value;
}


#define ASM_SWAP(val) ({\
    register unsigned long tmp = val;\
    asm volatile ("swap %0" : "=d" (tmp) : "0" (tmp));\
    tmp;})


//=============================================================================
// draws a horizontal line from (x1,y) to (x2,y) in LCD_MEM using A_NORMAL
//=============================================================================
static inline void HorizontalLineNormal(short x1,short x2,short y) {
    static const unsigned short table1[16] = {0xffff,0x7fff,0x3fff,0x1fff,
                                              0x0fff,0x07ff,0x03ff,0x01ff,
                                              0x00ff,0x007f,0x003f,0x001f,
                                              0x000f,0x0007,0x0003,0x0001};
    static const unsigned short table2[16] = {0x0000,0x8000,0xc000,0xe000,
                                              0xf000,0xf800,0xfc00,0xfe00,
                                              0xff00,0xff80,0xffc0,0xffe0,
                                              0xfff0,0xfff8,0xfffc,0xfffe};
    short           dx;
    short           sx;
    unsigned short* p;

    if (x1>x2) {
        short tmp = x1;
        x1 = x2;
        x2 = tmp;
    }

    p  = (unsigned short*)(LCD_MEM+(y<<5)-(y<<1)+((x1>>3)&0xfffe));
    dx = x2-x1;
    sx = x1 & 0x000f;

    if (dx<16) {
        unsigned long val = (ASM_SWAP(table1[dx])) >> sx;
        *(unsigned long*)p |= val;
        return;
    }

    if (sx) {
        *p++ |= table1[sx];
        dx -= (16-sx);
    }
    while (dx >= 16) {
        *p++ |= 0xffff;
        dx-=16;
    }
    if (dx) *p |= table2[dx];
}


//=============================================================================
// draws the number strings corresponding to the bits at the bottom of screen
//=============================================================================
static inline void DrawNumberStrings(void) {
    static const char* s1[] = {"128","64","32","16","8","4","2","1"};
    static const char* s2[] = {"x80","x40","x20","x10","x08","x04","x02","x01"};
    static const short x[]  = {18,C89_92( 35, 37),C89_92( 50, 54),
                                  C89_92( 65, 71),C89_92( 82, 90),
                                  C89_92( 97,107),C89_92(112,124),
                                  C89_92(127,141)};
    short x2 = 18;
    short i;

    for (i=0;i<8;i++,x2+=C89_92(15,17)) {
        DrawStr(x[i], C89_92(64, 88), s1[i], A_NORMAL);
        DrawStr(x2,   C89_92(72, 96), s2[i], A_NORMAL);
    }
}


//=============================================================================
// as usual: the main function ...
//=============================================================================
void _main(void) {
	short         oldfont;
	unsigned char row[C89_92(7, 10)];
	unsigned char oldrow[C89_92(7, 10)];
	short         i;
	short         j;
	INT_HANDLER   oldint1;
	INT_HANDLER   oldint5;
	LCD_BUFFER    lcd;

    #if defined(USE_TI89)
	static short rows[7]	= {0xFFFE,0xFFFD,0xFFFB,0xFFF7,0xFFEF,0xFFDF,0xFFBF};
    #else
	static short rows[10]	= {0xFFFE,0xFFFD,0xFFFB,0xFFF7,0xFFEF,0xFFDF,0xFFBF,
							   0xFF7F,0xFEFF,0xFDFF};
    #endif

    // check if this program is running on the right calculator type
	if (!CheckHWMatch()) {
		ST_helpMsg("ERROR: can only run on a "DESIRED_CALCTYPE);
		return;
	}

    // save screen for later restore
    LCD_save(lcd);

    // save old interrupt vectors and set INT1 and INT5 to dummy handlers
	oldint1 = GetIntVec(AUTO_INT(1));
	oldint5 = GetIntVec(AUTO_INT(5));
	SetIntVec(AUTO_INT(1), DUMMY_HANDLER);
	SetIntVec(AUTO_INT(5), DUMMY_HANDLER);

    // save previously set font and clear the screen
	oldfont = FontSetSys(F_4x6);
	ClearScreen();

#if defined(USE_TI89)
	DrawStr(0, 0, "Bit \x16", A_NORMAL);
#endif

	for (i=0, j=C89_92(22,17); i<8; i++, j+=C89_92(15,17)) {
		static char bitstring[C89_92(2,6)] = C89_92("0","Bit 0");
		bitstring[C89_92(0,4)] = 7 - i + '0';
		DrawStr(j, 0, bitstring, A_NORMAL);
    }

    DrawNumberStrings();

	for (i=0, j=8; i<C89_92(7,10); i++,j+=8) {
#if defined(USE_TI89)
        static const char* mask[] = {"FFFE","FFFD","FFFB","FFF7","FFEF","FFDF",
                                     "FFBF"};
#else
        static const char* mask[] = {"FFFE","FFFD","FFFB","FFF7","FFEF","FFDF",
                                     "FFBF","FF7F","FEFF","FDFF"};
#endif
        static char bitx[] = "Bit 0";

        bitx[4] = '0' + i;
	    DrawStr(0, j, bitx, A_NORMAL);
        DrawStr(C89_92(138,153), j, mask[i], A_NORMAL);
    }

    // draws vertical lines ... fastest possible way!
    for (i=0, j=C89_92(17,16); i<9; i++, j+=C89_92(15,17)) {
        unsigned char *pixaddr = ((unsigned char*)(LCD_MEM))+(j>>3);
        unsigned char pixmask  = (unsigned char)(0x80 >> ((j)&7));
        short         y1       = 0;

        while (y1++<=C89_92(68+8,92+8)) {
            *pixaddr |= pixmask;
            pixaddr  += 30;
        }
    }

    // draw the horizontal lines ...
    for (i=0, j=6; i<C89_92(8,11); i++, j+=8) {
        HorizontalLineNormal(0, C89_92(154,169), j);
    }

	DrawStr(C89_92(50,90), C89_92(95,123), "Press [ON] to quit", A_NORMAL);

    // main loop ...
	do {
		short ioff;
		for (i=0, ioff=7; i<C89_92(7, 10); i++, ioff+=8) {
			short joff;
			oldrow[i] = row[i];
			row[i]    = _rowread(rows[i]);
			if (row[i] == oldrow[i]) {
				for (j=0, joff=C89_92(123,136); j<8; j++,joff-=C89_92(15,17)) {
					if (row[i] & 1 << j) DrawPressed(joff,ioff);
					else                 DrawBlank(joff, ioff);
				}
			}
		}
	}
    /*loop until the on key is pressed*/
	while (!(~(*(volatile unsigned char *)0x60001A) & 2));

    /*loop until the on key is released*/
	while(~(*(volatile unsigned char *)0x60001A) & 2);

    // restore interrupts
	SetIntVec(AUTO_INT(1), oldint1);
	SetIntVec(AUTO_INT(5), oldint5);

    // restore previously set font and restore screen
	FontSetSys(oldfont);
    LCD_restore(lcd);

    OSClearBreak(); // clear the break key (just to make sure ...)

    //-------------------------------------------------------------------------
    // empty the keyboard queue
    //
    // this is necessary, because if a user holds down a key and presses the
    // [on] key, this key would otherwise dispatched to the AMS, which is NOT
    // always a good idea. IMHO it's better to clear the queue before exiting
    //-------------------------------------------------------------------------
    OSqclear(kbd_queue());
}

//#############################################################################
//###################### NO MORE FAKES BEYOND THIS LINE #######################
//#############################################################################
//
//=============================================================================
// Revision History
//=============================================================================
//
// $Log: ttrow.c,v $
// Revision 2.2  2002/03/15 12:11:29  tnussb
// added output of numbers at bottom in hexcode, too
//
// Revision 2.1  2002/03/15 11:34:12  tnussb
// initial version
//
//
