/**[txh]********************************************************************

  DOS Screen (TScreenDOS) functions.

  Copyright (c) 1998-2002 by Salvador E. Tropea (SET)
  Contains code Copyright (c) 1996-1998 by Robert H”hne.

  Some code for handling 8x10 fonts is adapted from djgpp libc:
  Copyright (C) 1995-1999 DJ Delorie

  Description:
  This module implements the low level DOS screen access.

  Configuration variables:
  ScreenWidth
  ScreenHeight
  ScreenPalette
  AppCP
  ScrCP
  InpCP
  LoadSecondaryFont
  ExtProgVideoMode
  VideoMode
  VGABIOSState      0 use registers, 1 use BIOS (default)
  SaveVGAState      0 don't try to save/restore VGA chip state,
                    1 try to save it using the strategy indicated by
                    VGABIOSState (default)
  
***************************************************************************/

#include <tv/configtv.h>

#ifdef TVCompf_djgpp
 #include <conio.h>
#endif

#define Uses_string
#define Uses_unistd
#define Uses_TScreen
#define Uses_TEvent
#define Uses_TGKey
#define Uses_TVOSClipboard
#define Uses_TVCodePage
#include <tv.h>

// I delay the check to generate as much dependencies as possible
#ifdef TVCompf_djgpp

#include <dos.h>
#include <malloc.h>
#include <go32.h>
#include <dpmi.h>
#include <sys/farptr.h>
#include <sys/movedata.h>

#include <pc.h>
#include <stdio.h>
#include <sys/mono.h>

#define TSCREEN_DEFINE_REGISTERS
#include <tv/dos/screen.h>
#include <tv/dos/key.h>
#include <tv/dos/mouse.h>

#define GET_DOS_VERSION                      0x3000
#define GET_GLOBAL_CODE_PAGE_TABLE           0x6601
#define CHARGEN_SET_BLOCK_SPECIFIER          0x1103
#define CHARGEN_LOAD_USER_SPECIFIED_PATTERNS 0x1110
#define GET_FONT_INFORMATION                 0x1130
// 0x11xx services (Sets the BIOS Rom)
#define FONT_8x8                             0x12
#define FONT_8x14                            0x11
#define FONT_8x16                            0x14
// GET_FONT_INFORMATION values
#define GET_FONT_8x8                         0x03
#define GET_FONT_8x14                        0x02
#define GET_FONT_8x16                        0x06

int   TScreenDOS::wasBlink=0;
int   TScreenDOS::slowScreen=0;
uchar TScreenDOS::primaryFontSet=0,
      TScreenDOS::secondaryFontSet=0,
      TScreenDOS::fontsSuspended=0;
int   TScreenDOS::origCPScr,
      TScreenDOS::origCPApp,
      TScreenDOS::origCPInp;
int   TScreenDOS::fontSeg=-1;
TScreenFont256 TScreenDOS::appFonts[2]={{0,0,NULL},{0,0,NULL}};

const unsigned mdaBaseAddress=0xB0000;

// Extern support functions
extern void SaveScreen();
extern void SaveScreenReleaseMemory();
extern void RestoreScreen();
extern void ScreenUpdate();
extern ushort user_mode;
extern char useBIOS_VGA_State;
extern char saveVGA_State;

void setBlinkState();
void setIntenseState();
int  getBlinkState();

TScreen *TV_DOSDriverCheck()
{
 TScreenDOS *drv=new TScreenDOS();
 if (!TScreen::initialized)
   {
    delete drv;
    return 0;
   }
 return drv;
}

