/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VVMODE.C
 *
 * DESCRIPTIVE NAME = Virtual Video Int 10h SetMode Processing
 *
 *
 * VERSION = V2.0
 *
 * DATE      11/10/88
 *
 * DESCRIPTION  This module contains the VVD's Int 10h SetMode processing.
 *
 *
 * FUNCTIONS    vvInt10Initialize()         Initialize the adapter via Int 10h
 *              vvInt10SetMode()            SetMode-initiation processing
 *              VVInt10SetModeReturn()      SetMode-return processing
 *              vvLoadCodePageFont()        Load current codepage font into VDM
 *              vvGetCodePageFont()         Return codepage font
 *              vvFixTridentRegisters()     Fix Trident Registers
 *              vvFixVideo7TextRegisters()  Fix Video7 text registers
 *              vvSVGACleanup()             Process CleanData array to fix registers to VGA defaults.
 *              vvFixSVGARegisters()        Fix up registers based on the mode set
 *              vvLockSVGARegisters()       Lock SVGA extended registers.
 *              vvUnLockSVGARegisters()     unlock SVGA extended registers.
 *              vvSVGAFixSetBgnd()          Fix up register state for background.
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#define  IO8BIT           /* CL386 Version 6.00.054 FLAG:              */
#include <mvdm.h>
#include <vvd.h>
#include "vvdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif
extern ULONG flVVD;
#pragma  BEGIN_SWAP_DATA
extern PVOID pPhysVRAM;

#ifdef   SVGA                                    /*                         */
extern BOOL fPMIProcessed,fPMILoaded;
extern ULONG ulSVGAChipType;
extern ULONG ulSVGAAdapterType;
extern ULONG ulNumLockCmds;
extern ULONG ulNumUnLockCmds;
extern USHORT ausLockReg[];
extern USHORT ausLockCmd[];
extern USHORT ausUnLockReg[];
extern USHORT ausUnLockCmd[];
extern CLEANDATA CleanData[];
extern BYTE aregSEQInit[];
extern PFNSVGASETBANK apfnSVGASetBank[];         /*            */
extern BOOL     flBgndExecSupported;             /*            */
#endif
#pragma  END_SWAP_DATA
#pragma  BEGIN_SWAP_CODE

