/****************************************************************************/
/* v_access.c  version 0.3                                                  */
/*                                                                          */
/* routines to access the video-blaster and compatible cards                */
/* for the GNU djgpp/gcc compiler                                           */
/*                                                                          */
/* (C)1994 Bernhard Schwall                                                 */
/*   based on routines from Chips & Technologies                            */
/*   published in TOOL 02/92                                                */
/*                                                                          */
/* version 0.1                                                              */
/*   main routines to display the video under dos and linux                 */
/*                                                                          */
/* version 0.2                                                              */
/*   access the videomemory under dos and linux                             */
/*                                                                          */
/* version 0.3                                                              */
/*   disable V_SetVolume if not Media Pro Plus                              */
/*   support of SB16-mixer                                                  */
/*                                                                          */
/****************************************************************************/

#include "v_access.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef linux
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#endif


static WORD Einstellung[10] = { 77, 31, 14, 30, 21,  3,  0,  0,  0,  0 };

static BYTE FarbEinstellung[7] = { 34, 26, 18, 32, 32, 32, 32 };

static WORD Lautstaerke, VideoAddress;
static INT  WindowPosX, WindowPosY;
static WORD WindowSizeX, WindowSizeY, ColorKey, WriteProtectMask,
            VideoSource, InputFormat, HorizontalZoom, VerticalZoom;
static BOOL Flag_TrueColor, Flag_HiColor, Flag_3_4, Freeze;
WORD PortNr;
BYTE *VidMem;

#ifdef linux
static INT Mem_descriptor;
#endif
/****************************************************************************/

void _regwrit(WORD Wert_X, WORD Wert_Y)
{
  if (Wert_Y > 1) return;
  port_out(24,PortNr);
  port_out((port_in(PortNr+1) & (~(1 << Wert_Y))) | 
           ((Wert_X & 1) << Wert_Y), PortNr+1);
}

WORD _regread(WORD Wert_X)
{
  port_out(24,PortNr);
  if (Wert_X > 2) return 0;
  return ((port_in(PortNr+1) >> Wert_X) & 1);
}

WORD _i2c_ack()
{
  WORD Result;

  _regwrit(1, 1);
  _regwrit(1, 0);
  Result = _regread(2);
  _regwrit(0, 0);
  return Result;
}

void _i2c_start()
{
  _regwrit(1, 1);
  _regwrit(1, 0);
  _regwrit(0, 1);
  _regwrit(0, 0);
}

void _i2c_stop()
{
  _regwrit(0, 1);
  _regwrit(1, 0);
  _regwrit(1, 1);
}

WORD _i2c_wdata(WORD Wert_X, WORD Wert_Y)
{
  WORD h;

  if (Wert_Y == 0)
    _i2c_start();
  for (h=0; h<8; h++){
    _regwrit(Wert_X >> 7, 1);
    _regwrit(1, 0);
    _regwrit(0, 0);
    Wert_X <<= 1;
  }
  return (_i2c_ack());
}

WORD read_i2c()
{
  WORD h, i;

  i = 0;
  for (h=0; h<8; h++){
    _regwrit(1, 0);
    i = (i << 1) | _regread(2);
    _regwrit(0, 0);
  }
  return i;
}

void setvideoheight(WORD MaxHoehe)
{
  WORD h;

  port_out(36,PortNr);
  h = port_in(PortNr + 1);
  port_out(37,PortNr);
  h += (((WORD)port_in(PortNr + 1)) & 3) << 8;
  h += MaxHoehe;
  port_outw((h & 0xff00) + 41,PortNr);
  port_outw((h << 8) + 40,PortNr);
}