TScreenDOS::TScreenDOS()
{
 // Currently initialization never fails
 initialized=1;
 if (dCB) dCB();

 TDisplayDOS::Init();

 TScreen::Resume=Resume;
 TScreen::Suspend=Suspend;
 TScreen::setCrtData=setCrtData;
 TScreen::clearScreen=clearScreen;
 TScreen::getCharacter=getCharacter;
 TScreen::getCharacters=getCharacters;
 TScreen::setCharacter=setCharacter;
 TScreen::setCharacters=setCharacters;
 TScreen::System_p=System;
 TScreen::getFontGeometry=GetFontGeometry;
 TScreen::setFont_p=SetFont;
 TScreen::restoreFonts=RestoreFonts;

 TVDOSClipboard::Init();
 THWMouseDOS::Init();
 TGKeyDOS::Init();

 long aux;
 if (optSearch("VGABIOSState",aux))
    useBIOS_VGA_State=aux;
 if (optSearch("SaveVGAState",aux))
    saveVGA_State=aux;

 // Set the code page
 int dosCodePage=437; // United States code page, for all versions before 3.3
 // get DOS version number, in the form of a normal number
 AX=GET_DOS_VERSION;
 dosInt();
 unsigned ver=AH | ((AL & 0xff)<<8);
 if (ver>=0x31E)
   {
    AX=GET_GLOBAL_CODE_PAGE_TABLE;
    dosInt();
    dosCodePage=BX;
   }
 // Look for user settings
 optSearch("AppCP",forcedAppCP);
 optSearch("ScrCP",forcedScrCP);
 optSearch("InpCP",forcedInpCP);
 // User settings have more priority than detected settings
 codePage=new TVCodePage(forcedAppCP!=-1 ? forcedAppCP : dosCodePage,
                         forcedScrCP!=-1 ? forcedScrCP : dosCodePage,
                         forcedInpCP!=-1 ? forcedInpCP : dosCodePage);
 SetDefaultCodePages(dosCodePage,dosCodePage,dosCodePage);

 flags0=CodePageVar | CanSetPalette | CanReadPalette | CursorShapes | UseScreenSaver |
        CanSetBFont | CanSetSBFont  | CanSetVideoSize;
 user_mode=startupMode=getCrtMode();
 SaveScreen();
 unsigned startScreenWidth=GetCols(), startScreenHeight=GetRows();

 unsigned maxX=startScreenWidth, maxY=startScreenHeight;
 if (optSearch("ScreenWidth",aux))
    maxX=aux;
 if (optSearch("ScreenHeight",aux))
    maxY=aux;
 if (frCB)
   {// Give a chance to the call back.
    primaryFontSet=1;
    appFonts[0].h=0;
    long aux=0;
    if (optSearch("LoadSecondaryFont",aux) && aux)
      {
       secondaryFontSet=1;
       appFonts[1].h=0;
      }
   }
 char *ext=optSearch("ExtProgVideoMode");
 if (optSearch("VideoMode",aux))
   {// if frCB installed it will use the application font
    setCrtMode((ushort)aux);
   }
 else if (ext)
   {
    setCrtModeExt(ext);
    if (frCB)
       SelectFont(charLines,False); // Try loading the application font
   }
 else if (maxX!=startScreenWidth || maxY!=startScreenHeight)
   {// if frCB installed it will use the application font
    setCrtModeRes(maxX,maxY);
   }
 else
   {
    if (frCB)
       SelectFont(charLines,False); // Try loading the application font
   }
 // User requested palette
 if (parseUserPalette())
    setPaletteColors(0,16,UserStartPalette);

 screenMode=getCrtMode();
 setCrtData();
 // We use the video memory as buffer, not a malloced buffer
 screenBuffer=(uint16 *)-1;
 suspended=0;
}

void TScreenDOS::Resume()
{
 if (!dual_display)
   {
    GetDisPaletteColors(0,16,OriginalPalette);
    SaveScreen();
    //if (screenMode == 0xffff)
    //  screenMode = getCrtMode();
    if (screenMode!=user_mode)
       setCrtMode(screenMode);
    if (wasBlink)
       setBlinkState();
    else
       setIntenseState();
    ResumeFonts();
    SetDisPaletteColors(0,16,ActualPalette);
   }
 else
   {
    THWMouseDOS::setEmulation(1);
   }
 setCrtData();
}

TScreenDOS::~TScreenDOS()
{
 suspend();                 // First suspend
 SaveScreenReleaseMemory(); // Now we can release the memory
 ReleaseMemFonts();
 THWMouseDOS::DeInit();
}

void TScreenDOS::Suspend()
{
 if (!dual_display)
   {
    wasBlink=getBlinkState();
    SuspendFonts();
    RestoreScreen();
    SetDisPaletteColors(0,16,OriginalPalette);
   }
}

