// Subject:  STEREO + LCD Glasses.
// vesareal-0.0.1
// TurboC 3.1 Real Mode 16bits MS_DOS and VESA 3.0 
// Curitiba, 08/Mar/2004 julio menezes: jmenezes@netpar.com.br
// Porting from DOS to LINUX with svgalib 1.4.3
// The important feature here is the bios call with LRMI_int() from svgalib
// Please set /etc/vga/libvga.config  to VESA 3.0
// You must be root to run this test.

// WARNING: DIRECT I/O TO GEFORCE4 ONLY ON A 32 BITS SYSTEM.
//
#define VESA3__
/*
   13/Mar/2004  first test with svgalib 1.4.3   
   14/Mar/2004  int dualpagestereo(void);
   19/Mar/2004  GeForce4 direct I/O. I apologise for this confused code.
   
   My thank's to:
  
   GNU/Linux community, no words,  while(1) { thank_you(); };
   Thiago Ramos dos Santos
   Matan Ziv-Av <matan@svgalib.org>: the svgalib creator.
   
   []s,
   jmenezes@netpar.com.br
   www.netpar.com.br/jmenezes

   /etc/vga/libvga.config    VESA 3.0
   Slackware 9.0 kernel  2.4.20 
   GeForce4 MMX 64MB
   Xfree86 4.3.0
   gcc -o stereo vesareal.c lmri.c  -lvgagl -lvga -lm
   or
   make -f vesareal.mak
   
   I'm trying to port my 32bitDOS WATCOMC Stereo application 
   with LCDglasses + VESA bios calls to GNU/LINUX.
   My application require arbitrary LEFT RIGHT display start address (panning).
   Not interlaced G1024x768x256 and a 120Hz capable Monitor (flicker free).
   It use an old Diamond card with 2MB. Two pages running on a 233Mhz CPU.
   My system makes direct IO to set the DisplayStartAddress on a S3 Card.   
   I need this because it must runs from inside an Hardware Interrupt Routine.
   REAL MODE x PROTECTED MODE 16 BITS BIOS ON A 32 BITS PROCESSOR. It stinks...
   
   
   I'm start working...
   
   I realised that LINUX xFree86 use Memory Mapped IO and not direct IO.
   Now I have to study how to set the DisplayStartAddress on my GEFORCE4. 
   I want:
   NOT interlaced G1024x768x16M32 on a 120Hz capable Monitor (flicker free).
   Logical screen greater than physical screen (virtual screen).
   My Geforce4 has 64MB.
   
--------------------------------------------------------------------------*/

#ifndef __linux__
  #define _MSDOS_ 
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define INT_VIDEO         0x10
#define G1024x768x16M32  0x118
#define MODO_TEXTO       0x003

// -------------------------- MSDOS specific ------------------------------
#ifdef _MSDOS_ 
  #include <dos.h>
  #include <conio.h>

#define ReadKeyboard()     getch()
#define KeyboardPressed()  kbhit()

int vesa(unsigned int func, unsigned int bx, unsigned int cx, unsigned int dx)
{
   union REGS regs;

   regs.x.ax = func;
   regs.x.bx = bx;
   regs.x.cx = cx;
   regs.x.dx = dx;
   return(int86(INT_VIDEO, &regs, &regs));
} // vesa

int SelectBank(int bank)
{
  vesa(0x4f05u, 0x00, 0, bank);
  vesa(0x4f05u, 0x01, 0, bank);
  return(bank);
} // SelectBank

#endif // __MSDOS__
//--------------------------------------------------------------------------

//----------------------------- LINUX specific ----------------------------
#ifdef __linux__
  #include <vga.h>    // import vga_*()
  #include <vgagl.h>  // import gl_*() 
  #include "lrmi.h"   // from svgalib sources for bios real mode calls

#define ReadKeyboard()      vga_getch()      // from svgalib
#define KeyboardPressed()   vga_getkey()  

int vesa(unsigned int func, unsigned int bx, unsigned int cx, unsigned int dx)
{
  struct LRMI_regs r;
  
  memset(&r, 0, sizeof(r));
  r.eax = func;
  r.ebx = bx; 
  r.ecx = cx; 
  r.edx = dx; 
  return(LRMI_int(0x10, &r));
} // vesa