void scale_video(WORD SizeX, WORD SizeY)
{
  WORD x, y, h, MaxHoehe;

  x = SizeX;
  if (Flag_TrueColor)
    x = (x * 3) >> 1;
  else {
    if (!Flag_HiColor)
      x <<= 1;
  }
  x <<= 5;
  if (x % 680 > 0)
    x = x / 680 + 1;
  else
    x /= 680;
  if (InputFormat == 1)
    MaxHoehe = 480;
  else
    MaxHoehe = 572;
  setvideoheight(MaxHoehe);
  y = SizeY << 6;
  if (y % MaxHoehe > 0)
    y = y / MaxHoehe + 1;
  else
    y /= MaxHoehe;
  port_out(33,PortNr);
  h = port_in(PortNr + 1) & 0xf3;
  if (x < 64) h |= 4;
  if (y < 64) h |= 8;
  port_out(h,PortNr + 1);
  port_out(32,PortNr);
  h = port_in(PortNr + 1) & 0xfb;
  if ((y & 0xff) <= 32)
    h |= 4;
  port_out(h,PortNr + 1);
  if (x < 64) port_outw((x << 8) + 45,PortNr);
  if (y < 64) {
    port_outw((y << 8) + 46, PortNr);
    port_outw((y << 8) + 47, PortNr);
  }
}

void setdisplaywindow(INT PosX, INT PosY, WORD SizeX, WORD SizeY)
{
  WORD HPosX, HPosY;
  INT  HSizeX, HSizeY;

  HPosX = PosX + Einstellung[0];
  HPosY = PosY + Einstellung[1];
  port_outw((HPosX << 8) + 65, PortNr);
  port_outw((HPosX & 0xff00) + 66, PortNr);
  port_outw((HPosY << 8) + 67, PortNr);
  port_outw((HPosY & 0xff00) + 68, PortNr);
  HSizeX = SizeX + HPosX - 1;
  HSizeY = SizeY + HPosY - 1;
  port_outw((HSizeX << 8) + 69, PortNr);
  port_outw((HSizeX & 0xff00) + 70, PortNr);
  port_outw((HSizeY << 8) + 71, PortNr);
  port_outw((HSizeY & 0xff00) + 72, PortNr);
}

void setdisplayposition(INT PosX, INT PosY)
{
  INT  HPosX, HPosY;
  WORD c, b, d;

  HPosX = (~PosX) + 1;
  c = HorizontalZoom+1;
  if (Flag_3_4) c++;
  HPosX = ((HPosX >> c) - Einstellung[2]) & 0x1fe;
  d = (HPosX >> 8) & 1;
  port_outw((HPosX << 8) + 73, PortNr);
  HPosY = (~PosY) + 1;
  HPosY = (HPosY >> VerticalZoom) - Einstellung[3];
  b = (((HPosY >> 8) << 4) & 16) | d;
  port_outw((HPosY << 8) + 74, PortNr);
  port_outw((b << 8) + 75, PortNr);
}

void fitvideo()
{
  BOOL f = Freeze;
  if (f == FALSE) V_FreezeVideo();
  scale_video(WindowSizeX, WindowSizeY);
  if (Flag_TrueColor)
    setdisplayposition((WindowPosX * 3) >> 1, WindowPosY);
  else
    setdisplayposition(WindowPosX, WindowPosY);
  if (f == FALSE) V_UnfreezeVideo();
  if (Flag_TrueColor)
    setdisplaywindow((WindowPosX * 3) >> 1, WindowPosY,
		     (WindowSizeX * 3) >> 1, WindowSizeY);
  else
    setdisplaywindow(WindowPosX, WindowPosY, WindowSizeX, WindowSizeY);
  scale_video(WindowSizeX, WindowSizeY);
}

void UpdateVideoWindow()
{
  V_DisableFieldReplication();
  if (HorizontalZoom > 0)
    V_HorizontalZoom(HorizontalZoom);
  if (VerticalZoom > 0)
    V_VerticalZoom(VerticalZoom);
  fitvideo();
}

/*----------------------------------------------------------------------*/