void TScreenDOS::setCrtData()
{
 if (dual_display)
   {
    screenMode  =7;
    screenWidth =80;
    screenHeight=25;
    cursorLines =0x0b0c;
   }
 else
   {
    screenMode  =getCrtMode();
    screenWidth =getCols();
    screenHeight=getRows();
    hiResScreen =Boolean(screenHeight>25);
    if (screenMode==7)
       cursorLines=0x0b0c; // ?
    else
       cursorLines=getCursorType();
    setCursorType(0);
   }
}

void TScreenDOS::clearScreen()
{
 if (dual_display)
    _mono_clear(); // djgpp's libc
 else
    TDisplay::clearScreen(screenWidth,screenHeight);
}

ushort TScreenDOS::getCharacter(unsigned offset)
{
 return _farpeekw(_dos_ds,(dual_display ? mdaBaseAddress : ScreenPrimary)+offset*2);
}

void TScreenDOS::getCharacters(unsigned offset,ushort *buf,unsigned count)
{
 if (slowScreen)
   {
    _farsetsel(_dos_ds);
    int ofs=(dual_display ? mdaBaseAddress : ScreenPrimary)+offset*2;
    while (count--)
      {
       *buf++=_farnspeekw(ofs);
       ofs+=2;
      }
   }
 else
   {
    movedata(_dos_ds,(dual_display ? mdaBaseAddress : ScreenPrimary)+offset*2,
             _my_ds(),(int)buf,count*2);
   }
}

void TScreenDOS::setCharacter(unsigned offset,ushort value)
{
 _farpokew(_dos_ds,(dual_display ? mdaBaseAddress : ScreenPrimary)+offset*2,value);
}

void TScreenDOS::setCharacters(unsigned offset,ushort *values,unsigned count)
{
 if (slowScreen)
   {
    _farsetsel(_dos_ds);
    int ofs = dual_display ? mdaBaseAddress : ScreenPrimary+offset*2;
    while (count--)
      {
       _farnspokew(ofs,*values++);
       ofs += 2;
      }
   }
 else
   {
    movedata(_my_ds(),(int)values,_dos_ds,
             (dual_display ? mdaBaseAddress : ScreenPrimary)+offset*2,count*2);
   }
}

int TScreenDOS::System(const char *command, pid_t *pidChild, int in,
                       int out, int err)
{
 // fork mechanism not available
 if (pidChild)
    *pidChild=0;
 // If the caller asks for redirection replace the requested handles
 if (in!=-1)
    dup2(in,STDIN_FILENO);
 if (out!=-1)
    dup2(out,STDOUT_FILENO);
 if (err!=-1)
    dup2(err,STDERR_FILENO);
 return system(command);
}

/*****************************************************************************
  Windows clipboard implementation using WinOldAp API.
*****************************************************************************/

#define USE_TB

// Numbers for the errors
#define WINOLDAP_NoPresent 1
#define WINOLDAP_ClpInUse  2
#define WINOLDAP_TooBig    3
#define WINOLDAP_Memory    4
#define WINOLDAP_WinErr    5

#define WINOLDAP_Errors    5

#define IDENTIFY_WinOldAp_VERSION 0x1700
#define OPEN_CLIPBOARD            0x1701
#define EMPTY_CLIPBOARD           0x1702
#define SET_CLIPBOARD_DATA        0x1703
#define GET_CLIPBOARD_DATA_SIZE   0x1704
#define GET_CLIPBOARD_DATA        0x1705
#define CLOSE_CLIPBOARD           0x1708

// Strings for the errors
const char *TVDOSClipboard::dosNameError[]=
{
 NULL,
 __("Windows not present"),
 __("Clipboard in use by other application"),
 __("Clipboard too big for the transfer buffer"),
 __("Not enough memory"),
 __("Windows reports error")
};

int TVDOSClipboard::isValid=0;
int TVDOSClipboard::Version;

int TVDOSClipboard::Init(void)
{
 AX=IDENTIFY_WinOldAp_VERSION;
 MultiplexInt();
 Version=AX;
 isValid=AX!=IDENTIFY_WinOldAp_VERSION;
 if (!isValid)
    TVOSClipboard::error=WINOLDAP_NoPresent;
 else
   {
    TVOSClipboard::copy=copy;
    TVOSClipboard::paste=paste;
    TVOSClipboard::available=1; // We have 1 clipboard
    TVOSClipboard::name="Windows";
    TVOSClipboard::errors=WINOLDAP_Errors;
    TVOSClipboard::nameErrors=dosNameError;
   }
 return isValid;
}