/***************************************************************************
 *
 * FUNCTION NAME = vvInt10Initialize()
 *
 * DESCRIPTION   = Initialize the adapter via Int 10h
 *
 *                 This subroutine initiates an Int 10h SetMode function
 *                 request for the current VDM.
 *
 * INPUT         = pcrf -> VDM register frame
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvInt10Initialize(PCRF pcrf)
{

  /*
  ** Only the primary VVD should initialize; to correctly
  ** initialize a secondary adapter like MONO would require
  ** changing the equipment flags, and depending on the order in
  ** which the two VDDs are loaded, we might end up in the
  **       mode.  It's far better to leave any secondary display
  ** hardware alone until the session initializes it itself
  */

  if (flVVD&VVD_PRIMARY && !(VDMData.flVDMXVideo&VDMX_INT10INIT))
  {

    /*
    ** Issue INT 10h to set the default mode; we presume that
    ** the equipment flags are already set up to match rb_bVMode
    */

    VDHPushRegs(VDHREG_AX|VDHREG_BP);
    AH(pcrf) = INT10_SETMODE;
    AL(pcrf) = VDMBase.rb_bVMode;
    VDHPushInt(BIOSINT_VIDEO);
    VDMData.flVDMVideo &= ~VDM_IOINIT;
    VDMData.flVDMXVideo |= VDMX_INT10INIT;

#ifdef   EGAVGA

    /*
    ** But first, issue special Video Seven BIOS call to determine
    ** whether this is a Video Seven card (AX=6F00 returns BH='V',BL='7'
    */

    if (!((PVVSTRING)(VDMData.pInt10ReturnData))->vva_nChars)
    {
      VDHPushRegs(VDHREG_AX|VDHREG_BX);
      AX(pcrf) = 0x6F00;
      VDHPushInt(BIOSINT_VIDEO);
      ((PVVSTRING)(VDMData.pInt10ReturnData))->vva_nChars = -0x6F;
      VDHArmReturnHook(VDMData.hhookInt10Return,
                       VDHARH_NORMAL_IRET);
    }
#endif
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvInt10SetMode()
 *
 * DESCRIPTION   = SetMode-initiation processing
 *
 *                 This subroutine is called directly from the actual Int 10h
 *                 hook for the SetMode case only, and takes care of hooking
 *                 the return from the SetMode call and notifying the mouse
 *                 of the new mode being set.
 *
 * INPUT         = pcrf -> VDM register frame
 *
 * OUTPUT        = EMULATED
 *                     TRUE
 *                 NOT EMULATED
 *                     FALSE (pass control to next VDD and/or ROM)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 deactivate pointer
 *                 disable pointer drawing for duration
 *                 hook return from Int 10h call
 *
 ****************************************************************************/

BOOL HOOKENTRY vvInt10SetMode(register PCRF pcrf)
{
  USHORT iretFrame[3];
  USHORT modeMax = 0xFFFF;                       /*                         */
  USHORT usNewMode;                              /*                         */
  BYTE bSeqIndx;                                 /*                         */
  ULONG ulEquip = VDMBase.rb_fsEquip&BIOSEQUIP_VIDEOMASK;

#ifdef  VTEXT                                                           //J-TS00V
  if (!(VDMData.GaleData.flVtext & VDMV_VTEXT_INSTALLED))               //J-TS00V
    vvCheckCallerVtext(pcrf);                                           //J-TS00V
  else                                                                  //J-TS00V
    vvCheckSetModeVtext(pcrf);                                          //J-TS00V
#endif  //VTEXT                                                         //J-TS00V

  usNewMode = AL(pcrf);

#ifdef  GALE                                                            //J-TS0331
  if (AX(pcrf) == 0x4F02)                                               //J-TS0331
    VDMData.GaleData.flGaleFlags |= VDMV_GALE_VESA_SET_MODE;            //J-TS0331
#endif  //GALE                                                          //J-TS0331

#ifdef   SVGA
                         /* VESA mode detection                             */
  if (AX(pcrf) == 0x4F02)                        /*                         */
  {                                              /*                         */
    usNewMode = BX(pcrf);                        /*                         */
  }                                              /*                         */
                          /* handle Video7 extended mode set                */
  if ((ulSVGAAdapterType == VIDEO7_ADAPTER) && (AX(pcrf) == 0x6f05))
    usNewMode = BL(pcrf);

  /*
  ** Setup sequencers to default values
  */

  if ((ulSVGAAdapterType == TRIDENT_ADAPTER) && (VDMData.flVDMVideo&VDM_FGND))
  {
    bSeqIndx = INB(PORT_SEQINDX);                /* save index              */
    OUTB(PORT_SEQINDX,
         0x0e);                                  /* unprotect register 0x0c */
    OUTB(PORT_SEQDATA,
         (INB(PORT_SEQDATA)|0x80)^0x02);
    OUTB(PORT_SEQINDX,
         0x0c);                                  /* Select register 0x0c    */
    OUTB(PORT_SEQDATA,
         aregSEQInit[0x0c]);                     /* and set default value   */
    OUTB(PORT_SEQINDX,
         bSeqIndx);                              /* restore index           */
    (*apfnSVGASetBank[ulSVGAAdapterType])(CURRENT_VDM,  /*                  */
                                          BANK0,
                                          TRUE);
  }
#endif

#ifdef   VDDDEBUGALL
  PRINTDEBUG("New video mode: %02xh\n",
             usNewMode);
#endif

#ifdef   PROPERTIES

  if (VDMData.flVDMXVideo&VDMX_CGARESTRICT)
  {

    /*
    ** If caller changed equipment flags, coerce to color
    */

    if (!(VDMData.flVDMXVideo&VDMX_MONORESTRICT) && ulEquip ==
       BIOSEQUIP_MONOVIDEO)
    {
      VDMBase.rb_fsEquip &= ~BIOSEQUIP_VIDEOMASK;
      VDMBase.rb_fsEquip |= (ulEquip = BIOSEQUIP_COLOR80VIDEO);
    }
    modeMax = BIOSVMODE_CO640X200X2;
  }

  if (VDMData.flVDMXVideo&VDMX_MONORESTRICT)
    modeMax = BIOSVMODE_MONO80;

  /*
  ** Ignore the mode change if it has been restricted
  */

  if (usNewMode > modeMax)
    return  TRUE;
#endif

#ifdef   MONO

  if (ulEquip == BIOSEQUIP_MONOVIDEO)
  {

    if (usNewMode == BIOSVMODE_MONO80)
      VDMData.flVDMXVideo |= VDMX_ACTIVE;
  }

  else
  {

    if (usNewMode != BIOSVMODE_MONO80)
      VDMData.flVDMXVideo &= ~VDMX_ACTIVE;
  }

#else

  if (flVVD&VVD_MONOPRESENT)
  {

    if (ulEquip == BIOSEQUIP_MONOVIDEO)
    {

      if (usNewMode == BIOSVMODE_MONO80)
        VDMData.flVDMXVideo &= ~VDMX_ACTIVE;
    }

    else
    {

      if (usNewMode != BIOSVMODE_MONO80)
        VDMData.flVDMXVideo |= VDMX_ACTIVE;
    }
  }
#endif

  /*
  ** If not the active display, lie low (like the ROM)
  */

  if (!(VDMData.flVDMXVideo&VDMX_ACTIVE))
    return  FALSE;

  /*
  ** Deactivate pointer, and disable further pointer drawing
  */

#ifdef  VTEXT                                                           //J-TS0927
  if (!(VDMData.GaleData.flVtext & VDMV_REDRAW_SCREEN))                 //J-TS0922
#endif  //VTEXT                                                         //J-TS0927
  VVHidePtr(CURRENT_VDM);
  VDMData.PtrData.fPtrFrozenVideo++;

#ifdef  GALE                                                            //J-TS00V
//J-TS0331  if(VDMData.flVDMGale && (AX(pcrf) != 0x4F02)) {                       //J-TS0908
  if (VDMData.flVDMGale &&                                              //J-TS0331
      !(VDMData.GaleData.flGaleFlags & VDMV_GALE_VESA_SET_MODE)) {      //J-TS0331
    if (((usNewMode & ~BIOSVINFO_DONTCLEAR) == 0x03) ||                 //J-TS00V
        ((usNewMode & ~BIOSVINFO_DONTCLEAR) == 0x73) ||                 //J-TS00V
        ((usNewMode & ~BIOSVINFO_DONTCLEAR) == 0x72))                   //J-TS00V
      AL(pcrf) = 0x12 | (AL(pcrf) & BIOSVINFO_DONTCLEAR);               //J-TS00V
  }                                                                     //J-TS00V
#endif  //GALE                                                          //J-TS00V

#ifdef  VTEXT                                                           //J-TS00V
  if (!(VDMData.GaleData.flVtext & VDMV_SUPPORTED_TEXT_MODE))           //J-TS0826
#endif  //VTEXT                                                         //J-TS00V
  VDMData.ulBIOSMode = usNewMode;
  VDMData.flVDMVideo |= VDM_MODECHANGING;

#ifdef  XVIO                                                            //J-TS00V
  vxSetStatus(CURRENT_VDM, XVIO_STATUS_MODECHANGING, XVIO_SET_FLAG);    //J-TS00V
#endif  //XVIO                                                          //J-TS00V

  /*
  ** Force trapping on retrace port
  */

  if (!(VDMData.flVDMXVideo&VDMX_RTRCEMULATE))
  {
    VDMData.flVDMXVideo |= (VDMX_RTRCOVERRIDE|VDMX_RTRCEMULATE);
    VVSpecialCaseIO(NULL,
                    NULL);
  }

  /*
  ** Now that the MODECHANGING bit is set, if we're doing CGA/MONO
  ** restriction, this is the time to fix up all the mappings in the
  ** video address range, so that the ROM will not stomp on user data
  */

  if (VDMData.flVDMXVideo&(VDMX_CGARESTRICT|VDMX_MONORESTRICT))
  {

    /*
    ** Switch stacks first
    */

    VDHPopStack(sizeof(iretFrame),
                SSToDS(&iretFrame));
    VDMData.vpOldStack = VPFROMVADDR(SS(pcrf),
                                     SP(pcrf));
    SS(pcrf) = LOSEG(VDMData.pvdmAltStack);
    SP(pcrf) = HIOFF(VDMData.pvdmAltStack);
    VDHPushStack(sizeof(iretFrame),
                 SSToDS(&iretFrame));
  }

  /*
  ** FLAG: 20-Apr-90
  **
  ** This EnableBuffer call is simply one of many possible ways to
  ** fix the following *kind* of problem:  Application is graphics mode,
  ** buffer enabled at A0000h, then switches to text mode by first loading
  ** the font at A0000h (which we miss because those pages are still
  ** enabled), then initializing the buffer at B8000h (which we *do* see).
  ** I prefer to not rely on INT 10h to catch the start of these sorts
  ** of transitions, but in this case, it's so cheap and compatible
  ** (and the full solution is so much more work), that I've given in.
  */

#ifdef  VTEXT                                                           //J-TS00V
  vvUnmapVtextPVB();                                                    //J-TS00V
#endif  //VTEXT                                                         //J-TS00V

  VVEnableBuffer(FALSE,
                 pcrf);
  VDHArmReturnHook(VDMData.hhookInt10SetModeReturn,
                   VDHARH_RECURSIVE_IRET);
  VDMData.nInt10Setmode++;                                     /*            */
  return  FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME = VVInt10SetModeReturn()
 *
 * DESCRIPTION   = SetMode-return processing
 *
 *                 This subroutine is called whenever an Int 10h SetMode
 *                 completes.  Its primary purpose is to notify the virtual
 *                 mouse driver of the new video mode, and reload the default
 *                 code page font if the VDM has entered/remained in a text
 *                 mode.
 *
 * INPUT         = p - (ignored)
 *                 pcrf -> VDM register frame (ignored)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 notify virtual mouse of screen/cell sizes
 *                 allow pointer drawing again
 *                 check for codepage font
 *                 if font exists
 *                   if foreground VDM
 *                     set copy state
 *                     copy font data into VRAM
 *                     restore copy state
 *                   else
 *                     copy font data directly into PLANE2 buffer
 *                   release font
 *
 ****************************************************************************/

VOID HOOKENTRY VVInt10SetModeReturn(PVOID p,PCRF pcrf)
{
  PBYTE pbFont;
  ULONG nbFont;
  USHORT iretFrame[3];
#ifdef   SVGA
  ULONG mstateVideo;                                            /*            */
  BYTE Tmp;                                                     /*            */
#endif

/*  register ULONG uMode;                                                   */

#ifdef   SVGA

  /*
  ** The following fix-up should dissappear when the 0x3bf problem
  ** is solved.
  */

  if (!(VDMData.flVDMVideo&VDM_WINDOWED))
  {

    switch (ulSVGAAdapterType)
    {
      case  TSENG_ADAPTER :
      case  VIDEO7_ADAPTER :
        vvFixSVGARegisters();
        break;
      case  TRIDENT_ADAPTER :
        if (AL(pcrf) <= 0x13)                                  /*                  */
           (*apfnSVGASetBank[ulSVGAAdapterType])(CURRENT_VDM,  /*                  */
                BANK0,                                         /*                  */
                TRUE);                                         /*                  */
        break;
      case  WESTERNDIG_ADAPTER :
      case  ATI_ADAPTER :
        break;
    }
  }
#endif
  RequestMutexSem(VDMData.hmxVideoState);

  if (VDMData.flVDMXVideo&(VDMX_CGARESTRICT|VDMX_MONORESTRICT))
  {

    /*
    ** Now switch stacks *back*
    */

    SS(pcrf) = WORDOF(VDMData.vpOldStack,
                      1);
    SP(pcrf) = WORDOF(VDMData.vpOldStack,
                      0);
  }

  VDMData.nInt10Setmode--;                                      /*            */

  /*
  ** If we initiated the SetMode, then restore user registers
  */

  if (VDMData.flVDMXVideo&VDMX_INT10INIT)
    if (!VDMData.nInt10Setmode)                                 /*            */
    {                                                           /*            */
      VDMData.flVDMXVideo &= ~VDMX_INT10INIT;
      VDHPopRegs(VDHREG_AX|VDHREG_BP);
    }                                                           /*            */

  /*
  ** Remove trapping enforcement on retrace port
  */

  if (VDMData.flVDMXVideo&VDMX_RTRCOVERRIDE)
    VDMData.flVDMXVideo &= ~(VDMX_RTRCOVERRIDE|VDMX_RTRCEMULATE);

#ifdef  GALE                                                            //J-TS00V
    /*** Force BIOS data area mode to character mode */                 //J-TS00V
  if (VDMData.flVDMGale && (VDMData.ulBIOSMode < 0x100)) {              //J-TS0908
    AL(pcrf) = VDMBase.rb_bVMode = VDMData.ulBIOSMode;                  //J-TS00V
    VDMBase.rb_bVMode  &= ~BIOSVINFO_DONTCLEAR;                         //J-TS5013
    VDMData.ulBIOSMode &= ~BIOSVINFO_DONTCLEAR;                         //J-TS5013
                                                                        //J-TS00V
    if ((VDMBase.rb_bVMode == 0x03) ||                                  //J-TS00V
        (VDMBase.rb_bVMode == 0x72) ||                                  //J-TS00V
        (VDMBase.rb_bVMode == 0x73)) {                                  //J-TS00V
      VDMBase.rb_nVRows       = 24;                                     //J-TS00V
      VDMBase.rb_nVCharHeight = 19;                                     //J-TS00V
    } else if ((VDMBase.rb_bVMode == 0x11) ||                           //J-TS00V
               (VDMBase.rb_bVMode == 0x12)) {                           //J-TS00V
      VDMBase.rb_nVRows       = 29;                                     //J-TS00V
      VDMBase.rb_nVCharHeight = 16;                                     //J-TS00V
    }                                                                   //J-TS00V
                                                                        //J-TS00V
    if (VDMBase.rb_bVMode == 0x03) {                                    //J-TS00V
      VDMBase.rb_wVLen        = 80 * 25 * 2;                            //J-TS00V
    } else if (VDMBase.rb_bVMode == 0x73) {                             //J-TS00V
      VDMBase.rb_wVLen        = 80 * 25 * 4;                            //J-TS00V
    }                                                                   //J-TS00V
                        // If KKC is shared, reserve 1 line.            //J-TS00V
    if  (VDMData.GaleData.GaleDBCSInpEnv != DBCS_INPUT_DOS_KKC)         //J-TS00V
      VDMBase.rb_nVRows--;                                              //J-TS00V
                                                                        //J-TS00V
    /*** Initialize screen related environment ***/                     //J-TS00V
    if (!(VDMData.ulBIOSMode & BIOSVINFO_DONTCLEAR))                    //J-TS00V
      VVInitScreen();                                                   //J-TS00V
                                                                        //J-TS00V
    /*** Load Default SBCS 16 font */                                   //J-TS00V
    VVLoadDefaultSBCS();                                                //J-TS00V
                                                                        //J-TS00V
    /*** ICHITARO's     ***/                                            //J-TS00V
    /*** ICHITARO draws a logo without clearing internal-latches ***/   //J-TS00V
    if (VDMBase.rb_bVMode == 0x12) {                                    //J-TS00V
      int i;                                                            //J-TS00V
      if (!(VDMData.flVDMVideo & VDM_FGND)) {                           //J-TS00V
        for (i = 0; i < MAX_PLANES; i++)                                //J-TS00V
          VDMData.bLatches[i] = 0;                                      //J-TS00V
        vvRestoreLatches(CURRENT_VDM, FALSE);                           //J-TS00V
      } else {                                                          //J-TS00V
        i = *(UCHAR *)0xA0000L;                                         //J-TS00V
      }                                                                 //J-TS00V
    }                                                                   //J-TS00V
                                                                        //J-TS00V
  }                                                                     //J-TS00V
  VDMData.GaleData.flGaleFlags &= !VDMV_GALE_VESA_SET_MODE;             //J-TS0331
#endif  //GALE                                                          //J-TS00V
#ifdef  VTEXT                                                           //J-TS00V
  if (VDMData.GaleData.flVtext & VDMV_VTEXT_INITIALIZING)               //J-TS00V
      VDMData.ulBIOSMode = 0x03;                                        //J-TS00V
#endif  //VTEXT                                                         //J-TS00V

#ifdef   SVGA
  mstateVideo = VDMData.mstateVideo;                            /*            */
#endif
  /*
  ** Get new state info, now that I/O initialization is complete
  */

  VDMData.flVDMVideo |= VDM_IOINIT;
  VDMData.flVDMVideo &= ~VDM_STATEINIT;
#ifdef  XVIO                                                            //J-TS00V
  vxSetStatus(CURRENT_VDM, XVIO_STATUS_STATEINIT, XVIO_RESET_FLAG);     //J-TS00V
#endif  //XVIO                                                          //J-TS00V
  vvUpdateAll(CURRENT_VDM,
              TRUE);

  #ifdef SVGA                                                   /*            */
  /*
  ** This is an attempt to handle the situation where the previous mode
  ** was 256-color and is changing to 16-color. BIOS clears VRAM while
  ** in 256-color mode, page states are therefore not updated appropriately
  ** when faults occur. So we invalidate the mappings here and take faults
  ** again in the correct mode. The converse also happens but page states
  ** are not required in 256-color modes and therefore do not interfere
  ** with the save/restore of VRAM.
  */
  if ((mstateVideo == MEMORY_GRFX256) &&
      (VDMData.mstateVideo == MEMORY_GRFX))
  {
    VVEnableBuffer(FALSE,
                   pcrf);
  }
  /*
  ** On WD adapters, in 132 column mode, font gets damaged while
  ** being restored due to a hardware problem. On WD's suggestion,
  ** the VRAM is being put into the 8 bit mode, in order to fix it.
  **/
  if ((ulSVGAAdapterType == WESTERNDIG_ADAPTER) &&        /*            */
      (VDMData.vvMode.vvm_nCols == 132))                       /*           */
  {
    OUTB(PORT_GDCINDX,0xB);
    Tmp = INB(PORT_GDCDATA) & 0xFB;     // put VRAM into 8 bit mode.
    OUTB(PORT_GDCDATA,Tmp);
  }                                                     /*            */
  #endif

  /*
  ** This should also be a good time to shrink the virtual buffer
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
    vvShrinkBuffer(CURRENT_VDM,
                   FALSE);

  /*
  ** Allow pointer drawing again;
  ** by this time, the virtual mouse will have disabled itself
  */

  VDMData.PtrData.fPtrFrozenVideo--;
  AssertTRUE(VDMData.PtrData.fPtrFrozenVideo >= 0);
  VDMData.flVDMVideo &= ~VDM_MODECHANGING;
#ifdef  XVIO                                                            //J-TS00V
  vxSetStatus(CURRENT_VDM, XVIO_STATUS_MODECHANGING, XVIO_RESET_FLAG);  //J-TS00V
#endif  //XVIO                                                          //J-TS00V

  /*
  ** Now that the MODECHANGING bit is clear, if we're doing CGA/MONO
  ** restriction, this is the time to fix up all the mappings in the
  ** video address range, so that the VDM will not stomp on font data
  */

  if (VDMData.flVDMXVideo&(VDMX_CGARESTRICT|VDMX_MONORESTRICT))
    VVEnableBuffer(TRUE,
                   pcrf);

  /*
  ** Have to release VDMState semaphore before adding/flushing events
  */

  ReleaseMutexSem(VDMData.hmxVideoState);

  /*
  ** If mode events were posted to the Shield, flush them now
  */

  vvFlushEvent(CURRENT_VDM,
               VVDEVENT_MODE,
               0);
  vvFlushEvent(CURRENT_VDM,
               VVDEVENT_LVB,
               0);

#ifdef  XVIO                                                            //J-TS00V
  if (VDMData.flVDMGale) {                                              //J-TS00V
    vxFlushEvent(CURRENT_VDM, VXEVENT_MODE, 0);                         //J-TS00V
    vxFlushEvent(CURRENT_VDM, VXEVENT_CURSOR, 0);                       //J-TS00V
                                                                        //J-TS00V
    /* Always add redraw_event after setmode    */                      //J-TS00V
    /* in order to redraw status line.          */                      //J-TS00V
    if ((VDMData.ulBIOSMode == 0x03) ||     /* if text mode */          //J-TS00V
        (VDMData.ulBIOSMode == 0x73)) {                                 //J-TS00V
      VXSCROLL vxScroll;                                                //J-TS00V
                                                                        //J-TS00V
      vxScroll.vxs_rcl.yTop    = 0;                                     //J-TS00V
      vxScroll.vxs_rcl.xLeft   = 0;                                     //J-TS00V
      vxScroll.vxs_rcl.yBottom = -1;                                    //J-TS00V
      vxScroll.vxs_rcl.xRight  = -1;                                    //J-TS00V
      vxScroll.vxs_fill        = 0x2000;                                //J-TS00V
      vxScroll.vxs_nRows       = 1;                                     //J-TS00V
      vxAddEvent(CURRENT_VDM, VXEVENT_REDRAW,                           //J-TS00V
                 (PPVOID)SSToDS(&vxScroll),                             //J-TS00V
                 XEVENT_FLUSH | XEVENT_FORCE);                          //J-TS00V
    }                                                                   //J-TS00V
  }                                                                     //J-TS00V
#endif  //XVIO                                                          //J-TS00V

#ifdef GALE                                                             //J-TS00V
  if(!VDMData.flVDMGale) {                                              //J-TS00V
#endif  //GALE                                                          //J-TS00V
#ifdef   EGAVGA

  /*
  ** Get the current (ie, new) video mode from the BIOS data area
  */

  if (VDMData.vvMode.vvm_cpID != 0)              /*                         */
    vvLoadCodePageFont();
#endif                                           /* EGAVGA                  */
#ifdef GALE                                                             //J-TS00V
  }                                                                     //J-TS00V
#endif  //GALE                                                          //J-TS00V

#ifdef   SVGA

  if ((((VDMBase.rb_bVMode&~BIOSVINFO_DONTCLEAR) > 0x13) &&
      (VDMData.flVDMVideo&VDM_WINDOWED) &&
      (VDMData.mstateVideo > MEMORY_TEXT)) ||
      ((VDMData.flVDMXVideo & VDMX_ENHANCEDMODE) &&             /*            */
       !(VDMData.flVDMVideo&VDM_FGND)))                         /*            */
  {

#ifdef  GALE                                                            //J-TS00V
   if(!VDMData.flVDMGale ||                                             //J-TS00V
      ((VDMData.ulBIOSMode != 0x03) &&                                  //J-TS00V
       (VDMData.ulBIOSMode != 0x73))) {                                 //J-TS00V
#endif  //GALE                                                          //J-TS00V
    if (!vdhBTS(&VDMData.flVDMVideo,
                LOG2(VDM_FROZEN)))
      VDHFreezeVDM(CURRENT_VDM);
    vvAddEvent(CURRENT_VDM,
               VVDEVENT_MODE,
               NULL,
               0);
#ifdef  GALE                                                            //J-TS00V
   }                                                                    //J-TS00V
#endif  //GALE                                                          //J-TS00V
  }
#endif
#ifdef  VTEXT                                                           //J-TS00V
    vvCheckDispDD(pcrf);                                                //J-TS00V
#endif  //VTEXT                                                         //J-TS00V
}

#ifdef   EGAVGA

/***************************************************************************
 *
 * FUNCTION NAME = vvLoadCodePageFont()
 *
 * DESCRIPTION   = Load current codepage font into VDM
 *
 *
 * INPUT         = None
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvLoadCodePageFont()
{
  INT nbFont;
  PBYTE pbFont;
  register INT i;
  register PBYTE pbSrc,pbDest;
  MSTATE tempState;                              /*                         */
  ULONG ulCellWidth,ulCellHeight;                /*                         */
  ULONG uMode;                                   /*                         */

  /*
  ** ignore VM BOOT guys                                                 
  */

  if (VDMData.flVDMXVideo&VDMX_V86MACHINE)
    return  TRUE;

  uMode = VDMBase.rb_bVMode;                     /*                         */

  if (uMode <= BIOSVMODE_CO80 || uMode == BIOSVMODE_MONO80)
  {                                              /*                         */
    nbFont = VDHGetCodePageFont(VDMData.PtrData.vmssVideo.vmss_ulCellWidth,
                                VDMData.PtrData.vmssVideo.vmss_ulCellHeight,
                                (PPVOID)SSToDS(&pbFont));

    if (!nbFont)
      return  FALSE;

    /*
    ** if foreground, transfer font to physical VRAM
    */

    if (VDMData.flVDMVideo&VDM_FGND)
    {
      pbDest = pPhysVRAM;
      tempState = VDMData.mstateCopy;            /*                         */
      VDMData.mstateCopy = MEMORY_NONE;          /* force change            */
      vvSetCopyState(CURRENT_VDM,
                     MEMORY_FONT,
                     PLANE2);
    }

    /*
    ** otherwise, transfer font to the "virtual" plane 2
    */

    else
    {

      if (VDMData.anpgPlane[BANK0][PLANE2] < 2)
        vvGrowPlane(CURRENT_VDM,
                    PLANE2,
                    2,
                    BANK0);                      /*                         */
      pbDest = VDMData.apPlane[BANK0][PLANE2];
    }
    pbSrc = pbFont;

    if (nbFont > PAGESIZE *2)
      nbFont = PAGESIZE *2;

    /*
    **           
    ** Mark font plane pages as such in the aapstate when
    ** loading the codepage font. This is required especially on
    ** Tridents, which if the previous mode left A0000 enabled will
    ** load the font plane before it sets its IO state. So, when
    ** GDC mode register is touched to set the mode, the font pages
    ** are invalidated and lost.
    */
    if (VDMData.mstateVideo == MEMORY_TEXT)     /*                          */
    {
      for(i=0;i<2;i++)
      {
        if((VDMData.aapstate[BANK0][i][PLANE2]&PAGE_VALID) != PAGE_FONT)
        {
          VDMData.aapstate[BANK0][i][PLANE2] &= ~PAGE_TYPE;
          VDMData.aapstate[BANK0][i][PLANE2] |= PAGE_FONT;
        }
      }
    }

    while (nbFont > 0)
    {

      for (i = 0; i < VDMData.PtrData.vmssVideo.vmss_ulCellHeight; i++,
           nbFont--)
        *pbDest++ = *pbSrc++;

      for (; i < 32; i++)
        *pbDest++ = 0;
    }

    if (VDMData.flVDMVideo&VDM_FGND)
    {                                            /*                         */
      vvRestoreCopyState(CURRENT_VDM,
                         SETIO_SHADOWED);
      VDMData.mstateCopy = tempState;            /*                         */
    }                                            /*                         */
  }

  else
  {                                              /*                         */

    switch (uMode)
    {                                    /*                                 */
      case  BIOSVMODE_CO320X200X4 :      /* BIOS mode 4                     */
      case  BIOSVMODE_BW320X200X4 :      /* BIOS mode 5                     */
      case  BIOSVMODE_CO640X200X2 :      /* BIOS mode 6                     */
      case  BIOSVMODE_CO320X200X16 :     /* BIOS mode D                     */
      case  BIOSVMODE_CO640X200X16 :     /* BIOS mode E                     */
      case  BIOSVMODE_CO320X200X256 :    /* BIOS mode 13                    */
        ulCellHeight = 8;                /* use 8x8 font                    */
        break;                           /*                                 */
      case  BIOSVMODE_MONO640X350X4 :    /* BIOS mode F                     */
      case  BIOSVMODE_CO640X350X16 :     /* BIOS mode 10                    */
        ulCellHeight = 14;               /* use 8x14 font                   */
        break;                           /*                                 */
      case  BIOSVMODE_CO640X480X2 :      /* BIOS mode 11                    */
      case  BIOSVMODE_CO640X480X16 :     /* BIOS mode 12                    */
        ulCellHeight = 16;               /* use 8x16 font                   */
        break;                           /*                                 */
      default  :                         /* no op anything else             */
        return  FALSE;                   /*                                 */
    }                                    /*                                 */

    ulCellWidth = 8;                     /* set default width               */

    /*
    ** read bitmap font from VIOTBL.DCP                                     
    */

    if (!(nbFont = VDHGetCodePageFont(ulCellWidth, /*                       */
                                      ulCellHeight,
                                      (PPVOID)SSToDS(&pbFont))))
      return  FALSE;


    if (nbFont > PAGESIZE)                  /* don't go beyond 4K           */
      nbFont = PAGESIZE;

    /*
    ** transfer font image from system memory into VDM                      
    */

    VDHCopyMem(pbFont,
               VDMData.pCPBuff,
               nbFont);                          /*                         */

    /*
    ** don't replace INT 1F if owner is neither us or ROM                   
    */
 /*                                                                         
  * if (SEGMENTOF16(VDMBase.rb_avpIVT[BIOSINT_VIDEOGRAPH]) == HISEG
  *    (VDMData.pCPBuff) || (SEGMENTOF16(VDMBase.rb_avpIVT[BIOSINT_VIDEOGRAPH]
  *    ) == VDMData.ROMint1fSeg))
  *   VDMBase.rb_avpIVT[BIOSINT_VIDEOGRAPH] = VPFROMVADDR(HISEG
  *      (VDMData.pCPBuff),
  *                                                       (LOOFF
  *                                                       (VDMData.pCPBuff)+
  *                                                       (ulCellHeight *128))
  *      );
  */
    /*
    ** replace INT 43 with the new font buffer                              
    */

    VDMBase.rb_avpIVT[0x43] = VPFROMVADDR(HISEG(VDMData.pCPBuff),
                                          LOOFF(VDMData.pCPBuff));
  }                                              /*                         */
  VDHReleaseCodePageFont(pbFont);
  return  TRUE;
}

#endif                                           /* EGAVGA                  */

#ifdef   EGAVGA

/***************************************************************************
 *
 * FUNCTION NAME = vvGetCodePageFont()
 *
 * DESCRIPTION   = Return codepage font
 *
 *                 This subroutine is called whenever an Int 10h subfunction
 *                 01h, 02h, 04h, 11h, 12h, 14h, 22h, 23h and 24h is called
 *                 with codepage enabled.
 *
 * INPUT         = None
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvGetCodePageFont(ULONG ulWidth,ULONG ulHeight,PBVDM pBuff)
{
  INT nbFont;                                    /*                         */
  PBYTE pbFont;                                  /*                         */


  if (!(nbFont = VDHGetCodePageFont(ulWidth,     /*                         */
                                    ulHeight,    /*                         */
                                    (PPVOID)SSToDS(&pbFont))))/*            */
    return  FALSE;                               /*                         */


  if (nbFont > PAGESIZE)     /* don't overflow font buffer                  */
    nbFont = PAGESIZE;                           /*                         */

  VDHCopyMem(pbFont,
             pBuff,
             nbFont);                            /*                         */

  VDHReleaseCodePageFont(pbFont);                /*                         */
  return  TRUE;                                  /*                         */
}                                                /*                         */
#endif                                           /* EGAVGA                  */