WORD V_wr_i2c(WORD Nr, WORD Index, WORD Wert)
{
  if (_i2c_wdata(Nr, 0) != 0) {
    _i2c_stop();
    return 0;
  }
  if (_i2c_wdata(Index, 1) != 0) {
    _i2c_stop();
    return 0;
  }
  if (_i2c_wdata(Wert, 2) != 0) {
    _i2c_stop();
    return 0;
  } else {
    _i2c_stop();
    return 1;
  }
}

void V_SetVolume(WORD Wert)
{
  Lautstaerke = (Wert &= 255);
#ifdef MEDIA_PRO_PLUS_MIXER
  V_wr_i2c(0x88, 3, Wert >> 2);
#endif
/* set the mixer of the SB16. Here should /dev/mixer be used if running   */
/* under linux                                                            */
#ifdef SB16_MIXER
  Wert >>= 3;
  port_out(0x38, 0x224);
  port_out(Wert << 3, 0x225);
  port_out(0x39, 0x224);
  port_out(Wert << 3, 0x225);
#endif
}

WORD V_GetVolume()
{
  return Lautstaerke;
}

void V_EnableVideo()
{
  port_out(64,PortNr);
  port_out(port_in(PortNr + 1) | 3, PortNr+1);
}

void V_DisableVideo()
{
  port_out(64, PortNr);
  port_out(port_in(PortNr + 1) & 0xfc, PortNr+1);
}

void V_SetColorKey(WORD Wert)
{
  WORD h, i;

  ColorKey = Wert;
  if (Wert > 0xf) {
    port_outw((Wert << 8) + 78, PortNr);
    return;
  }
  h = port_in(0x3da);
  port_out(Wert & 0xff, 0x3c0);
  h = port_in(0x3c1);
  i = port_in(0x3da);
  port_out(32, 0x3c0);
  port_outw((h << 8) + 78, PortNr);
}

WORD V_GetColorKey()
{
  return ColorKey;
}

void V_EnableFieldReplication()
{
  port_out(80, PortNr);
  port_out(port_in(PortNr + 1) | 8, PortNr+1);
  port_out(32, PortNr);
  port_out(port_in(PortNr + 1) | 4, PortNr+1);
}

void V_DisableFieldReplication()
{
  port_out(80, PortNr);
  port_out(port_in(PortNr + 1) & 0xf7, PortNr+1);
  port_out(32, PortNr);
  port_out(port_in(PortNr + 1) & 0xfb, PortNr+1);
}

void V_FreezeVideo()
{
  WORD h, i;

  port_out(32, PortNr);
  h = port_in(PortNr + 1);
  i = 0;
  do {
    port_out(32, PortNr);
    h = port_in(PortNr + 1);
    port_out(h & 0xfe, PortNr + 1);
    i++;
  } while (((h & 1) != 0) && (i != 0xffffL));
  Freeze = TRUE;
}

void V_UnfreezeVideo()
{
  port_out(32, PortNr);
  port_out(port_in(PortNr + 1) | 1, PortNr+1);
  Freeze = FALSE;
}

BOOL V_Freeze()
{
  return Freeze;
}

void V_SetWriteProtectMask(WORD Wert)
{
  WriteProtectMask = Wert;
  port_outw((Wert << 8) + 7, PortNr);
  port_outw((Wert & 0xff) + 8, PortNr);
}

WORD V_GetWriteProtectMask()
{
  return WriteProtectMask;
}

void V_SetVideoSource(WORD Wert)
{
  VideoSource = Wert & 3;
  if (Wert == 0) {
    V_wr_i2c(0x8a, 10, 2);
    return;
  }
  if (Wert == 1) {
    V_wr_i2c(0x8a, 10, 42);
    return;
  }
  if (Wert == 2)
    V_wr_i2c(0x8a, 10, 18);
  else
    V_wr_i2c(0x8a, 10, 10);
}

WORD V_GetVideoSource()
{
  return VideoSource;
}