int TVDOSClipboard::AllocateDOSMem(unsigned long size,unsigned long *BaseAddress)
{
 #ifdef USE_TB
 unsigned long tbsize=_go32_info_block.size_of_transfer_buffer;

 if (size<=tbsize)
   {
    *BaseAddress=__tb;
    return 1;
   }
 #endif
 if (size>0x100000)
   {
    TVOSClipboard::error=WINOLDAP_TooBig;
    return 0;
   }
 AH=0x48;
 BX=(size>>4)+(size & 0xF ? 1 : 0);
 MultiplexInt();
 if (r.x.flags & 1)
   {
    TVOSClipboard::error=WINOLDAP_TooBig;
    return 0;
   }
 *BaseAddress=AX<<4;
 return 1;
}

void TVDOSClipboard::FreeDOSMem(unsigned long Address)
{
 #ifdef USE_TB
 if (Address==__tb)
    return;
 #endif
 AH=0x49;
 ES=Address>>4;
 TDisplayDOS::dosInt();
}

//#define DEBUG_CLIPBOARD
#ifdef DEBUG_CLIPBOARD
 ushort messageBox( const char *msg, ushort aOptions );
 ushort messageBox( ushort aOptions, const char *fmt, ... );
 #define DBGMessage(a) messageBox(a,0x402)
 #define DBGMessage2(a,b) messageBox(0x402,a,b)
#else
 #define DBGMessage(a)
 #define DBGMessage2(a,b)
#endif

int TVDOSClipboard::copy(int id, const char *buffer, unsigned len)
{
 if (!isValid || id!=0) return 0;

 unsigned long dataoff;
 char cleaner[32];
 int winLen;
 int ret=1;

 AX=OPEN_CLIPBOARD;
 MultiplexInt();
 if (AX==0)
   {
    DBGMessage("Error opening the clipboard");
    TVOSClipboard::error=WINOLDAP_ClpInUse;
    return 0;
   }
 // Erase the current contents of the clipboard
 AX=EMPTY_CLIPBOARD;
 MultiplexInt();
 winLen=((len+1)+0x1F) & ~0x1F;
 memset(cleaner,0,32);
 if (AllocateDOSMem(winLen,&dataoff))
   {
    dosmemput(buffer,len,dataoff);
    dosmemput(cleaner,winLen-len,dataoff+len);
    AX=SET_CLIPBOARD_DATA;
    DX=7; // OEM text
    BX=dataoff & 0x0f;
    ES=(dataoff>>4) & 0xffff;
    SI=winLen>>16;
    CX=winLen & 0xffff;
    MultiplexInt();
    FreeDOSMem(dataoff);
    if (AX==0)
      {
       DBGMessage("Error copying to clipboard");
       TVOSClipboard::error=WINOLDAP_WinErr;
       ret=0;
      }
   }
 else
   {
    TVOSClipboard::error=WINOLDAP_Memory;
    DBGMessage("Error allocating DOS memory");
    ret=0;
   }
 AX=CLOSE_CLIPBOARD;
 MultiplexInt();
 return ret;
}

char *TVDOSClipboard::paste(int id, unsigned &len)
{
 if (!isValid || id!=0) return NULL;

 char *p=NULL;
 unsigned long BaseAddress;
 unsigned long size;

 AX=OPEN_CLIPBOARD;
 MultiplexInt();
 if (AX==0)
   {
    DBGMessage("Error at open clipboard");
    TVOSClipboard::error=WINOLDAP_ClpInUse;
    return NULL;
   }
 AX=GET_CLIPBOARD_DATA_SIZE;
 DX=1;
 MultiplexInt();
 size=AX+(DX<<16);
 DBGMessage2("Size of clipboard %d",size);
 if (size)
   {
    if (AllocateDOSMem(size,&BaseAddress))
      {
       p=new char[size];
       if (p)
         {
          DX=7;
          BX=BaseAddress & 0x0f;
          ES=(BaseAddress>>4) & 0xffff;
          AX=GET_CLIPBOARD_DATA;
          MultiplexInt();
          dosmemget(BaseAddress,size,p);
          len=strlen(p);
          DBGMessage2("Got %d bytes in string",len);
         }
       else
          TVOSClipboard::error=WINOLDAP_Memory;
       FreeDOSMem(BaseAddress);
      }
    else
      {
       TVOSClipboard::error=WINOLDAP_Memory;
       DBGMessage("Error allocating DOS memory");
      }
   }
 else
   {
    p=new char[1];
    *p=0;
    len=0;
   }
 AX=CLOSE_CLIPBOARD;
 MultiplexInt();
 return p;
}

