/*
**	Command & Conquer Red Alert(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/****************************************************************************
*
*         C O N F I D E N T I A L -- W E S T W O O D  S T U D I O S
*
*----------------------------------------------------------------------------
*
* FILE
*     vesavid.c
*
* DESCRIPTION
*     VESA video manager. (32-Bit protected mode)
*
* PROGRAMMER
*     Denzil E. Long, Jr.
*
* DATE
*     January 26, 1995
*
*----------------------------------------------------------------------------
*
* PUBLIC
*     InitVESA         - Initialize the VESA video manager.
*     UninitVESA       - Uninitialize the VESA video manager.
*     SetVESAMode      - Set the display to the specified VESA video mode.
*     ReadVESAModeInfo - Read the VESA mode information from the video card.
*     SetVESAWindow    - Set VESA window A's start address.
*
****************************************************************************/

#include <stdio.h>
#include <mem.h>
#include <dos.h>

#ifndef __WATCOMC__
#include <pldos32.h>
#include <pharlap.h>
#else
#include "realmode.h"
#endif

#include "vesavid.h"

/*---------------------------------------------------------------------------
 * PRIVATE DECLARATIONS
 *-------------------------------------------------------------------------*/

#ifdef __WATCOMC__
static short _VInfoSel = NULL;
static short _VInfoSeg = NULL;
static short _ModeInfoSel = NULL;
static short _ModeInfoSeg = NULL;

#else  /* __WATCOMC__ */
/* _regs       - Registers used for calling software interrupts.
 * _rpVInfo    - Real pointer to VInfo structure in conventional memory.
 * _rpModeInfo - Real pointer to ModeInfo structure in conventional memory.
 * _VInfo      - Protected mode copy of VInfo structure.
 * _ModeInfo   - Protected mode copy of ModeInfo structure.
 */
static SWI_REGS _regs;
static REALPTR _rpVInfo = NULL;
static REALPTR _rpModeInfo = NULL;
static VESAInfo _VInfo;
static VESAModeInfo _ModeInfo;

#endif /* __WATCOMC__ */


/****************************************************************************
*
* NAME
*     InitVESA - Initialize the VESA video manager.
*
* SYNOPSIS
*     Error = InitVESA()
*
*     long InitVESA(void);
*
* FUNCTION
*     Initialize the VESA video system. Get the VESA information from the
*     VESA video bios.
*
* INPUTS
*     NONE
*
* RESULT
*     Error - 0 if successful, or -1 error/VESA not supported.
*
****************************************************************************/