void V_SetInputFormat(WORD Wert)
{
  Wert &= 1;
  InputFormat = Wert;
  if (Wert == 1) {
    V_wr_i2c(0x8a, 6, 34);
    V_wr_i2c(0x8a, 8, 119);
  } else {
    V_wr_i2c(0x8a, 6, 50);
    V_wr_i2c(0x8a, 8, 56);
  }
  UpdateVideoWindow();
}

WORD V_GetInputFormat()
{
  return InputFormat;
}

void V_SetColor(WORD x, WORD Wert)
{
  if (x > 6) return;
  if (x == 3) {
    Wert = (Wert << 2) + 128;
    V_wr_i2c(0x8a, 7, Wert);
  } else
    V_wr_i2c(0x88, x, Wert);
  FarbEinstellung[x] = Wert;
}

WORD V_GetColor(WORD x)
{
  if (x > 6) return 0;
  return (FarbEinstellung[x]);
}

void V_SetVideoAddress(WORD Wert)
{
  Wert &= 15;
  port_out(6, PortNr);
  port_out((port_in(PortNr + 1) & 0xf0) | Wert, PortNr+1);

#ifdef linux
  munmap((caddr_t)(0x00100000*VideoAddress), 0x00100000);
  VidMem = (BYTE *) mmap(
    (caddr_t) (0x00100000*Wert),           /* memoryaddress         */
    0x00100000,                            /* size                  */
    PROT_READ|PROT_WRITE,                  /* allow read and write  */
    MAP_SHARED|MAP_FIXED,                  /* fix memory to address */
    Mem_descriptor,
    (0x00100000*Wert)                      /* same as first param.  */
  );
  if (VidMem == NULL){
    fprintf(stderr,"can't access memory.\n");
    exit(-1);
  }
#else  /* address for DJGPP, mapped at 0xe0000000+VideoAddress */
  VidMem = (BYTE *)(0xe0000000 + (0x00100000*Wert));
#endif
  VideoAddress = Wert;
}

void V_SetCaptureAddress(WORD x, WORD y)
{
  x <<= 1;
  port_outw(((x << 8) & 0xf7) + 42, PortNr);
  x &= 0x700;
  y = (y & 0xfffeL) << 2;
  port_outw((x | (y << 8)) + 43, PortNr);
  port_outw((y & 0xff00L) + 44, PortNr);
}