/*****************************************************************************
  Fonts routines
*****************************************************************************/

int TScreenDOS::GetFontGeometry(unsigned &w, unsigned &h)
{
 w=8;
 h=charLines;
 return 1;
}

/**[txh]********************************************************************

  Description:
  That selects what fonts are used for no/intense foreground colors.@p
  The bits of the call are a little tweaked because it's EGA compatible:@*
---VGA---@*
 0,1,4 block selected by characters with attribute bit 3 clear@*
 2,3,5 block selected by characters with attribute bit 3 set@*

***************************************************************************/

void TScreenDOS::SetDualCharacter(int b1, int b2)
{
 int value;
 // No intense foreground font
 value=b1 & 0x3;
 if (b1 & 0x4)
    value|=0x10;
 // intense foreground font
 value|=(b2 & 0x3)<<2;
 if (b2 & 0x4)
    value|=0x20;

 AX=CHARGEN_SET_BLOCK_SPECIFIER;
 BL=value;
 videoInt();
}

void TScreenDOS::SetFontBIOS(int which, unsigned height, uchar *data,
                             int modeRecalculate)
{
 dosmemput(data,256*height,__tb);
 ES=__tb>>4;    /* pass pointer to our font in ES:BP */
 BP=__tb & 0xF;
 DX=0;          /* 1st char: ASCII 0 */
 CX=256;        /* 256 chars */
 BH=height;     /* points per char */
 BL=which;      /* block */
 AX=CHARGEN_LOAD_USER_SPECIFIED_PATTERNS;
 if (!modeRecalculate)
    AL&=0xF;    /* force full mode recalculate, service 0x1100 */
 videoInt();
}

void TScreenDOS::ReleaseMemFonts()
{
 if (appFonts[0].data)
   {
    free(appFonts[0].data);
    appFonts[0].data=NULL;
   }
 if (appFonts[1].data)
   {
    free(appFonts[1].data);
    appFonts[1].data=NULL;
   }
}

int TScreenDOS::MemorizeFont(int which, TScreenFont256 *font)
{
 // Keep a copy of the user font
 appFonts[which].w=font->w;
 appFonts[which].h=font->h;
 if (appFonts[which].data)
    free(appFonts[which].data);
 unsigned size=font->h*256;
 appFonts[which].data=(uchar *)malloc(size);
 if (!appFonts[which].data)
    return 0;
 memcpy(appFonts[which].data,font->data,size);
 return 1;
}

void TScreenDOS::SuspendFonts()
{
 if (fontsSuspended)
    return;
 fontsSuspended=1;
 if (!primaryFontSet && !secondaryFontSet)
    return; // Nothing to do
 DisableDualFont();
 SelectRomFont(charLines,0,0);
}

void TScreenDOS::ResumeFonts()
{
 if (!fontsSuspended)
    return;
 fontsSuspended=0;
 if (!primaryFontSet && !secondaryFontSet)
    return; // Nothing to do
 if (primaryFontSet)
    SetFontBIOS(0,charLines,appFonts[0].data,0);
 if (secondaryFontSet)
   {
    SetFontBIOS(1,charLines,appFonts[1].data,0);
    EnableDualFont();
   }
}