long InitVESA(void)
{
	#ifdef __WATCOMC__
	union REGS   r;
	struct SREGS sr;
	RMInfo       rmi;
	long         error = -1;

	/* Allocate real-mode memory for VESA structure. */
	r.x.eax = 0x0100;
	r.x.ebx = (sizeof(VESAInfo) + 15) >> 4;
	int386(0x31, &r, &r);

	if (r.x.cflag == 0) {
		_VInfoSel = r.w.dx;
		_VInfoSeg = r.w.ax;

		/* Allocate real-mode memory for VESAModeInfo structure. */
		r.x.eax = 0x0100;
		r.x.ebx = (sizeof(VESAModeInfo) + 15) >> 4;
		int386(0x31, &r, &r);

		if (r.x.cflag == 0) {
			_ModeInfoSel = r.w.dx;
			_ModeInfoSeg = r.w.ax;

			/* Clear VESAInfo structure. */
			memset(MK_PTR(0, _VInfoSeg), 0, sizeof(VESAInfo));

			/* Get VESA information. */
			memset(&rmi, 0, sizeof(RMInfo));
			rmi.eax = 0x4F00;
			rmi.edi = 0;
			rmi.es = _VInfoSeg;

			segread(&sr);
			r.w.ax = 0x0300;
			r.h.bl = 0x10;
			r.h.bh = 0;
			r.w.cx = 0;
			sr.es = FP_SEG(&rmi);
			r.x.edi = FP_OFF(&rmi);
			int386x(0x31, &r, &r, &sr);

			if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) {
				error = 0;
			}
		}
	}


	if (error != 0) {
		UninitVESA();
	}

	return (error);

	#else  /* __WATCOMC__ */

	unsigned short rseg;
	long           paras;
	long           error = -1;

	/* Calculate size of VESAInfo structure in paragraphs */
	paras = (sizeof(VESAInfo) + 15) >> 4;
	
	/* Allocate real-mode memory for VESA structure. */
	if (_dx_real_alloc(paras, (unsigned short *)&rseg,
			(unsigned short *)&paras) == 0) {

		RP_SET(_rpVInfo, 0, rseg);

		/* Calculate size of VESAModeInfo structure in paragraphs */
		paras = (sizeof(VESAModeInfo) + 15) >> 4;

		/* Allocate real-mode memory for VESAModeInfo structure. */
		if (_dx_real_alloc(paras, (unsigned short *)&rseg,
				(unsigned short *)&paras) == 0) {

			RP_SET(_rpModeInfo, 0, rseg);

			/* Clear the input buffer */
			FillRealMem(_rpVInfo, 0, sizeof(VESAInfo));

			/* Set up function call */
			_regs.eax = 0x4F00;
			_regs.edi = RP_OFF(_rpVInfo);
			_regs.es = RP_SEG(_rpVInfo);
			_dx_real_int(0x10, &_regs);

			if (_regs.eax == 0x004F) {
				ReadRealMem(&_VInfo, _rpVInfo, sizeof(VESAInfo));
				error = 0;
			}
		}
	}

	if (error != 0) {
		UninitVESA();		
	}

	return (error);

	#endif /* __WATCOMC__ */
}


/****************************************************************************
*
* NAME
*     UninitVESA - Uninitialize the VESA video manager.
*
* SYNOPSIS
*     UninitVESA()
*
*     void UninitVESA(void);
*
* FUNCTION
*
* INPUTS
*     NONE
*
* RESULT
*     NONE
*
****************************************************************************/

void UninitVESA(void)
{
	#ifdef __WATCOMC__
	union REGS r;

	/* Free VESAInfo structure */
	if (_VInfoSeg != NULL) {
		r.x.eax = 0x0101;
		r.x.edx = _VInfoSel;
		int386(0x31, &r, &r);

		_VInfoSeg = NULL;
		_VInfoSel = NULL;
	}

	/* Free VESAModeInfo structure */
	if (_ModeInfoSeg != NULL) {
		r.x.eax = 0x0101;
		r.x.edx = _VInfoSel;
		int386(0x31, &r, &r);

		_ModeInfoSeg = NULL;
		_ModeInfoSel = NULL;
	}

	#else  /* __WATCOMC__ */

	/* Free VESAInfo structure */
	if (_rpVInfo != NULL) {
		_dx_real_free(RP_SEG(_rpVInfo));
		_rpVInfo = NULL;
	}

	/* Free VESAModeInfo structure */
	if (_rpModeInfo != NULL) {
		_dx_real_free(RP_SEG(_rpModeInfo));
		_rpModeInfo = NULL;
	}
	#endif /* __WATCOMC__ */
}


/****************************************************************************
*
* NAME
*     SetVESAMode - Set the display adapter to the given VESA video mode.
*
* SYNOPSIS
*     VESAModeInfo = SetVESAMode(Mode)
*
*     VESAModeInfo *SetVESAMode(long);
*
* FUNCTION
*     Set the display adapter to the specified VESA video mode.
*
* INPUTS
*     Mode - VESA video mode to set the display to.
*
* RESULT
*     VESAModeInfo - Pointer to VESA mode information structure or NULL if
*                    error.
*
****************************************************************************/

