/****************************************************************************
*
*                   SciTech OS Portability Manager Library
*
*  ========================================================================
*
*    The contents of this file are subject to the SciTech MGL Public
*    License Version 1.0 (the "License"); you may not use this file
*    except in compliance with the License. You may obtain a copy of
*    the License at http://www.scitechsoft.com/mgl-license.txt
*
*    Software distributed under the License is distributed on an
*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
*    implied. See the License for the specific language governing
*    rights and limitations under the License.
*
*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
*
*    The Initial Developer of the Original Code is SciTech Software, Inc.
*    All Rights Reserved.
*
*  ========================================================================
*
* Language:     ANSI C
* Environment:  any
*
* Description:  Test program to check the ability to generate real mode
*               interrupts and to be able to obtain direct access to the
*               video memory from protected mode. Compile and link with
*               the appropriate command line for your DOS extender.
*
*               Functions tested:   PM_getBIOSSelector()
*                                   PM_mapPhysicalAddr()
*                                   PM_int86()
*
****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include "pmapi.h"

uchar   *bios;          /* Pointer to BIOS data area        */
uchar   *videoPtr;      /* Pointer to VGA framebuffer       */
void    *stateBuf;      /* Console state save buffer        */

/* Routine to return the current video mode number */

int getVideoMode(void)
{
    return PM_getByte(bios+0x49);
}

/* Routine to set a specified video mode */

void setVideoMode(int mode)
{
    RMREGS r;

    r.x.ax = mode;
    PM_int86(0x10, &r, &r);
}

/* Routine to clear a rectangular region on the display by calling the
 * video BIOS.
 */

void clearScreen(int startx, int starty, int endx, int endy, unsigned char attr)
{
    RMREGS r;

    r.x.ax = 0x0600;
    r.h.bh = attr;
    r.h.cl = startx;
    r.h.ch = starty;
    r.h.dl = endx;
    r.h.dh = endy;
    PM_int86(0x10, &r, &r);
}

/* Routine to fill a rectangular region on the display using direct
 * video writes.
 */

#define SCREEN(x,y) (videoPtr + ((y) * 160) + ((x) << 1))

void fill(int startx, int starty, int endx, int endy, unsigned char c,
    unsigned char attr)
{
    unsigned char   *v;
    int             x,y;

    for (y = starty; y <= endy; y++) {
        v = SCREEN(startx,y);
        for (x = startx; x <= endx; x++) {
            *v++ = c;
            *v++ = attr;
            }
        }
}

/* Routine to display a single character using direct video writes */

void writeChar(int x, int y, unsigned char c, unsigned char attr)
{
    unsigned char *v = SCREEN(x,y);
    *v++ = c;
    *v = attr;
}

/* Routine to draw a border around a rectangular area using direct video
 * writes.
 */

static unsigned char border_chars[] = {
    186, 205, 201, 187, 200, 188        /* double box chars */
    };

void border(int startx, int starty, int endx, int endy, unsigned char attr)
{
    unsigned char   *v;
    unsigned char   *b;
    int             i;

    b = border_chars;

    for (i = starty+1; i < endy; i++) {
        writeChar(startx, i, *b, attr);
        writeChar(endx, i, *b, attr);
        }
    b++;
    for (i = startx+1, v = SCREEN(startx+1, starty); i < endx; i++) {
        *v++ = *b;
        *v++ = attr;
        }
    for (i = startx+1, v = SCREEN(startx+1, endy); i < endx; i++) {
        *v++ = *b;
        *v++ = attr;
        }
    b++;
    writeChar(startx, starty, *b++, attr);
    writeChar(endx, starty, *b++, attr);
    writeChar(startx, endy, *b++, attr);
    writeChar(endx, endy, *b++, attr);
}

int main(void)
{
    int     orgMode;
    PM_HWND hwndConsole;

    printf("Program running in ");
    switch (PM_getModeType()) {
        case PM_realMode:
            printf("real mode.\n\n");
            break;
        case PM_286:
            printf("16 bit protected mode.\n\n");
            break;
        case PM_386:
            printf("32 bit protected mode.\n\n");
            break;
        }

    hwndConsole = PM_openConsole(0,0,0,0,0,true);
    printf("Hit any key to start 80x25 text mode and perform some direct video output.\n");
    PM_getch();

    /* Allocate a buffer to save console state and save the state */
    if ((stateBuf = PM_malloc(PM_getConsoleStateSize())) == NULL) {
        printf("Unable to allocate console state buffer!\n");
        exit(1);
        }
    PM_saveConsoleState(stateBuf,0);
    bios = PM_getBIOSPointer();
    orgMode = getVideoMode();
    setVideoMode(0x3);
    if ((videoPtr = PM_mapPhysicalAddr(0xB8000,0xFFFF,true)) == NULL) {
        printf("Unable to obtain pointer to framebuffer!\n");
        exit(1);
        }

    /* Draw some text on the screen */
    fill(0, 0, 79, 24, 176, 0x1E);
    border(0, 0, 79, 24, 0x1F);
    PM_getch();
    clearScreen(0, 0, 79, 24, 0x7);

    /* Restore the console state on exit */
    PM_restoreConsoleState(stateBuf,0);
    PM_free(stateBuf);
    PM_closeConsole(hwndConsole);

    /* Display useful status information */
    printf("\n");
    printf("Original Video Mode = %02X\n", orgMode);
    printf("BIOS Pointer = %08X\n", (int)bios);
    printf("Video Memory = %08X\n", (int)videoPtr);
    return 0;
}

