/* graph1.c (emx+gcc) -- Copyright (c) 1987-1995 by Eberhard Mattes */

#include <stdlib.h>
#include <graph.h>
#include <dos.h>
#include <memory.h>
#include <sys/hw.h>
#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS
#define INCL_VIO
#include <os2emx.h>
#include <os2thunk.h>
#include "graph2.h"

#define FALSE 0
#define TRUE  1

static int graph_mode = 0;
static int setmode = 0;
static int old_mode_number = 3;
static int _g_save_saved = 0;
static int _g_save_planes = 0;
static int _g_save_segsize = 0;

#if 0
static void *_g_save_buf = NULL;
#endif

static VIOMODEINFO old_vio_mode;
static VIOMODEINFO new_vio_mode;
static VIOPHYSBUF phys;

int g_xsize = 1;
int g_ysize = 1;
int g_colors = 1;

int _g_xsize = 1;
int _g_ysize = 1;
int _g_colors = 1;

int _g_clipx0 = 0;
int _g_clipy0 = 0;
int _g_clipx1 = 0;
int _g_clipy1 = 0;

int _g_locklevel = 0;

unsigned char *_g_mem = NULL;

unsigned short _g_out[3];

/* This is required by emxomf to define the symbol. */
__asm__ (".stabs \"__j_graph\", 21, 0, 0, 0xffffffff");

extern int _j_graph[];

static int g_mode_on (int width, int height, int mode_number);
static void g_mode_off (void);
static void g_mode_ega (void);
static void g_mode_vgl (void);
static void g_init2 (void);
static void g_mode_s (void);
static void set_jumps (int mode);


int _ggetmode (void)
{
  union REGS r;

  r.h.ah = 0x0f;
  _int86 (0x10, &r, &r);
  return r.h.al;
}


void _gsetmode (int mode)
{
  union REGS r;

  r.h.ah = 0x00;
  r.h.al = (unsigned char)mode;
  _int86 (0x10, &r, &r);
}


void g_wmode (int wmode)
{
}


void g_lock (void)
{
  BYTE not_locked;

  if (_osmode == OS2_MODE && _g_locklevel == 0)
    {
      ++_g_locklevel;
      VioScrLock (LOCKIO_WAIT, &not_locked, 0);
    }
}


void g_unlockall (void)
{
  if (_g_locklevel != 0)
    {
      _g_locklevel = 1;
      g_unlock ();
    }
}


void g_unlock (void)
{
  if (_osmode == OS2_MODE && _g_locklevel > 0)
    {
      --_g_locklevel;
      if (_g_locklevel == 0)
        VioScrUnLock (0);
    }
}


int g_modeset (int mode, int flag)
{
  g_xsize = g_ysize = g_colors = 1; setmode = 0;
  switch (mode)
    {
    case G_MODE_EGA_C:
      g_xsize = 640; g_ysize = 200; g_colors = 16; setmode = 14;
      break;
    case G_MODE_EGA_E:
      g_xsize = 640; g_ysize = 350; g_colors = 16; setmode = 16;
      break;
    case G_MODE_VGA_H:
      g_xsize = 640; g_ysize = 480; g_colors = 16; setmode = 18;
      break;
    case G_MODE_VGA_L:
      g_xsize = 320; g_ysize = 200; g_colors = 256; setmode = 19;
      break;
    default:
      mode = G_MODE_OFF;
      break;
    }
  return mode;
}