WORD V_Initialize(char *name, INT clear)
{
  WORD Config[0x2c] = {
    0x002b, 0x1001, 0x1906, 0xff07, 0xff08, 0x0009, 0x2612, 0x1013, 0x3318,
    0x2120, 0x0d21, 0x1022, 0x0023, 0x2224, 0x0025, 0xb026, 0x0227, 0x2028,
    0x0229, 0x002a, 0x002b, 0x002c, 0x202d, 0x202e, 0x202f, 0x0630, 0x9438,
    0xa340, 0x0041, 0x0042, 0x2043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
    0xf849, 0xe04a, 0x114b, 0x124c, 0x004d, 0x004e, 0x004f, 0x0050
  };

  BYTE Config2[28] = {
    0x64, 0x35, 0x0a, 0xf8, 0xcd, 0xfe, 0x22, 0x00, 0x77, 0xe0, 0x02, 0x00,
    0x3f, 0x12, 0x16, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3f, 0x00,
    0x81, 0x10, 0x00, 0x00
  };

  INT  i, hsync = 0, vsync = 0;
  WORD v, w;
  FILE *cfgfile;
  char line[255], *tmp, FName[250];
  int  Volume;

  Volume = 0;
  VideoSource = 0;
  InputFormat = 0;
  HorizontalZoom = 0;
  VerticalZoom = 0;
  VideoAddress = 13;
  PortNr = 0xad6;
  if (*name == 0) strcpy(FName,"video.cfg");
  else strcpy(FName,name);
  if ((cfgfile = fopen(FName,"r")) != NULL) {
  /* read config file */
    i = 1;
    while (!feof(cfgfile)){
      fgets(line,250,cfgfile);
      if (*line != '#'){
        tmp = line;
        while ((*tmp != '=') && (*tmp != 0)) tmp++;
        if (*tmp == 0){
          fprintf(stderr,"error in file %s, line %i\n", FName, i);
          exit(-1);
        }
        *tmp++ = 0;
        if (!strcmp(line,"Port")){
          if ((PortNr = atoi(tmp)) == 0){
            fprintf(stderr,"wrong port number in file %s, line %i\n", FName,i);
            exit(-1);
          }
        }
        else
        if (!strcmp(line,"Memory")){
          if ((VideoAddress = atoi(tmp)) == 0){
            fprintf(stderr,"wrong video address in file %s, line %i\n",FName,i);
            exit(-1);
          }
        }
        else
        if (!strcmp(line,"DisplayWindowX")) Einstellung[0] = atoi(tmp);
        else
        if (!strcmp(line,"DisplayWindowY")) Einstellung[1] = atoi(tmp);
        else
        if (!strcmp(line,"DisplayAddressX")) Einstellung[2] = atoi(tmp);
        else
        if (!strcmp(line,"DisplayAddressY")) Einstellung[3] = atoi(tmp);
        else
        if (!strcmp(line,"ShiftClockStart")) Einstellung[4] = atoi(tmp);
        else
        if (!strcmp(line,"PaletteSkew")) Einstellung[5] = atoi(tmp);
        else
        if (!strcmp(line,"HorizontalSync")){
          if (*tmp == '+') hsync = 1;
          else if (*tmp != '-'){
            fprintf(stderr,"'+' or '-' for horizontal sync expected\n");
            fprintf(stderr,"file %s, line %i\n",FName,i);
            exit(-1);
          }
        }
        else
        if (!strcmp(line,"VerticalSync")){
          if (*tmp == '+') vsync = 1;
          else if (*tmp != '-'){
            fprintf(stderr,"'+' or '-' for vertical sync expected\n");
            fprintf(stderr,"file %s, line %i\n",FName,i);
            exit(-1);
          }
        }
        else
        if (!strcmp(line,"Brightness")) FarbEinstellung[0] = atoi(tmp);
        else
        if (!strcmp(line,"Saturation")) FarbEinstellung[1] = atoi(tmp);
        else
        if (!strcmp(line,"Contrast")) FarbEinstellung[2] = atoi(tmp);
        else
        if (!strcmp(line,"Hue")) FarbEinstellung[3] = atoi(tmp);
        else
        if (!strcmp(line,"Red")) FarbEinstellung[4] = atoi(tmp);
        else
        if (!strcmp(line,"Green")) FarbEinstellung[5] = atoi(tmp);
        else
        if (!strcmp(line,"Blue")) FarbEinstellung[6] = atoi(tmp);
        else
        if (!strcmp(line,"VideoSource")) VideoSource = atoi(tmp);
        else
        if (!strcmp(line,"InputFormat")) InputFormat = atoi(tmp);
        else
        if (!strcmp(line,"HorizontalZoom")) HorizontalZoom = atoi(tmp);
        else
        if (!strcmp(line,"VerticalZoom")) VerticalZoom = atoi(tmp);
        else
        if (!strcmp(line,"Volume")) Volume = atoi(tmp);
        else{
          fprintf(stderr,"unknown command in file %s, line %i\n",FName,i);
          fprintf(stderr,"%s=%s\n",line,tmp);
          exit(-1);
        }
      } /* no comment */
      i++;
    } /* while line in file */
  } /* file found */
  Freeze = FALSE;
  Flag_HiColor = FALSE;
  Flag_TrueColor = FALSE;
  Flag_3_4 = FALSE;
  ColorKey = 0;

#ifdef linux
  if (iopl(3) != 0){
    fprintf(stderr,"can't open ports.\n");
    exit(-1);
  }
  if ((Mem_descriptor = open("/dev/mem", O_RDWR)) < 0){
    fprintf(stderr,"can't open /dev/mem.\n");
    exit(-1);
  }
  VidMem = (BYTE *) mmap(
    (caddr_t) (0x00100000*VideoAddress),   /* memoryaddress         */
    0x00100000,                            /* size                  */
    PROT_READ|PROT_WRITE,                  /* allow read and write  */
    MAP_SHARED|MAP_FIXED,                  /* fix memory to address */
    Mem_descriptor,
    (0x00100000*VideoAddress)              /* same as first param.  */
  );
  if (VidMem == NULL){
    fprintf(stderr,"can't access memory.\n");
    exit(-1);
  }
#else  /* address for DJGPP, mapped at 0xe0000000+VideoAddress */
  VidMem = (BYTE *)(0xe0000000 + (0x00100000*VideoAddress));
#endif

  port_out(PortNr & 0xff, PortNr);
  port_outw(0x3ff, PortNr);
  port_outw(0x7a4e, PortNr);
  port_in(PortNr+1);
  /*
  if (port_in(PortNr+1) != 122) {
    fprintf(stderr,"videocard not found.\n");
    exit(-1);
  }
  */
  for (i = 1; (WORD)i <= Config[0]; i++) {
    port_outw(Config[i], PortNr);
  }
  port_outw(((Einstellung[4]) << 8) + 76, PortNr);
  port_out(64, PortNr);
  port_out((port_in(PortNr + 1) & 63) | (Einstellung[5] << 6), PortNr+1);
  port_outw(0x13, PortNr);
  port_outw(((Einstellung[6]) << 8) + 18, PortNr);
  port_outw(0x113, PortNr);
  port_outw(((Einstellung[7]) << 8) + 18, PortNr);
  for (i = 0; i <= 11; i++) {
    V_wr_i2c(0x8a, i, Config2[i]);
  }
  for (i = 0; i <= 15; i++) {
    V_wr_i2c(0x88, i, Config2[i + 12]);
  }
  V_SetVolume(0);
  for (i=0; i<=6; i++)
    V_SetColor(i, FarbEinstellung[i]);

  V_wr_i2c(0xbe, 1, 0);
  V_wr_i2c(0xbe, 2, 0);
  V_wr_i2c(0xbe, 3, 5);
  V_wr_i2c(0x8a, 9, 0xe0 | (Einstellung[8] & 4));
  V_SetInputFormat(InputFormat);
  V_SetVideoSource(VideoSource);
  if ((Einstellung[8] & 2) > 0) {
    port_out(80, PortNr);
    port_out(port_in(PortNr + 1) | 1, PortNr+1);
  }
  port_out(77, PortNr);
  port_out(((Einstellung[8] >> 3) & 1) | (Einstellung[8] & 48), PortNr+1);
  if ((Einstellung[8] & 8) > 0)
    Flag_3_4 = TRUE;
  else
    Flag_3_4 = FALSE;
  port_out(34, PortNr);
  v = port_in(PortNr + 1);
  port_out(35, PortNr);
  v += (((WORD) port_in(PortNr + 1)) << 8) & 0x300;
  port_out(38, PortNr);
  w = port_in(PortNr + 1);
  port_out(39, PortNr);
  w += (((WORD) port_in(PortNr + 1)) << 8) & 0x300;
  if (w == v)
    return 0;
  port_out(36, PortNr);
  v = port_in(PortNr + 1);
  port_out(37, PortNr);
  v += (((WORD) port_in(PortNr + 1)) << 8) & 0x300;
  port_out(40, PortNr);
  w = port_in(PortNr + 1);
  port_out(41, PortNr);
  w += (((WORD) port_in(PortNr + 1)) << 8) & 0x300;
  if (w == v)
    return 0;
  V_SetVideoAddress(VideoAddress);
  V_SetCaptureAddress(0, 0);
  V_HorizontalZoom(HorizontalZoom);
  V_VerticalZoom(VerticalZoom);
  V_SetSkewFactor(7,hsync);
  V_SetSkewFactor(8,vsync);
  if (clear == 1){
    V_FreezeVideo();
    memset(VidMem, 0, 0x100000);
    V_UnfreezeVideo();
  }
  V_SetVolume(Volume);
  return 1;
}