int TScreenDOS::SetFont(int changeP, TScreenFont256 *fontP,
                        int changeS, TScreenFont256 *fontS,
                        int fontCP, int appCP)
{
 if (!changeP && !changeS) return 1;
 // Check for restore fonts
 if (changeP && !fontP && ((!changeS && !secondaryFontSet) || (changeS && !fontS)))
   {
    DisableDualFont();
    SelectRomFont(charLines,0,0);
    ReleaseMemFonts();
    secondaryFontSet=primaryFontSet=0;
    if (fontCP!=-1)
      {
       if (appCP==-1)
          TVCodePage::SetScreenCodePage(fontCP);
       else
          TVCodePage::SetCodePage(appCP,fontCP,-1);
      }
   return 1;
   }
 // Solve the sizes
 unsigned wP=8,hP=charLines,
          wS=8,hS=charLines;
 int newP=changeP && fontP;
 if (newP)
   {
    wP=fontP->w;
    hP=fontP->h;
   }
 int newS=changeS && fontS;
 if (newS)
   {
    wS=fontS->w;
    hS=fontS->h;
   }
 if (wP!=wS || hP!=hS) return 0;
 // Check if the size is in the range
 if (wP!=8 || hP!=charLines)
    return 0;
 // Memorize the new fonts
 if (newP && !MemorizeFont(0,fontP)) return 0;
 if (newS && !MemorizeFont(1,fontS)) return 0;

 // Change the requested fonts
 if (changeP)
   {
    if (!primaryFontSet)
       TVCodePage::GetCodePages(origCPApp,origCPScr,origCPInp);
    if (fontP)
      {
       SetFontBIOS(0,charLines,fontP->data,0);
       primaryFontSet=1;
      }
    else
      {
       SelectRomFont(charLines,0,0);
       primaryFontSet=0;
      }
   }
 if (changeS)
   {
    if (fontS)
      {
       SetFontBIOS(1,charLines,fontS->data,0);
       EnableDualFont();
       secondaryFontSet=1;
      }
    else
      {
       DisableDualFont();
       secondaryFontSet=0;
      }
   }
 if (changeP && fontCP!=-1)
   {
    if (appCP==-1)
       TVCodePage::SetScreenCodePage(fontCP);
    else
       TVCodePage::SetCodePage(appCP,fontCP,-1);
   }
 return 1;
}

void TScreenDOS::RestoreFonts()
{
 if (!primaryFontSet && !secondaryFontSet)
    return; // Protection
 SetFont(1,NULL,1,NULL,origCPScr,origCPApp);
}

/**[txh]********************************************************************

  Description:
  Selects a font of the desired size. Currently only 8x8, 8x10, 8x14 and
8x16 are supported but I did it in this way because nobody knows about the
future.@p
  If noForce is != 0 then the routine doesn't set the fonts. This option
must be used when you know that BIOS already loaded the fonts. Using it
avoids an extra load. The derived classes takes the desition according to
the selected font, so if the user selected a font the load is forced.@p
  If modeRecalculate is 1 the call to set the BIOS font is made using the
bit 4 on, that means the BIOS will recalculate some things like the number
of rows and cols in the screen. That's avoided when an external program
sets the mode because a recalculation can fail.@p

  Return:
  non-zero if fails, zero if all ok.

***************************************************************************/

int TScreenDOS::SelectRomFont(int height, int which, int modeRecalculate)
{
 //UseDefaultFontsNextTime=0;
 switch (height)
   {
    case 8:
         SetRomFonts(FONT_8x8,which,modeRecalculate);
         break;
    case 10:
         if (Load8x10Font(which,modeRecalculate))
            return 1;
         break;
    case 14:
         SetRomFonts(FONT_8x14,which,modeRecalculate);
         break;
    case 16:
         SetRomFonts(FONT_8x16,which,modeRecalculate);
         break;
    default:
         return 1;
   }
 return 0;
}

void TScreenDOS::SetRomFonts(int sizeFont, int which, int modeRecalculate)
{
 if (!modeRecalculate)
    sizeFont&=0xF;
 BL=which;
 AH=0x11;
 AL=sizeFont;
 videoInt();
}


/**[txh]********************************************************************

  Description: 
  Stretch a 8x8 font to the 8x10 character box.  This is required to
use 80x40 mode on a VGA or 80x35 mode on an EGA, because the character
box is 10 lines high, and the ROM BIOS doesn't have an appropriate font.
So we create one from the 8x8 font by adding an extra blank line
from each side.
  
***************************************************************************/