#ifdef   SVGA                                    /*                         */

/***************************************************************************
 *
 * FUNCTION NAME = vvFixTridentRegisters()
 *
 * DESCRIPTION   = Fix Trident Registers
 *
 *                 Make sure interlacing gets fixed up going background.
 *
 *                 Also flip a bit in the bank select register. Since we are
 *                 going background and UpdateIOState has already happened
 *                 this is a 'safe' place to put it. This bit is toggled on
 *                 a write to the bank select register and therefore is read
 *                 back in the opposite state to which it was written.
 *
 *           - EXTRA SUPPORT FOR CHIP LEVEL C WITH READ/WRITE
 *                              REGISTER AT INDEX 0X0C.
 *
 * INPUT         = HVDM
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID PRIVENTRY vvFixTridentRegisters(HVDM hvdm)
{
  BYTE bSeqIndx,bCRTIndx,bChipLevel,bConfig,bNMIEnable,bTmp;

  pVDMData(hvdm)->aregSEQData[0x0e] ^= 0x02;     /* flip this for restore   */
  bSeqIndx = INB(PORT_SEQINDX);         /* save SEQ index */    /*          */
  OUTB(PORT_SEQINDX,                                            /*          */
       0x0b);                           /* This changes the Mode
                                           Control                 */
  OUTB(PORT_SEQDATA,                                            /*          */
       0x01);                           /* register's definition
                                           to 'old'                */
  OUTB(PORT_SEQINDX,                                            /*          */
       0x0d);                           /* Old Mode Control reg    */
  OUTB(PORT_SEQDATA,                                            /*          */
       0x00);
  OUTB(PORT_SEQINDX,                                            /*          */
       0x0e);                           /* 128k page mode reg      */
  bNMIEnable = INB(PORT_SEQDATA);       /* bit 5: disable NMI *//*          */
  OUTB(PORT_SEQINDX,                                            /*          */
       0x0b);                           /* This changes the Mode
                                           Control                 */
  bChipLevel = INB(PORT_SEQDATA);       /* reg's def to 'new' *//*          */
  OUTB(PORT_SEQINDX,
       0x0d);                           /* New Mode Control reg    */
  OUTB(PORT_SEQDATA,                                            /*          */
       INB(PORT_SEQDATA)&~0x01);        /* clear ext CLK sel */ /*          */

  if ((bChipLevel >= 3) &&              /* 8900B is v3, 8900C is
                                           v4                      */
     (bNMIEnable&0x20))
  {
    OUTB(PORT_SEQINDX,                                          /*          */
         0x0c);                         /* Configuration port reg
                                           1                       */
    bConfig = INB(PORT_SEQDATA);                                /*          */

    if (bConfig&0x60)
    {                                   /* if both bits set        */
      bConfig &= ~0x20;                 /* clear bit 5             */
      OUTB(PORT_SEQINDX,                                        /*          */
           0x0e);                       /* first unprotect
                                           register 0x0c           */
      bTmp = INB(PORT_SEQDATA)^0x02;    /* get bank sel (New Mode) */ /*          */
      OUTB(PORT_SEQDATA,                                        /*          */
           bTmp|0x80);                  /* set bit to unprotect    */
      OUTB(PORT_SEQINDX,                                        /*          */
           0x0c);                       /* Configuration port reg
                                           1                       */
      OUTB(PORT_SEQDATA,                                        /*          */
           bConfig);                    /* set new DRAM
                                           configuration           */
      OUTB(PORT_SEQINDX,                                        /*          */
           0x0e);                       /* restore 0x0e            */
      OUTB(PORT_SEQDATA,                                        /*          */
           bTmp);                       /* restore bank select     */
    }
  }
  OUTB(PORT_SEQINDX,                                            /*          */
       bSeqIndx);                       /* restore index           */
  bCRTIndx = INB(PORT_COLRCRTINDX);     /* save index              */
  OUTB(PORT_COLRCRTINDX,                                        /*          */
       0x1e);                           /* CRTC Module Test reg    */
  OUTB(PORT_COLRCRTDATA,                                        /*          */
       INB(PORT_COLRCRTDATA)&~0x04);    /* clear interlace */   /*          */
  OUTB(PORT_COLRCRTINDX,                                        /*          */
       bCRTIndx);                       /* restore index           */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvFixVideo7TextRegisters()
 *
 * DESCRIPTION   = Fix Video7 text registers
 *
 *                 When BIOS sets 132 column modes, it sets the extended
 *                 register 0xFD (Timing Select) to a typical value of 0xcd
 *                 when read. If we blindly save and then restore this value,
 *                 the timing is incorrect. The lower nibble of this byte
 *                 determines text mode timings, and is usually set to 2.
 *                 On completion of a mode set, we get called here and reset
 *                 the register to 2. (I've consulted Headland Technology
 *                 and they confirmed the value )
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvFixVideo7TextRegisters()
{
  USHORT invalue;


  if (VDMBase.rb_nVCols == 132)
  {                                              /* Only extended text mode */
    invalue = INB(PORT_SEQINDX);
    OUTW(PORT_SEQINDX,
         0x0000);                                /* start synch reset       */
    OUTW(PORT_SEQINDX,
         0x02fd);                                /* set text timing         */
    OUTW(PORT_SEQINDX,
         0x0300);                                /* end synch reset         */
    OUTB(PORT_SEQINDX,
         invalue);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSVGACleanup()
 *
 * DESCRIPTION   = Process CleanData array to fix registers to VGA defaults.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvSVGACleanup(VOID)
{
  register INT i;
  register BYTE bIn;
  BYTE bIndex;

  for (i = 0; CleanData[i].Command; i++)

    switch (CleanData[i].Command)               /*             */
    {
      case PMICMD_RMWB:                         /*             */
        bIndex = INB(CleanData[i].IndexPort);
        OUTB(CleanData[i].IndexPort,
             CleanData[i].Index);
        bIn = INB(CleanData[i].DataPort);
        bIn &= CleanData[i].ANDMask;
        bIn |= CleanData[i].ORMask;
        OUTB(CleanData[i].IndexPort,
             CleanData[i].Index);
        OUTB(CleanData[i].DataPort,
             bIn);
        OUTB(CleanData[i].IndexPort,
             bIndex);
        break;

      case PMICMD_RMWW:                         /*            */
        bIn = INB(CleanData[i].IndexPort);
        bIn &= CleanData[i].ANDMask;
        bIn |= CleanData[i].ORMask;
        OUTB(CleanData[i].DataPort,
             bIn);
        break;
    }

}

/***************************************************************************
 *
 * FUNCTION NAME = vvFixSVGARegisters()
 *
 * DESCRIPTION   = Fix up registers based on the mode set
 *
 *                 This subroutine fixes up various register states after an
 *                 int 10 set mode request completes.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvFixSVGARegisters()
{
  register ULONG uMode;
  USHORT invalue;
//  BOOL flLocked;                                                             


  if (!fPMILoaded)
    return ;
  uMode = VDMData.ulBIOSMode&~BIOSVINFO_DONTCLEAR;

//            No more locks
//  if (flLocked = vvSVGAGetLockState(CURRENT_VDM))               /*            */
    vvUnLockSVGARegisters();

  if (uMode > 0x13)
  {

    /*
    ** This must be an SVGA mode
    */

    switch (ulSVGAAdapterType)
    {
      case  WESTERNDIG_ADAPTER :
        vvSVGACleanup();
        break;
      case  VIDEO7_ADAPTER :
        vvFixVideo7TextRegisters();
        break;
      case  TSENG_ADAPTER :
      case  TRIDENT_ADAPTER :
      case  ATI_ADAPTER :
        break;
    }
  }

  else
  {

    /*
    ** This must be a standard VGA mode
    */

    switch (ulSVGAAdapterType)
    {
      case  VIDEO7_ADAPTER :
        vvFixVideo7TextRegisters();
        break;
      case  TSENG_ADAPTER :
      case  TRIDENT_ADAPTER :
      case  WESTERNDIG_ADAPTER :
      case  ATI_ADAPTER :
        break;
    }
  }

//          
//  if (flLocked)
//    vvLockSVGARegisters(flLocked);                      /*                         */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvLockSVGARegisters()
 *
 * DESCRIPTION   = Lock SVGA extended registers.
 *
 * INPUT         = ULONG ulLockState    Lock State Mask
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvLockSVGARegisters(ULONG ulLockState)   /*                */
{
  USHORT i;
  ULONG ulLockMaskPosition = 0x1;                       /*                */
  for (i=0;i<ulNumLockCmds;i++,ulLockMaskPosition <<= 1) {
     
     if (ulLockMaskPosition & ulLockState)             /*                */
         OUTB(ausLockReg[i], ausLockCmd[i]);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvUnLockSVGARegisters()
 *
 * DESCRIPTION   = unlock SVGA extended registers.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvUnLockSVGARegisters()
{
  USHORT i;
  BYTE bGDCIndex, bColrCRTIndex, bMonoCRTIndex, bSEQIndex;      /*          */

  /*!! This is not a good long term solution */                 /*          */
  /*!! What we really need is a push/pop lock */                /*          */
  /*!! section in the PMI file! */                              /*          */

  bGDCIndex = INB(PORT_GDCINDX);          /* save GDC index */  /*          */
  bColrCRTIndex = INB(PORT_COLRCRTINDX);  /* save CRT index */  /*          */
  bMonoCRTIndex = INB(PORT_MONOCRTINDX);  /* save CRT index */  /*          */
  bSEQIndex = INB(PORT_SEQINDX);          /* save SEQ index */  /*          */

  for (i = 0; i < ulNumUnLockCmds; i++)
    OUTB(ausUnLockReg[i],
         ausUnLockCmd[i]);

  OUTB(PORT_GDCINDX, bGDCIndex);         /* restore GDC index *//*          */
  OUTB(PORT_COLRCRTINDX, bColrCRTIndex); /* restore CRT index *//*          */
  OUTB(PORT_MONOCRTINDX, bMonoCRTIndex); /* restore CRT index *//*          */
  OUTB(PORT_SEQINDX, bSEQIndex);         /* restore SEQ index *//*          */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSVGAFixSetBgnd()
 *
 * DESCRIPTION   = Fix up register state for background.
 *
 *                 This subroutine fixes up various register states when the
 *                 the VDM is going into the background.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvSVGAFixSetBgnd(HVDM hvdm)
{
  USHORT i,j;
//  BOOL flLocked;                                                             


  if (!fPMILoaded)
    return ;

  /*
  ** Cleanup is executed at the end of setbgnd. If 2F handler was
  ** invoked, state of the lock registers is not necessarily the
  ** same as set by the SetBgnd (unlocked). Therefore, do an explicit
  ** unlock.                                              
  */
//            if (flLocked = vvSVGAGetLockState(hvdm))                      /*            */
    vvUnLockSVGARegisters();

  if (ulSVGAAdapterType == TRIDENT_ADAPTER)
    vvFixTridentRegisters(hvdm);

  else
    vvSVGACleanup();

//            no more locks
//  if (flLocked)
//    vvLockSVGARegisters(flLocked);             /*                         */
}

#endif                                           /*                         */
#pragma  END_SWAP_CODE