void V_Exit(){
#ifdef linux
  munmap((caddr_t)(0x00100000*VideoAddress), 0x00100000);
  close(Mem_descriptor);
#endif
  V_DisableVideo();
  V_SetVolume(0);
}

void V_HorizontalZoom(WORD Wert)
{
  WORD h;

  Wert &= 3;
  HorizontalZoom = Wert;
  port_out(77, PortNr);
  port_out((port_in(PortNr + 1) & 0xfc) | Wert, PortNr+1);
  h = (Wert << 1) + Einstellung[4];
  port_outw((h << 8) + 76, PortNr);
}

void V_VerticalZoom(WORD Wert)
{
  Wert &= 3;
  VerticalZoom = Wert;
  port_out(77, PortNr);
  port_out((port_in(PortNr + 1) & 0xf3) + (Wert << 2), PortNr+1);
}

void V_CreateWindow(INT x, INT y, WORD b, WORD h)
{
  x = x-26;  /* -26 bei meinen Einstellungen */
  WindowPosX = x;
  WindowPosY = y;
  WindowSizeX = b;
  WindowSizeY = h;
  setdisplaywindow(x, y, b, h);
  UpdateVideoWindow();
}

void V_SetWindowPosition(INT x, INT y)
{
  WindowPosX = x-26;
  WindowPosY = y;
  setdisplayposition(x, y);
  UpdateVideoWindow();
}