int g_mode (int mode)
{
  g_unlockall ();
  if (mode == G_MODE_OFF)
    {
      if (graph_mode != 0)
        {
          g_mode_off ();
          graph_mode = 0;
        }
      return FALSE;
    }
  else
    {
      g_mode (G_MODE_OFF);
      g_lock ();
      if (_osmode == OS2_MODE)
        {
          _g_save_saved = 0;
          old_vio_mode.cb = sizeof (old_vio_mode);
          VioGetMode (&old_vio_mode, 0);
        }
      else
        old_mode_number = _ggetmode ();
      mode = g_modeset (mode, G_SET_KEEP);
      if (setmode == 0)
        {
          g_unlock ();
          return FALSE;
        }
      _g_xsize = g_xsize; _g_ysize = g_ysize; _g_colors = g_colors;
      _g_clipx0 = 0; _g_clipx1 = _g_xsize - 1;
      _g_clipy0 = 0; _g_clipy1 = _g_ysize - 1;
      switch (setmode)
        {
        case 14:                /* G_MODE_EGA_C */
        case 16:                /* G_MODE_EGA_E */
        case 18:                /* G_MODE_VGA_H */
          _g_save_planes = 4;
          _g_save_segsize = (_g_xsize / 8) * _g_ysize;
          if (!g_mode_on (80, 25, setmode))
            {
              g_unlock ();
              return FALSE;
            }
          g_mode_ega ();
          break;
        case 19:    /* G_MODE_VGA_L */
          _g_save_planes = 1;
          _g_save_segsize = _g_xsize * _g_ysize;
          if (!g_mode_on (40, 25, setmode))
            {
              g_unlock ();
              return FALSE;
            }
          g_mode_vgl ();
          break;
        }
      g_wmode (G_NORM);
      graph_mode = setmode;
      g_init2 ();
      g_mode_s ();
      if (_osmode == OS2_MODE)
        g_clear (G_BLACK);
      g_unlock ();
      return TRUE;
    }
}


static int g_mode_on (int width, int height, int mode_number)
{
  if (_osmode == OS2_MODE)
    {
#if 0
      TID tid;

      if (DosAllocMem (&_g_save_buf, _g_save_planes * _g_save_segsize,
                       PAG_COMMIT|PAG_READ|PAG_WRITE|OBJ_TILE))
        return FALSE;
      if (DosCreateThread (&tid, redraw_wait, 0, 2, 0x2000) != 0)
        return FALSE;
      if (DosCreateThread (&tid, mode_wait, 0, 2, 0x2000) != 0)
        return FALSE;
#endif
      new_vio_mode = old_vio_mode;
      switch (_g_colors)
        {
        case 4:
          new_vio_mode.color = 2; break;
        case 16:
          new_vio_mode.color = 4; break;
        case 256:
          new_vio_mode.color = 8; break;
        default:
          if (setmode == 15 || setmode == 17)
            new_vio_mode.color = 0;
          else
            new_vio_mode.color = 1;
          break;
        }
      new_vio_mode.fbType = VGMT_OTHER | VGMT_GRAPHICS;
      new_vio_mode.hres = _g_xsize;
      new_vio_mode.vres = _g_ysize;
      new_vio_mode.row = height;
      new_vio_mode.col = width;
      VioSetMode (&new_vio_mode, 0);
      phys.pBuf = (PBYTE)0xa0000;
      phys.cb = 0x10000;
      if (VioGetPhysBuf (&phys, 0) != 0)
        return FALSE;
      _g_mem = MAKEP (phys.asel[0], 0);
    }
  else
    {
      if (_g_mem == NULL)
        _g_mem = _memaccess (0xa0000, 0xaffff, 1);
      if (_g_mem == NULL)
        return FALSE;
      _gsetmode (mode_number);
    }
  return TRUE;
}


static void g_mode_off (void)
{
  if (_osmode == OS2_MODE)
    {
      VioSetMode (&old_vio_mode, 0);
#if 0
      VioSavRedrawUndo (1, 1, 0);
      VioModeUndo (1, 1, 0);
      DosFreeMem (_g_save_buf);
#endif
    }
  else
    _gsetmode (old_mode_number);
  set_jumps (0);
}


static void g_init2 (void)
{
}


struct jentry
{
  int mode;
  void **dst;
  void *func;
};


static void set_jumps (int mode)
{
  const int *ip;
  const struct jentry *p;

  ip = &_j_graph[0];
  if ((unsigned)*ip < 0x10000)  /* ld */
    ++ip;
  while (*ip != 0)
    {
      if (*ip != -1 && *ip != -2) /* ld, emxomf */
        {
          p = (const struct jentry *)*ip;
          if (p->mode == mode)
            *p->dst = p->func;
        }
      ++ip;
    }
}


static void g_mode_ega (void)
{
  
  _portaccess (0x3ce, 0x3cf);
  set_jumps (GJ_EGA);
}


static void g_mode_vgl (void)
{
  set_jumps (GJ_VGL);
}


static void g_mode_s (void)
{
}
