/*++

Copyright (c) 1993 Microsoft Corporation

Module Name:

    spvidvga.c

Abstract:

    Text setup display support displays with a text mode.

Author:

    Ted Miller (tedm) 2-Aug-1993

Revision History:

--*/



#include "spprecmp.h"
#pragma hdrstop

//
// Vector for text-mode functions.
//

VIDEO_FUNCTION_VECTOR VgaVideoVector =

    {
        VgaDisplayString,
        VgaClearRegion,
        VgaSpecificInit,
        VgaSpecificTerminate,
        VgaSpecificInitPalette
    };



BOOLEAN VgaInitialized = FALSE;

VOID
pSpvgaInitializeFont(
    VOID
    );

VOID
VgaSpecificInit(
    IN PVIDEO_MODE_INFORMATION VideoModes,
    IN ULONG                   NumberOfModes,
    IN ULONG                   ModeSize
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    VIDEO_MODE VideoMode;
    ULONG mode;
    VIDEO_CURSOR_ATTRIBUTES VideoCursorAttributes;

    PVIDEO_MODE_INFORMATION pVideoMode = &VideoModes[0];

    if(VgaInitialized) {
        return;
    }

    //
    // Find standard 80x25 text mode.
    //

    for(mode=0; mode<NumberOfModes; mode++) {

        if(!(pVideoMode->AttributeFlags & VIDEO_MODE_GRAPHICS)
        && (pVideoMode->VisScreenWidth  == 720)
        && (pVideoMode->VisScreenHeight == 400))
        {
            break;
        }

        pVideoMode = (PVIDEO_MODE_INFORMATION) (((PUCHAR) pVideoMode) + ModeSize);
    }

    if(mode == NumberOfModes) {
        KdPrint(("SETUP: Desired video mode not supported!\n"));
        SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_BADMODE, 0);
        while(TRUE);    // loop forever
    }

    VideoVars.VideoModeInfo = *pVideoMode;

    //
    // Set the desired mode.
    //
    VideoMode.RequestedMode = VideoVars.VideoModeInfo.ModeIndex;

    Status = ZwDeviceIoControlFile(
                VideoVars.hDisplay,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_VIDEO_SET_CURRENT_MODE,
                &VideoMode,
                sizeof(VideoMode),
                NULL,
                0
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set mode %u (status = %lx)\n",VideoMode.RequestedMode,Status));
        SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_SETMODE, Status);
        while(TRUE);    // loop forever
    }

    pSpvidMapVideoMemory(TRUE);

    pSpvgaInitializeFont();

    //
    // Shut the hardware cursor off.
    //
    RtlZeroMemory(&VideoCursorAttributes,sizeof(VideoCursorAttributes));
    Status = ZwDeviceIoControlFile(
                VideoVars.hDisplay,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_VIDEO_SET_CURSOR_ATTR,
                &VideoCursorAttributes,
                sizeof(VideoCursorAttributes),
                NULL,
                0
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to turn hw cursor off (status = %lx)\n",Status));
    }

    VgaInitialized = TRUE;

    ASSERT(VideoVars.VideoModeInfo.ScreenStride = 160);
    ASSERT(VideoVars.VideoModeInfo.AttributeFlags & VIDEO_MODE_PALETTE_DRIVEN);
    VideoVars.ScreenWidth  = 80;
    VideoVars.ScreenHeight = VideoVars.VideoModeInfo.VisScreenHeight / FontCharacterHeight;
}