int SelectBank(int bank)
{
  vga_setwritepage(bank);
  return(bank);
} // SelectBank
  
#endif // __linux__
//--------------------------------------------------------------------------


void ClearBank64K(int bank, long argbL, long argbR)
{
  unsigned long *p, *q;
  int i, j;

  if ((bank < 0) || (bank > 96)) return;

  SelectBank(bank);
  #ifdef __linux__
    p = (unsigned long*)vga_getgraphmem();   // 64k memory window NOT 0xA000
  #else  
    p = (unsigned long*)MK_FP(0xA000,0x0000);     // DOS 64K bank window
  #endif  

  for (i = 0; i < 16; i++) {                        // 16K * 4 = 64K
    q = p + 1024 * i;                               // line beginning
    for (j = 0; j < 512; q++, j++)                  // half line
      *q = argbL; //rof
    for (j = 512; j < 1024; q++, j++)               // thru the end
      *q = argbR; //rof
  } //rof
} // ClearBank64K


int SetDisplayStartAddress(int x, int y)
{
  return(vesa(0x4f07u, 0x00, x, y));
}

int SetFreeRunningStereoOnOff(int onoff)
{
  return(vesa(0x4f07u, onoff ? 0x05 : 0x06, 0, 0));
}

/*-----------------------------------------------------------------------*/
// 19/Mar/2004 a MMIO test for my GEFORCE4 nv18
// Some dirt code. I apologise
// Using Direct I/O

#ifdef __linux__
  #include <sys/io.h>    // ioperm()
#endif // __linux__

//#include <compiler.h>
//#include <nv_local.h>
//#include <riva_hw.h>

// Values from /var/log/Xfree86Config.0.log
//(--) NV(0): Linear framebuffer at 0xDC000000
//(--) NV(0): MMIO registers at 0xE0000000

//MeuLinuxUsaMemoryMappedIO-NV_WR08=---*(volatile CARD8 *)(((CARD8*)((volatile pointer)(chip->PCIO))) + ((0x3D4))) = ((dado));
/*
 * Typedefs to force certain sized values.
 */
typedef unsigned char  U008;
typedef unsigned short U016;
typedef unsigned int   U032;

#define CARD8 U008


typedef struct { 
  volatile  U008 *PCIO;
  volatile  U008 *IO;
} RIVA_HW_INST;

/*
 * HW access macros.
*/

//#include "xf86_OSproc.h"

//#ifdef MAPEAMENTO_IO

#ifdef MAPEAMENTO_IO

#define MMIO_IN8(base, offset) 	*(volatile CARD8 *)(((CARD8*)(base)) + (offset))
#define MMIO_OUT8(base, offset, val) *(volatile CARD8 *)(((CARD8*)(base)) + (offset)) = (val)

/* these assume memory-mapped I/O, and not normal I/O space */
#define NV_WR08(p,i,d)  MMIO_OUT8(p, i, (d))
#define NV_RD08(p,i)    MMIO_IN8(p, (i))

//#define NV_WR08(p,i,d)  MMIO_OUT8((volatile pointer)(p), (i), (d))
//#define NV_RD08(p,i)    MMIO_IN8((volatile pointer)(p), (i))
#define NV_WR16(p,i,d)  MMIO_OUT16((volatile pointer)(p), (i), (d))
#define NV_RD16(p,i)    MMIO_IN16((volatile pointer)(p), (i))
#define NV_WR32(p,i,d)  MMIO_OUT32((volatile pointer)(p), (i), (d))
#define NV_RD32(p,i)    MMIO_IN32((volatile pointer)(p), (i))

//#if 1
#define VGA_WR08(p,i,d) NV_WR08(p,i,d)
#define VGA_RD08(p,i)   NV_RD08(p,i)
//#else
//#define VGA_WR08(p,i,d) outb(i,d)
//#define VGA_RD08(p,i)   inb(i)
//#endif

#else // MAPEAMENTO_IO GCC outb(data,port); Replacement for My direct I/O
#ifdef __linux__
  #define VGA_RD08(p,i)      inb((i))
  #define VGA_WR08(p,i,d)    outb((d),(i))