void V_SetWindowSize(WORD b, WORD h)
{
  WindowSizeX = b;
  WindowSizeY = h;
  scale_video(b, h);
  UpdateVideoWindow();
}

void V_SetRegister(WORD Nr, WORD Wert)
{
  WORD h;

  if (Nr > 255) {
    V_wr_i2c(Wert >> 8, Wert & 0xff, Nr & 255);
    return;
  }
  h = port_in(PortNr);
  port_out(Nr, PortNr);
  port_out(Wert, PortNr + 1);
  port_out(h, PortNr);
}

WORD V_GetRegister(WORD Nr)
{
  WORD Result;
  BYTE h;

  if ((Nr & 0xff00L) != 0)
    return 0xff;
  h = port_in(PortNr);
  port_out(Nr, PortNr);
  Result = port_in(PortNr + 1);
  port_out(h, PortNr);
  return Result;
}

void V_SetSkewFactor(WORD x, WORD Wert)
{
  WORD a;

  switch (x) {

  case 0:
    Einstellung[0] = (Einstellung[0] & 0xf800) | (Wert & 0x07ff);
    break;

  case 1:
    Einstellung[1] = (Einstellung[1] & 0xfc00) | (Wert & 0x03ff);
    break;

  case 2:
    Einstellung[2] = (Einstellung[2] & 0xfc00) | (Wert & 0x03ff);
    break;

  case 3:
    Einstellung[3] = (Einstellung[3] & 0xfe00) | (Wert & 0x01ff);
    break;

  case 4:
    Einstellung[4] = (Einstellung[4] & 0xff80) | (Wert & 0x007f);
    port_out(76, PortNr);
    port_out(Einstellung[4] & 0x00ff, PortNr+1);
    break;

  case 5:
    Einstellung[5] = (Einstellung[5] & 0xfffc) | (Wert & 0x0003);
    port_out(64, PortNr);
    a = (port_in(PortNr + 1) & 0x3f) | (Einstellung[5] << 6);
    port_out(a & 0xff, PortNr+1);
    break;

  case 6:
    Einstellung[6] = (Einstellung[6] & 0xfc00) | (Wert & 0x03ff);
    port_out(19, PortNr);
    port_out(1, PortNr + 1);
    port_out(18, PortNr);
    port_out(Einstellung[6] >> 8, PortNr+1);
    port_out(19, PortNr);
    port_out(0, PortNr + 1);
    port_out(18, PortNr);
    port_out(Einstellung[6] & 0xff, PortNr+1);
    break;

  case 7:
    if (Wert == 0) {
      a = V_GetRegister(77) & 0xef;
      Einstellung[4] &= 0xffef;
    } else {
      a = V_GetRegister(77) | 16;
      Einstellung[4] |= 16;
    }
    V_SetRegister(77, a);
    break;

  case 8:
    if (Wert == 0) {
      a = V_GetRegister(77) & 0xdf;
      Einstellung[4] &= 0xffdf;
    } else {
      a = V_GetRegister(77) | 32;
      Einstellung[4] |= 32;
    }
    V_SetRegister(77, a);
    break;

  case 9:
    if (Wert == 0) {
      Flag_HiColor = TRUE;
      Einstellung[9] |= 1;
    } else {
      Flag_HiColor = FALSE;
      Einstellung[9] &= 0xfffe;
    }
    break;

  case 10:
    if (Wert == 0) {
      Flag_TrueColor = TRUE;
      Einstellung[9] |= 4;
    } else {
      Flag_TrueColor = FALSE;
      Einstellung[9] &= 0xfffb;
    }
    break;

  case 11:
    if (Wert == 0) {
      V_wr_i2c(0x8a, 9, 0xe4);
      Einstellung[8] |= 4;
    } else {
      V_wr_i2c(0x8a, 9, 0xe0);
      Einstellung[8] &= 0xfffb;
    }
    break;

  case 12:
    if (Wert == 0){
      Einstellung[8] |= 8;
      Flag_3_4 = TRUE;
    }
    else{
      Einstellung[8] &= 0xfff7;
      Flag_3_4 = FALSE;
    }
    V_HorizontalZoom(HorizontalZoom);
    break;
  }
  UpdateVideoWindow();
}