BOOLEAN
VgaSpecificInitPalette(
    VOID
    )
{

    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;

    USHORT InitialPalette[] = {
        16, // 16 entries
        0,  // start with first palette register
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

    Status = ZwDeviceIoControlFile(
                VideoVars.hDisplay,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_VIDEO_SET_PALETTE_REGISTERS,
                InitialPalette,
                sizeof(InitialPalette),
                NULL,
                0
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set palette (status = %lx)\n",Status));
        return(FALSE);
    }

    return (TRUE);
}


VOID
VgaSpecificTerminate(
    VOID
    )

/*++

Routine Description:

    Perform text display specific termination.  This includes

    - unmapping video memory

Arguments:

    None.

Return Value:

--*/

{
    if(VgaInitialized) {

        pSpvidMapVideoMemory(FALSE);
        VgaInitialized = FALSE;
    }
}



VOID
VgaDisplayString(
    IN PWSTR String,
    IN UCHAR Attribute,
    IN ULONG X,                 // 0-based coordinates (character units)
    IN ULONG Y
    )

/*++

Routine Description:

    Write a character or string of characters to the display.

Arguments:

    Character - supplies a single character to be displayed
        at the given position.

    Attribute - supplies the attributes for the character.

    X,Y - specify the character-based (0-based) position of the output.

Return Value:

    None.

--*/

{
    PUCHAR Destination;
    PUCHAR OemString;
    PUCHAR pch;

    ASSERT(X < VideoVars.ScreenWidth);
    ASSERT(Y < VideoVars.ScreenHeight);

    //
    // Convert unicode string to oem, guarding against overflow.
    //
    RtlUnicodeToOemN(
        VideoVars.SpvCharTranslationBuffer,
        VideoVars.SpvCharTranslationBufferSize-1,     // guarantee room for nul
        NULL,
        String,
        (wcslen(String)+1)*sizeof(WCHAR)
        );

    VideoVars.SpvCharTranslationBuffer[VideoVars.SpvCharTranslationBufferSize-1] = 0;

    OemString = VideoVars.SpvCharTranslationBuffer;

    Destination = (PUCHAR)VideoVars.VideoMemoryInfo.FrameBufferBase
                + (Y * VideoVars.VideoModeInfo.ScreenStride)
                + (2*X);

    for(pch=OemString; *pch; pch++) {

        WRITE_REGISTER_UCHAR(Destination  ,*pch);
        WRITE_REGISTER_UCHAR(Destination+1,Attribute);

        Destination += 2;
    }
}



VOID
VgaClearRegion(
    IN ULONG X,
    IN ULONG Y,
    IN ULONG W,
    IN ULONG H,
    IN UCHAR Attribute
    )

/*++

Routine Description:

    Clear out a screen region to a specific attribute.

Arguments:

    X,Y,W,H - specify rectangle in 0-based character coordinates.

    Attribute - Low nibble specifies attribute to be filled in the rectangle
        (ie, the background color to be cleared to).

Return Value:

    None.

--*/


{
    PUSHORT Destination;
    USHORT  Fill;
    ULONG   i,j;

    Destination = (PUSHORT)((PUCHAR)VideoVars.VideoMemoryInfo.FrameBufferBase
                +           (Y * VideoVars.VideoModeInfo.ScreenStride)
                +           (2*X));

    Fill = ((USHORT)VideoVars.AttributeToColorValue[Attribute] << 12) + ' ';

    for(i=0; i<H; i++) {

        for(j=0; j<W; j++) {
            WRITE_REGISTER_USHORT(&Destination[j],Fill);
        }

        Destination += VideoVars.VideoModeInfo.ScreenStride / sizeof(USHORT);
    }
}


VOID
pSpvgaInitializeFont(
    VOID
    )

/*++

Routine Description:

    Set up font support for the VGA.  This assumes that the mode has been
    set to the standard 720x400 VGA text mode.  The current font (in .fnt
    format) is transformed into a vga-loadable font and then loaded into
    the VGA character generator.

Arguments:

    None.

Return Value:

    None.

--*/

{
    USHORT i;
    PVIDEO_LOAD_FONT_INFORMATION DstFont;
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PUCHAR FontBuffer;
    ULONG FontBufferSize;

    FontBufferSize = (256*FontCharacterHeight) + sizeof(VIDEO_LOAD_FONT_INFORMATION);
    FontBuffer = SpMemAlloc(FontBufferSize);

    DstFont = (PVIDEO_LOAD_FONT_INFORMATION)FontBuffer;

    DstFont->WidthInPixels = 9;
    DstFont->HeightInPixels = (USHORT)FontCharacterHeight;
    DstFont->FontSize = 256*FontCharacterHeight;

    //
    // Special case character 0 because it is not in vgaoem.fon, and we don't
    // want to use the default character for it.
    //
    RtlZeroMemory(DstFont->Font,FontCharacterHeight);

    //
    // If i is not a USHORT, then (i<=255) is always TRUE!
    //
    for(i=1; i<=255; i++) {

        UCHAR x;

        if((i < FontHeader->FirstCharacter) || (i > FontHeader->LastCharacter)) {
            x = FontHeader->DefaultCharacter;
        } else {
            x = (UCHAR)i;
        }

        x -= FontHeader->FirstCharacter;

        RtlMoveMemory(
            DstFont->Font + (i*FontCharacterHeight),
            (PUCHAR)FontHeader + FontHeader->Map[x].Offset,
            FontCharacterHeight
            );
    }

    Status = ZwDeviceIoControlFile(
                VideoVars.hDisplay,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_VIDEO_LOAD_AND_SET_FONT,
                FontBuffer,
                FontBufferSize,
                NULL,
                0
                );

    SpMemFree(FontBuffer);

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set vga font (%lx)\n",Status));
        SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_SETFONT, Status);
        while(TRUE);    // loop forever
    }
}