#else
  #define VGA_RD08(p,i)      inp((i))
  #define VGA_WR08(p,i,d)    outp((i),(d))  // DOS
#endif
#endif // MAPEAMENTO_IO

//#define MMIO_GEFORCE4  (U008*)0xE0000000    // cause segment fault
#define MMIO_GEFORCE4    (U008*)0x00000000

#define VGA_IOBASE_COLOR  0x3D0


void ForceValuesToMySystem(RIVA_HW_INST *chip)
{
#ifdef __linux__
  int ok;
//chip->PCIO = NULL;
  chip->PCIO = MMIO_GEFORCE4;
  chip->IO = (MMIO_GEFORCE4 + VGA_IOBASE_COLOR);

   ok = ioperm(0x3c0, (0x3df - 0x3c0 + 1), 1);
   if (ok != 0) {
      fprintf(stderr,"ForceValuesToMySystem()  ioperm desabled\n");
      exit(0);
   } //fi
#else
  chip = chip;
#endif
} //ForceValuesToMySystem


// Unlock extended registers of GeForce4 = nv18.
static void My_nv10LockUnlock(RIVA_HW_INST *chip,  int LockUnlock)
{
   VGA_WR08(chip->PCIO, 0x3D4, 0x1F);
   VGA_WR08(chip->PCIO, 0x3D5, LockUnlock ? 0x99 : 0x57);
}

// start =  ((y * 1024 + x) * 4)  G1024x768x256.
// 0 <= start < VideoCardMemory - (1024 * 768) * 4;
static void My_SetStartAddress(RIVA_HW_INST *chip, unsigned start)
{
    int offset = start >> 2;
    int pan    = (start & 3) << 1;
    unsigned char tmp;

    /*
     * Unlock extended registers.
     */
//    chip->LockUnlock(chip, 0);
    My_nv10LockUnlock(chip, 0);

    /*
     * Set start address.
     */
    VGA_WR08(chip->PCIO, 0x3D4, 0x0D);
    VGA_WR08(chip->PCIO, 0x3D5, offset);

    offset >>= 8;
    VGA_WR08(chip->PCIO, 0x3D4, 0x0C);
    VGA_WR08(chip->PCIO, 0x3D5, offset);

    offset >>= 8;
    VGA_WR08(chip->PCIO, 0x3D4, 0x19);
    tmp = VGA_RD08(chip->PCIO, 0x3D5);
    VGA_WR08(chip->PCIO, 0x3D5, (offset & 0x01F) | (tmp & ~0x1F));

    VGA_WR08(chip->PCIO, 0x3D4, 0x2D);
    tmp = VGA_RD08(chip->PCIO, 0x3D5);
    VGA_WR08(chip->PCIO, 0x3D5, (offset & 0x60) | (tmp & ~0x60));
    /*
     * 4 pixel pan register.
     */
//  offset = VGA_RD08(chip->PCIO, chip->IO + 0x0A);

// pan is not working. why ???
    offset = VGA_RD08(chip->PCIO, VGA_IOBASE_COLOR + 0x0A);
    VGA_WR08(chip->PCIO, 0x3C0, 0x13);
    VGA_WR08(chip->PCIO, 0x3C0, pan);
} // My_SetStartAddress

/*-----------------------------------------------------------------------*/