WORD V_GetSkewFactor(WORD x)
{
  if (x > 12) return 0;
  switch (x) {

  case 0:
    return Einstellung[0] & 0x07ff;
    break;

  case 1:
    return Einstellung[1] & 0x03ff;
    break;

  case 2:
    return Einstellung[2] & 0x03ff;
    break;

  case 3:
    return Einstellung[3] & 0x01ff;
    break;

  case 4:
    return Einstellung[4] & 0x007f;
    break;

  case 5:
    return Einstellung[5] & 0x0003;
    break;

  case 6:
    return Einstellung[6] & 0x03ff;
    break;

  case 7:
    if ((Einstellung[4] & 16) > 0) return 1;
    else                           return 0;
    break;

  case 8:
    if ((Einstellung[4] & 32) > 0) return 1;
    else                           return 0;
    break;

  case 9:
    if ((Einstellung[9] & 1) > 0) return 1;
    else                          return 0;
    break;

  case 10:
    if ((Einstellung[9] & 4) > 0) return 1;
    else                          return 0;
    break;

  case 11:
    if ((Einstellung[8] & 4) > 0) return 1;
    else                          return 0;
    break;

  case 12:
    if ((Einstellung[8] & 8) > 0) return 1;
    else                          return 0;
    break;
  }
  return 0;
}

void V_EnableInterlace()
{
  port_out(80, PortNr);
  port_out(port_in(PortNr+1) | 1, PortNr+1);
  Einstellung[8] |= 2;
}

void V_DisableInterlace()
{
  port_out(80, PortNr);
  port_out(port_in(PortNr+1) & 0xfe, PortNr+1);
  Einstellung[8] &= 0xfffd;
}

void V_WaitGrabbed()
{
  port_out(9, PortNr);
  while ((port_in(PortNr+1) & 8) > 0) {}
  while ((port_in(PortNr+1) & 8) == 0) {}
  while ((port_in(PortNr+1) & 8) > 0) {}
  while ((port_in(PortNr+1) & 8) == 0) {}
  while ((port_in(PortNr+1) & 8) > 0) {}
}

/*----------------------------------------------------------------------*/