void TScreenDOS::MaybeCreate8x10Font(void)
{
 unsigned char *p;
 unsigned long src, dest, i, j;

 if (fontSeg!=-1)
    return;
 int buf_pm_sel;
 
 /* Allocate buffer in conventional memory. */
 fontSeg=__dpmi_allocate_dos_memory(160,&buf_pm_sel);

 if (fontSeg==-1)
    return;

 /* Get the pointer to the 8x8 font table.  */
 p=(uchar *)malloc(2560); /* 256 chars X 8x10 pixels */
 if (p==(uchar *)0)
   {
    //errno=ENOMEM;
    __dpmi_free_dos_memory(buf_pm_sel);
    fontSeg=-1;
    return;
   }
 BH=GET_FONT_8x8;
 AX=GET_FONT_INFORMATION;
 videoInt();
 src=(((unsigned)ES)<<4)+BP;
 dest=((unsigned)fontSeg)<<4;

 /* Now copy the font to our table, stretching it to 8x10. */
 _farsetsel(_dos_ds);
 for (i=0; i<256; i++)
    {
     /* Fill first extra scan line with zeroes. */
     _farnspokeb(dest++, 0);

     for (j=0; j<8; j++)
        {
         uchar val=_farnspeekb(src++);
         _farnspokeb(dest++,val);
        }

     /* Fill last extra scan line with zeroes. */
     _farnspokeb(dest++,0);
    }
}

/* Load the 8x10 font we created into character generator RAM.  */
int TScreenDOS::Load8x10Font(int which, int modeRecalculate)
{
 MaybeCreate8x10Font();         /* create if needed */
 if (fontSeg==-1)
    return 1;
 ES=fontSeg;              /* pass pointer to our font in ES:BP */
 BP=0;
 DX=0;                    /* 1st char: ASCII 0 */
 CX=256;                  /* 256 chars */
 BH=10;                   /* 10 points per char */
 BL=which;                /* block */
 AX=CHARGEN_LOAD_USER_SPECIFIED_PATTERNS;
 if (!modeRecalculate)
    AL&=0xF;
 videoInt();
 return 0;
}

/**[txh]********************************************************************

  Description:
  Used after setting a video mode to load the user font or the BIOS font.
The force parameter indicates if we must load the BIOS font or if the font
was already loaded.@p
  If a font was set the application this function tries to reuse it. If the
font doesn't match the size used by the video mode calls a call back
requesting a new font. If the routine fails to get a propper font the BIOS
font is restored.
  
***************************************************************************/

void TScreenDOS::SelectFont(unsigned height, Boolean Force)
{
 int fontWasLoaded=primaryFontSet || secondaryFontSet;

 if (primaryFontSet && !fontsSuspended)
   {
    if (height==appFonts[0].h)
       SetFontBIOS(0,height,appFonts[0].data,0);
    else
      {
       int fontOk=0;
       // The one provided by the application isn't suitable
       if (frCB)
         {// Try asking the application to provide a new one
          TScreenFont256 *font=frCB(0,8,height);
          if (font)
            {
             SetFontBIOS(0,height,font->data,0);
             MemorizeFont(0,font);
             fontOk=1;
             DeleteArray(font->data);
             delete font;
            }
         }
       if (!fontOk)
         {
          if (Force)
             SelectRomFont(height,0,1);
          primaryFontSet=0;
         }
      }
   }
 else
   {
    if (Force)
       SelectRomFont(height,0,1);
   }

 if (!fontsSuspended && secondaryFontSet)
   {
    if (height==appFonts[1].h)
       SetFontBIOS(1,height,appFonts[1].data,0);
    else
      {
       int fontOk=0;
       // The one provided by the application isn't suitable
       if (frCB)
         {// Try asking the application to provide a new one
          TScreenFont256 *font=frCB(1,8,height);
          if (font)
            {
             SetFontBIOS(1,height,font->data,0);
             MemorizeFont(1,font);
             fontOk=1;
             DeleteArray(font->data);
             delete font;
            }
         }
       if (!fontOk)
         {
          DisableDualFont();
          secondaryFontSet=0;
         }
      }
   }

 if (fontWasLoaded && !(primaryFontSet || secondaryFontSet))
    // Restore the original encoding if we forced ROM fonts
    TVCodePage::SetCodePage(origCPApp,origCPScr,origCPInp);
}


#else // DJGPP

#include <tv/dos/screen.h>
#include <tv/dos/key.h>
#include <tv/dos/mouse.h>

#endif // else DJGPP