int main(void)
{
  int i;
  RIVA_HW_INST chip;

#ifdef __linux__
  if (!LRMI_init()) return 0; //fi
  vga_init();
  vga_setmode(G1024x768x16M32);                    // set graphics
#else
  vesa(0x4f02u, G1024x768x16M32, 0, 0);            // set graphics
#endif

  for (i = 0; i < 48; i++)                        // left  screen  green
    ClearBank64K(i, 0x00009900L, 0x00007700L); //rof

  for (i = 48; i < 96; i++)                       // right screen  red
    ClearBank64K(i, 0x00770000L, 0x00990000L); //rof

//testeMMIO-------------------------------
//#ifdef __linux__
  ForceValuesToMySystem(&chip);
  My_SetStartAddress(&chip, 0u);
  ReadKeyboard();

  My_SetStartAddress(&chip, (1024u * 768u * 4u));       // * 4 = RGBA
  ReadKeyboard();

  My_SetStartAddress(&chip, 0u);
  ReadKeyboard();

  My_SetStartAddress(&chip, (1024u * 700u * 4u));
  ReadKeyboard();

  for (i = 0; i < 32767; i++)
    My_SetStartAddress(&chip,( 1024u * (rand() % 1000)  * 4u)); //rof

  for (i = 0; i < 25; i++) {
    ReadKeyboard();
    My_SetStartAddress(&chip,( 1024u * (rand() % 1000)  * 4u));
    if (ReadKeyboard() == 'q') break;
  } //rof

//#endif //__linux__
//----------------------------------------

//
//#define  COMPILE_THIS
#ifdef COMPILE_THIS

  SetDisplayStartAddress(0,0);                   // left
  ReadKeyboard();

  SetDisplayStartAddress(0,768);                 // rigth
  ReadKeyboard();

  for (i = 0; i < 1024 - 128; i += 128) {        // panning
    SetDisplayStartAddress(i , i);
    ReadKeyboard();
  } //rof

  for (i = 0; i < 5000; i++)
    SetDisplayStartAddress(rand() % 1000,  rand() % 1000); //rof

  SetDisplayStartAddress(0,700);   // rigth DOWN
  ReadKeyboard();
  SetDisplayStartAddress(0,800);   // rigth UP
  ReadKeyboard();

  SetDisplayStartAddress(0,0);
  ReadKeyboard();

  while (KeyboardPressed()) ReadKeyboard(); //w
  SetFreeRunningStereoOnOff(1);
  ReadKeyboard();

  SetFreeRunningStereoOnOff(0);
  ReadKeyboard();

  SetFreeRunningStereoOnOff(1);

  while (KeyboardPressed()) ReadKeyboard();
  
  i = 1;
  while (! KeyboardPressed()) {
    SetDisplayStartAddress(0, i * 700);
    i = (! i);
  } //w

  SetFreeRunningStereoOnOff(0);
#endif // COMPILE_THIS
  
  vesa(0x4f02u, MODO_TEXTO, 0, 0);
  return(1);
} //main


/* HOW IT WAS:
//------------------------------------------------------------- JM 10/10/97 
// CHIP S3 (Diamond Stealth),  2MB memory board:  (y=1280+768)=2048
// Problem: Can't call VESA functions from inside INTERRUPT routine. 
// FLIP. LEFT: y=0;  RIGHT: y = +-768.    0 <= y < (2048 - 768)   
// Right page must move a little UP and DOWN to adjust with the LEFT.
//  3d4h index 38h (R/W): Extensions Enable
//  bit 0-7  Writing 48h to this register enables the extended registers,
//  writing 0 disables them.
//  3d4h index 39h (R/W): Extensions Enable 2
//  bit 0-7  Write A5h to this register to unlock extensions.
INT16 Diamond_DisplayStart(INT16 y)               // y = page begining
{
  #define IND  0x3d4

  outpw(IND,0x4838);     // unlock S3 Chip extentions
  outpw(IND,0xA539);     // writing 0xA5

  outpw(IND,0x0069 | (y & 0xFF00));
  outpw(IND,0x000c | ((y & 0x00FF) << 8));

  outpw(IND,0x000d);    
  return(y);
} // Diamond_DisplayStart 
//--------------------------------------------------------------------------

#define LPT_ADDRESS 0x378

//------------------------------------------------------------- JM 10/10/97 
// Interrupt: take IRQ from LPT, swap LCD  lens and page flipping.          
// Vertical Sync Pulse from Monitor inserted on LPT.                        
// VESA Display Data Channel (DDC) Standard. 15-pin mini Sub-D:
// VESA Pins:
// 13  Horzontal Sync
// 14  Vertical  Sync
//
void __interrupt __far iNovaIntChinon(__CPPARGS)
{
// 0x379 read pin 12 to know the open lens of the LCD glasses 0=Left 1=Right
  Chinon_.LR = (INT16)((inp(LPTADDRESS + 1) & 0x0020) >> 5);  //0 or 1

  if (Chinon_.LR)
    Diamond_DisplayStart(768 + ChinonGetPyRight());   // adjust Right
  else Diamond_DisplayStart(0);                       // Left

  outp(PIC00,EOI);                        // 8259 IRQ DONE 
  outp(LPTADDRESS + 2, 0x00);            //  fix PC bug 
  outp(LPTADDRESS + 2, 0x10);            //  INT LPT ON 
} // iNovaIntChinon 
//--------------------------------------------------------------------------

THIS LINUX PORT NEEDS:

libvgagl.so.1 => /usr/lib/libvgagl.so.1 (0x40025000)
libvga.so.1 => /usr/lib/libvga.so.1 (0x40033000)
libm.so.6 => /lib/libm.so.6 (0x40086000)
libc.so.6 => /lib/libc.so.6 (0x400a9000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

*/