VESAModeInfo *SetVESAMode(long mode)
{
	VESAModeInfo *vminfo;

	/* Get mode info */
	if ((vminfo = ReadVESAModeInfo(mode)) != NULL) {

		/* If the mode is supported, set it. */
		if ((vminfo->Attributes & 0x01) != 0) {

			#ifdef __WATCOMC__
			{
			union REGS r;
			
			r.x.eax = 0x4F02;
			r.x.ebx = mode;
			int386(0x10, &r, &r);

			if (r.x.eax != 0x004F)
				vminfo = NULL;
			}

			#else  /* __WATCOMC__ */

			/* Set up function call */
			_regs.eax = 0x4F02;
			_regs.ebx = mode;
			_dx_real_int(0x10, &_regs);

			if (_regs.eax != 0x004F) {
				vminfo = NULL;
			}
			#endif /* __WATCOMC__ */
		}
	}

	return (vminfo);
}


/****************************************************************************
*
* NAME
*     ReadVESAModeInfo - Read the VESA mode information from the video card.
*
* SYNOPSIS
*     VESAModeInfo = ReadVESAModeInfo(Mode)
*
*     VESAModeInfo *ReadVESAModeInfo(long);
*
* FUNCTION
*     Read information about the specified mode from the VESA video BIOS.
*
* INPUTS
*     Mode - Mode ID to get information about.
*
* RESULT
*     VESAModeInfo - Pointer to VESA mode information structure or NULL if
*                    error.
*
****************************************************************************/

VESAModeInfo *ReadVESAModeInfo(long mode)
{
	VESAModeInfo *vminfo = NULL;

	#ifdef __WATCOMC__
	union REGS   r;
	struct SREGS sr;
	RMInfo       rmi;

	/* Make sure we have real-mode memory. */
	if (_ModeInfoSeg != NULL) {
		memset(MK_PTR(0, _ModeInfoSeg), 0, sizeof(VESAModeInfo));

		/* Get mode information. */
		memset(&rmi, 0, sizeof(RMInfo));
		rmi.eax = 0x4F01;
		rmi.ecx = mode;
		rmi.edi = 0;
		rmi.es = _ModeInfoSeg;

		segread(&sr);
		r.w.ax = 0x0300;
		r.w.bx = 0x0010;
		r.w.cx = 0;
		sr.es = FP_SEG(&rmi);
		r.x.edi = FP_OFF(&rmi);
		int386x(0x31, &r, &r, &sr);

		if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) {
			vminfo = (VESAModeInfo *)MK_PTR(0, _ModeInfoSeg);
		}
	}

	#else  /* __WATCOMC__ */

	/* Make sure we have real-mode memory. */
	if (_rpModeInfo != NULL) {

		/* Clear the input buffer */
		FillRealMem(_rpModeInfo, 0, sizeof(VESAModeInfo));

		/* Set up function call */
		_regs.eax = 0x4F01;
		_regs.ecx = mode;
		_regs.edi = RP_OFF(_rpModeInfo);
		_regs.es = RP_SEG(_rpModeInfo);
		_dx_real_int(0x10, &_regs);

		if (_regs.eax == 0x004F) {
			ReadRealMem(&_ModeInfo, _rpModeInfo, sizeof(VESAModeInfo));
			vminfo = &_ModeInfo;
		}
	}
	#endif /* __WATCOMC__ */

	return (vminfo);
}


/****************************************************************************
*
* NAME
*     SetVESAWindow - Set VESA window A's start address.
*
* SYNOPSIS
*     Error = SetVESAWindow(GrainNum)
*
*     long SetVESAWindow(long);
*
* FUNCTION
*     This function invokes the Window Function, whose address is provided
*     in the VESAModeInfo structure. The 'GrainNum' must be in granularity
*     units as specified in the ModeInfo structure.
*
* INPUTS
*     GrainNum - Granularity number to set window to.
*
* RESULT
*     NONE
*
****************************************************************************/

void SetVESAWindow(long grain)
{
	#ifdef __WATCOMC__
	#else  /* __WATCOMC__ */

  RMC_BLK regp;
	
	regp.eax = 0x4F05;
	regp.ebx = 0x00;
	regp.edx = grain;
	_dx_call_real(_ModeInfo.WinFunc, &regp, 0);
	#endif /* __WATCOMC__ */
}