/*-------------------------------------
# meu primeiro Makefile. Usar TAB
# $(oqueexpandir)
CFLAGS = -Wall
LIBS = -lvgagl -lvga -lm
#
# main target executavel: dependencias
# e o que fazer
stereo: vesareal.o lrmi.o
	gcc -o stereo vesareal.o lrmi.o   $(CFLAGS) $(LIBS)
#
# source targets obj: dependencias
vesareal.o: vesareal.c
	gcc -c vesareal.c $(CFLAGS)
lrmi.o: lrmi.c	
	gcc -c lrmi.c $(CFLAGS)
-----------------------------------*/


/*
 * Virtialized chip interface. Makes RIVA 128 and TNT look alike.
 */

/*
typedef struct _riva_hw_inst
{
    // Chip specific settings.
    
    U032 Architecture;
    U032 Version;
    U032 CrystalFreqKHz;
    U032 RamAmountKBytes;
    U032 MaxVClockFreqKHz;
    U032 RamBandwidthKBytesPerSec;
    U032 EnableIRQ;
    U032 IO;
    U032 VBlankBit;
    U032 FifoFreeCount;
    U032 FifoEmptyCount;
    U032 CursorStart;
    
    // Non-FIFO registers.
    
    volatile U032 *PCRTC;
    volatile U032 *PRAMDAC;
    volatile U032 *PFB;
    volatile U032 *PFIFO;
    volatile U032 *PGRAPH;
    volatile U032 *PEXTDEV;
    volatile U032 *PTIMER;
    volatile U032 *PMC;
    volatile U032 *PRAMIN;
    volatile U032 *FIFO;
    volatile U032 *CURSOR;
    volatile U032 *CURSORPOS;
    volatile U032 *VBLANKENABLE;
    volatile U032 *VBLANK;
    volatile U008 *PVIO;
    volatile U008 *PDIO;
    
    // Common chip functions.
    
    int  (*Busy)(struct _riva_hw_inst *);
    void (*CalcStateExt)(struct _riva_hw_inst *,struct _riva_hw_state *,int,int,int,int,int,int);
    void (*LoadStateExt)(struct _riva_hw_inst *,struct _riva_hw_state *);
    void (*UnloadStateExt)(struct _riva_hw_inst *,struct _riva_hw_state *);
    void (*SetStartAddress)(struct _riva_hw_inst *,U032);
    void (*SetSurfaces2D)(struct _riva_hw_inst *,U032,U032);
    void (*SetSurfaces3D)(struct _riva_hw_inst *,U032,U032);
    int  (*ShowHideCursor)(struct _riva_hw_inst *,int);
    void (*LockUnlock)(struct _riva_hw_inst *, int);
    
    // Current extended mode settings.
    
    struct _riva_hw_state *CurrentState;
    
    // FIFO registers.
    
    RivaRop                 *Rop;
    RivaPattern             *Patt;
    RivaClip                *Clip;
    RivaPixmap              *Pixmap;
    RivaScreenBlt           *Blt;
    RivaBitmap              *Bitmap;
    RivaLine                *Line;
    RivaTexturedTriangle03  *Tri03;
    RivaTexturedTriangle05  *Tri05;
} RIVA_HW_INST;
*/

